聊聊分布式 SQL 数据库Doris(八)
稀疏索引
密集索引:文件中的每个搜索码值都对应一个索引值,就是叶子节点保存了整行.
稀疏索引:文件只为索引码的某些值建立索引项.
稀疏索引的创建过程包括将集合中的元素分段,并给每个分段中的最小元素创建索引。在搜索时,先定位到第一个大于搜索值的索引的前一个索引,然后从该索引所在的分段中从前向后顺序遍历,直到找到该搜索值的元素或第一个大于该搜索值的元素。
Doris中的前缀索引、Bloom Filter属于稀疏索引.
以mysql为例,主键索引是稠密索引; 非主键索引(非聚簇索引)是稀疏索引. 如下是mysql的B+树索引结构图.
主键索引, 注意叶子节点的主键值时有序的.
非主键索引
联合索引
稀疏索引占用空间少,但是在查询的精确率上还是相对于稠密索引还是比较慢的,因为不需要顺序查找,还有回表。
稠密索引那就是相对来说比较快,因为他可以精确定位数据,但是占用的空间比较大。
参考:
delete
delete: 本质上是存储了一个删除条件,在查询时会对每一行记录应用这个删除条件做过滤,因此当有大量删除条件时,查询效率就会降低。
批量删除: 仅适用于 UNIQUE KEY 模型,解决了delete大批量数据的性能问题; Doris内部会增加一个隐藏列__DORIS_DELETE_SIGN__
. 该列的类型为bool,聚合函数为replace. 在导入与读取时,增加隐藏列的判断,筛选过滤掉不必要的数据.
参考:
更新
Doris中存储的数据都是以追加(Append)的方式进入系统,这意味着所有已写入的数据是不可变更(immutable)的。所以Doris采用标记的方式来实现数据更新的目的; 利用查询引擎自身的 where 过滤逻辑,从待更新表中筛选出需要被更新(被标记)的行。再利用 Unique 模型自带的 Value 列新数据替换旧数据的逻辑,将待更新的行变更后,再重新插入到表中,从而实现行级别更新。
适用场景
- 对满足某些条件的行,修改其取值;
- 点更新,小范围更新,待更新的行最好是整个表的非常小的一部分;因为大批量数据下整行更新,会导致性能较低。
- update 命令只能在 Unique 数据模型的表中执行;因为只有该模型可以保证主键的唯一性,从而支持按主键对数据进行更新。
假设 Doris 中存在一张订单表,其中 订单id 是 Key 列,订单状态,订单金额是 Value 列。数据状态如下:
订单 | 订单金额 | 订单状态 |
---|---|---|
1 | 100 | 待付款 |
这时候,用户点击付款后,Doris 系统需要将订单id 为 '1' 的订单状态变更为 '待发货',就需要用到 Update 功能。
UPDATE test_order SET order_status = '待发货' WHERE order_id = 1;
更新结果如下:
订单 | 订单金额 | 订单状态 |
---|---|---|
1 | 100 | 待发货 |
用户执行 UPDATE 命令后,系统会进行如下三步:
-
第一步:读取满足 WHERE 订单id=1 的行 (1,100,'待付款')
-
第二步:变更该行的订单状态,从'待付款'改为'待发货' (1,100,'待发货')
-
第三步:将更新后的行再插入原表中,从而达到更新的效果。
订单 | 订单金额 | 订单状态 |
---|---|---|
1 | 100 | 待付款 |
1 | 100 | 待发货 |
由于表 test_order 是 UNIQUE 模型,所以相同 Key 的行,之后后者才会生效,所以最终效果如下:
订单 | 订单金额 | 订单状态 |
---|---|---|
1 | 100 | 待发货 |
部分列更新
Doris默认的更新是行更新. 列更新可以很大程度上提高写入与并发性能.
Unique Key模型的Merge-on-Write结合MVCC支持部分列更新.
Aggregate Key模型将聚合函数设置为REPLACE_IF_NOT_NULL
支持部分列更新.
更新原理
Unique Key模型的列更新实现:用户通过正常的导入方式将一部分列的数据写入Doris的Memtable,此时Memtable中并没有整行数据,在Memtable下刷的时候,会查找历史数据,用历史数据补齐一整行,并写入数据文件中,同时将历史数据文件中相同key的数据行标记删除。
Aggregate Key模型,则是直接利用聚合函数筛选过滤。
使用建议:
- 对写入性能要求较高,查询性能要求较低的用户,建议使用Aggregate Key模型
- 对查询性能要求较高,对写入性能要求不高(例如数据的写入和更新基本都在凌晨低峰期完成),或者写入频率不高的用户,建议使用Unique Key模型merge-on-write实现
参考: