背景
2022年5月,PolarDB-X为了加强数据库HTAP一体化能力,便开始构思行列混存架构,希望通过列式存储格式的数据进一步加强AP能力,同时具有更好的数据压缩比,降低存储成本,在数据分析场景能够让用户有更好的体验。
开源PolarDB-X V2.4版本在2024年4月份发布,首次推出列存索引能力,在原来的架构上增加了列存引擎节点(Columnar),目前的架构如下:

计算节点(CN, Compute Node)
计算节点是系统的入口,采用无状态设计,包括 SQL 解析器、优化器、执行器等模块。负责数据分布式路由、计算及动态调度,负责分布式事务 2PC 协调、全局二级索引维护等,同时提供 SQL 限流、三权分立等企业级特性。存储节点(DN, Data Node)
存储节点负责数据的持久化,基于多数派 Paxos 协议提供数据高可靠、强一致保障,同时通过 MVCC 维护分布式事务可见性。元数据服务(GMS, Global Meta Service)
元数据服务负责维护全局强一致的 Table/Schema, Statistics 等系统 Meta 信息,维护账号、权限等安全信息,同时提供全局授时服务(即 TSO)。日志节点(CDC, Change Data Capture)
日志节点提供完全兼容 MySQL Binlog 格式和协议的增量订阅能力,提供兼容 MySQL Replication 协议的主从复制能力。列存节点 (Columnar)
列存节点提供持久化列存索引,实时消费分布式事务的binlog日志,基于对象存储介质构建列存索引,能满足实时更新的需求、以及结合计算节点可提供列存的快照一致性查询能力。
设计思路
第一步,当然是确定架构,该架构需满足如下期望点:
不希望对现有的行存造成影响,需要进行资源隔离,所以新增列存节点。
采用计算存储分离架构,能够较大提高资源利用率和高可用能力,所以需要计算节点和存储节点分离。
沿用分布式框架,加强可扩展性,所以采用共享存储。
满足这三点的前提下,一个大概的分布式数据库雏形就出现了:

