这是本节的多页打印视图。 点击此处打印.

返回本页常规视图.

对数据库性能执行分析

可以分析数据库操作以评估性能。分析可以提供如下信息:

  • 分配给每个运算符的内存量和线程数。

  • 数据在查询执行期间的不同时间点流经每个运算符的方式。

  • 查询是否为网络绑定。

分析数据有助于为数据库设计注意事项提供有价值的输入,例如如何最好地对投影进行分段和排序,或促进在群集中更好地分布数据处理。

例如,分析可能会显示数据倾斜,其中某些节点处理的数据比其他节点多。系统表 执行_引擎_配置文件 中的 rows produced 计数器显示每个运算符所处理的行数。比较给定运算符跨所有节点的 rows produced 可以揭示是否存在数据倾斜问题。

此部分中的主题重点介绍如何通过 vsql 语句获取配置文件数据。您还可以在管理控制台中查看分析数据。

1 - 启用分析

可以在以下三个范围内启用分析:

Vertica 元函数 SHOW_PROFILING_CONFIG 显示是否在全局范围和会话范围内启用分析。在以下示例中,该函数显示在当前会话的所有类别中禁用了分析,并在所有类别中全局启用了分析:

=> SELECT SHOW_PROFILING_CONFIG();
SHOW_PROFILING_CONFIG ------------------------------------------
 Session Profiling: Session off, Global on
 EE Profiling:      Session off, Global on
 Query Profiling:   Session off, Global on
(1 row)

全局分析

当为给定类别启用或禁用全局分析时,该设置将在所有数据库会话中保留。可以使用 ALTER DATABASE 设置全局分析,如下所示:

ALTER DATABASE db-spec SET profiling-category = {0 | 1}

profiling-category 使用以下实参之一指定分析类别:

例如,以下语句对当前 (DEFAULT) 数据库全局启用查询分析:

=> ALTER DATABASE DEFAULT SET GlobalQueryProfiling = 1;

会话分析

可以为当前会话启用会话分析,并一直持续到您显式禁用分析或会话结束。可以使用以下 Vertica 元函数设置会话分析:

profiling-type 使用以下实参之一指定要启用或禁用的分析数据的类型:

例如,以下语句为每个查询的执行运行启用会话范围分析:

=> SELECT ENABLE_PROFILING('ee');
   ENABLE_PROFILING
----------------------
 EE Profiling Enabled
(1 row)

语句分析

可以通过在单个 SQL 语句前面加上关键字 PROFILE,来启用对这些语句的分析。可以分析 SELECT 语句或任何 DML 语句(例如 INSERTUPDATECOPYMERGE)。有关详细信息,请参阅分析单个语句

分析范围的优先级

Vertica 按优先级降序在以下范围内检查会话和查询分析:

  1. 语句分析(最高)

  2. 会话分析(如果启用了全局分析,则忽略)

  3. 全局分析(最低)

无论查询和会话分析设置如何,Vertica 始终在相关系统表中保存最少量的分析数据:QUERY_PROFILESQUERY_PLAN_PROFILESSESSION_PROFILES

对于执行引擎分析,Vertica 首先检查配置参数 SaveDCEEProfileThresholdUS 的设置。如果查询运行时间超过指定阈值(默认为 60 秒),Vertica 会收集该查询的执行引擎数据,并将其保存到系统表 QUERY_CONSUMPTION执行_引擎_配置文件 中。仅当查询的持续时间低于阈值时,Vertica 才使用其他范围(语句、会话、全局)的分析设置。

2 - 分析单个语句

要分析单个语句,请在其前面加上 PROFILE 前缀。可以分析查询 (SELECT) 语句或任何 DML 语句,例如 INSERTUPDATECOPYMERGE。语句返回分析摘要:

  • 分析标识符 transaction_idstatement_id

  • 查询的启动内存

  • 所需的总内存

例如:

=> PROFILE SELECT customer_name, annual_income FROM public.customer_dimension
   WHERE (customer_gender, annual_income) IN (SELECT customer_gender, MAX(annual_income)
   FROM public.customer_dimension GROUP BY customer_gender);NOTICE 4788:  Statement is being profiled
