历史数据归档的问题
大部分业务数据的读写特征,都是最新产生的数据会更频繁的被读取或者更新,而更久之前的数据(如1年之前的聊天记录,或者订单信息)则很少会被访问, 而随着业务运行时间的增加,数据库系统中会沉淀大量很少甚至不会被访问到的数据,这部分数据和最新产生的数据混合在一起会产生一系列问题:
- 历史数据和最新的数据存储在一个数据数据库系统中,导致磁盘空间不足。
- 大量数据共享数据库内存缓存空间,磁盘IOPS等,导致性能问题。
- 数据量太大导致数据备份时间过长甚至失败,而且备份出来的数据存放也是一个问题。
针对此问题,一种做法做法是对历史数据做归档,将长期不使用的数据迁移至以文件形式存储的廉价存储设备上,比如阿里云OSS或者阿里云数据库DBS服务。然而实际业务系统中,历史数据并不完全是静态的,针对几个月甚至几年前的“旧”数据依旧存在实时的,低频的查询甚至更新需求,在阿里巴巴内部,类似淘宝/天猫的历史订单查询,企业级办公软件钉钉几年前的聊天信息查询,菜鸟海量物流的历史物流订单详情等。 为了解决历史数据的读取和更新问题,可以使用一个单独的数据库系统作为归档数据的存储目的地,称之为历史库. 业务对单独的历史库系统一般具有如下的诉求:
- 具有非常巨大的容量,业务可以放心的持续将线上数据保存到历史库中,而不用担心容量问题。
- 支持和在线数据库系统一样的访问接口,如都是MySQL协议等,业务可以和在线业务相同的接口访问历史库
- 必须具有低廉的成本,如使用压缩减少数据所占磁盘空间,廉价存储介质等,确保可以使用较大的代价保存海量的数据。
- 具备一定的读写能力,满足低频的读写需求。
作为世界上使用最广泛的开源数据库系统,MySQL生态中一直缺乏一个好用的历史数据归档存储方案,既满足大容量低成本同时又具备一定的读写能力。虽然业界曾经推出过一些高压缩引擎如TokuDB, MyRocks等,但是受限于单物理机磁盘容量限制,存储的数据量有限。PolarDB历史库的推出即为满足这一需求。
PolarDB历史库产品
阿里云数据库团队将公司内部广泛使用的高压缩引擎X-Engine引擎与PolarDB相结合,使得PolarDB同时支持InnoDB引擎和X-Engine引擎,其中InnoDB引擎负责在线业务的高性能混合读写,X-Engine引擎负责归档数据的低频读写。
在PolarDB双引擎的架构上,我们推出了一款主要基于X-Engine引擎存储的数据库产品:PolarDB历史库。历史库单实例的存储空间上限为200TB,结合X-Engine引擎3~5倍的压缩能力,可提供近600TB~1PB的原始数据存储能力,能满足绝大部分客户的历史数据归档对存储容量的需求。
使用PolarDB 历史库(X-Engine)具有如下几个优势:
- 超大的容量,200TB和存储空间加上X-Engine数据压缩能力,可提供超500TB以上的原始数据存储容量,同时容量按需付费,不用预先为未来的数据增长预备存储空间。
- PolarDB历史库与官方MySQL的协议一致, 相比于将历史数据备份到HBase等NoSQL产品, 业务应用程序不用修改代码即可同时访问在线库和历史库。
- 借助PolarDB底层共享存储提供的快速备份能力,再大的实例也可以实现对数据的快速备份,备份数据上传到OSS等廉价存储设备,确保数据永不丢失。
由于PolarDB 历史库提供了超大存储容量,它可以同时作为多个业务历史数据的汇聚地,以方便对所有历史数据进行集中存储和管理,用户可以在如下几个场景中使用历史库:
- 将PolarDB 历史库作为线下自建数据库实例的冷数据存储地,线下自建数据库服务包括且不限于MySQL/Postgre/Sql Server等关系数据库。
- 将PolarDB历史库作为阿里云RDS MySQL或者PolarDB MySQL数据库服务的归档存储地, 将较少访问到的历史数据迁移到PolarDB X-Engine中存储,释放在线实例的空间以降低成本并提升性能。
- 直接将PolarDB 历史库作为大容量关系数据库使用,以满足一些写入数据量巨大,但读频次较低的业务的需求(如系统监控日志等)
在线库和历史库之间的数据迁移,可以使用阿里云DTS或者DMS进行,其中DTS可以持续试试的将在线库的内容同步到历史库,而DMS则可以周期性的将在线数据批量导入到历史库。
PolarDB历史库技术架构
PolarDB历史库功能的推出依赖阿里巴巴数据团队之前在数据库和存储等方向上的创新和突破:
- 阿里巴巴自研的基于LSM-tree架构的存储引擎X-Engine提供了强大的数据压缩能力,满足了归档数据库对低存储成本的要求。
- PolarDB借助于共享分布式存储服务,实现了存储容量在线平滑扩容,同时计算节点和存储节点之间采用高速网络互联,并通过RDMA协议进行数据传输,使I/O性能不再成为瓶颈。集成到PolarDB的X-Engine引擎同样获得了这些技术优势。 下面我们别讲解X-Engine引擎的基础特点,以及如何将X-Engine与PolarDB相结合以提供一个有竞争力的历史库技术方案。
X-Engine存储引擎
PolarDB历史库通过引入X-Engine获得存储空间节省的优势,X-Engine引擎可以用如下几个关键点对其进行描述:
- X-Engine使用了LSM-Tree的分层架构,最近写入的热点数据和历史写入冷数据分开索引,同时创新性的使用事务流水线技术,把事务处理的几个阶段并行起来,极大提升了写入吞吐。
- 分层存储底层的数据是大部分时候为静态只读,在数据页中所有记录采用前缀编码,同时每个数据页中的数据都是紧凑排列不会留空洞,最后底层数据都会默认进行压缩,因此相比原始数据可获得数倍的空间压缩。
- X-Engine对传统LSM-tree性能影响比较大的Compaction过程做了大量优化,如拆分数据存储粒度,利用数据更新热点较为集中的特征,尽可能的在合并过程中复用数据。精细化控制LSM的形状,减少I/O和计算代价,有效缓解了合并过程中的空间增大。 X-Engine本身的实现非常复杂,远非几句话可描述,本篇不对其展开详细讲述,对其技术细节的解读可以参看X-Engine简介。 X-Engine在阿里巴巴集团内部就作为一个自研引擎集成到AliSQL之中,也集成到公有云RDS MySQL当中,作为归档引擎售卖,而现在我们将其集成到了PolarDB当中。
PolarDB双引擎
PolarDB的最初版本是基于InnoDB引擎设计的,其技术架构可以参见文章PolareDB产品架构,在InnoDB引擎上实现物理复制,并在此基础上支持一写多读已经非常具有技术挑战。X-Engine是一个完整独立的事务引擎,具有独立的REDO日志,磁盘数据管理,缓存管理,事务并发控制等模块,将X-Engine移植进PolarDB并实现双引擎的一写多读更具挑战。我们通过大量的工程创新将PolarDB带入双引擎时代:
- 合并X-Engine的事务WAL日志流和InnoDB的REDO日志流,实现了一套日志流和传输通道同时服务于InnoDB引擎和X-Engine引擎,管控逻辑以及与共享存储的交互逻辑无需做任何改变,同时未来新增其他引擎时也可以复用发这套架构。
- 将X-Engine的IO模块对接到PolarDB InnoDB所使用的用户态文件系统PFS上,如此实现InnoDB与X-Engine共享同一个分布式块设备. 同时依靠底层分布式存储实现了快速备份。
- 在X-Engine中实现了基于WAL日志的物理复制功能,并且一步到位的引入并行WAL回放机制,实现了RW节点与RO节点之间毫秒级别的复制延迟。在此基础之上,我们实现了在RO上提供支持事务一致性读的能力。
除了涉及到X-Engine支持一写多读需要支持的功能改造之外,PolarDB X-Engine还有很多项工程改进,如针对历史库场景大表DDL的问题,除了部分支持instant DDL的schema变更操作,X-Engine也支持并行DDL功能,对那些需要copy表的DDL操作进行加速。
在PolarDB双引擎架构下,我们实现了在一套代码下支持两个事务引擎的一写多读,保证了PolarDB产品架构的简洁和一致用户体验。
历史库单计算节点架构
PolarDB集群版基于共享存储实现了一写多读,集群中有一个主节点(可读可写)和至少一个只读节点,但是在历史库场景下,用户一般需要巨大的存储容量,但由于读写量较小,RW节点的计算资源都无法利用完,更无须RO节点提供的读扩展能力。在RW和RO规格相同时,相当于浪费了一半的计算资源。
借助X-Engine引擎带来的数据压缩能力,可以降低客户的存储成本, 而在历史库当中我们使用单RW节点来提供服务,省去了RO节点的计算资源成本。当然去除了RO节点,在灾难场景,如RW节点异常Crash时,需要更长的崩溃恢复时间。但是依靠底层分布式存储提供的高可用能力,我们依然提供了99.95%的可用性。
在历史库这样一个低频读写的场景(很多时候数据为异步批量导入到历史库),用稍低一点的可用性换取成本节省,对很多用户是可以接受的。而对于那些对可用性要求比较高的客户,我们也即将在PolarDB集群版本中提供X-Engine引擎,在降低存储成本的同时,提供与标准版一样的可用性指标。
历史库单节点架构下,日常不提供RO节点, 在需要对节点进行运维操作,如进行节点升级需要重启时,通过部署临时的RO节点并升级为RW节点的方式,可以降低升级操作对客户读写的影响。
单节点时节点替换流程如上图所示,影响业务的时间为替换过程中HA将流量从原RW切换到新的RW的瞬间。