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

返回本页常规视图.

收集数据库统计信息

Vertica 基于成本的查询优化器依赖数据统计信息来生成查询计划。如果统计信息不完整或已过时,优化器将倾向于使用不理想的计划来执行查询。

查询表时,Vertica 优化器会按以下方式检查统计信息:

  1. 如果表已分区,则优化器会检查该查询所需的分区最近是否已被分析过。如果已分析,则优化器将检索这些统计信息并使用它们来促进查询计划。

  2. 否则,优化器将使用表级别的统计信息(如果可用)。

  3. 如果没有可用的有效分区或表级别的统计信息,则优化器将假设数据值均匀分布,且所有投影的存储使用量均相等。

统计管理功能

Vertica 提供了两个函数来生成有关表数据的最新统计信息: ANALYZE_STATISTICSANALYZE_STATISTICS_PARTITION 分别收集表级别和分区级别的统计信息。计算统计数据之后,这两个函数将数据存储在数据库编录中。

这两个函数均执行以下操作:

  • 使用 历史查询(最新时期)收集统计信息,不会进行任何锁定。

  • 执行快速数据采样,从而加速具有大量列的相对较小的表的分析。

  • 识别已删除的数据而不是忽略删除标记。

Vertica 还提供了多种函数来帮助您管理数据库统计信息,例如,导出导入统计信息、验证统计信息和删除统计信息。

在收集所需统计信息后,可以运行工作负载分析器来检索有关表现不佳的查询及其根本原因的提示,并获得调整建议。

1 - 收集表统计信息

ANALYZE_STATISTICS 从存储目标表投影的所有节点中收集并聚合数据样本和存储信息。

您可以在多个级别中设置收集的范围:

ANALYZE_STATISTICS 还可以控制它收集的数据样本的大小。

分析所有数据库表

如果 ANALYZE_STATISTICS 未指定表,则它将收集所有数据库表及其投影的统计信息。例如:

=> SELECT ANALYZE_STATISTICS ('');
 ANALYZE_STATISTICS
--------------------
                  0
(1 row)

分析单个表

您可以按以下方式计算单个表的统计信息:

=> SELECT ANALYZE_STATISTICS ('public.store_orders_fact');
 ANALYZE_STATISTICS
--------------------
                  0
(1 row)

当您查询系统表 PROJECTION_COLUMNS 时,它将确认已在所有表列中收集 store_orders_fact 所有投影的统计信息:

=> SELECT projection_name, statistics_type, table_column_name,statistics_updated_timestamp
    FROM projection_columns WHERE projection_name ilike 'store_orders_fact%' AND table_schema='public';
   projection_name    | statistics_type | table_column_name | statistics_updated_timestamp
----------------------+-----------------+-------------------+-------------------------------
 store_orders_fact_b0 | FULL            | product_key       | 2019-04-04 18:06:55.747329-04
 store_orders_fact_b0 | FULL            | product_version   | 2019-04-04 18:06:55.747329-04
 store_orders_fact_b0 | FULL            | store_key         | 2019-04-04 18:06:55.747329-04
 store_orders_fact_b0 | FULL            | vendor_key        | 2019-04-04 18:06:55.747329-04
 store_orders_fact_b0 | FULL            | employee_key      | 2019-04-04 18:06:55.747329-04
 store_orders_fact_b0 | FULL            | order_number      | 2019-04-04 18:06:55.747329-04
 store_orders_fact_b0 | FULL            | date_ordered      | 2019-04-04 18:06:55.747329-04
 store_orders_fact_b0 | FULL            | date_shipped      | 2019-04-04 18:06:55.747329-04
 store_orders_fact_b0 | FULL            | quantity_ordered  | 2019-04-04 18:06:55.747329-04
 store_orders_fact_b0 | FULL            | shipper_name      | 2019-04-04 18:06:55.747329-04
 store_orders_fact_b1 | FULL            | product_key       | 2019-04-04 18:06:55.747329-04
 store_orders_fact_b1 | FULL            | product_version   | 2019-04-04 18:06:55.747329-04
...
(20 rows)

分析表列

