尽管您可以从头开始编写自定义投影,但 Vertica 建议您使用 Database Designer 创建设计来作为起点。这样可确保您获得满足基本要求的投影。
1 - 写入并部署自定义投影
在编写自定义投影之前,请仔细查看规划您的设计中的主题。不遵循这些注意事项可能会生成无法正常工作的投影。
要手动修改或创建投影:
-
使用
CREATE PROJECTION
语句编写脚本以创建所需的投影。 -
使用元命令
\i
在 vsql 中运行脚本。注意
您必须加载一个具有逻辑架构的数据库。 -
对于 K-safe 数据库,调用 Vertica 元函数
GET_PROJECTIONS
在新预测表上。检查输出,验证所有投影是否有足够的伙伴被识别为安全。 -
如果您为已包含数据的表创建投影,请调用
REFRESH
或START_REFRESH
更新新投影。否则,这些投影不可用于查询处理。 -
调用
MAKE_AHM_NOW
,将 Ancient History Mark (AHM) 设置为最近的时期。 -
对于不再需要的投影,请调用
DROP PROJECTION
,否则将浪费磁盘空间并降低加载速度。 -
对于所有数据库投影,请调用
ANALYZE_STATISTICS
:=> SELECT ANALYZE_STATISTICS ('');
此函数从用于存储投影的所有节点中收集和聚合数据样本及存储信息,然后将统计信息写入到编录中。
2 - 设计超级投影
超投影需要满足以下要求:
-
它们必须包含表中的每个列。
-
对于 K-safe 设计,必须在数据库群集内的所有节点上复制超投影(适用于维度表),或者与伙伴投影进行配对并跨越所有节点进行分段(适用于很大的表和中等的表)。有关投影及其存储方式的概述,请参阅投影和使用投影的高可用性。有关设计细节,请参阅K-safety 设计。
要实现最大可用性,超投影需要最大限度降低存储要求,同时最大限度提升查询性能。为了实现这一目标,超投影中列的排序顺序以存储要求和常用查询为基础。
3 - 排序顺序的优势
列排序顺序是将存储空间要求降至最低和最大限度提高查询性能的重要因素。
将存储空间要求降至最低
最小化存储不但可以节省物理资源,而且还可以减少磁盘 I/O,以显著提高性能。通过在排序顺序中优先考虑低基数列,可以最大程度地减少投影存储。这会减少 Vertica 检索查询结果时所存储和访问的行数。
确定投影排序列后,分析其数据并选择效率最高的编码方法。Vertica 优化器使用运行长度编码 (RLE) 为列提供首选项,因此请确保在适当的情况下使用它。运行长度编码将相同值的序列(运行)替换为包含值和出现次数的单个对。因此,它特别适合用于运行长度较大的低基数列。
最大限度提高查询性能
可通过列排序顺序提高查询性能,方法如下:
-
排序顺序应尽可能地使用最低基数确定列的优先级。
-
请勿在 LONG VARBINARY 和 LONG VARCHAR 类型的列上排序投影。
另请参阅
选择排序顺序:最佳实践4 - 选择排序顺序:最佳实践
选择投影的排序顺序时,Vertica 具有多条建议,可以帮助您实现最大查询性能,如以下示例中所示。
组合使用 RLE 和排序顺序
当处理低基数列中的谓词时,组合使用 RLE 和排序可最大程度减少存储要求,同时实现最大查询性能。
假设您具有一个包含以下值和编码类型的 students
表:
您可能具有类似如下所示的查询:
SELECT name FROM studentsWHERE gender = 'M' AND pass_fail = 'P' AND class = 'senior';
最快的数据访问方式是先处理非重复值数量最少的低基数列,然后再处理高基数列。对于对 gender
、class
、pass_fail
和 name
具有相同限制的查询,以下排序顺序可最大程度减少存储要求,同时实现最大查询性能。如下所示指定投影的 ORDER BY 子句:
ORDER BY students.gender, students.pass_fail, students.class, students.name
在本示例中,gender
列由两个 RLE 条目表示,pass_fail
列由四个条目表示,而 class
列由 16 个条目表示,而不考虑 students
表的基数。Vertica 可有效地查找一组满足所有谓词的行,从而大幅减少了在排序顺序早期出现的 RLE 编码列搜索工作。因此,如果您在局部谓词中使用低基数列(如上例所示),请尽早将这些列按照非重复基数的递增顺序(即按照每列中非重复值数量的递增顺序)放入投影排序顺序中。
如果先使用 student.class
排序此表,则可以提高仅对 student.class
列存在限制的查询的性能,并改善 student.class
列(包含非重复值数量最多的列)的压缩性能,但其他列也不会压缩。确定哪个投影更好取决于工作负载中的特定查询及其相对重要性。
随着列基数的增加,通过压缩节省的存储空间会降低;但是,随着在该列中存储值所需字节数的增加,通过压缩节省的存储空间也会增加。
充分发挥 RLE 优势
为了充分发挥 RLE 编码的优势,请仅在列的平均运行长度大于 10(排序时)时使用该编码。例如,假设您具有包含以下列的表,各列按基数由低到高的顺序排列:
address.country, address.region, address.state, address.city, address.zipcode
zipcode
列可能在具有相同 zip 代码的行中没有 10 个排序条目,因此对该列执行运行长度编码很可能没有优势,而且可能会导致压缩性能变差。但在排序的运行长度中可能有超过 10 个国家/地区,因此对该国家/地区列应用 RLE 可以提高性能。
先放置基数较低的列以实现函数依赖项
一般而言,在联接顺序中先放置用于局部谓词(如上例所示)的列可使谓词求值更加高效。另外,如果通过基数较高的列来唯一确定基数较低的列(如仅 city_id 唯一确定 state_id),则在排序顺序中先放置基数较低、通过函数确定的列总比先放置基数较高的列好。
例如,在以下排序顺序中,customer_info 表中的 Area_Code 列排在 Number 列之前。
ORDER BY = customer_info.Area_Code, customer_info.Number, customer_info.Address
在查询中,先放置 Area_Code
列,以便仅扫描 Number
列中以 978 开头的值。
=> SELECT AddressFROM customer_info WHERE Area_Code='978' AND Number='9780123457';
合并联接排序
处理联接时,Vertica 优化器会从两种算法中进行选择:
-
合并联接 — 如果两个输入都按联接列预先排序,优化器会选择合并联接,因为这种算法更快而且使用的内存更少。
-
哈希联接 — 利用哈希联接算法,Vertica 会使用较小的(内部)联结表在内存中构建联接列的哈希表。哈希联接没有排序要求,但却会消耗更多内存,因为 Vertica 会构建一个包含内部表值的哈希表。如果投影未存储在联接列上,则优化器会选择一种哈希联接。
如果两个输入都预先排序,合并联接不必执行任何预处理,因而可以更快地执行联接。Vertica 使用的术语“排序-合并联接”是指在合并联接之前必须对至少一个输入排序的情况。只有当外部输入端已按联接列排序时,Vertica 才会排序内部输入端。
为了使 Vertica 查询优化器能够为特定联接选择使用有效的合并联接,请在联接的两端创建投影,并在相应投影中先放置联接列。如果两个表都非常大,内存中无法容纳任何一个表,那么这样做将非常重要。如果预期内存中可以同时容纳某个表要联接到的所有表,那么合并联接的优势比哈希联接要小很多,很可能并不值得为任何一个联接列创建投影。
在重要查询中按列排序
如果您有一个重要查询,并且您会定期运行该查询,那么将该查询 WHERE 子句或 GROUP BY 子句中指定的列先放入排序顺序中可以为您节省时间。
如果该查询使用高基数列(如社会保险号),则将该列先放入投影的排序顺序中可能会牺牲存储空间,但您的最重要查询将得到优化。
按大小排序基数相等的列
如果有两个基数相等的列,则在排序顺序中先放置较大的列。例如,CHAR(20) 列占用 20 字节,但 INTEGER 列占用 8 字节。通过将 CHAR(20) 列放在 INTEGER 列前面,可以更好地压缩投影。
按非重复基数由低到高的顺序,先排序外键列
假设您有一个事实表,排序顺序中的前四列组成了另一个表的外键。为了获得最佳压缩效果,我们为事实表选择一个排序顺序,使其先显示外键,并以非重复基数的递增顺序排列。在设计事实表的投影时,还可应用其他因素,如按时间维度(若存在)分区。
在以下示例中,表 inventory
用于存储库存数据,product_key
和 warehouse_key
是 product_dimension
和 warehouse_dimension
表的外键:
=> CREATE TABLE inventory (
date_key INTEGER NOT NULL,
product_key INTEGER NOT NULL,
warehouse_key INTEGER NOT NULL,
...
);
=> ALTER TABLE inventory
ADD CONSTRAINT fk_inventory_warehouse FOREIGN KEY(warehouse_key)
REFERENCES warehouse_dimension(warehouse_key);
ALTER TABLE inventory
ADD CONSTRAINT fk_inventory_product FOREIGN KEY(product_key)
REFERENCES product_dimension(product_key);
库存表应当先按 warehouse_key 排序,然后再按 product_key 排序,因为 warehouse_key
列的基数很可能低于 product_key
的基数。
5 - 确定列访问速度的优先级
如果您测量和设置群集内的存储位置的性能,Vertica 将使用此信息确定在哪里基于列的排名来存储列。有关详细信息,请参阅设置存储性能。
如何对列排名
Vertica 将投影排序顺序中包含的列存储在最快的可用存储位置。未包含在投影排序顺序中的列将存储在稍慢的磁盘中。每个投影的列按如下方式排序:
-
排序顺序中的列具有最高优先级(编号 >;1000)。
-
排序顺序中最后一列的排序编号为 1001。
-
排序顺序中倒数第二的列排序编号为 1002,以此类推,直到排序顺序中的第一列,其排序编号为 1000 + 排序列数。
-
剩余列的排序编号在 1000–1 之间,从 1000 开始,每列减一。
Vertica 随后将列从最高排号到最低排号存储在磁盘中。它将最高排号的列放在最快的磁盘上,将最低排号的列放在最慢的磁盘上。
覆盖默认列排名
通过手动覆盖这些列的默认排名,您可以修改将哪些列存储在快速磁盘上。要完成此操作,请设置列列表中的 ACCESSRANK
关键字。确保使用一个当前尚未用于另一个列的整数。例如,如果您要为某列提供最快速的访问排名,可使用一个明显高于 1000 + 排序列数量之和的数字。这样一来,您便可以随着时间的推移输入更多列,而不会跌入到您设置的访问排名中。
以下示例将列 store_key
的访问等级设置为 1500:
CREATE PROJECTION retail_sales_fact_p (
store_key ENCODING RLE ACCESSRANK 1500,
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;