Apache doris 排序键及ShortKey Index

简介: Apache Doris中为加速查询,在内部组织并存储数据时,会把表中数据按照指定的列进行排序,这部分用于排序的列(可以是一个或多个列),可以称之为Sort Key。明细模型中Sort Key就是指定的用于排序的列(即 DUPLICATE KEY 指定的列),聚合模型中Sort Key列就是用于聚合的列(即 AGGREGATE KEY 指定的列),唯一主键模型中Sort Key就是指定的满足唯一性约束的列(即 UNIQUE KEY 指定的列)。

1.排序列的原理


Apache Doris中为加速查询,在内部组织并存储数据时,会把表中数据按照指定的列进行排序,这部分用于排序的列(可以是一个或多个列),可以称之为Sort Key。明细模型中Sort Key就是指定的用于排序的列(即 DUPLICATE KEY 指定的列),聚合模型中Sort Key列就是用于聚合的列(即 AGGREGATE KEY 指定的列),唯一主键模型中Sort Key就是指定的满足唯一性约束的列(即 UNIQUE KEY 指定的列)。下图中的建表语句中Sort Key都为 (user_id, date, city, age, sex)。


CREATE TABLE user_access_dup
(
    `user_id` LARGEINT NOT NULL COMMENT "用户id",
    `date` DATE NOT NULL COMMENT "数据灌入日期时间",
    `city` VARCHAR(20) COMMENT "用户所在城市",
    `age` SMALLINT COMMENT "用户年龄",
    `sex` TINYINT COMMENT "用户性别",
     user_name VARCHAR(32) DEFAULT '',
    `last_visit_date` DATETIME DEFAULT "1970-01-01 00:00:00" COMMENT "用户最后一次访问时间",
    `cost` BIGINT DEFAULT "0" COMMENT "用户总消费",
    `max_dwell_time` INT DEFAULT "0" COMMENT "用户最大停留时间",
    `min_dwell_time` INT DEFAULT "99999" COMMENT "用户最小停留时间"
)
DUPLICATE KEY(user_id, date, city, age, sex)
DISTRIBUTED BY HASH(city) BUCKETS 10;
CREATE TABLE user_access_agg
(
    `user_id` LARGEINT NOT NULL COMMENT "用户id",
    `date` DATE NOT NULL COMMENT "数据灌入日期时间",
    `city` VARCHAR(20) COMMENT "用户所在城市",
    `age` SMALLINT COMMENT "用户年龄",
    `sex` TINYINT COMMENT "用户性别",
     user_name VARCHAR(32) DEFAULT '',
    `last_visit_date` DATETIME REPLACE DEFAULT "1970-01-01 00:00:00" COMMENT "用户最后一次访问时间",
    `cost` BIGINT SUM DEFAULT "0" COMMENT "用户总消费",
    `max_dwell_time` INT MAX DEFAULT "0" COMMENT "用户最大停留时间",
    `min_dwell_time` INT MIN DEFAULT "99999" COMMENT "用户最小停留时间"
)
AGGREGATE KEY(user_id, date, city, age, sex)
DISTRIBUTED BY HASH(city) BUCKETS 10;
CREATE TABLE user_access_unique
(
    `user_id` LARGEINT NOT NULL COMMENT "用户id",
    `date` DATE NOT NULL COMMENT "数据灌入日期时间",
    `city` VARCHAR(20) COMMENT "用户所在城市",
    `age` SMALLINT COMMENT "用户年龄",
    `sex` TINYINT COMMENT "用户性别",
     user_name VARCHAR(32) DEFAULT '',
    `last_visit_date` DATETIME DEFAULT "1970-01-01 00:00:00" COMMENT "用户最后一次访问时间",
    `cost` BIGINT DEFAULT "0" COMMENT "用户总消费",
    `max_dwell_time` INT DEFAULT "0" COMMENT "用户最大停留时间",
    `min_dwell_time` INT EFAULT "99999" COMMENT "用户最小停留时间"
)
UNIQUE KEY(user_id, date, city, age, sex)
DISTRIBUTED BY HASH(city) BUCKETS 10;

各表数据都依照user_id, date, city, age, sex这四列排序。这里有两点需要注意:


排序列的定义必须出现在建表语句中其他列的定义之前。的建表语句为例,三个表的排序列可以是user_id, date, city, age, sex,或者user_id, date, city, age, sex,user_name,但不能是user_id, date, city, ,user_name,或者user_id, date, city, age, sex


排序列的顺序是由create table语句中的列顺序决定的。


DUPLICATE/UNIQUE/AGGREGATE KEY中顺序需要和create table语句保持一致。以user_access_dup表为例,也就是说下面的建表语句会报错。


-- 错误的建表语句