在表中,您可以将分析范围缩小到其列的子集。通过这种方式,可以为包含许多列的大型表节省大量的处理开销。如果您经常查询这些表中的特定列,则此功能十分有用。

例如,您可以仅选择那些经常查询的列,而不是收集 store_orders_fact 中所有列的统计信息:product_keyproduct_versionorder_numberquantity_shipped

=> SELECT DROP_STATISTICS('public.store_orders_fact');
=> SELECT ANALYZE_STATISTICS ('public.store_orders_fact', 'product_key, product_version, order_number, quantity_ordered');
 ANALYZE_STATISTICS
--------------------
                  0
(1 row)

如果再次查询 PROJECTION_COLUMNS,它将返回以下结果:

=> SELECT projection_name, statistics_type, table_column_name,statistics_updated_timestamp
    FROM projection_columns WHERE projection_name ilike 'store_orders_fact%' AND table_schema='public';
   projection_name    | statistics_type | table_column_name | statistics_updated_timestamp
----------------------+-----------------+-------------------+------------------------------
 store_orders_fact_b0 | FULL            | product_key       | 2019-04-04 18:09:40.05452-04
 store_orders_fact_b0 | FULL            | product_version   | 2019-04-04 18:09:40.05452-04
 store_orders_fact_b0 | ROWCOUNT        | store_key         | 2019-04-04 18:09:40.05452-04
 store_orders_fact_b0 | ROWCOUNT        | vendor_key        | 2019-04-04 18:09:40.05452-04
 store_orders_fact_b0 | ROWCOUNT        | employee_key      | 2019-04-04 18:09:40.05452-04
 store_orders_fact_b0 | FULL            | order_number      | 2019-04-04 18:09:40.05452-04
 store_orders_fact_b0 | ROWCOUNT        | date_ordered      | 2019-04-04 18:09:40.05452-04
 store_orders_fact_b0 | ROWCOUNT        | date_shipped      | 2019-04-04 18:09:40.05452-04
 store_orders_fact_b0 | FULL            | quantity_ordered  | 2019-04-04 18:09:40.05452-04
 store_orders_fact_b0 | ROWCOUNT        | shipper_name      | 2019-04-04 18:09:40.05452-04
 store_orders_fact_b1 | FULL            | product_key       | 2019-04-04 18:09:40.05452-04
 store_orders_fact_b1 | FULL            | product_version   | 2019-04-04 18:09:40.05452-04
 store_orders_fact_b1 | ROWCOUNT        | store_key         | 2019-04-04 18:09:40.05452-04
 store_orders_fact_b1 | ROWCOUNT        | vendor_key        | 2019-04-04 18:09:40.05452-04
 store_orders_fact_b1 | ROWCOUNT        | employee_key      | 2019-04-04 18:09:40.05452-04
 store_orders_fact_b1 | FULL            | order_number      | 2019-04-04 18:09:40.05452-04
 store_orders_fact_b1 | ROWCOUNT        | date_ordered      | 2019-04-04 18:09:40.05452-04
 store_orders_fact_b1 | ROWCOUNT        | date_shipped      | 2019-04-04 18:09:40.05452-04
 store_orders_fact_b1 | FULL            | quantity_ordered  | 2019-04-04 18:09:40.05452-04
 store_orders_fact_b1 | ROWCOUNT        | shipper_name      | 2019-04-04 18:09:40.05452-04
(20 rows)

在这种情况下,只有对于那些已运行 ANALYZE_statistic,列 statistics_type 才会设置为 FULL。剩余的表列将设置为 ROWCOUNT,表示仅收集这些表列的行统计信息

数据收集百分比

默认情况下,Vertica 从磁盘中收集的样本固定为统计数据的 10%。指定从磁盘中读取的数据百分比,使您在决定样本准确性和速度方面具有更大的控制权。

所收集数据的百分比影响收集时间和准确性:

  • 百分比越小,速度越快,但返回的数据样本越小,这可能会影响直方图准确性。

  • 百分比越大,从磁盘读取的数据越多。数据收集变慢;但数据样本更大,因而直方图准确性更高。

例如:

从 20% 的磁盘中收集 shipping_dimension 的所有投影数据:

=> SELECT ANALYZE_STATISTICS ('shipping_dimension', 20);
 ANALYZE_STATISTICS