如图所示,左边的PolarDB-X是之前只有行存的架构,在此基础上,增加一个列存引擎,负责同步行存的数据,并以列式格式存储到共享存储中,以一个个文件的形式存储数据,列存只读计算节点负责接收用户的sql请求,解析成需要查询的各个算子,通过读取共享存储中的文件,返回数据给用户。至此,一个完整的数据链路就形成了,各个组件的功能也明确,下一步,便是细化各个组件的设计。
第二步,确定共享存储的形态,列存更多是面向分析型计算场景,该数据场景对存储有几点要求:
海量存储:存储量大,对存储伸缩性高,可扩展。
带宽高:AP场景计算量大,多线程并发高,需要较高带宽。
持久化和安全性:存储需具备的基本特点。
存储成本尽可能低:在满足性能和可靠性要求下,尽可能降低存储成本,是所有用户期望的。
综合上述几点,PolarDB-X首选了阿里云的对象存储(OSS)作为常用场景,阿里云对象存储OSS(Object Storage Service)是一款海量、安全、低成本、高可靠的云存储服务,可提供99.9999999999%(12个9)的数据持久性,99.995%的数据可用性,性能上普通用户便可达10Gbps带宽,最重要的,它的存储成本极低,按量付费存储价格:
存储类型 | 标准型单价 | 低频访问型单价 | 归档型单价 |
数据存储(本地冗余存储) | 0.12 元/GB/月 | 0.08 元/GB/月 | 0.033 元/GB/月 |
数据存储(同城冗余存储) | 0.15 元/GB/月 | 0.10 元/GB/月 | 0.033 元/GB/月 |
OSS存储价格截取的2024年9月阿里云官网OSS价格计算表,详细请见:https://www.aliyun.com/price/product?spm=a2c4g.11186623.0.0.24c21bfbH7J03f#/oss/detail/ossbag
如此低廉的价格成本能够让列存存储成本降到非常低,而且列式存储文件支持不同的压缩算法,通常具备3~10倍的数据压缩效率,更加降低压缩成本。当然,OSS存储比较适合公有云用户,对于其它应用场景,需支持其它共享存储,例如NFS、S3等,所以列存引擎和计算节点需要抽象出文件系统接口,方便后续兼容不同的存储模型。
第三步,列存只读计算节点如何设计,PolarDB-X行存已经是一个较为成熟的计算节点设计方案,但列存和行存在优化器和执行器层面会有较大区别,AP场景和TP场景有着本质上的区别,所以列存只读计算节点需要设计一套适合AP场景的优化器和执行器,PoldarDB-X选择在现有的行存计算节点上进行设计,使用同一套代码,以不同模式启动,方便后续在行列混存一体化上面设计出最佳执行计划,充分利用行存和列存特性,用户执行一条sql,可同时在行存和列存组合执行,返回用户结果,在行列混存的新型数据库架构中造探索出最佳实践,以更好地适应多样化的数据处理需求,在数据库前沿方向进行技术创新。
第四步,设计合适的列存存储引擎,完成数据同步,将行存转成列存,该部分也是本文将要详细介绍的内容。
列存引擎
列存引擎从大的功能方向上看,主要负责两件事:同步,存储。同步指能够从行存数据中同步到列存,保证行列数据一致。存储指可以将行存数据转成列式存储,形成一个个列式存储文件。当然,列存存储引擎作为一个产品,还需在高可用、持久化、数据校验、存储空间等方面进行设计,逐步将产品优化并完善,以最终满足业务生产的所有需求,从而发展为成熟的工业级解决方案。
同步
PolarDB-X属于Mysql生态的分布式数据库产品,数据同步是Mysql生态不同产品进行数据流动的重要手段,MySQL 通过 Binlog 机制实现了与下游系统的实时增量数据同步,例如Kafka、Canal、DTS等同步工具,都支持通过binlog进行上下游同步。PolarDB-X在早期便启动全局binlog计划,已经有CDC节点,将分布式数据库下的多个DN的Binlog进行合并排序,形成全局一致的binlog,可供其它Mysql生态系统或工具进行增量数据同步。更多CDC相关内容可查看以往CDC历史文章注1。

PolarDB-X为了实现分布式事务的高可用和一致性,引入了全局时间戳功能注2(TimeStampOracle,TSO),每一个事务提交时都包含一个TSO值(Commit TSO,CTS),CDC节点对事务的排序也是按照CTS值顺序,形成全局binlog,可行的理论分析可见PolarDB-X 全局 Binlog 解读之理论篇注3。这意味着PolarDB-X的全局binlog相比于普通binlog,为每一个事务新增了CTS值记录,CTS值代表该事务的提交时间戳,该值有序递增,CTS值也可认为是行存的一个快照点,进而可衍生为行存快照的一个快照版本,请记住该值,该值和列存索引也息息相关。
列存引擎继续兼容Mysql生态,将通过binlog来进行增量数据同步,而在PolarDB-X中,则通过全局binlog来进行数据消费,让列存索引的数据和行存数据一致,全局binlog可看成是所有DML操作的日志记录,简单理解可认为是一个个事务操作记录,如下图所示:

所有的数据修改记录成一个个事务方式,且每个Commit事件后会有一个CTS记录,代表该事务的提交TSO值,列存引擎则通过消费一个个binlog文件,检测到包含列存索引的表,则对应的Insert/Delete/Update事件需要进行数据同步,完成数据写入列存。列存引擎消费binlog将会以事务为单位,以保证事务一致性,不会在事务中间提交。消费binlog事件是一个串行操作,为了提高消费效率,攒批消费,通过batch方式能够极大提高消费速度,但是攒批意味着需要等待时间,如何平衡性能和行列延迟也是需要考虑的。

