选择排序顺序:最佳实践
选择投影的排序顺序时,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
的基数。