-------------------
                 0
(1 row)

通过将 percent 参数设置为 100,收集整个磁盘中的数据:

=> SELECT ANALYZE_STATISTICS ('shipping_dimension', 'shipping_key', 100);
 ANALYZE_STATISTICS
--------------------
                  0
(1 row)

采样大小

ANALYZE_STATISTICS 根据从所有收集的数据中随机选择的一组行来构建列直方图。无论百分比设置如何,该函数始终创建一个统计样本,其中最多包含下列两项中的较小者(近似值):

  • 217 (131,072) 行

  • 1 GB 内存所能容纳的行数

如果列所具有的行数少于最大样本大小,则 ANALYZE_STATISTICS 从磁盘中读取所有行并分析整个列。

下表显示设置为其他百分比的 ANALYZE_STATISTICS 如何从给定列获取统计样本:

2 - 收集分区统计信息

ANALYZE_STATISTICS_PARTITION 收集和聚合指定表中一系列分区的数据样本和存储信息。Vertica 将收集的统计信息写入数据库编录。

例如,下表存储销售数据并按订单日期进行分区:

CREATE TABLE public.store_orders_fact
(
    product_key int,
    product_version int,
    store_key int,
    vendor_key int,
    employee_key int,
    order_number int,
    date_ordered date NOT NULL,
    date_shipped date NOT NULL,
    quantity_ordered int,
    shipper_name varchar(32)
);

ALTER TABLE public.store_orders_fact PARTITION BY date_ordered::DATE GROUP BY CALENDAR_HIERARCHY_DAY(date_ordered::DATE, 2, 2) REORGANIZE;
ALTER TABLE public.store_orders_fact ADD CONSTRAINT fk_store_orders_product FOREIGN KEY (product_key, product_version) references public.product_dimension (product_key, product_version);
ALTER TABLE public.store_orders_fact ADD CONSTRAINT fk_store_orders_vendor FOREIGN KEY (vendor_key) references public.vendor_dimension (vendor_key);
ALTER TABLE public.store_orders_fact ADD CONSTRAINT fk_store_orders_employee FOREIGN KEY (employee_key) references public.employee_dimension (employee_key);

在每个工作日结束时,您可能会调用 ANALYZE_STATISTICS_PARTITION 并收集有关最新(今天)分区的所有数据的统计信息:

=> SELECT ANALYZE_STATISTICS_PARTITION('public.store_orders_fact', CURRENT_DATE::VARCHAR(10), CURRENT_DATE::VARCHAR(10));
 ANALYZE_STATISTICS_PARTITION
------------------------------
                            0
(1 row)

该函数为 store.store_sales_fact 中的最新分区生成一组新统计信息。如果您每天早上查询这张表昨天的销售额,优化器会使用这些统计信息来生成优化的查询计划:

=> EXPLAIN SELECT COUNT(*) FROM public.store_orders_fact WHERE date_ordered = CURRENT_DATE-1;

                                           QUERY PLAN
------------------------------------------------------------------------------------------------------------------------
 QUERY PLAN DESCRIPTION:
 ------------------------------

 EXPLAIN SELECT COUNT(*) FROM public.store_orders_fact WHERE date_ordered = CURRENT_DATE-1;

 Access Path:
 +-GROUPBY NOTHING [Cost: 2, Rows: 1] (PATH ID: 1)
 |  Aggregates: count(*)
 |  Execute on: All Nodes
 | +---> STORAGE ACCESS for store_orders_fact [Cost: 1, Rows: 222(PARTITION-LEVEL STATISTICS)] (PATH ID: 2)
 | |      Projection: public.store_orders_fact_v1_b1
 | |      Filter: (store_orders_fact.date_ordered = '2019-04-01'::date)
 | |      Execute on: All Nodes

缩小收集范围

ANALYZE_STATISTICS 一样,ANALYZE_STATISTICS_PARTITION 允许您将分析范围缩小到 表列的子集。您还可以控制它收集的数据样本的大小。有关这些选项的详细信息,请参阅收集表统计信息

收集多个分区范围的统计信息

