这是本节的多页打印视图。
点击此处打印.
返回本页常规视图.
投影
与传统数据库将数据存储在表中不同,Vertica 会将表数据以物理方式存储在
投影(即表列的集合)中。
投影以优化查询执行的格式存储数据。与实体化视图类似,它们会将结果集存储在磁盘上,而不是每次在查询中使用时都进行计算。Vertica 会使用更新数据或新数据自动刷新这些结果集。
投影提供了以下优势:
-
对数据进行压缩和编码,以减少存储空间。Vertica 也会尽可能使用编码数据表示形式执行操作,以避免解码成本。这种结合了压缩和编码的方法可在优化磁盘空间的同时最大限度地提高查询性能。
-
有助于将数据分布到整个数据库群集中。根据大小不同,投影可通过分段或复制形式分布在各个群集节点中。例如,大型表的投影可分段后分布到所有节点上。小型表的未分段投影可以复制到所有节点上。
-
对最终用户透明。Vertica 查询优化器会自动选取最佳投影来执行给定查询。
-
提供高可用性和恢复能力。Vertica 会将表列复制到群集中的至少 K+1 个节点上。如果一台计算机在
K-Safe 环境中发生故障,则数据库会使用其余节点上复制的数据继续运行。当该节点恢复正常运行时,它会自动查询其他节点,以恢复数据和丢失的对象。有关详细信息,请参阅
使用容错组的高可用性和
使用投影的高可用性。
1 - 投影类型
Vertica 表通常具有多个投影,每个投影都定义为包含不同的内容。对于给定表,投影内容的范围和组织可能会有所不同。这些差异通常可以分为以下几种投影类型:
超投影
对于数据库中的每个表,Vertica 需要至少一个包含表中所有列的超投影。在没有查询特定投影的情况下,Vertica 使用表的超投影,它可以支持任何查询和 DML 操作。
在某些条件下,Vertica 会在创建表时立即自动创建表的超投影。首次向该表加载数据时,如果其中不存在超投影,Vertica 还将创建一个超投影。
CREATE PROJECTION
如果它指定包括所有表列,则可以创建超投影。一个表可以有多个超投影。
虽然超投影可以支持对表的所有查询,但它们并不能促进特定查询的最佳执行。
查询特定的投影
查询特定的投影是仅包含处理给定查询所需的表列子集的投影。查询特定的投影可显著提高查询的性能,因为已针对查询优化这些投影。
聚合投影
当使用已包含聚合数据的投影时,包括表达式或聚合函数(例如 SUM 和 COUNT)的查询可以更有效地执行。对于涉及大量数据的查询,情况更是如此。
Vertica 提供几种类型的投影来存储从聚合函数或表达式中返回的数据。
-
实时聚合投影:包含通过锚表中的列聚合列值的列的投影。您还可以定义包括 用户定义的变换函数 的实时聚合投影。
-
Top-K 投影:一种实时聚合投影,用于从选定行的一个分区中返回前 k 行。创建一个满足 Top-K 查询条件的 Top-K 投影。
-
预聚合 UDTF 结果的投影:调用用户定义的转换函数 (UDTF) 的实时聚合投影。为了在查询这种类型的投影时最大限度降低开销,Vertica 会在后台处理 UDTF 函数,并将其结果存储在磁盘上。
-
包含表达式的投影:具有通过锚表列计算列值的列的投影。
有关详细信息,请参阅投影中的预聚合数据。
2 - 创建投影
Vertica 支持两种投影创建方法:Database Designer 和 CREATE PROJECTION 语句。
使用 Database Designer 创建投影
Vertica 建议使用 Database Designer 设计物理架构,方法是在具有代表性的数据示例上运行它。Database Designer 生成用于创建投影的 SQL,如下所示:
-
分析
逻辑架构、示例数据和示例查询(可选)。
-
以 SQL 脚本的形式设计可自动或手动部署的
物理架构。
有关详细信息,请参阅创建数据库设计。
手动创建投影
CREATE PROJECTION
定义一个投影,如以下示例所示:
=> CREATE PROJECTION retail_sales_fact_p (
store_key ENCODING RLE,
pos_transaction_number ENCODING RLE,
sales_dollar_amount,
cost_dollar_amount )
AS SELECT
store_key,
pos_transaction_number,
sales_dollar_amount,
cost_dollar_amount
FROM store.store_sales_fact
ORDER BY store_key
SEGMENTED BY HASH(pos_transaction_number) ALL NODES;
投影定义包括以下组成部分:
列列表和编码
SQL 语句的这一部分列出投影中的每一列,并定义每一列的编码。Vertica 支持编码数据,这有助于查询执行产生较少的磁盘 I/O。
CREATE PROJECTION retail_sales_fact_P (
store_key ENCODING RLE,
pos_transaction_number ENCODING RLE,
sales_dollar_amount,
cost_dollar_amount )
基本查询
投影的基本查询子句确定要包含在投影中的列。
AS SELECT
store_key,
pos_transaction_number,
sales_dollar_amount,
cost_dollar_amount
排序顺序
投影的 ORDER BY
子句确定如何对投影数据进行排序。排序顺序可确定按照逻辑分组的值的位置,以便磁盘读取可以一次识别许多结果。为了获得最佳性能,请勿按 LONG VARBINARY 和 LONG VARCHAR 列对投影进行排序。有关详细信息,请参阅ORDER BY 子句。
ORDER BY store_key
分段
投影的分段子句指定如何在数据库中的所有节点之间分布投影数据。均匀分布负载有助于最大限度地访问投影数据。对于大型表,使用 SEGMENTED BY HASH
按分段分布投影数据。例如:
SEGMENTED BY HASH(pos_transaction_number) ALL NODES;
对于小型表,使用 UNSEGMENTED
关键字复制表数据。Vertica 会在所有群集节点上创建未分段投影的相同副本。复制可确保较高的可用性和恢复能力。
为了获得最大性能,请勿按 LONG VARBINARY 和 LONG VARCHAR 列对投影进行分段。
有关设计注意事项的详细信息,请参阅创建自定义设计。
3 - 投影命名
Vertica 根据以下约定标识投影,其中 proj‑basename 是由 CREATE PROJECTION 分配给此投影的名称。
未分段投影
未分段投影遵循以下命名约定:
分段投影
企业模式
在企业模式下,分段投影使用以下命名约定:
proj‑basename_boffset
此名称标识分段投影的伙伴实例投影,其中 offset 是投影相对于所有其他伙伴实例投影的节点位置。所有伙伴实例投影共享相同的投影基本名称。例如:
=> SELECT projection_basename, projection_name FROM projections WHERE anchor_table_name = 'store_orders';
projection_basename | projection_name
---------------------+-----------------
store_orders | store_orders_b0
store_orders | store_orders_b1
(2 rows)
一个例外情况是:Vertica 使用以下约定来命名实时聚合投影:proj‑basename、
proj‑basename_b1
,依此类推。
Eon 模式
在 Eon 模式下,分段投影使用以下命名约定:
proj‑basename
注意
Eon 模式使用公共存储中的分片来对表数据进行分段,这些分片在功能上等同于企业模式伙伴实例投影。有关详细信息,请参阅
分片和订阅。
重命名和复制的表的投影
Vertica 在以下两种情况下使用相同的逻辑重命名现有投影:
在这两种情况下,Vertica 使用以下算法重命名投影:
-
循环访问锚定在重命名表或新表上的所有投影,并检查其名称是否以原始表名称为前缀:
-
如果是,比较原始表名称和投影基本名称:
-
检查新的投影名称是否已经存在。如果不存在,则保存它们。否则,通过根据需要将版本号附加到新的基本名称(
new‑basename_v1
、
new‑basename_v2
,依此类推)来解决名称冲突。
示例
自动投影始终是超投影:
=> CREATE TABLE store.store_dimension
store_key int NOT NULL,
store_name varchar(64),
...
) UNSEGMENTED ALL NODES;
CREATE TABLE
=> COPY store.store_dim FROM '/home/dbadmin/store_dimension_data.txt';
50
=> SELECT anchor_table_name, projection_basename, projection_name FROM projections WHERE anchor_table_name = 'store_dimension';
anchor_table_name | projection_basename | projection_name
-------------------+---------------------+-----------------------
store_dimension | store_dimension | store_dimension_super
store_dimension | store_dimension | store_dimension_super
store_dimension | store_dimension | store_dimension_super
(3 rows)
未分段投影名称在所有节点上都具有 _unseg
后缀:
=> CREATE TABLE store.store_dimension( store_key int NOT NULL, store_name varchar(64), ... ); CREATE TABLE => CREATE PROJECTION store_dimension AS SELECT * FROM store.store_dimension UNSEGMENTED ALL NODES; WARNING 6922: Projection name was changed to store_dimension_unseg because it conflicts with the basename of the table store_dimension CREATE PROJECTION => SELECT anchor_table_name, projection_basename, projection_name FROM projections WHERE anchor_table_name = 'store_dimension'; anchor_table_name | projection_basename | projection_name -------------------+---------------------+----------------------- store_dimension | store_dimension | store_dimension_unseg store_dimension | store_dimension | store_dimension_unseg store_dimension | store_dimension | store_dimension_unseg (3 rows)
以下示例创建分段表 testRenameSeg
并使用数据填充它:
=> CREATE TABLE testRenameSeg (a int, b int);
CREATE TABLE
dbadmin=> INSERT INTO testRenameSeg VALUES (1,2);
OUTPUT
--------
1
(1 row)
dbadmin=> COMMIT;
COMMIT
Vertica 会为此表自动创建两个伙伴实例超投影:
=> \dj testRename*
List of projections
Schema | Name | Owner | Node | Comment
--------+-----------------------+---------+------------------+---------
public | testRenameSeg_b0 | dbadmin | |
public | testRenameSeg_b1 | dbadmin | |
以下 CREATE PROJECTION
语句显式为表创建额外的投影:
=> CREATE PROJECTION nameTestRenameSeg_p AS SELECT * FROM testRenameSeg;
=> CREATE PROJECTION testRenameSeg_p AS SELECT * FROM testRenameSeg;
=> CREATE PROJECTION testRenameSeg_pLap AS SELECT b, MAX(a) a FROM testRenameSeg GROUP BY b;
=> CREATE PROJECTION newTestRenameSeg AS SELECT * FROM testRenameSeg;
=> \dj *testRenameSeg*
List of projections
Schema | Name | Owner | Node | Comment
--------+------------------------+---------+------+---------
public | nameTestRenameSeg_p_b0 | dbadmin | |
public | nameTestRenameSeg_p_b1 | dbadmin | |
public | newTestRenameSeg_b0 | dbadmin | |
public | newTestRenameSeg_b1 | dbadmin | |
public | testRenameSeg_b0 | dbadmin | |
public | testRenameSeg_b1 | dbadmin | |
public | testRenameSeg_pLap | dbadmin | |
public | testRenameSeg_pLap_b1 | dbadmin | |
public | testRenameSeg_p_b0 | dbadmin | |
public | testRenameSeg_p_b1 | dbadmin | |
(10 rows)
如果重命名锚表,Vertica 还会重命名其投影:
=> ALTER TABLE testRenameSeg RENAME TO newTestRenameSeg;
ALTER TABLEn=> \dj *testRenameSeg*
List of projections
Schema | Name | Owner | Node | Comment
--------+--------------------------+---------+------+---------
public | nameTestRenameSeg_p_b0 | dbadmin | |
public | nameTestRenameSeg_p_b1 | dbadmin | |
public | newTestRenameSeg_b0 | dbadmin | |
public | newTestRenameSeg_b1 | dbadmin | |
public | newTestRenameSeg_pLap_b0 | dbadmin | |
public | newTestRenameSeg_pLap_b1 | dbadmin | |
public | newTestRenameSeg_p_b0 | dbadmin | |
public | newTestRenameSeg_p_b1 | dbadmin | |
public | newTestRenameSeg_v1_b0 | dbadmin | |
public | newTestRenameSeg_v1_b1 | dbadmin | |
(10 rows)
两组伙伴实例投影未重命名,因为它们的名称不以原始表名称为前缀:
-
nameTestRenameSeg_p_b0
-
nameTestRenameSeg_p_b1
-
newTestRenameSeg_b0
-
newTestRenameSeg_b1
当重命名其他投影时,Vertica 发现表的超投影(最初是 testRenameSeg
)与现有投影 newTestRenameSeg
之间存在潜在冲突。它通过将版本号 _v1
和 _v2
附加到超投影的新名称解决了此冲突:
-
newTestRenameSeg_v1_b0
-
newTestRenameSeg_v1_b1
4 - 自动投影
自动投影是 Vertica 为临时表和持久表自动生成的
超投影。通常,如果没有为表定义任何投影,Vertica 会在您首次将数据加载到该表时自动为该表创建投影。以下规则适用于所有自动投影:
自动投影触发器
创建自动投影的条件会依据表是临时表还是持久表而有所不同:
默认分段和排序顺序
如果 CREATE TABLE
或 CREATE TEMPORARY TABLE
省略分段(SEGMENTED BY
或 UNSEGMENTED
)或者 ORDER BY
子句,则 Vertica 按如下方式对自动投影进行分段和排序:
-
如果表的创建语句省略分段(SEGMENTED BY 或 UNSEGMENTED
)子句,则 Vertica 检查配置参数 SegmentAutoProjection
以确定是创建分段的自动投影还是未分段的自动投影。默认情况下,此参数设置为 1(启用)。
-
如果启用了 SegmentAutoProjection
,并且表的创建语句也省略 ORDER BY
子句,则 Vertica 会根据表的创建方式对表的自动投影进行分段和排序:
例如,下表是在没有主键或外键的情况下定义的:
=> CREATE TABLE testAutoProj(c10 char (10), v1 varchar(140) DEFAULT v2||v3, i int, c5 char(5), v3 varchar (80), d timestamp, v2 varchar(60), c1 char(1));
CREATE TABLE
=> INSERT INTO testAutoProj VALUES
('1234567890',
DEFAULT,
1,
'abcde',
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor ',
current_timestamp,
'incididunt ut labore et dolore magna aliqua. Eu scelerisque',
'a');
OUTPUT
--------
1
(1 row)
=> COMMIT;
COMMIT
在 INSERT 语句首次将数据加载到该表之前,Vertica 自动为该表创建一个超投影:
=> SELECT export_objects('', 'testAutoProj_b0');
--------------------------------------------------------
CREATE PROJECTION public.testAutoProj_b0 /*+basename(testAutoProj),createtype(L)*/
( c10, v1, i, c5, v3, d, v2, c1 )
AS
SELECT testAutoProj.c10,
testAutoProj.v1,
testAutoProj.i,
testAutoProj.c5,
testAutoProj.v3,
testAutoProj.d,
testAutoProj.v2,
testAutoProj.c1
FROM public.testAutoProj
ORDER BY testAutoProj.c10,
testAutoProj.v1,
testAutoProj.i,
testAutoProj.c5,
testAutoProj.v3,
testAutoProj.d,
testAutoProj.v2,
testAutoProj.c1
SEGMENTED BY hash(testAutoProj.i, testAutoProj.c5, testAutoProj.d, testAutoProj.c1, testAutoProj.c10, testAutoProj.v2, testAutoProj.v3, testAutoProj.v1) ALL NODES OFFSET 0;
SELECT MARK_DESIGN_KSAFE(1);
(1 row)
5 - 未分段投影
在许多情况下,维度表相对较小,因此您无需将它们分段。相应地,您应该设计一个 K-safe 数据库,以便可以复制其维度表的投影并且无需在所有群集节点上进行分段。您可以使用包括子句 UNSEGMENTED ALL NODES
的
CREATE PROJECTION
语句创建未分段投影。此子句用于指定在所有群集节点上创建投影的相同实例。
以下示例展示了如何为表 store.store_dimension
创建未分段投影:
=> CREATE PROJECTION store.store_dimension_proj (storekey, name, city, state)
AS SELECT store_key, store_name, store_city, store_state
FROM store.store_dimension
UNSEGMENTED ALL NODES;
CREATE PROJECTION
Vertica 使用相同的名称来标识未分段投影的所有实例 — 在此示例中为 store.store_dimension_proj
。关键字 ALL NODES
指定在所有节点上复制投影:
=> \dj store.store_dimension_proj
List of projections
Schema | Name | Owner | Node | Comment
--------+----------------------+---------+------------------+---------
store | store_dimension_proj | dbadmin | v_vmart_node0001 |
store | store_dimension_proj | dbadmin | v_vmart_node0002 |
store | store_dimension_proj | dbadmin | v_vmart_node0003 |
(3 rows)
有关投影名称约定的详细信息,请参阅投影命名。
6 - 分段投影
通常,您会为大型事实表创建分段投影。Vertica 将分段投影拆分为大小相似的块(段),并将这些段均匀地分布在整个群集中。系统 K-safety 确定在不同节点上创建和维护每个段的多少个副本(伙伴示例)。
投影分段可实现以下目标:
-
确保高可用性和恢复。
-
将查询执行工作负载分散到多个节点上。
-
允许每个节点针对不同查询工作负载进行优化。
哈希分段
Vertica 使用哈希分段对大型投影进行分段。哈希分段可基于内置的哈希函数对投影进行分段。该内置哈希函数可使多个节点中的数据实现正态分布,从而优化查询的执行。在投影中,要进行哈希的数据由一列或多列值组成,每一列都包含大量唯一值,并且值的分布偏移程度在可接受的范围内。主键列通常满足这些条件,因此它们通常用作哈希函数实参。
可以使用包含 SEGMENTED BY
子句的
CREATE PROJECTION
语句创建分段投影。
以下 CREATE PROJECTION
语句将创建投影 public.employee_dimension_super
。它指定包含表 public.employee_dimension
中的所有列。哈希分段子句将调用 Vertica HASH
函数,对列 employee_key
中的投影数据进行分段;此外,它还包括 ALL NODES
子句,指定在群集中的所有节点上均匀分布投影数据:
=> CREATE PROJECTION public.employee_dimension_super
AS SELECT * FROM public.employee_dimension
ORDER BY employee_key
SEGMENTED BY hash(employee_key) ALL NODES;
如果数据库是 K-safe 的,Vertica 会为此投影创建多个伙伴实例,并将它们分布在群集中的不同节点上。在本例中,数据库 K-safety 设置为 1,因此 Vertica 会为此投影创建两个伙伴实例。它使用投影名称 employee_dimension_super
作为所创建的两个伙伴实例标识符的基本名称,在本例中为 employee_dimension_super_b0
和 employee_dimension_super_b1
:
=> SELECT projection_name FROM projections WHERE projection_basename='employee_dimension_super';
projection_name
-----------------------------
employee_dimension_super_b0
employee_dimension_super_b1
(2 rows)
7 - K-safe 数据库投影
对于分段投影和未分段投影,K-safety 的实施方式有所不同,如下所述。示例假定在 3 节点数据库中将数据库 K-safety 设置为 1,并对两个表使用投影:
分段投影
在 K-safe 数据库中,数据库需要每个投影段的 K+1 个实例或伙伴实例。例如,如果数据库 K-safety 设置为 1,则数据库要求每个投影段有两个实例或伙伴实例。
可以通过
CREATE PROJECTION
选项 KSAFE
设置各个分段投影的 K-safety。投影 K-safety 必须大于等于数据库 K-safety。如果省略设置 KSAFE
,投影将从数据库获取 K-safety。
以下
CREATE PROJECTION
为事实表 store.store_orders_fact
定义分段投影:
=> CREATE PROJECTION store.store_orders_fact
(prodkey, ordernum, storekey, total)
AS SELECT product_key, order_number, store_key, quantity_ordered*unit_price
FROM store.store_orders_fact
SEGMENTED BY HASH(product_key, order_number) ALL NODES KSAFE 1;
CREATE PROJECTION
CREATE PROJECTION
语句中的以下关键字与设置投影 K-safety 相关:
未分段投影
在 K-safe 数据库中,必须在所有节点上复制未分段投影。因此,未分段投影的 CREATE PROJECTION
语句必须包括分段子句 UNSEGMENTED ALL NODES
。这指示 Vertica 在所有群集节点上创建投影的相同实例(伙伴实例)。如果在单个节点上创建未分段投影,则 Vertica 会认为它不安全,并且不会使用它。
以下示例展示了如何为表 store.store_dimension
创建未分段投影:
=> CREATE PROJECTION store.store_dimension_proj (storekey, name, city, state)
AS SELECT store_key, store_name, store_city, store_state
FROM store.store_dimension
UNSEGMENTED ALL NODES;
CREATE PROJECTION
Vertica 使用相同的名称来标识未分段投影的所有实例 — 在此示例中为 store.store_dimension_proj
。关键字 ALL NODES
指定在所有节点上复制投影:
=> \dj store.store_dimension_proj
List of projections
Schema | Name | Owner | Node | Comment
--------+----------------------+---------+------------------+---------
store | store_dimension_proj | dbadmin | v_vmart_node0001 |
store | store_dimension_proj | dbadmin | v_vmart_node0002 |
store | store_dimension_proj | dbadmin | v_vmart_node0003 |
(3 rows)
有关投影名称约定的详细信息,请参阅投影命名。
8 - 分区范围投影
Vertica 支持指定分区键范围的投影。默认情况下,投影存储分区表数据的所有行。随着时间的推移,此要求可能会产生越来越多的开销:
- 随着数据的累积,就算有大量数据不经常查询,也需要越来越多的存储量。
- 大型投影可能会阻止优化,例如更好的编码,或者更改投影排序顺序或分段。对投影的 DDL 进行此类更改需要刷新整个投影。根据投影大小,此刷新操作可能会持续数小时甚至数天。
您可以通过为分区表创建投影,指定相对较窄的分区键范围,最大程度地减少这些问题。例如,表 store_orders
按 order_date
进行分区,如下所示:
=> CREATE TABLE public.store_orders(order_no int, order_date timestamp NOT NULL, shipper varchar(20), ship_date date);
CREATE TABLE
=> ALTER TABLE store_orders PARTITION BY order_date::DATE GROUP BY date_trunc('month', (order_date)::DATE);
ALTER TABLE
如果需要,可以创建 store_orders
的投影,指定表的连续分区键范围。在以下示例中,投影 ytd_orders
指定仅包括从一年的第一天开始下单的订单:
=> CREATE PROJECTION ytd_orders AS SELECT * FROM store_orders ORDER BY order_date
ON PARTITION RANGE BETWEEN date_trunc('year',now())::date AND NULL;
WARNING 4468: Projection <public.ytd_orders_b0> is not available for query processing. Execute the select start_refresh() function to copy data into this projection.
The projection must have a sufficient number of buddy projections and all nodes must be up before starting a refresh
WARNING 4468: Projection <public.ytd_orders_b1> is not available for query processing. Execute the select start_refresh() function to copy data into this projection.
The projection must have a sufficient number of buddy projections and all nodes must be up before starting a refresh
CREATE PROJECTION
=> SELECT refresh();
refresh
---------------------------------------------------------------------------------------
Refresh completed with the following outcomes:
Projection Name: [Anchor Table] [Status] [Refresh Method] [Error Count] [Duration (sec)]
----------------------------------------------------------------------------------------
"public"."ytd_orders_b1": [store_orders] [refreshed] [scratch] [0] [0]
"public"."ytd_orders_b0": [store_orders] [refreshed] [scratch] [0] [0]
(1 row)
每个 ytd_orders
伙伴实例投影仅需要每节点 7 个 ROS 容器,而锚表的超投影需要 77 个容器:
=> SELECT COUNT (DISTINCT ros_id) NumROS, projection_name, node_name FROM PARTITIONS WHERE projection_name ilike 'store_orders_b%' GROUP BY node_name, projection_name ORDER BY node_name;
NumROS | projection_name | node_name
--------+-----------------+------------------
77 | store_orders_b0 | v_vmart_node0001
77 | store_orders_b1 | v_vmart_node0001
77 | store_orders_b0 | v_vmart_node0002
77 | store_orders_b1 | v_vmart_node0002
77 | store_orders_b0 | v_vmart_node0003
77 | store_orders_b1 | v_vmart_node0003
(6 rows)
=> SELECT COUNT (DISTINCT ros_id) NumROS, projection_name, node_name FROM PARTITIONS WHERE projection_name ilike 'ytd_orders%' GROUP BY node_name, projection_name ORDER BY node_name;
NumROS | projection_name | node_name
--------+-----------------+------------------
7 | ytd_orders_b0 | v_vmart_node0001
7 | ytd_orders_b1 | v_vmart_node0001
7 | ytd_orders_b0 | v_vmart_node0002
7 | ytd_orders_b1 | v_vmart_node0002
7 | ytd_orders_b0 | v_vmart_node0003
7 | ytd_orders_b1 | v_vmart_node0003
(6 rows)
分区范围要求
分区范围表达式必须符合适用于表级分区的要求,例如,分区键格式和数据类型验证。
以下要求和约束特别适用于分区范围投影:
锚表依赖
如前所述,分区范围投影取决于按同一表达式分区的锚表。如果从投影的锚表中移除表分区,Vertica 会删除从属投影。同样,如果修改锚表的 partition 子句,Vertica 也会删除投影。
以下例外情况适用:如果锚表的新 partition 子句保持分区表达式不变,则从属投影不会被删除,并且仍可用于查询。例如,表 store_orders
及其投影 ytd_orders
最初按如下方式分区:
=> ALTER TABLE store_orders PARTITION BY order_date::DATE GROUP BY DATE_TRUNC('month', (order_date)::DATE);
...
=> CREATE PROJECTION ytd_orders AS SELECT * FROM store_orders ORDER BY order_date
ON PARTITION RANGE BETWEEN date_trunc('year',now())::date AND NULL;
如果现在修改 store_orders
以使用分层分区,Vertica 会对表数据及其分区范围投影进行重新分区:
=> ALTER TABLE store_orders PARTITION BY order_date::DATE GROUP BY CALENDAR_HIERARCHY_DAY(order_date::DATE, 2, 2) REORGANIZE;
NOTICE 4785: Started background repartition table task
ALTER TABLE
由于 store_orders
和 ytd_orders
投影继续按照 order_date
列进行分区,因此 ytd_orders
投影仍然有效。此外,投影数据的范围保持不变,因此投影不需要刷新。但是在后台,Tuple Mover 会根据其锚表的新分层分区以静默方式重新组织投影 ROS 容器:
=> SELECT COUNT (DISTINCT ros_id) NumROS, projection_name, node_name FROM PARTITIONS WHERE projection_name ilike 'ytd_orders%' GROUP BY node_name, projection_name ORDER BY node_name;
NumROS | projection_name | node_name
--------+-----------------+------------------
38 | ytd_orders_b0 | v_vmart_node0001
38 | ytd_orders_b1 | v_vmart_node0001
38 | ytd_orders_b0 | v_vmart_node0002
38 | ytd_orders_b1 | v_vmart_node0002
38 | ytd_orders_b0 | v_vmart_node0003
38 | ytd_orders_b1 | v_vmart_node0003
(6 rows)
修改现有投影
可以使用 ALTER PROJECTION 修改投影的分区范围。如果新范围位于前一个范围内,则不需要刷新。否则,需要先刷新,然后才能使用投影处理查询。
例如,投影 ytd_orders
之前指定了一个从当年第一天开始的分区范围。以下 ALTER PROJECTION 语句将范围更改为从去年 10 月 1 日开始。新范围先于前一个范围,因此 Vertica 会发出刷新指定的投影 ytd_orders_b0
及其伙伴实例投影 ytd_orders_b1
的警告:
=> ALTER PROJECTION ytd_orders_b0 ON PARTITION RANGE BETWEEN
add_months(date_trunc('year',now())::date, -3) AND NULL;
WARNING 10001: Projection "public.ytd_orders_b0" changed to out-of-date state as new partition range is not covered by existing partition range
HINT: Call refresh() or start_refresh() to refresh the projections
WARNING 10001: Projection "public.ytd_orders_b1" changed to out-of-date state as new partition range is not covered by existing partition range
HINT: Call refresh() or start_refresh() to refresh the projections
ALTER PROJECTION
动态分区范围
投影的分区范围可以是静态范围,由始终解析为相同值的表达式设置。例如,以下投影指定一个介于 21/06/01 和 21/06/30 之间的静态范围:
=> CREATE PROJECTION last_month_orders AS SELECT * FROM store_orders ORDER BY order_date ON PARTITION RANGE BETWEEN
'2021-06-01' AND '2021-06-30';
...
CREATE PROJECTION
更典型的是,分区范围表达式使用稳定的日期函数(例如 ADD_MONTHS、DATE_TRUNC 和 NOW)来指定动态范围。在以下示例中,分区范围设置为从上个月的第一天开始。随着日历日期推进到下个月,分区范围也随之向前推进:
=> ALTER PROJECTION last_month_orders_b0 ON PARTITION RANGE BETWEEN
add_months(date_trunc('month', now())::date, -1) AND NULL;
ALTER PROJECTION
最佳实践是,始终通过将最大范围设置为 NULL 来将其保留为无限期,并依靠查询来确定要提取的最大数据量。例如,提取上个月下单的所有物料订单的查询可能如下所示:
=> SELECT * from store_orders WHERE order_date BETWEEN
add_months(date_trunc('month', now())::date, -1) AND
add_months(date_trunc('month', now())::date + dayofmonth(now()), -1);
为执行此查询而生成的查询计划表明它使用的是分区范围投影 last_month_orders
:
=> EXPLAIN SELECT * from store_orders WHERE order_date BETWEEN
add_months(date_trunc('month', now())::date, -1) AND
add_months(date_trunc('month', now())::date + dayofmonth(now()), -1);
Access Path:
+-STORAGE ACCESS for store_orders [Cost: 34, Rows: 763 (NO STATISTICS)] (PATH ID: 1)
| Projection: public.last_month_orders_b0
| Materialize: store_orders.order_date, store_orders.order_no, store_orders.shipper, store_orders.ship_date
| Filter: ((store_orders.order_date >= '2021-06-01 00:00:00'::timestamp(0)) AND (store_orders.order_date <= '2021-06-3
0 00:00:00'::timestamp(0)))
| Execute on: All Nodes
动态分区范围维护
Projection Maintainer 是一项后台服务,它每小时使用投影范围表达式检查一次投影。如果投影中任一表达式的值发生变化,Projection Maintainer 将比较 PARTITION_RANGE_MIN
和 PARTITION_RANGE_MAX
中的新旧值,以确定分区范围是缩小还是扩大了:
-
如果分区范围在任一方向缩小(即 PARTITION_RANGE_MIN 大于其先前值或 PARTITION_RANGE_MAX 小于其先前值),则 Projection Maintainer 按照以下方式操作:
-
如果分区范围在任一方向扩大(即 PARTITION_RANGE_MIN 小于其先前值,或 PARTITION_RANGE_MAX 大于其先前值),则 Projection Maintainer 保持投影和 PROJECTIONS 表不变。由于分区范围保持不变,Vertica 将现有投影数据视为最新数据,因此也永远不会刷新。
例如,以下投影会创建一个分区范围,其中包括当前月的所有订单:
=> CREATE PROJECTION mtd_orders AS SELECT * FROM store_orders ON PARTITION RANGE BETWEEN
date_trunc('month', now())::date AND NULL;
如果您在 2021 年 7 月创建此分区,则最小分区范围表达式 date_trunc('month', now())::date
最初解析为该月的第一天:2021-07-01。在下个月开始时,在 2021‑08‑01 00:00
和 2021‑08‑01 01:00
之间的某个时间,Projection Maintainer 会将最小范围表达式与系统时间进行比较。然后,它按如下所示进行操作:
-
更新 PROJECTIONS 表,并将投影 mtd_orders
的 PARTITION_RANGE_MIN
设置为 2021-08-01
。
-
对 MERGEOUT 请求进行排队,以从此投影的分区范围中清除键早于 2021-08-01
的所有行。
重要
鉴于上面显示的示例,您可以考虑按如下方式设置投影的最大分区范围表达式:
add_months(date_trunc('month', now()), 1) - 1
此表达式将始终解析为当前月的最后一天。对于每个新的月份,最大分区范围将比之前的值大一个月。如前所述,Projection Maintainer 会忽略分区范围的任何扩大,因此它将保持 mtd_orders
的最小和最大分区范围值不变。为避免此类问题,请始终将最大分区表达式设置为 NULL。
9 - 刷新投影
当为已包含数据的表创建投影时,Vertica 不会自动将该数据加载到新投影中。相反,您必须显式刷新该投影。在执行此操作之前,投影无法参与对其锚表执行查询。
可以使用以下函数之一刷新投影:
这两个函数都会更新维护有关投影刷新状态的信息的系统表:PROJECTION_REFRESHES、PROJECTIONS 和 PROJECTION_CHECKPOINT_EPOCHS。
如果刷新会违反表或架构磁盘配额,则操作将失败。有关详细信息,请参阅磁盘配额。
获取投影刷新信息
可以查询 PROJECTION_REFRESHES 和 PROJECTIONS 系统表来查看刷新操作的进度。还可以调用 GET_PROJECTIONS 函数来查看给定表的投影刷新的最终状态:
=> SELECT GET_PROJECTIONS('customer_dimension');
GET_PROJECTIONS
----------------------------------------------------------------------------------------------------------
Current system K is 1.
# of Nodes: 3.
Table public.customer_dimension has 2 projections.
Projection Name: [Segmented] [Seg Cols] [# of Buddies] [Buddy Projections] [Safe] [UptoDate] [Stats]
----------------------------------------------------------------------------------------------------
public.customer_dimension_b1 [Segmented: Yes] [Seg Cols: "public.customer_dimension.customer_key"] [K: 1]
[public.customer_dimension_b0] [Safe: Yes] [UptoDate: Yes] [Stats: RowCounts]
public.customer_dimension_b0 [Segmented: Yes] [Seg Cols: "public.customer_dimension.customer_key"] [K: 1]
[public.customer_dimension_b1] [Safe: Yes] [UptoDate: Yes] [Stats: RowCounts]
(1 row)
刷新方法
Vertica 可以刷新其伙伴实例之一中的投影(如有)。在这种情况下,目标投影将获取源伙伴实例的历史数据。否则,将使用刷新操作时的最新时期数据从头开始刷新投影。在这种情况下,投影不能参与刷新操作之前的任何时期的历史查询。
要确定用来刷新给定投影的方法,请查询 PROJECTION_REFRESHES 系统表中的 REFRESH_METHOD 列。
10 - 删除投影
可以通过 DROP PROJECTION 语句显式删除投影。当删除投影的锚表时,也会隐式删除投影。