如上图所示,用户写入PolarDB-X行存,依次提交事务1~5,PolarDB-X的CDC节点需要一定时间生成全局binlog,这部分延迟比较小,一般在几百毫秒以内,列存引擎消费binlog事件,可以发现,如果列存引擎攒批一起提交,需要等一定时间,会导致列存与行存之间的延迟增大,而Batch攒批方式能够加速消费binlog速度,所以列存引擎会根据流量来判断提交时机,当数据量达到一定大小或者列存延迟达到阈值,便触发一次提交,以保证列存与行存之间的延迟不会太大。同时,每一次列存引擎提交数据,都会产生一个列存版本快照,如图中所示V1、V2、V3,binlog事件包含CTS值,所以列存版本会以该值作为版本号,记录下TSO值,该事务提交TSO值与行存事务可见性相关,因此,可以得到一个列存版本快照对应的行存快照,为后续列存和行存数据校验是否一致提供了理论基础。
存储
列存引擎的数据输入有了,通过消费binlog事件,以一个或多个事务为间隔进行提交,每一个事务包含Insert/Delete/Update事件,这三种事件代表了DML操作,可以发现,这些事件的组合是以行存为基础,一行一行数据进行消费,列存引擎需要将这些数据转成列式存储,上传到共享存储OSS中。
首先,在列式存储文件格式上,PolarDB-X选择了Apache ORC格式,Apache ORC是一种高效的列式存储格式,专为Hadoop生态系统设计,旨在提高大规模数据存储和处理效率,在数仓场景和分析型查询方面应用广泛。

Orc格式以stripe为单位,每个stripe内存储数据,并包含索引信息,文件尾部记录了每个stripe的位置,便于快速定位到数据位置,具有如下几个特点:
高性能:经过长期优化,已是较为成熟的产品,IO读写性能较高。
高压缩率:支持多种压缩算法,例如ZLIB, SNAPPY, LZO, LZ4, ZSTD等。
灵活的索引结构和统计信息:支持多种索引,内嵌多种统计信息,例如最大值、最小值等。
数据结构丰富:支持复杂数据类型,包括structs、lists、maps等。
列式存储格式确定,下面需要考虑如何高效行存转列存,设计合适的存储模型,考虑几个限制:
共享存储OSS只支持追加写,不支持随机修改。
Orc列式格式一般是批量数据,单个文件内容较大。
想象一种最简单的场景,只包含Insert操作,所有数据都是有效数据,最直接的存储结构如下图所示:

可以通过先追加写入CSV文件,等文件写满,把整个文件的行式存储转成ORC列式存储文件,就是一个完整的流程。实际场景则有Delete操作,Update操作,对已有数据的修改,将会增加存储架构的复杂度,常见做法有如下几种:
第一种,Copy on write(COW)模型,指当执行数据更新时,从原来的数据复制出来一个副本,在副本上进行数据更新,当副本更新完成后把原始数据替换为当前副本。如下图所示,该方式对于文件更新来说是灾难性的操作,往往每个文件可能只会更改少部分数据,但是需要重写整个文件,写放大问题严重,所以COW更适合数据块单位较小的数据结构,例如B+树结构,对某个数据节点进行更新时,则可通过COW方式更新数据,带来的写放大也可以接受。

第二种方式,LSM树方式,指使用LSM树结构存储数据,将随机写入改成顺序写入,Delete/Update也可以追加写入数据,代表该值的新版本数据,在内存中写满阈值后,形成一个个SST文件,后台通过compaction进行数据合并,去除冗余数据和排序数据。如下图所示,将SSTable文件格式改成列式存储,能够较好适配写入,但是读取性能由于需要读多层数据,涉及文件较多,需归并处理,对读性能牺牲较大。

第三种方式,Delta Main模型(类LSM树结构),利用LSM树思想,写入时依然顺序写入数据,数据更改也是追加记录,追加记录认为是Delta Data(增量数据),等Delta Data写满阈值,转成Main Data数据(主体数据),一个单位内的数据可以认为是一段范围内的数据、一个分区内的数据或者一个文件内的数据,可根据实际情况进行分隔,这样对读性能进行优化,一段范围内的数据只需要读取Main Data和Delta Data,需要归并的数据大大降低,并且利用后台异步线程尽可能将Delta Data数据转成Main Data,进一步减少读开销。