如果指定多个分区,它们必须是连续的分区。不同的统计数据集合可以重叠。例如,下表 t1 在列 c1 中已分区:

=> SELECT export_tables('','t1');
                                          export_tables
-------------------------------------------------------------------------------------------------
CREATE TABLE public.t1
(
    a int,
    b int,
    c1 int NOT NULL
)
PARTITION BY (t1.c1);


=> SELECT * FROM t1 ORDER BY c1;
 a  | b  | c1
----+----+----
  1 |  2 |  3
  4 |  5 |  6
  7 |  8 |  9
 10 | 11 | 12
(4 rows)

对于此数据集,您可以在 t1 上调用 ANALYZE_STATISTICS_PARTITION 两次。连续调用会收集分区键的两个重叠范围(3 到 9 和 6 到 12)的统计信息:

=> SELECT drop_statistics_partition('t1', '', '');
 drop_statistics_partition
---------------------------
                         0
(1 row)

=> SELECT analyze_statistics_partition('t1', '3', '9');
 analyze_statistics_partition
------------------------------
                            0
(1 row)

=> SELECT analyze_statistics_partition('t1', '6', '12');
 analyze_statistics_partition
------------------------------
                            0
(1 row)

=> SELECT table_name, min_partition_key, max_partition_key, row_count FROM table_statistics WHERE table_name = 't1';
 table_name | min_partition_key | max_partition_key | row_count
------------+-------------------+-------------------+-----------
 t1         | 3                 | 9                 |         3
 t1         | 6                 | 12                |         3
(2 rows)

如果两个统计信息集合重叠,Vertica 仅存储每个分区范围的最新统计信息。因此,在上一个示例中,Vertica 仅使用来自第二个集合的分区键 6 到 9 的统计信息。

为给定分区键范围收集的统计信息始终取代先前为该范围的子集收集的统计信息。例如,假设 ANALYZE_STATISTICS_PARTITION 调用指定了分区键 3 到 12,则收集的统计信息是之前收集的两组统计信息的超集,因此它将取代两者:


=> SELECT analyze_statistics_partition('t1', '3', '12');
 analyze_statistics_partition
------------------------------
                            0
(1 row)

=> SELECT table_name, min_partition_key, max_partition_key, row_count FROM table_statistics WHERE table_name = 't1';
 table_name | min_partition_key | max_partition_key | row_count
------------+-------------------+-------------------+-----------
 t1         | 3                 | 12                |         4
(1 row)

最后,ANALYZE_STATISTICS_PARTITION 收集分区键 3 到 6 的统计信息。此集合是上一个集合的子集,因此 Vertica 将保留这两个集合,并使用每个集合的最新统计信息:


=> SELECT analyze_statistics_partition('t1', '3', '6');
 analyze_statistics_partition
------------------------------
                            0
(1 row)

=> SELECT table_name, min_partition_key, max_partition_key, row_count FROM table_statistics WHERE table_name = 't1';
 table_name | min_partition_key | max_partition_key | row_count
------------+-------------------+-------------------+-----------
 t1         | 3                 | 12                |         4
 t1         | 3                 | 6                 |         2
(2 rows)

受支持的日期/时间函数

ANALYZE_STATISTICS_PARTITION 可以在分区表达式已指定以下日期/时间函数之一的表中收集分区级统计信息:

要求和限制

ANALYZE_STATISTICS_PARTITION 具有以下要求和限制:

  • 该表必须进行分区,并且不能包含未分区的数据。

  • 表分区表达式必须指定单个列。支持以下表达式:

    • 仅指定列的表达式,即对所有列值进行分区。例如:

      PARTITION BY ship_date GROUP BY CALENDAR_HIERARCHY_DAY(ship_date, 2, 2)
      
    • 如果列是 DATETIMESTAMP/TIMESTAMPTZ,则分区表达式可以指定支持的日期/时间函数,该函数会返回该列或其任何部分,例如月份或年份。例如,以下分区表达式指定对列 order_date 的年份部分进行分区:

      PARTITION BY YEAR(order_date)
      
    • 该表达式对列执行加法或减法。例如:

      PARTITION BY YEAR(order_date) -1
      
  • 表分区表达式不能将指定列强制转换为另一种数据类型。

  • Vertica 不从以下投影中收集任何统计数据:

    • 实时聚合投影和 Top-K 投影

    • 定义这些投影可以在表达式中包含 SQL 函数

