本文为您介绍Hologres中的关键索引,如Distribution Key、Event Time Column(Segment Key)和Clustering Key,帮助您在Hologres开发过程中快速上手使用索引,提升查询性能。
分布式数据仓库Hologres基本运行原理
Hologres是一个分布式数据仓库,采用并行计算和向量计算技术实现秒级查询响应,因此数据的分布特征对性能有关键影响,包括数据在多个分布式节点间的分布均衡性(distribution_key),以及单个节点内文件之间的分布有序性(event_time_column/segment_key)。同时Hologres在OLAP场景默认使用列存储格式,因此数据在文件内的有序性(clustering_key)也至关重要。掌握这三个概念,在性能优化时可以事半功倍。由于数据分布特征是在数据写入时确定,调整成本高,因此建议在建表时,设计与数据布局相关的三个属性。而与数据布局无直接关联的属性,如位图索引(bitmap_columns),字典编码(dictionary_columns)等,可以在建表之后,按需调整。
同时Hologres的元数据采用三级结构Database>Schema>Table,建议逻辑相关的表内聚在Schema下,避免跨库查询。Database是元数据隔离的基本单位,不是资源隔离的单位。
SQL优化的基本原理:减少IO,优化并发
建表时设计合适的数据分布,能够使SQL在执行时快速命中数据,减少IO消耗,以更少的计算资源,实现更高的查询性能,同时均衡的数据分布也使得并发资源可以充分发挥,避免单点瓶颈。下图是一个SQL从发起到获取数据的执行流程,可以通过下图理解减少IO的流程。
分区剪枝(Partition Pruning):SQL执行时,对于目标分区表,会通过分区裁剪,定位到所在分区。如果查询条件和分区不匹配,需要遍历所有分区,会引起过多的IO扫描,通常分区选择日粒度比较合适。对于非分区表直接略过,不进行分区裁剪。
分片剪枝(Shard Pruning):通过分布键(distribution_key)快速定位到数据所在的数据分片,可以减少单个SQL执行时的资源消耗,对于并发SQL,满足更高的吞吐能力;如果无法定位到某个分片,会通过分布式框架调度所有的分片参与计算,单个SQL的并行度更高,资源使用更多,但并发能力会降低,部分需要集中化执行的算子会带来额外的Shuffle开销。通常分布键选择订单ID、用户ID、事件ID等分布比较均衡的字段,多个需要JOIN的表使用相同的分布键,可以使相关的数据分片到同一个Shard,通过Local JOIN实现更高的JOIN效率。
文件剪枝(Segment Key Pruning):通过分段键(event_time_column/segment_key),快速定位到单个节点内部多个文件中的数据所在文件位置,避免打开不需要访问的文件。如果无法过滤,则需要遍历所有的文件。
聚簇剪枝(Clustering Key Pruning):通过聚簇键(clustering_key),快速定位单个文件内部的数据段,提高范围查询和字段排序的效率。
SQL优化实践
此处以TPC-H的部分Query为例,为您介绍如何设置Hologres的索引,以获得更好的查询性能。关于TPC-H的详细介绍,请参见测试方案介绍。
TPCH SQL参考实践
TPC-H Q1 Query
TPC-H Q1主要是对lineitem表的部分字段做聚合查询和过滤筛选。其中:
l_shipdate <=
:过滤查询。需要设置索引以支持范围过滤,可快速过滤出所需数据。
--TPC-H Q1
SELECT
l_returnflag,
l_linestatus,
SUM(l_quantity) AS sum_qty,
SUM(l_extendedprice) AS sum_base_price,
SUM(l_extendedprice * (1 - l_discount)) AS sum_disc_price,
SUM(l_extendedprice * (1 - l_discount) * (1 + l_tax)) AS sum_charge,
AVG(l_quantity) AS avg_qty,
AVG(l_extendedprice) AS avg_price,
AVG(l_discount) AS avg_disc,
COUNT(*) AS count_order
FROM
lineitem
WHERE
l_shipdate <= DATE '1998-12-01' - INTERVAL '120' DAY
GROUP BY
l_returnflag,
l_linestatus
ORDER BY
l_returnflag,
l_linestatus;
TPC-H Q4 Query
TPC-H Q4主要是对lineitem和orders表进行关联查询。其中:
o_orderdate >= DATE '1996-07-01'
:过滤查询。需要设置索引以支持范围过滤,可快速过滤出所需数据。l_orderkey = o_orderkey
:两表JOIN。需要为两个表设置同一个索引,最好能进行Local JOIN,以减少数据在两表交互时的Shuffle操作。--TPC-H Q4 Query SELECT o_orderpriority, COUNT(*) AS order_count FROM orders WHERE o_orderdate >= DATE '1996-07-01' AND o_orderdate < DATE '1996-07-01' + INTERVAL '3' MONTH AND EXISTS ( SELECT * FROM lineitem WHERE l_orderkey = o_orderkey AND l_commitdate < l_receiptdate ) GROUP BY o_orderpriority ORDER BY o_orderpriority;
建表建议
在上述的Q1和Q4查询中,主要涉及lineitem和orders两张表,分别如下:
hologres_dataset_tpch_100g.lineitem
Q1和Q4中均涉及对lineitem表的查询,其中使用的字段和查询条件不同。
对于Q1 Query:主要用l_shipdate做范围过滤筛选。clustering_key可以利用文件内的有序性加速范围过滤,因此我们可将l_shipdate设置为Clustering Key。segment_key(event_time_column)用于保持文件间的有序性,对于单调递增/递减的日期字段,也建议设置为Segment Key,可以有效进行文件层过滤,因此也可将l_shipdate设置为Segment Key。
对于Q4 Query :主要用lineitem表的l_orderkey字段与orders表的o_orderkey字段进行关联查询(JOIN)。distribution_key用于指定数据的分布策略,系统会根据distribution_key,将相同的数据存放在同一个Shard上,当两张表在同一个Table Group内,并且JOIN的字段是Distribution Key时,在进行数据写入时,会自动将两张表重相同Key的记录分发到同一个Shard上,当表进行JOIN时,只需要在当前节点进行Local JOIN,无需按照JOIN Key进行数据Shuffle,避免了运行时数据打散和重分发,可以显著提高执行效率。因此,我们将l_orderkey设置为Distribution Key。
最终lineitem表的表结构如下:
BEGIN; CREATE TABLE hologres_dataset_tpch_100g.lineitem ( l_ORDERKEY BIGINT NOT NULL, L_PARTKEY INT NOT NULL, L_SUPPKEY INT NOT NULL, L_LINENUMBER INT NOT NULL, L_QUANTITY DECIMAL(15,2) NOT NULL, L_EXTENDEDPRICE DECIMAL(15,2) NOT NULL, L_DISCOUNT DECIMAL(15,2) NOT NULL, L_TAX DECIMAL(15,2) NOT NULL, L_RETURNFLAG TEXT NOT NULL, L_LINESTATUS TEXT NOT NULL, L_SHIPDATE TIMESTAMPTZ NOT NULL, L_COMMITDATE TIMESTAMPTZ NOT NULL, L_RECEIPTDATE TIMESTAMPTZ NOT NULL, L_SHIPINSTRUCT TEXT NOT NULL, L_SHIPMODE TEXT NOT NULL, L_COMMENT TEXT NOT NULL, PRIMARY KEY (L_ORDERKEY,L_LINENUMBER) ) WITH ( distribution_key = 'L_ORDERKEY',--在join时可以实现local join clustering_key = 'L_SHIPDATE',--加速范围过滤 event_time_column = 'L_SHIPDATE'--加速文件裁剪 ); COMMIT;
hologres_dataset_tpch_100g.orders
本示例中,orders表参与Q4 Query的计算。
将orders表的o_orderkey字段设置为Distribution Key,可有效利用Local JOIN的能力,提升关联(JOIN)查询的效率。
o_orderdate字段主要用于日期字段的过滤查询,所以将其设置为Segment Key,加速文件的裁剪。
最终orders表的表结构如下:
BEGIN; CREATE TABLE hologres_dataset_tpch_100g.orders ( O_ORDERKEY BIGINT NOT NULL PRIMARY KEY, O_CUSTKEY INT NOT NULL, O_ORDERSTATUS TEXT NOT NULL, O_TOTALPRICE DECIMAL(15,2) NOT NULL, O_ORDERDATE timestamptz NOT NULL, O_ORDERPRIORITY TEXT NOT NULL, O_CLERK TEXT NOT NULL, O_SHIPPRIORITY INT NOT NULL, O_COMMENT TEXT NOT NULL ) WITH ( distribution_key = 'O_ORDERKEY',--在join时可以实现local join event_time_column = 'O_ORDERDATE'--加速文件裁剪 ); COMMIT;
样例数据导入
通过HoloWeb一键导入公共数据集功能,将TPC-H 100 GB的数据快速导入至Hologres实例中,详情请参见一键导入公共数据集。
性能测试结果对比
为表设置了合适的属性(索引)后,测试优化前后的性能结果。
测试环境
实例规格:32 Core。
网络类型:VPC网络。
使用PSQL客户端执行两次Query,取第二次执行的时间。
测试结论
对于单表的过滤查询,将过滤字段设置为Clustering Key,可以有效加速查询。
对于多表关联的查询,将JOIN字段设置为Distribution Key,可以显著加速JOIN效率。
Query
Hologres设置索引的Latency
Hologres未设置任何索引的Latency
Q1
48.293 ms
59.483 ms
Q4
822.389 ms
3027.957 ms
附录资料参考
阅读更多
技术原理篇
Hologres技术原理揭秘(架构、存储引擎、计算引擎等核心原理揭秘):阿里巴巴云原生实时数仓核心技术揭秘。
服务开通篇
如何选择规格:实例规格概述。
子账号必读:RAM用户权限授权快速入门。
数据导入篇
Flink实时写入、维表查询:Flink全托管。
MySQL、Oracle、PolarDB等数据库数据实现整库实时同步:配置数据源(来源为MySQL)。
导入OSS数据:OSS数据湖加速。
数据写入必读:使用Fixed Plan可将数据写入、更新效率提升10倍,详情请参见Fixed Plan加速SQL执行。
数据查询篇
基于场景建表建议:场景化建表调优指南。
建表必读:掌握关键参数distribution_key、clustering_key、event_time_column及bitmap_index,了解语法和索引,设置合理的表结构性能提升N倍,详情请参见建表概述。
内部表性能调优:优化内部表的性能。
MaxCompute加速必读:通过创建外部表加速查询MaxCompute数据。
运维监控篇
活跃Query(排查正在运行的Query、当前是否存有锁或者被锁):Query管理。
慢Query(排查失败、耗时较长的Query):慢Query日志查看与分析。
读写分离、负载隔离:主从实例读写分离部署(共享存储)。
实践案例篇
实践与案例集:行业典型场景最佳实践与经典用户案例。