PolarDB-X的列存引擎采用Delta Main模型(类LSM树结构),兼顾写性能与读性能,在此基础上,设计通过binlog同步列存数据的存储结构,在该场景下尽可能找到最合适的存储模型。
首先在数据布局方面,列存引擎沿用了PolarDB-X的分区策略,可通过特定的分区规则将数据切割成若干个分区,可将列存索引数据分割成一个个分区数据,对于并行写入和并行读取都能有收益,目前支持Hash/Key/Range/List等分区策略,更多分区相关内容可参见产品说明注4。

在单个数据分区内,随着不断同步binlog数据,列存引擎会批量提交事务,这个动作称为Group Commit,会把Insert/Delete/Update操作在数据所在分区进行写入,所有分区更改一起并行事务提交,数据写入文件,元数据(列存索引元信息、文件元信息等)写入MetaDB,事务提交需保证原子性,同时提交会让列存数据版本进行更新,记录下同步到的binlog位点,如果提交出错,列存引擎重新从已提交版本的binlog位点开始同步即可。具体的Group Commit操作如下图所示:

Group Commit操作对binlog事件类型进行不同处理:
Insert事件,代表新增数据,将数据追加写入CSV文件(代表追加写入的新增数据)。
Delete事件,代表删除某行数据,需要增加删除标记,删除标记为哪个文件的哪行数据是无效的,所以会为每个文件构建bitmap标记,需要删除,则增加标识,bitmap采用的RoaringBitmap,RoaringBitmap支持序列化、反序列化,OR操作合并bitmap等,每次提交某文件需要增加删除标记,则新增该bitmap的序列化数据,读取时,将该文件的所有bitmap通过OR操作合并成一个bitmap,则是该文件的所有删除标记,一个分区内的所有文件bitmap数据会不停追加写入DEL文件中(实际为bitmap序列化数据),因为bitmap数据一般比较小,聚合到一个文件中,以避免小块数据写入。
Update事件,Update事件实际转为Delete操作+Insert操作。
DDL事件,ddl事件会单独提交,不会和数据更改操作一起提交,所以DDL事件会单独执行DDL操作。
可以发现,图中还存在一个主键索引,该作用便是Delete事件快速定位到该行数据的位置(哪个文件,文件位置),才方便增加删除标记,所以列存引擎会维护一个<主键,位置>的键值存储(Key-Value Store),每行数据都会有一个主键值代表其唯一性,而位置信息则是{文件id,第几行index},4B+4B字节大小即可,所以主键索引是一个小value的KV持久化存储。
Group Commit由于是批量事务一起提交,包含多个Insert/Delete/Update操作,所以列存引擎可以先在内存聚合更改,对于同一个主键值的不同操作,可直接在内存就去除中间的冗余历史版本,以减少提交操作,例如insert,然后Delete该行数据,无需额外写入,对于同一主键的任意写入行为,可拆解成一下几种情况:
操作类型(同一主键值) | 操作 | merge结果 |
Insert | mem.add(pk, row) | \ |
Delete | mem.remove(pk) | \ |
Update | mem.remove(pk, row1) mem.add(pk, row2) | \ |
Insert-Insert | \ (主键冲突,非预期) | \ |
Insert-Delete | mem.add(pk, row) mem.remove(pk) | nothing |
Insert-Update | mem.add(pk, row1) mem.remove(pk, row1) mem.add(pk, row2) | mem.add(pk,row2) |
Update-Insert | \ (主键冲突,非预期) | \ |
Update-Delete | mem.remove(pk, row1) mem.add(pk, row2) mem.remove(pk, row2) | mem.remove(pk,row1) |
Update-Update | mem.remove(pk, row1) mem.add(pk, row2) mem.remove(pk, row2) mem.add(pk, row3) | mem.remove(pk,row1) mem.add(pk,row3) |
Delete-Insert | mem.remove(pk) mem.add(pk, row) | mem.remove(pk) mem.add(pk, row) |
Delete-Delete | \ (重复删除,非预期) | \ |
Delete-Update | \ (修改不存在数据,非预期) | \ |
可以发现,有一些操作之间可以合并简化,减少中间数据提交。
从列存只读节点读取列存数据视角来看,列存数据版本中,每个分区内包含三种文件:
CSV文件:追加写入的行式存储数据,因为数据是一行一行写入的多列数据组合,看起来和csv格式类似,所以称呼csv文件,早期csv文件里面存储的数据就是严格的可视字符数据,可直接用csv查看工具打开,但在后续测试中发现,没法保证列数据中是否包含分隔符,需要对数据进行扫描,包含分隔符特殊处理,导致需要消耗较多CPU资源,影响同步数据,后续直接将binlog中的字节数据序列化存储到csv文件,并不是csv格式,但文件名称保留下来的。当CSV文件长度或行数达到阈值时,会进行compaction,转成列式存储的ORC文件。
ORC文件:列式存储格式的文件数据,一旦生成,不会再修改,也是列式存储的主体数据,一个个文件代表了一段范围的数据,文件内是有序排列的。
DEL文件:记录一个分区内所有文件的删除bitmap标记数据,相当于每一个CSV或ORC文件都有一个对应的bitmap数据,数据存储在DEL文件中。