3 - 分析行计数

Vertica 允许分别通过 ANALYZE_ROW_COUNTANALYZE_EXTERNAL_ROW_COUNT 获取投影和外部表的行计数。

投影行计数

ANALYZE_ROW_COUNT 为轻量级操作,它会收集一组最少的统计数据并聚合一个投影的行计数,将其保存在数据库编录中。在许多情况下,此数据满足许多优化器对生成最佳查询计划的要求。在以下情况下,将调用此操作:

您可以通过调用 DO_TM_TASK 显式调用 ANALYZE_ROW_COUNT。例如:

=> SELECT DO_TM_TASK('analyze_row_count', 'store_orders_fact_b0');
                                              do_tm_task
------------------------------------------------------------------------------------------------------
 Task: row count analyze
(Table: public.store_orders_fact) (Projection: public.store_orders_fact_b0)

(1 row)

您可以通过设置配置参数 AnalyzeRowCountInterval 更改 Vertica 定期收集行级统计信息的时间间隔。例如,可将收集间隔更改为 1 小时(3600 秒):

=> ALTER DATABASE DEFAULT SET AnalyzeRowCountInterval = 3600;
ALTER DATABASE

外部表行计数

ANALYZE_EXTERNAL_ROW_COUNT 计算外部表的确切行数。优化器将利用该计数来优化用于访问外部表的查询。当外部表参与联接时,这会特别有用。此函数可以让优化器确定较小的表,以便用作联接的内部输入,并有助于提高查询性能。

以下查询计算外部表 loader_rejects 中的确切行数:

=> SELECT ANALYZE_EXTERNAL_ROW_COUNT('loader_rejects');
 ANALYZE_EXTERNAL_ROW_COUNT
----------------------------
                 0

4 - 取消收集统计信息

要取消统计信息收集中期分析,请在 vsql 上执行 CTRL-C 或调用 INTERRUPT_STATEMENT() 函数。

如果要删除指定表或类型的统计信息,请调用 DROP_STATISTICS() 函数。

5 - 获取表统计信息的数据

Vertica 通过两种方式提供给定表及其列和分区的统计信息:

  • 查询优化器将通知您统计信息的可用性,以处理给定的查询。

  • 系统表 PROJECTION_COLUMNS 将显示表列可用的统计信息类型以及上次更新的时间。

查询评估

在谓词选择性估算期间,查询优化器可以确定直方图何时不可用或过期。如果谓词中的值超出了直方图的最大范围,则统计信息已过时。如果没有直方图可用,则没有统计信息可用于计划。

当优化器检测到过时统计信息或没有统计信息时(例如遇到没有相应直方图的列谓词时),优化器会执行以下操作:

  • 显示并记录一条您应运行 ANALYZE_STATISTICS 的消息。

  • 使用一个统计信息条目对 EXPLAIN 生成的查询计划进行注释。

  • 在生成查询计划时忽略过时的统计信息。优化器会根据其他注意事项(例如 FK-PK 约束)来创建查询计划。

例如,以下查询计划片段显示没有统计信息(直方图不可用):

| | +-- Outer -> STORAGE ACCESS for fact [Cost: 604, Rows: 10K (NO STATISTICS)]

以下查询计划片段显示谓词超出了直方图的范围:

| | +-- Outer -> STORAGE ACCESS for fact [Cost: 35, Rows: 1 (PREDICATE VALUE OUT-OF-RANGE)]

PROJECTION_COLUMNS 中的统计数据

系统表 PROJECTION_COLUMNS 中的两列将显示每个表列的统计状态,如下所示:

  • STATISTICS_TYPE 返回此列的统计信息类型,如下所示:NONEROWCOUNTFULL

  • STATISTICS_UPDATED_TIMESTAMP 将返回此列中最后一次收集统计信息的时间。

例如,以下示例架构定义了名为 trades 的表,该表将高度相关的列 bidask 分组在一起,并单独存储 stock 列:

=> CREATE TABLE trades (stock CHAR(5), bid INT, ask INT);
=> CREATE PROJECTION trades_p (
     stock ENCODING RLE, GROUPED(bid ENCODING DELTAVAL, ask))
     AS (SELECT * FROM trades) ORDER BY stock, bid;
=> INSERT INTO trades VALUES('acme', 10, 20);
=> COMMIT;

PROJECTION_COLUMNS 表中查询 trades 表:

=> SELECT table_name AS table, projection_name AS projection, table_column_name AS column, statistics_type, statistics_updated_timestamp AS last_updated
     FROM projection_columns WHERE table_name = 'trades';
 table  | projection  | column | statistics_type | last_updated
--------+-------------+--------+-----------------+--------------
 trades | trades_p_b0 | stock  | NONE            |
 trades | trades_p_b0 | bid    | NONE            |
 trades | trades_p_b0 | ask    | NONE            |
 trades | trades_p_b1 | stock  | NONE            |
 trades | trades_p_b1 | bid    | NONE            |
 trades | trades_p_b1 | ask    | NONE            |
(6 rows)

statistics_type 列将为 trades 表中的所有列返回 NONE,而 statistics_updated_timestamp 则为空,因为尚未收集此表中的统计信息。

现在,请对 stock 列运行 ANALYZE_STATISTICS

=> SELECT ANALYZE_STATISTICS ('public.trades', 'stock');
 ANALYZE_STATISTICS
--------------------
                  0
(1 row)

现在,当您查询 PROJECTION_COLUMNS 时,它将返回以下结果:

=> SELECT table_name AS table, projection_name AS projection, table_column_name AS column, statistics_type, statistics_updated_timestamp AS last_updated
     FROM projection_columns WHERE table_name = 'trades';
 table  | projection  | column | statistics_type |         last_updated
--------+-------------+--------+-----------------+-------------------------------
 trades | trades_p_b0 | stock  | FULL            | 2019-04-03 12:00:12.231564-04
 trades | trades_p_b0 | bid    | ROWCOUNT        | 2019-04-03 12:00:12.231564-04
 trades | trades_p_b0 | ask    | ROWCOUNT        | 2019-04-03 12:00:12.231564-04
 trades | trades_p_b1 | stock  | FULL            | 2019-04-03 12:00:12.231564-04
 trades | trades_p_b1 | bid    | ROWCOUNT        | 2019-04-03 12:00:12.231564-04
 trades | trades_p_b1 | ask    | ROWCOUNT        | 2019-04-03 12:00:12.231564-04
(6 rows)

这一次,查询结果将出现几项变化:

6 - 统计信息收集最佳实践

当以下一个或多个条件成立时,您应调用 ANALYZE_STATISTICS 或 ANALYZE_STATISTICS_PARTITION:

  • 首次加载大量数据。

  • 刷新了新的投影。

  • 行数发生显著变化。

  • 向表中添加了新列。

  • 列最小值/最大值发生显著变化。

  • 添加了具有参照完整性约束的新主键值。应当重新分析主键和外键表。

  • 表与所联接到的表的相对大小发生了显著变化,例如,表之前比其他表大五十倍,现在只比其他表大五倍。

  • 重新计算直方图所必需的数据分布情况发生了显著偏差,例如,某个事件导致特定股票的交易量变得异常高。

  • 数据库数据库长时间处于非活动状态。

开销注意事项

运行 ANALYZE_STATISTICS 是一种有效但可能需要运行较长时间的操作。在生产环境中,您可以将其与查询和加载操作并发运行。然而,参数会给系统资源(CPU 和内存)带来很大开销,以致于给查询和加载操作带来不利影响。为了最大限度地减少开销,请考虑在那些受到重大活动影响的分区上调用 ANALYZE_STATISTICS_PARTITIONS(通常是指最近加载的分区,其中包括表的活动分区)。您可以指定表列的子集(通常是查询次数最多的列),进一步缩小这两个函数的范围。

相关工具

您可调用 ANALYZE_WORKLOAD,并利用它返回的优化建议诊断和解决与统计信息有关的许多问题。如果更新统计信息后依然发现查询性能不佳,请通过 Database Designer 运行询问,然后选择增量作为设计类型。