选择排序顺序:最佳实践

选择投影的排序顺序时,Vertica 具有多条建议,可以帮助您实现最大查询性能,如以下示例中所示。

组合使用 RLE 和排序顺序

当处理低基数列中的谓词时,组合使用 RLE 和排序可最大程度减少存储要求,同时实现最大查询性能。

假设您具有一个包含以下值和编码类型的 students 表:

您可能具有类似如下所示的查询:

SELECT name FROM studentsWHERE gender = 'M' AND pass_fail = 'P' AND class = 'senior';

最快的数据访问方式是先处理非重复值数量最少的低基数列,然后再处理高基数列。对于对 genderclasspass_failname 具有相同限制的查询,以下排序顺序可最大程度减少存储要求,同时实现最大查询性能。如下所示指定投影的 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_keywarehouse_keyproduct_dimensionwarehouse_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 的基数。