可以发现,读取时应尽可能缓存Delta Data,特别是DEL数据,这样读取某个ORC文件时,便能快速过滤无效数据,所以让DEL数据常驻内存是收益较大的,同时ORC文件是范围有序的,通过排序键进行排序,能够较好地进行裁剪,读取符合条件的数据,而CSV文件是无序的,无法裁剪,这部分数据缓存起来也能提高读取性能,因此,Delta Data应尽量小,且优先缓存。
列存的主键索引不参与列存读节点进行读取,原因:1、目前功能是为了列存引擎写节点定位主键所在文件位置,如果读节点也可访问KV存储,分布式并发读写访问KV存储,无疑增加KV存储结构复杂度。2、该KV存储只记录了主键和所在文件位置,对于单个KV读取必然高效,但是这是列存,更多用于大批数据分析查询场景,单行数据查询不如使用行存,也能高效读取。
列存主键索引
前文讲到,列存引擎包含主键索引来快速定位删除的主键的所在文件位置,方便较快给该文件的bitmap增加删除标记,所以列存的主键索引是一个小value的KV存储,该KV存储结构也是需要进行设计,首先考虑KV存储的使用场景特征:
KV是主键+文件位置,文件位置信息为{文件id,第几行index},4B+4B=8B字节大小。
KV存储结构需具备持久化特性,存储在共享存储中。
请求包括Read/Insert/Delete,无需范围读取。
高性能读取,每次请求可批量操作。
常用的KV存储结构主要有:Hash、B+Tree、Radix-Tree、LSM树等。不用范围读取情况下,采用Hash结构无疑是性能最佳的选择,但是目前存在的持久化Hash产品较少,常用于内存中的缓存,持久化还需结合共享存储只能顺序写入的特点,所以PolarDB-X选择自研适用于该场景下的KV存储,结合Hash结构的高性能与LSM树的IO优化,设计了Hash-LSM树的KV存储,无需范围读取的LSM树结构,如下图所示:

在LSM树结构的基础上进行变化,无需范围排序,当追加写日志写满后,通过hash规则分散数据,每一个Hash Bucket使用SST文件存储数据,如果单个SST文件较大后,又可再次通过hash规则分散数据,可以控制每一个SST的数据大小,以防Hash Bucket过大,导致读取性能下降,读取时,可根据key通过hash规则快速定位到Hash Bucket所在的SST数据中,快速读取该KV值。关于列存主键索引更多设计细节,后续将在文章《PolarDB-X 列存索引|高效更新的秘密》内详细阐述。
compaction
列存引擎中还包含一个较为重要的操作,便是compaction,它是文件重新组织的重要操作手段,无论是空间回收,文件排序,都得通过后台异步compaction任务来完成,如下图所示。