CREATE TABLE user_access_dup
(
    `user_id` LARGEINT NOT NULL COMMENT "用户id",
    `date` DATE NOT NULL COMMENT "数据灌入日期时间",
    `city` VARCHAR(20) COMMENT "用户所在城市",
    `age` SMALLINT COMMENT "用户年龄",
    `sex` TINYINT COMMENT "用户性别",
     user_name VARCHAR(32) DEFAULT '',
    `last_visit_date` DATETIME DEFAULT "1970-01-01 00:00:00" COMMENT "用户最后一次访问时间",
    `cost` BIGINT DEFAULT "0" COMMENT "用户总消费",
    `max_dwell_time` INT DEFAULT "0" COMMENT "用户最大停留时间",
    `min_dwell_time` INT EFAULT "99999" COMMENT "用户最小停留时间"
)
DUPLICATE KEY(date,user_id,city,age,sex)
DISTRIBUTED BY HASH(city) BUCKETS 10;
-- 正确的建表语句
CREATE TABLE user_access_dup
(
    `user_id` LARGEINT NOT NULL COMMENT "用户id",
    `date` DATE NOT NULL COMMENT "数据灌入日期时间",
    `city` VARCHAR(20) COMMENT "用户所在城市",
    `age` SMALLINT COMMENT "用户年龄",
    `sex` TINYINT COMMENT "用户性别",
     user_name VARCHAR(32) DEFAULT '',
    `last_visit_date` DATETIME DEFAULT "1970-01-01 00:00:00" COMMENT "用户最后一次访问时间",
    `cost` BIGINT DEFAULT "0" COMMENT "用户总消费",
    `max_dwell_time` INT DEFAULT "0" COMMENT "用户最大停留时间",
    `min_dwell_time` INT EFAULT "99999" COMMENT "用户最小停留时间"
)
DUPLICATE KEY(user_id, date, city, age, sex)
DISTRIBUTED BY HASH(city) BUCKETS 10;
DUPLICATE KEY列顺序与CREATE TABLE中不一致

再来看一下排序列在查询中的效果,图1中排序列的效果可分三种情况进行描述:


用户查询时如果条件包含上述两列,则可以大幅地降低扫描数据行,如:

select sum(cost) from user_access_dup where user_id = 123 and city = 2 ;

如果查询只包含site_id一列,也能定位到只包含user_id 的数据行,如:


select sum(cost) from user_access_dup where user_id = 123;

如果查询只包含city一列,那么需要扫描所有的数据行,排序的效果相当于大打折扣,如:


select sum(cost) from user_access_dup  where city = 2;

在第一个case中,为了定位到数据行的位置,需进行二分查找,以找到指定区间。假设数据行非常多,直接对user_id, date, city, age, sex进行二分查找,需要把两列数据都加载到内存中,这会消耗大量内存空间。为优化这个细节,Doris在Sort Key的基础上引入稀疏的shortkey index,Sort Index的内容会比数据量少1024倍,因此会全量缓存在内存中,实际查找的过程中可以有效加速查询。当Sort Key列数非常多时,会占用大量内存, 为了避免这种情况, 对shortkey index索引项做了限制:


shortkey 的列只能是排序键的前缀;


shortkey 列数不超过3;


字节数不超过36字节;


不包含FLOAT/DOUBLE类型的列;


VARCHAR类型列只能出现一次, 并且是末尾位置;


当shortkey index的末尾列为CHAR或者VARCHAR类型时, shortkey的长度会超过36字节;

当用户在建表语句中指定short_key属性时,例如”short_key” = “4”指定4个列作为short_key, 可突破上述限制;


  1. 如何选择排序列


从上面的介绍可以看出,如果用户在查询user_access_dup表时只选择city做查询条件,排序列相当于失去了功效。因此排序列的选择是和查询模式息息相关的,经常作为查询条件的列建议放在Sort Key中。


当Sort Key涉及多个列的时候,谁先谁后也有讲究,区分度高、经常查询的列建议放在前面。在user_access_dup表中,city的取值个数是固定的(城市数目是固定的),而user_id的取值个数要比city大得多,而且还在不断变多,因此user_id区分度就比city要高不少。


还是以user_access_dup表为例:


如果用户需要经常按user_id + date + city + age + sex的组合进行查询,那么把user_id 放在Sort Key第一列就是更加有效的一种方式。


如果用户需要经常用 city进行查询,偶尔按照 user_id + date + city + age + sex 组合查询,那么把 city 放在 Sort Key 的第一列就更为合适。


当然有一种极端情况,就是按user_id + date组合查询、以及city + date 查询的比例不相上下。那么这个时候,可以创建一个city + date 为组合 key 的 RollUp 表,RollUp表会为city,date 再建一个Sort Index。


  1. 注意事项

  2. 由于Doris的shortkey索引大小固定(只有36字节),所以不会存在内存膨胀的问题。需要注意的是:


排序列中包含的列必须是从第一列开始,并且连续的。


排序列的顺序是由create table语句中的列顺序决定的。


Sort Key不应该包含过多的列。如果选择了大量的列用于Sort Key,那么排序的开销会导致数据导入的开销增加。


在大多数时候,Sort Key的前面几列也能很准确的定位到数据行所在的区间,更多列的排序也不会带来查询的提升




目录
相关文章
|
2月前
|
存储 自然语言处理 BI
|
2月前
|
Apache Java 数据库连接
Apache Doris 2.0.15 版本发布
Apache Doris 2.0.15 版本已于 2024 年 9 月 30 日正式与大家见面,该版本提交了 157 个改进项以及问题修复,进一步提升了系统的性能及稳定性,欢迎大家下载体验。
|
3月前
|
SQL 消息中间件 关系型数据库
Apache Doris Flink Connector 24.0.0 版本正式发布
该版本新增了对 Flink 1.20 的支持,并支持通过 Arrow Flight SQL 高速读取 Doris 中数据。
|
3月前
|
存储 SQL 缓存
快手:从 Clickhouse 到 Apache Doris,实现湖仓分离向湖仓一体架构升级
快手 OLAP 系统为内外多个场景提供数据服务,每天承载近 10 亿的查询请求。原有湖仓分离架构,由离线数据湖和实时数仓组成,面临存储冗余、资源抢占、治理复杂、查询调优难等问题。通过引入 Apache Doris 湖仓一体能力,替换了 Clickhouse ,升级为湖仓一体架构,并结合 Doris 的物化视图改写能力和自动物化服务,实现高性能的数据查询以及灵活的数据治理。
快手:从 Clickhouse 到 Apache Doris,实现湖仓分离向湖仓一体架构升级
|
2天前
|
存储 SQL Apache
Apache Doris 创始人:何为“现代化”的数据仓库?
3.0 版本是 Apache Doris 研发路程中的重要里程碑,他将这一进展总结为“实时之路”、“统一之路”和“弹性之路”,详细介绍了所对应的核心特性的设计思考与应用价值,揭晓了 2025 年社区发展蓝图
Apache Doris 创始人:何为“现代化”的数据仓库?
|
4天前
|
SQL 存储 数据处理
别让你的CPU打盹儿:Apache Doris并行执行原理大揭秘!
别让你的CPU打盹儿:Apache Doris并行执行原理大揭秘!
30 1
别让你的CPU打盹儿:Apache Doris并行执行原理大揭秘!
|
29天前
|
存储 消息中间件 分布式计算
Cisco WebEx 数据平台:统一 Trino、Pinot、Iceberg 及 Kyuubi,探索 Apache Doris 在 Cisco 的改造实践
Cisco WebEx 早期数据平台采用了多系统架构(包括 Trino、Pinot、Iceberg 、 Kyuubi 等),面临架构复杂、数据冗余存储、运维困难、资源利用率低、数据时效性差等问题。因此,引入 Apache Doris 替换了 Trino、Pinot 、 Iceberg 及 Kyuubi 技术栈,依赖于 Doris 的实时数据湖能力及高性能 OLAP 分析能力,统一数据湖仓及查询分析引擎,显著提升了查询性能及系统稳定性,同时实现资源成本降低 30%。
Cisco WebEx 数据平台:统一 Trino、Pinot、Iceberg 及 Kyuubi,探索 Apache Doris 在 Cisco 的改造实践
|
19天前
|
SQL 存储 Apache
Apache Doris 3.0.3 版本正式发布
亲爱的社区小伙伴们,Apache Doris 3.0.3 版本已于 2024 年 12 月 02 日正式发布。该版本进一步提升了系统的性能及稳定性,欢迎大家下载体验。
|
1月前
|
存储 SQL Apache
Apache Doris 开源最顶级基于MPP架构的高性能实时分析数据库
Apache Doris 是一个基于 MPP 架构的高性能实时分析数据库,以其极高的速度和易用性著称。它支持高并发点查询和复杂分析场景,适用于报表分析、即席查询、数据仓库和数据湖查询加速等。最新发布的 2.0.2 版本在性能、稳定性和多租户支持方面有显著提升。社区活跃,已广泛应用于电商、广告、用户行为分析等领域。
Apache Doris 开源最顶级基于MPP架构的高性能实时分析数据库
|
1月前
|
SQL 存储 数据处理
兼顾高性能与低成本,浅析 Apache Doris 异步物化视图原理及典型场景
Apache Doris 物化视图进行了支持。**早期版本中,Doris 支持同步物化视图;从 2.1 版本开始,正式引入异步物化视图,[并在 3.0 版本中完善了这一功能](https://www.selectdb.com/blog/1058)。**

推荐镜像

更多