HINT:  Select * from v_monitor.execution_engine_profiles where transaction_id=45035996274760535 and statement_id=1;
NOTICE 3557:  Initiator memory for query: [on pool general: 2783428 KB, minimum: 2312914 KB]
NOTICE 5077:  Total memory required by query: [2783428 KB]
  customer_name   | annual_income
------------------+---------------
 James M. McNulty |        999979
 Emily G. Vogel   |        999998
(2 rows)

可以使用分析标识符 transaction_idstatement_id 从系统表 执行_引擎_配置文件QUERY_PLAN_PROFILES 中获取此查询的详细分析信息。还可以使用这些标识符从系统表 QUERY_CONSUMPTION 中获取资源消耗数据。

例如:

=> SELECT path_id, path_line::VARCHAR(68), running_time FROM v_monitor.query_plan_profiles
    WHERE transaction_id=45035996274760535 AND statement_id=1 ORDER BY path_id, path_line_index;
 path_id |                              path_line                               |  running_time
---------+----------------------------------------------------------------------+-----------------
       1 | +-JOIN HASH [Semi] [Cost: 631, Rows: 25K (NO STATISTICS)] (PATH ID:  | 00:00:00.052478
       1 | |  Join Cond: (customer_dimension.customer_gender = VAL(2)) AND (cus |
       1 | |  Materialize at Output: customer_dimension.customer_name           |
       1 | |  Execute on: All Nodes                                             |
       2 | | +-- Outer -> STORAGE ACCESS for customer_dimension [Cost: 30, Rows | 00:00:00.051598
       2 | | |      Projection: public.customer_dimension_b0                    |
       2 | | |      Materialize: customer_dimension.customer_gender, customer_d |
       2 | | |      Execute on: All Nodes                                       |
       2 | | |      Runtime Filters: (SIP1(HashJoin): customer_dimension.custom |
       4 | | | +---> GROUPBY HASH (GLOBAL RESEGMENT GROUPS) (LOCAL RESEGMENT GR | 00:00:00.050566
       4 | | | |      Aggregates: max(customer_dimension.annual_income)         |
       4 | | | |      Group By: customer_dimension.customer_gender              |
       4 | | | |      Execute on: All Nodes                                     |
       5 | | | | +---> STORAGE ACCESS for customer_dimension [Cost: 30, Rows: 5 | 00:00:00.09234
       5 | | | | |      Projection: public.customer_dimension_b0                |
       5 | | | | |      Materialize: customer_dimension.customer_gender, custom |
       5 | | | | |      Execute on: All Nodes                                   |
(17 rows)

3 - 标记语句

要快速识别查询和其他操作以进行分析和调试,请包含 LABEL 提示。

LABEL 提示在以下语句中有效:

例如:

SELECT /*+label(myselectquery)*/ COUNT(*) FROM t;
INSERT /*+label(myinsertquery)*/ INTO t VALUES(1);

向一个或多个语句添加标签后,查询 QUERY_PROFILES 系统表,以查看哪些查询是使用您提供的标签运行的。QUERY_PROFILES 系统表 IDENTIFIER 列返回您之前分配给语句的用户定义标签。您还可以获取其他特定于查询的数据,这些数据可用于查询其他系统表,例如事务 ID。

例如:

=> SELECT identifier, query FROM query_profiles;
     identifier | query
 ---------------+-----------------------------------------------------------
  myselectquery | SELECT /*+label(myselectquery)*/ COUNT(*) FROM t;
  myinsertquery | INSERT /*+label(myinsertquery)*/ INTO t VALUES(1);
  myupdatequery | UPDATE /*+label(myupdatequery)*/ t SET a = 2 WHERE a = 1;
  mydeletequery | DELETE /*+label(mydeletequery)*/ FROM t WHERE a = 1;
                | SELECT identifier, query from query_profiles;
(5 rows)

4 - 实时分析

可以通过查询系统表 执行_引擎_配置文件 来监控长时间运行的查询的执行。此表包含可用于内部操作和用户语句的分析计数器。可以使用 Linux watch 命令频繁地查询此表。

对实时分析数据的查询需要事务 ID。如果事务执行多个语句,则查询还需要语句 ID 来标识所需的语句。如果分析单个查询,则查询会返回语句的事务和语句 ID。还可以从 SYSTEM_SESSIONS 系统表中获取事务和语句 ID。

分析计数器

执行_引擎_配置文件 系统表包含可用于内部操作和用户语句的分析计数器。实时分析计数器可用于所有语句的执行,包括 合并恢复刷新等内部操作。除非在特定 SQL 语句中使用 PROFILE 关键字显式地启用分析,或者普遍地为数据库和/或当前会话启用分析,否则在语句完成后分析计数器将不可用。

有用的计数器包括:

  • 执行时间 (µs)

  • 生成的行数

  • 合并阶段总数

  • 已完成的合并阶段数

  • 当前临时文件大小(字节数)

可以通过查询 执行_引擎_配置文件 来查看所有可用的计数器:

=> SELECT DISTINCT(counter_name) FROM EXECUTION_ENGINE_PROFILES;

若要监控分析计数器,可使用检索到的事务 ID (a000000000027) 运行类似如下所示的命令:

=> SELECT * FROM execution_engine_profiles
   WHERE TO_HEX(transaction_id)='a000000000027'
   AND counter_name = 'execution time (us)'
   ORDER BY node_name, counter_value DESC;

以下示例将查找每个节点上执行时间最长的运算符:

=> SELECT node_name, operator_name, counter_value execution_time_us FROM V_MONITOR.EXECUTION_ENGINE_PROFILES WHERE counter_name='execution time (us)' LIMIT 1 OVER(PARTITION BY node_name ORDER BY counter_value DESC);
    node_name     | operator_name | execution_time_us
------------------+---------------+-------------------
 v_vmart_node0001 | Join          |            131906
 v_vmart_node0002 | Join          |            227778
 v_vmart_node0003 | NetworkSend   |            524080
(3 rows)

Linux watch 命令

可以使用 Linux watch 命令经常地监控长时间运行的查询。常见使用案例包括:

  • 观察每个 Vertica 群集节点上查询计划内的运算符执行情况。

  • 监控可能在各群集节点之间不平衡的工作负载,例如,一些节点处于空闲状态,而其他节点却处于活动状态。这种不平衡情况可能是由数据偏离或硬件问题所导致。

在以下示例中,watch 将查询每个节点上执行时间最长的运算符。该命令指定每秒重新执行一次查询:

watch -n 1 -d "vsql VMart -c\"SELECT node_name, operator_name, counter_value execution_time_us
FROM v_monitor.execution_engine_profiles WHERE counter_name='execution time (us)'
LIMIT 1 OVER(PARTITION BY node_name ORDER BY counter_value DESC);

Every 1.0s: vsql VMart -c"SELECT node_name, operator_name, counter_value execution_time_us FROM v_monitor.execu...  Thu Jan 21 15:00:44 2016

    node_name     | operator_name | execution_time_us
------------------+---------------+-------------------
 v_vmart_node0001 | Root          |            110266
 v_vmart_node0002 | UnionAll      |             38932
 v_vmart_node0003 | Scan          |             22058
(3 rows)

5 - 分析查询资源消耗

Vertica 收集所有查询(包括那些失败的查询)的资源使用情况数据,并将这些数据汇总到系统表 QUERY_CONSUMPTION 中。这些数据包括有关每个查询的以下信息:

  • 时钟持续时间

  • 消耗的 CPU 周期

  • 保留和分配的内存

  • 发送和接收的网络字节数

  • 读取和写入的磁盘字节数

  • 溢出的字节数

  • 分配的线程数

  • 输出到客户端的行数

  • 读取和写入的行数

可以通过查询的事务和语句 ID 获取有关各查询的信息。TRANSACTION_IDSTATEMENT_ID 列为每个查询语句提供唯一的键。

例如,会分析以下查询:

=> PROFILE SELECT pd.category_description AS 'Category', SUM(sf.sales_quantity*sf.sales_dollar_amount) AS 'Total Sales'
     FROM store.store_sales_fact sf
     JOIN public.product_dimension pd ON pd.product_version=sf.product_version AND pd.product_key=sf.product_key
     GROUP BY pd.category_description;
NOTICE 4788:  Statement is being profiled
HINT:  Select * from v_monitor.execution_engine_profiles where transaction_id=45035996274751822 and statement_id=1;
NOTICE 3557:  Initiator memory for query: [on pool general: 256160 KB, minimum: 256160 KB]
NOTICE 5077:  Total memory required by query: [256160 KB]
             Category             | Total Sales
----------------------------------+-------------
 Non-food                         |  1147919813
 Misc                             |  1158328131
 Medical                          |  1155853990
 Food                             |  4038220327
(4 rows)

可以使用 Vertica 返回的事务和语句 ID 从 QUERY_CONSUMPTION 获取分析数据,例如,通过网络为给定查询发送的总字节数:

=> SELECT NETWORK_BYTES_SENT FROM query_consumption WHERE transaction_id=45035996274751822 AND statement_id=1;
 NETWORK_BYTES_SENT
--------------------
             757745
(1 row)

QUERY_CONSUMPTION 与 EXECUTION _ENGINE_PROFILES

QUERY_CONSUMPTION 包括它从 执行_引擎_配置文件 中的计数器汇总的数据。在前面的示例中,NETWORK_BYTES_SENT 汇总了可通过 EXECUTION_ENGINE_PROFILES 中的多个计数器访问的数据。EXECUTION_ENGINE_PROFILES 的等效查询如下所示:

=> SELECT operator_name, counter_name, counter_tag, SUM(counter_value) FROM execution_engine_profiles
     WHERE transaction_id=45035996274751822 AND statement_id=1 AND counter_name='bytes sent'
     GROUP BY ROLLUP (operator_name, counter_name, counter_tag) ORDER BY 1,2,3, GROUPING_ID();
 operator_name | counter_name |          counter_tag           |  SUM
---------------+--------------+--------------------------------+--------
 NetworkSend   | bytes sent   | Net id 1000 - v_vmart_node0001 | 252471
 NetworkSend   | bytes sent   | Net id 1000 - v_vmart_node0002 | 251076
 NetworkSend   | bytes sent   | Net id 1000 - v_vmart_node0003 | 253717
 NetworkSend   | bytes sent   | Net id 1001 - v_vmart_node0001 |    192
 NetworkSend   | bytes sent   | Net id 1001 - v_vmart_node0002 |    192
 NetworkSend   | bytes sent   | Net id 1001 - v_vmart_node0003 |      0
 NetworkSend   | bytes sent   | Net id 1002 - v_vmart_node0001 |     97
 NetworkSend   | bytes sent   |                                | 757745
 NetworkSend   |              |                                | 757745
               |              |                                | 757745
(10 rows)

QUERY_CONSUMPTIONEXECUTION_ENGINE_PROFILES 也有如下不同:

  • QUERY_CONSUMPTION 保存来自所有查询的数据,无论其持续时间如何,也无论是否显式分析它们。它还包括有关不成功查询的数据。

  • EXECUTION_ENGINE_PROFILES 仅包括来自执行长度超过设定阈值或您显式分析的查询的数据。它也不包括不成功查询的数据。

6 - 分析查询计划

要通过查询计划及其各个 路径监控实时数据流,请查询以下系统表:

执行_引擎_配置文件QUERY_PLAN_PROFILES。这些表提供了有关 Vertica 如何执行查询计划及其各个 路径的数据:

每个查询计划路径都具有唯一的 ID,如以下 EXPLAIN 输出片段中所示。

每个表都提供特定于路径的数据。例如,QUERY_PLAN_PROFILES 为每个路径提供高级别数据,包括:

  • 查询操作执行时长

  • 路径操作所用的内存大小

  • 通过网络发送/接收的数据大小

例如,您可能观察到 GROUP BY HASH 操作的执行时间为 0.2 秒,使用的内存为 100MB。

要求

实时分析至少需要待监控事务的 ID。如果事务包括多个语句,则还需要语句 ID。可以通过对要分析的查询发出 PROFILE 来获取语句和事务 ID。随后可使用这些标识符来查询系统表 EXECUTION_ENGINE_PROFILESQUERY_PLAN_PROFILES

有关详细信息,请参阅分析单个语句

6.1 - 获取小型查询的查询计划状态

存储在 执行_引擎_配置文件 系统表中的实时分析计数器可用于当前执行的所有语句,包括 合并等内部操作。

如果满足以下任一条件,则可以在查询执行完成后使用分析计数器:

  • 查询是通过 PROFILE 命令运行的

  • Vertica 元函数 ENABLE_PROFILING 启用了系统范围的分析。

  • 查询运行了两秒以上。

分析计数器保存在 EXECUTION_ENGINE_PROFILES 系统表中,直至超出存储配额。

例如:

  1. 分析查询以从 EXECUTION_ENGINE_PROFILES 获取 transaction_idstatement_id。例如:

    => PROFILE SELECT * FROM t1 JOIN t2 ON t1.x = t2.y;
    NOTICE 4788:  Statement is being profiled
    HINT:  Select * from v_monitor.execution_engine_profiles where transaction_id=45035996273955065 and statement_id=4;
    NOTICE 3557:  Initiator memory for query: [on pool general: 248544 KB, minimum: 248544 KB]
    NOTICE 5077:  Total memory required by query: [248544 KB]
     x | y |   z
    ---+---+-------
     3 | 3 | three
    (1 row)
    
  2. 查询系统表 QUERY_PLAN_PROFILES

    => SELECT ... FROM query_plan_profiles
         WHERE transaction_id=45035996273955065 and statement_id=4;
         ORDER BY transaction_id, statement_id, path_id, path_line_index;
    

6.2 - 获取大型查询的查询计划状态

实时分析用于监控大型(长时间运行的)查询。请按照以下步骤监控大型查询的计划:

  1. 通过查询系统表 CURRENT_SESSION 获取要分析的查询计划的语句和事务 ID:

    => SELECT transaction_id, statement_id from current_session;
      transaction_id   | statement_id
    -------------------+--------------
     45035996273955001 |            4
    (1 row)
    
  2. 运行查询:

    => SELECT * FROM t1 JOIN t2 ON x=y JOIN ext on y=z;
    
  3. 查询系统表 QUERY_PLAN_PROFILES,并对 transaction_id、statement_id、path_id 和 path_line_index 列进行排序。

    => SELECT ... FROM query_plan_profiles WHERE transaction_id=45035996273955001 and statement_id=4
       ORDER BY transaction_id, statement_id, path_id, path_line_index;
    

也可以使用 Linux watch 命令监控长时间运行的查询(请参阅实时分析)。

示例

以下一系列命令会为长时间运行的查询创建表,然后查询系统表 QUERY_PLAN_PROFILES

  1. 创建名为 longq 的表:

    => CREATE TABLE longq(x int);
    CREATE TABLE
    => COPY longq FROM STDIN;
    Enter data to be copied followed by a newline.
    End with a backslash and a period on a line by itself.
    >> 1
    >> 2
    >> 3
    >> 4
    >> 5
    >> 6
    >> 7
    >> 8
    >> 9
    >> 10
    >> \.
    => INSERT INTO longq SELECT f1.x+f2.x+f3.x+f4.x+f5.x+f6.x+f7.x
          FROM longq f1
          CROSS JOIN longq f2
          CROSS JOIN longq f3
          CROSS JOIN longq f4
          CROSS JOIN longq f5
          CROSS JOIN longq f6
          CROSS JOIN longq f7;
      OUTPUT
    ----------
     10000000
    (1 row)
    => COMMIT;
    COMMIT
    
  2. 通过使用 vsql \o 命令禁止查询在终端窗口上输出:

    => \o /home/dbadmin/longQprof
    
  3. 查询新表:

    => SELECT * FROM longq;
    
  4. 获取事务和语句 ID:

    => SELECT transaction_id, statement_id from current_session;
      transaction_id   | statement_id
    -------------------+--------------
     45035996273955021 |            4
    (1 row)
    
  5. 关闭 \o 命令以便 Vertica 继续将查询计划信息保存到您已指定的文件。或者,使其保持打开状态并在查询系统表 QUERY_PLAN_PROFILES 后检查该文件。

    => \o
    
  6. 查询系统表 QUERY_PLAN_PROFILES

    => SELECT
         transaction_id,
         statement_id,
         path_id,
         path_line_index,
         is_executing,
         running_time,
         path_line
       FROM query_plan_profiles
       WHERE transaction_id=45035996273955021 AND statement_id=4
       ORDER BY transaction_id, statement_id, path_id, path_line_index;
    

6.3 - 改善 QUERY_PLAN_PROFILES 输出的可读性

QUERY_PLAN_PROFILES 表的输出会因 path_line 列而非常宽。为提高可读性,使用下面的一个或多个选项查询 QUERY_PLAN_PROFILES

  • transaction_idstatement_idpath_idpath_line_index 对输出排序:

    => SELECT ... FROM query_plan_profiles
         WHERE ...
        ORDER BY transaction_id, statement_id, path_id, path_line_index;
    
  • 使用列别名减小列宽:

    => SELECT statement_id AS sid, path_id AS id, path_line_index AS order,
         is_started AS start, is_completed AS end, is_executing AS exe,
         running_time AS run, memory_allocated_bytes AS mem,
         read_from_disk_bytes AS read, received_bytes AS rec,
         sent_bytes AS sent, FROM query_plan_profiles
         WHERE transaction_id=45035996273910558 AND statement_id=3
         ORDER BY transaction_id, statement_id, path_id, path_line_index;
    
  • 使用 vsql \o 命令将 EXPLAIN 输出重定向至文件:

    => \o /home/dbadmin/long-queries
    => EXPLAIN SELECT * FROM customer_dimension;
    => \o
    

6.4 - 管理查询分析数据

Vertica 会一直保留查询数据,直至超出表的存储配额,此时它会自动清除最旧的查询,以便为新查询腾出空间。还可以通过调用以下函数之一来清除已分析的数据:

  • CLEAR_PROFILING 会从内存中清除已分析的数据。例如,下列命令清除常规查询运行信息的分析,如已用的查询字符串和查询持续时间。

    => SELECT CLEAR_PROFILING('query');
    
  • CLEAR_DATA_COLLECTOR 会清除与数据收集器表和函数相关的所有内存及磁盘记录,并重置 DATA_COLLECTOR 系统表中的收集统计信息。

  • FLUSH_DATA_COLLECTOR 会一直等到内存日志移动到磁盘,然后刷新数据收集器,使 DataCollector 日志与磁盘存储同步。

配置数据保留策略

Vertica 按照配置的保留策略的规定保留它收集的历史数据

6.5 - 分析次优查询计划

如果分析未涵盖不理想的查询,调用下列函数之一可能有所帮助:

  • ANALYZE_WORKLOAD 分析系统表中保存的系统信息,并根据统计信息、系统和 数据收集器事件以及数据库-表-投影设计的组合提供优化建议。

  • ANALYZE_STATISTICS 从存储与指定表或列相关联的投影的所有节点中收集并聚合数据示例和存储信息。

您还可以通过 Database Designer 运行查询。请参阅增量设计

7 - 计数器信息的示例视图

EXECUTION_ENGINE_PROFILES 表包含每个分析计数器的数据作为表中的一行。例如,执行时间 (us) 计数器在一个行中,该行生成的计数器在另一行中。由于有许多不同的分析计数器,因此每个运算符存在多行分析数据。系统默认安装一些示例视图,以简化查看分析计数器的过程。

运行脚本以创建示例视图

以下脚本将创建 v_demo 架构并将视图置于该架构中。

/opt/vertica/scripts/demo_eeprof_view.sql

使用示例视图查看计数器值

每个分析计数器都有一个视图,用于简化查看单个计数器值的过程。例如,若要查看所有运算符的执行时间,请从数据库发出以下命令:

=> SELECT * FROM v_demo.eeprof_execution_time_us;

要查看可用于所有分析查询的所有计数器值:

=> SELECT * FROM v_demo.eeprof_counters;

要选择可用于所有分析查询的所有不同运算符:

=> SELECT * FROM v_demo.eeprof_operators;

合并示例视图

以下视图可以合并:

=> SELECT * FROM v_demo.eeprof_execution_time_us
    NATURAL LEFT OUTER JOIN v_demo.eeprof_rows_produced;

要查看执行时间和为特定事务生成的行以及按每个节点执行时间所排列的 statement_id

=> SELECT * FROM v_demo.eeprof_execution_time_us_rank
    WHERE transaction_id=45035996273709699
    AND statement_id=1
    ORDER BY transaction_id, statement_id, node_name, rk;

要查看按每个节点执行时间所排列的前五个运算符:

=> SELECT * FROM v_demo.eeprof_execution_time_us_rank
    WHERE transaction_id=45035996273709699
    AND statement_id=1 AND rk<=5
    ORDER BY transaction_id, statement_id, node_name, rk;