数据会先写入行式存储的csv文件,该文件内的数据是无序的,当一个行式CSV文件写满,需要转成列式存储文件的ORC文件,ORC文件内部是有序的,这个行转成列式的操作,可称为csv compaction。
列存ORC文件组织目前只有两层:
level 0: 该层内文件之间范围可重叠;
level 1: 该层内文件之间范围不可重叠;
只设计两层的原因,尽可能让orc文件间范围不重叠,增加读请求的裁剪能力,提高读性能。大部分情况都是随机写入数据,所以level 1层文件/level 0层文件比例越大,会导致写放大也越大,需要平衡读写性能,常见做法是增加层数,但是PolarDB-X的列存索引还有分区能力,可控制一个分区的数据量大小,所以不会导致数据无限膨胀,导致写放大倍增,两层结构能够适应数据量。
为了防止level 0层文件无限增多,orc文件之间范围不重叠,则需要将对多个orc文件进行重排序,具体操作为选取level 0层的文件,和level 0层所选文件范围有交集的level 1层文件,进行orc文件重排序,最后放入level 1层文件,可称为orc compaction。
PolarDB-X的列存引擎是通过增加delete bitmap来标记每个文件中的删除空洞,当orc文件内的空洞较大时,也可通过重写orc文件,来达到去除无效数据空洞,回收存储空间,可称为delete compaction。
总之,PolarDB-X列存引擎对于需要重新组织文件数据的方式,都可称为comapction,这些任务以后台异步方式调度运行,还有多种不同目的性的compaction任务,这里不再一一概述。
如何构建列存索引
前面讲述了列存引擎如何同步数据,如果新建列存索引,初始数据为空,开始通过binlog导入数据是可正常服务的,但是实际场景则存在,表内已有数据,或者历史binlog文件已过期不存在,这种情况则需要特殊处理,列存索引的构建流程如下图所示:

当列存引擎在binlog事件流中,检测到创建列存索引ddl事件后,便开始同步该表DML操作,形成的数据称为增量数据,同时会另起线程去PolarDB-X行存通过select语句捞取已有数据,称为全量数据。等全量数据捞取完毕,最后将全量数据和增量数据进行合并,便是该列存索引的所有数据,后续继续同步binlog数据即可。其中,还有挺多实现细节,后续会再出一篇文章详细介绍构建流程。
高可用
列存引擎在高可用方面采用主备架构,如下图所示,常驻备节点处于启动状态,一旦主节点故障,备节点可迅速选主进行行到列的数据同步,保证列存延迟不高,热备方式能够满足大部分应用场景。只用两节点原因:列存引擎不存储任何数据,无状态的,列存的元数据存储在MetaDB中,MetaDB是三节点paxos架构,具备高可用性,列存的文件数据存储在共享存储中,OSS也具备极高的可用性,随时可通过增加列存引擎写节点,参与选主成为主节点,所以平时一个备节点足够。

列存引擎节点选主采用租约机制,在MetaDB中存在系统表,记录列存引擎的所有节点信息,同一时刻有且只有一个节点可用选为主节点,其余节点为备节点,主节点可不断续约以避免频繁主备切换(HA),备节点也会每隔一段时间尝试选为主节点,当主节点出现故障,租约过期,则备节点可获取租约,成为主节点,完成一次主备切换。
考虑到备节点处于热备状态,没有负载,资源利用率低,只有主节点在提供服务,所以PolarDB-X的列存引擎还支持主节点将一些后台任务调度到备节点进行执行,例如一些compaction任务,以减轻主节点压力,同时提高备节点资源利用率。
使用场景
PolarDB-X列存索引已于2024年4月上线公有云,至今已有很多用户进行使用,有一些用户也已上线生产业务,在诸多用户的实际使用场景探索下,目前PolarDB-X已探索出列存索引的几种常用用法,包括:高性能查询、历史数据快照读取、列存归档数据等。
高性能查询
PolarDB-X是一个分布式HTAP数据库,对于复杂分析型AP查询,路由到列存只读实例上查询将能极大提高查询性能,同时也不会影响TP流量,能够较好地贴近用户场景。在负载流量分流上面,PolarDB-X支持智能路由和手动路由两种方式,如下图所示:

可通过配置参数实现流量自动路由到列存只读实例,读取列存数据,也可应用程序根据不同数据库连接串,访问行存或列存,以达到业务使用的最佳实践,详细介绍可查看行列路由机制注5。
历史数据快照查询
前文讲到,列存引擎是通过全局binlog进行数据同步,以一个个事务为边界进行批量提交,创造出一个个列存数据版本,而且列存数据都是通过追加写入文件或新建文件,不会修改文件,所以每一个列存数据版本都是包含一系列文件,和这些文件的长度元信息。列存版本将会包含TSO值,该TSO值取自Binlog记录的事务TSO,每一个列存版本都是一个可读取的快照版本。如下图所示:

除了列存自动提交的快照点位外,PolarDB-X提供了用户指令call polardbx.columnar_flush()
,该指令会写入到binlog事件中,并返回用户TSO值,当列存在Binlog同步中识别到该事件,则会主动强制提交一个列存版本,相当于用户创建了一个自己需要的列存快照版本。用户可根据该TSO值查询列存数据,通过select * from table_name as of tso xxx;
可看到数据的不同版本,查询历史数据,如下图所示:

只要PolarDB-X列存引擎不清理过期文件,则可读取历史快照数据,当然,需要保留较长时间的数据文件,但是OSS存储以极低的存储成本是可以接受的,用户可自定义快照保留时间,满足业务需求。关于PolarDB-X如何满足事务一致性快照读,可查看往期文章《PolarDB-X 列存索引 | 满足事务一致性的快照读》。
列存归档数据
很多业务场景都有数据归档的需求,将不再频繁访问但仍然需要保留的数据移动到成本较低的存储介质上,PolarDB-X的列存索引正是将大批量数据存储到低成本的OSS上,正好符合归档的存储特性,并且列式存储能够有效压缩数据,压缩效率能达到3-10倍左右,进一步降低存储成本。PolarDB-X通过列存索引进行归档的操作如下图所示:

比较典型的归档场景便是按时间维度进行归档,通过时间分区规则将数据划分为一个个分区,创建列存索引后,列存引擎会通过binlog同步数据,当用户需要归档以前的某段时间数据时,可通过特殊的事务标记在行存删除数据,在binlog事件中存在特殊值代表归档事务,列存发现归档事务后,忽略该事务的同步,不删除数据,则行存删除了需要归档的数据,列存保留了需要归档的数据,用户通过访问列存数据,便能读取到归档数据,达到了归档的效果。当归档数据需要进行清理时,列存可通过删除2024年1月分区,清理掉该分区的文件数据,便清理了不再需要归档的数据。可以发现,仍然是通过分区管理,将集中式的数据进行归档,这样方便管理和清理。PolarDB-X正在探索支持行级归档,通过归档事务,可删除任意维度的行数据,而列存保留了这些数据,列存数据也能根据特殊条件清理归档数据,在未来这些特性会一一实现。
总结
本文主要介绍了PolarDB-X列存引擎的基础架构和大体实现,同时介绍了目前列存索引的多种使用场景,后续还会有更多文章详细介绍列存引擎的各个模块设计细节。目前,在阿里云购买PolarDB-X2.0实例的用户都是免费赠送列存引擎写节点,可直接体验列存功能,欢迎广大用户进行使用。
注释:
注1:PolarDB-X CDC之"兼容MySQL,高于MySQL":https://zhuanlan.zhihu.com/p/698942695
注2:PolarDB-X 全局时间戳服务的设计,https://zhuanlan.zhihu.com/p/360160666
注3:PolarDB-X 全局 Binlog 解读之理论篇,https://zhuanlan.zhihu.com/p/462995079
注4:PolarDB-X 分区概述,https://help.aliyun.com/zh/polardb/polardb-for-xscale/overview-partition-table
注5:PolarDB-X 行列路由机制,https://help.aliyun.com/zh/polardb/polardb-for-xscale/row-column-routing-mechanism




