我们有幸邀请到高德技术专家荆中志,分享高德在选型面向未来的数据库背后的思考及具体行动。本文从关系型数据库谈起,讲述高德从确定选型方向到最终选型落地的历程,并提供压测结果参考,以及高德在数据迁移过程中的具体方案与实践总结。
亿级 DAU(Daily Active User,日活跃用户数)的高德,每时每刻都在生产大量的数据,数据量的快速增长给当前业务存储带来越来越高的挑战。以我所在部门的某大体量服务为例,存储在数据库中的数据动辄上百 TB,并且 TPS(含QPS)在万级,如何处理这些数据并降低后续扩容迁移成本、存储成本,成为了摆在我们面前的重要问题。
同时,随着高德深入本地服务,如何在业务发展期提前做好技术布局,保障现在和未来服务可用性、稳定性,以应对未来业务的飞速成长,也是我们重点关注的。
综合已知隐患,考虑技术上的提前布局,我们需要解决以下 5 个问题:
第一,透明可扩展性与降低维护成本的问题。如何让数据库能跟随业务发展增长容量,像在业务初期使用小规格的存储,但随着业务发展可通过增加机器横向扩展,并且能依据业务量动态扩缩容,从而减少数据迁移带来的技术风险和迁移成本?
第二,节约成本同时保持性能的问题。面对如存储 TB 级别的大体量业务,如何降低存储成本,但同时又能保持数据库性能在可接受范围内?像用 ODPS 存储 TB 级别数据,虽然成本低,但读写时间太长,无法做在线业务。
第三,可用性、稳定性、容灾能力的问题。对于 DAU 过亿的应用,任何故障都会被无限放大,因此服务的稳定性至关重要,如何提升服务的可用性、提高存储的稳定性,以及同城及跨地域容灾能力?
第四,强一致性的问题。面对结算财务类数据,如何保证强一致性,保障数据不丢失?即使主节点挂掉,主从切换,还能保证已写入主节点的数据不会丢失。
第五,兼容性,降低改造成本的问题。支持 MySQL 协议,可实现存储平滑升级,降低业务系统的代码改造成本,最好的情况是业务系统不需要更改对存储系统操作的SQL,就可以实现改造升级。
面对以上需要解决的 5 个问题,我们期望达到以下目标:
- 提升数据库的扩展性以及弹性扩缩容的能力,使得存储横向扩展对上层系统透明,降低数据迁移的风险。
- 数据库可以进行数据压缩和数据共享,减少数据占用的存储容量,降低成本。
- 提升存储稳定性,使得系统拥有高可用和高容灾能力,能拥有应对机房级别甚至城市级别的容灾能力。
- 能提供强一致的存储保证,对于结算财务类的数据,可保障较高的一致性,而非用最终一致性的妥协换取其他方面的能力。
- 兼容原有存储的协议,至少是大部分常用协议,极大减少业务系统的改造成本,实现业务系统所用存储的平滑升级。
- 要能支持较高并发读写能力。
为了实现以上的目标,找到更适合高德场景的数据库,我们对市面上目前比较流行的数据库,进行了深入的研究与对比。
浅谈关系型数据库的 3 个发展方向
单机版数据库显然无法满足我们现在的需求,而对于关系型数据库的 3 个发展方向:Sharding On MySQL 、NewSQL 和 Cloud Native DB(云原生数据库) ,它们都有各自优势的应用场景。
- Sharding On MySQL 在应用侧或代理层做分片来管理多个 MySQL 物理库,来解决 MySQL 单机容量和性能不足的问题。虽然计算存储资源可扩展,但在扩展和数据迁移时,业务系统层面需要做大量的改造和业务数据的灰度,为了保证业务不停机会产生大量的改造成本和数据迁移的风险。
- NewSQL 数据库在国内以 OceanBase 和 TiDB 这类原生支持分布式的关系型数据库为代表,与 Sharding On MySQL不同的是,NewSQL将分片功能作为数据库的一部分来实现,并提供了动态扩缩容的能力,且对上层业务透明,具备透明可扩展性。
- Cloud Native DB 在国内以 PolarDB 为代表,具备池化的资源,也可以实现存储资源的动态扩缩容。其一般采用主备方式来实现高可用,同时其扩容和主备探活等功能需要大量依赖外部系统。
确定选型方向:NewSQL 和 Cloud Native DB
为解决现有问题,减少开发和维护成本,我们选择了 NewSQL 和 Cloud Native DB 作为选型方向,并对代表数据库 OceanBase、TiDB 和 PolarDB 进行简要对比。
在对比 OceanBase、TiDB 和 PolarDB 之前,先介绍下其分别采用的架构知识作为补充。如图 1 所示,TiDB、OceanBase 采用的 Shard-Nothing 架构的每个节点有独立计算和存储功能,且节点之间不共享数据。PolarDB 采用的 Shared-Storage(即 Shared-Disk)架构是在存储层做了统一和共享,但计算节点是独立节点。
接下来,我们梳理了一张 OceanBase、TiDB 和 PolarDB 在架构、硬件要求、节点角色、扩展性等 12 个维度的对比表。经过多角度的考虑和平衡,我们最终选择了 OceanBase 作为升级的目标数据库。
为什么选择 OceanBase?
原因一:强一致性保障。OceanBase 基于 Paxos 一致性协议实现数据多副本存储,多数据中心的数据副本同步写入,可以保证数据的强一致性,这样在一个副本挂掉后,其他节点有完整的数据。
原因二:极致高可用。OceanBase 采用无共享的多副本架构,系统没有单点障碍,保证系统持续可用。支持两地三中心和三地五中心部署,对于要求 CP 的结算类业务来说,可保障数据的城市级容灾。
原因三:透明可扩展性。OceanBase 是可以动态扩容的,扩容后分区表的数据会自动均衡到新节点上,对上层业务透明,节省迁移成本。在业务高速发展时,数据库的扩容对业务系统透明。
原因四:稳定性。OceanBase 可实现业务连续不间断地服务。如果某个节点出现异常,可以自动剔除此服务节点,该节点对应的数据有多个其他副本,对应的数据服务也由其他节点提供。甚至当某个数据中心出现异常,OceanBase 也可以在短时间内将服务节点切换到其他数据中。
原因五:节省存储成本。OceanBase 存储具有高度压缩的能力,数据可以压缩到原有数据到1/3左右,对于存储量大的业务会极大降低存储成本。并且 OceanBase 的租户级别资源隔离,可以多租户共享集群冗余资源,减少资源的预置,降低费用成本。
原因六:性能强劲。OceanBase 作为准内存数据库,采用 LSM-Tree 存储,增量数据操作内存,减少随机写,读写性能超传统关系型数据库,并且 OceanBase 的TPC-C 测试打破了世界纪录。
原因七:兼容性、降低改造成本。OceanBase 也支持 MySQL 协议,可以直接使用 MySQL 驱动访问,基本上可以把它当成分布式的 MySQL 使用。
OceanBase 采用 Shared-Nothing 架构,各节点之间完全对等,每个节点都有自己的 SQL 引擎、存储引擎,运行在普通 PC 服务器组成的集群上,具备高可用、可扩展、高性能、低成本、云原生等核心特性。其中,高可用、可扩展的保证主要依赖于其整体架构和 OBProxy、OBServer、GTS、RootService 等服务,高性能、低成本主要源于其存储引擎。下面一一展开介绍。
高可用
首 OBProxy、OBServer、GTS、RootService 保证了 OceanBase 的高可用。
- OBProxy。代理上层应用的请求 SQL 并转发到指定的 OBServer,是没有持久化状态,其工作依赖的所有数据库信息都来自于对数据库服务的访问,因此,OBProxy 故障不会导致数据丢失。OBProxy 的选择和摘除需由负载均衡组件来决定。OceanBase Cloud 不需要用户管理。
- OBServer。OceanBase 集群的组成节点上分布着以分区为单位的数据,以 Zone 的方式将 OBServer 分组,一个 Zone 有完整的数据副本。同一分区的数据多个副本散落在多个 Zone 里。其中只有一个副本接收修改操作,叫主副本(Leader),其他副本叫从副本(Follower)。通过 Multi-Paxos 协议保证副本间数据的一致。并通过Multi-Paxos进行选主操作。
- GTS。GTS 用于提供全局时间戳服务。该服务也是高可用的,通过特殊分区保持三个副本,为租户内执行的所有事务提供事务的读取快照版本和提交版本,保证全局的事务顺序。OceanBase 使用与分区副本一致的方案保证全局时间戳服务的可靠性与可用性。
- RootService。相当于 OceanBase 集群的总控,其也是通过 Paxos 协议实现的高可用和选举,并可指定其副本数量。RootService 通过监听 OBServer 汇报的心跳包来判断 OBServer 的状态,并对 OBServer 活跃或摘除状态的控制,以及其存储的分区副本数据的迁移。
可扩展
保证可扩展的主要是 Zone。OceanBase 会将分区表的多个分区均匀分散在每个 Zone 的所有 OBServer 上。扩容时只需要为其增加资源节点(在集群维度添加),通过修改租户的资源规格的方式即可将资源分配给指定租户。
新分配的资源会形成新的 OBServer。然后,集群的 RootService 会调度分区副本在同一 Zone 内进行迁移,直到各 OBServer 负载差值达到最小。此外,主 Zone 的各个分区主副本也会在每个 OBServer 上进行自动均衡,避免主副本的分配倾斜导致部分 OBServer 的更新负载过大。
此处,还会涉及一个问题:使用 Paxos 必然会引入脑裂,那如何避免三地五中心的五个 Zone 副本在其中一个故障时产生脑裂呢?
为避免极端情况下会产生脑裂的问题。比如 SH-1、SH-2 为一个网络分区 A,HZ-1、HZ-2、SZ-1 为一个网络分区 B,且 SH-1 为主时,分区 B 可能会产生新的主 Zone 来负责所有的分区主副本。
OceanBase 使用租约的方式来避免脑裂,如果租约的序列号是每个节点自增的话,可能会导致节点序列号很高但日志较晚的情况,最终导致服务不可用。不过 OceanBase 的选举服务会通过从 GTS 获取最新的时间戳,保证了新旧 Leader 的租约是不重叠的,并且新 Leader 能够保证 GTS 提供的时间戳不回退。这样就可以保证在同一租约里只有一个有效的主副本 ,避免了脑裂的问题。
需要注意的是,GTS 作为选举依赖的关键服务,其高可用性影响了整个 OceanBase 集群的高可用性。
高性能
OceanBase 的高性能主要源于其存储引擎采用了基于 LSM-Tree 的架构,把基线数据和增量数据分别保存在磁盘(SSTable)和内存(MemTable)中,具备读写分离的特点。对数据的修改都是增量数据,日志顺序写、数据写内存。因此, DML 基本是内存操作,性能非常高。读的时候,数据可能会在内存里有更新过的版本,在持久化存储里有基线版本,需要把两个版本进行合并,获得一个最新版本。
OceanBase 的增量数据是写到内存中的,这也是 LSM-Tree 的结构特性所决定,天然为优化写性能而设计的。但如果读数据时,就需要聚合增量的 MemTable 和磁盘上的数据来提供,而磁盘上分级的 SSTable 读会增大延时。因此,OceanBase 在内存中做了 RowCache、BlockCache 等缓存手段减少直接读磁盘的操作。同时,OceanBase 推荐使用大内存的规格配置。一方面用于存储更多的增量数据(MemTable 更大),另一方面为更大的内存做数据的缓存。
OceanBase 对 RowCache 构建了布隆过滤器,并对布隆过滤器进行缓存。OLTP 业务大部分操作为小查询,通过小查询优化避免了传统数据库解析整个数据块的开销,达到了接近内存数据库的性能。
当内存的增量数据达到一定规模时,会触发增量数据和基线数据的合并,把增量数据落盘。同时。每天晚上的空闲时刻,系统也会启动每日合并。另外,由于基线是只读数据,而且内部采用连续存储的方式,OceanBase 可以根据不同特点的数据采用不同的压缩算法,既能做到高压缩比,又不影响查询性能,大大降低了成本。
为了摸清阿里云上的 OceanBase 如何落地,也为了积累使用新技术的经验。我们选择了影响较小、体量较小的业务——赔付业务系统先打头阵试水。
我们申请了 OceanBase 的测网集群并进行了迁移核心功能(DDL 和 DML)的测试,以便提前发现并解决现有赔付业务系统在迁移时存在的兼容问题。幸运的是,赔付系统涉及的所有 CRUD 操作及创建表、删除表、修改表、增删索引操作都能正常执行,这样的结果也减少了迁移时的心理压力。
在核心业务 SQL 兼容问题测试通过后,我们开始着手对 OceanBase 集群进行压测,因为当时没发现较好的压测平台,所以是通过应用机器间接压测 OceanBase 测网集群的。
在正式使用 OceanBase 前,我们对它的性能进行了简单压测。因为手里没有专用的数据库压测工具,是通过应用程序的接口间接压测,也导致了压测的 QPS 偏低。
我们先使用高德压测机对三台 4 核 8G 的应用进行压测,间接压测 8 核 32 G的 OceanBase 数据库。在读 3000QPS 和写 1500QPS 下,OceanBase性能仍然较好,RT 都在 1ms 以内,优秀的性能是毋庸置疑的。
不过经过分析得出结论,由于 OceanBase 是准内存数据库,需要的内存较大,在内存中做了缓存并且有增量数据也是在内存中,容易产生热点数据,因此压测 OceanBase 变成了压测 OceanBase 内存中的数据,内存性能相比磁盘有千百倍差距,性能好也是理所当然的,就没再继续压测。
从压测数据可以看出,OceanBase 的高性能一方面是来源于其内存数据,热点数据几乎都在内存中也就保证了 OceanBase 在大部分时间内的性能都是优秀的。但如果是比较冷门的数据访问时,可能会需要从磁盘上加载数据,也可能会需要访问到多层的 SSTabel,而该请求的 RT 因测试数据较少,还暂未确定。
此外,我们也了解了携程使用 OceanBase 的经验:相对于 MySQL,OceanBase 方案读性能平均提升 2 倍,写性能平均提升 3 倍,性能提升明显;基于数据编码以及存储引擎自带多级压缩技术,使得 OceanBase 方案相比 MySQL 节省 2/3 存储资源,很大程度上降低了硬件成本。
从携程的使用性能截图可以看到,OceanBase 的读写 RT 都在 1ms 上下,和我们的压测结论相近。
数据迁移方案演进
完成功能验证和性能的基本验证后,我们就需要着手将赔付系统数据平滑迁移到 OceanBase 了。为了保证数据的平滑迁移并且线上服务不停机,当时我们考虑使用阿里云的DTS(Data Transmission Service,数据传输服务)平台,可以将原有存储在 XDB 中的存量数据全量迁移到 OceanBase,并通过增量数据方式实现 XDB 数据向 OceanBase 的同步,来保证二者数据的一致。
但经过深入了解,我们发现 DTS 平台暂时只支持 TDDL 内的 XDB 实例数据迁移,不支持异构迁移。因此,为保障服务数据的平滑迁移,我们选择通过 OMS(OceanBase Migration Service,OceanBase 数据迁移工具)进行业务原有数据的增全量迁移。
OMS 支持异构数据的迁移,如从 MySQL 迁移到 OceanBase,主要功能是通过订阅源库的 Binlog 并将其存储在 OMS 的 Store 中,给下游提供订阅来完成源库数据的迁移。而全量数据是需要从原库查询并同步的,订阅 Binlog 的方式只适合增量数据同步。OMS 在迁移时支持结构同步、全量数据同步、增量数据同步、数据校验及反向同步等功能,可实现两端数据库的双向同步。
除了数据迁移功能外,OMS 还支持数据订阅,数据订阅是提供客户端使业务方自主处理和消费 OceanBase 的 Binlog ,以实现自己个性化的需求,如数据同步到 Lindorm、ES等。如下图所示,为增全量的数据迁移流程。
遗憾的是,OceanBase 虽然支持 MySQL 迁移,但原生 MySQL 对阿里巴巴集团内普遍使用的 TDDL 方式的库访问模式并没有进行相关集成。此时,我们需要面对两个选择:
- 等待 OMS 实现适配,业务暂停上 OceanBase。
- 先用其他方案绕过 OMS,业务侧去保证数据一致,先上 OceanBase 来验证数据库核心能力。
最终我们选择了业务方(即我们自己)来实现数据的增全量同步。那如果业务方来实现增全量同步时,应该怎么选择方案呢?我们了解到,在 DTS 平台不可支持的情况下,同期使用阿里云上 OceanBase 集群的其他团队都是使用 Datax全量 + 业务侧增量双写方式实现数据迁移。经过权衡,我们也决定选用该方式实现业务快速上线,尽早验证 OceanBase 的数据库功能。
确定方案后,我们便不再犹豫,从下图的两个方案中选择了 Plan B 方案来迁移数据,通过 Diamond 配置来实现数据库的动态切换,使用 MAC 平台来对 XDB 和 OceanBase 数据进行离线校验。
你可能在想:
- Plan A 的难点是什么,为什么不能等 OMS 准备妥当后再迁移呢?
- 此刻使用的 OceanBase 是怎样连接的,怎么没看到 TDDL 的身影呢,那业务侧是否要改造呢?
Plan A 的难点在于流程和权限问题,因为如果要通过 MySQL 方式同步,那么需要申请生产环境数据库可用作迁移的账号密码,而这个流程具有高风险,且需要沟通和审批的人比较多,甚至到了高管级别,获批难度较大、获批时间难以预估。另外,即使账号审批下来,后续的迁移流程是否有其他问题,如没有接口对外调用,也未可知。为了稳妥和效率,我们没有采用 PlanA。
我们也确实没有提到 TDDL,也没有通过 TDDL 来访问 OceanBase,而是通过 MySQL 驱动的方式,以账密来访问 OceanBase 的。此时需要将原 TDDL 的 TDataSource 换成其他的 MySQL 数据源来访问 OceanBase。当然也会遇到直接使用账密带来的安全问题。最终,我们还是将直连 OceanBase 的方式改为了通过 TDDL 间连,避免账密的明文泄漏问题。
虽然我们的方案并非最优雅的数据迁移方案,却是我们当前经过多方面评估后认为最合适的方案。而我们期望的最优雅的方式是,让 OceanBase 能完美的与业务中间件平台等结合,避免业务方做大量的非业务相关的工作。
业务上线后是处在应用双写 XDB 和 OceanBase 的状态,并通过 MAC 在 T+1 时间验证历史所有数据的一致性,如果 OceanBase 有数据和 XDB 不一致的情况,再通过 ODPS 的方式重新同步全量数据到 OceanBase,直到 OceanBase 数据稳定多日与 XDB 数据一致。
在迁移改造过程中,需要注意的是:
第一,不可使用数据库自增 ID。因为 OceanBase 和 XDB 自增 ID 不同可能会导致数据错乱,即使初始设置的完全一样,需要通过 TDDL 的自定义 sequence 来保证 XDB 和 OceanBase 两边数据主键的一致。
第二,从写(双写 XDB 和 OceanBase,指定了先写 XDB,那么写 OceanBase 就是从写,反之一样)的库即使失败不可阻塞业务流程,也随时都可以重新全量同步,使得数据与主写的库一致。
赔付业务系统是高德目前作为试点的第一个系统,在我们负责的业务系统中,后续还可能迁移财务系统、清结算系统等更多系统到 OceanBase。虽然当前的 OceanBase Cloud 称不上完美,但在与 OceanBase 团队一起探索过程中积累的经验是十分宝贵的。赔付业务系统上线 OceanBase 后,我们对收益预期做了一个初步的评估:
第一,我们对比了 XDB 和 OceanBase 的费用账单。OceanBase 在通过数据编码和压缩后具有较高的压缩比,正常业务一般能压缩到原来的 30% 左右,OceanBase 的整体价格也是通过数据压缩取胜的。经过换算,在 XDB 的规格为 16 核 64 G,存储使用 6T 的情况下,OceanBase 需要 8 核 32G,存储只需要 1.8T,以此存储量(即拐点存储量在6T左右)为基础继续增长,OceanBase 的费用会越来越划算。
还有个隐形费用是 DTS 平台(包括 DRC 订阅),我们现在的费用中,DTS 费用几乎和 XDB 费用相当,而 OceanBase 的 OMS 暂时是免费的。
此外,OceanBase 作为 HTAP 模式的数据库,具备一定的 OLAP 能力并做了一定的性能优化,后续可以试点将 Lindorm 的数据迁移到 OceanBase,用 OceanBase 代替Llindorm 的部分角色,这样既能统一在线数据的存储,又能节省 Lindorm 的使用费用。
第二,拥有了异地容灾能力。利用 OceanBase 的三地五中心的部署和强一致特性,业务可实现城市级别的容灾,保证数据不丢,极大提升了业务的稳定性。
第三,利用 OceanBase 的分布式特性,业务系统的数据存储具备了动态扩容的能力,业务无感知的平滑扩容,保证业务不停机。并节省了业务提量猛增后的数据库扩容和迁移成本,极大降低了数据库容量不足的风险。
第四,平滑迁移至原生的分布式关系型数据库 OceanBase,为团队内推广使用原生分布式数据库积累了宝贵的经验。
第五,相比 MySQL,OceanBase 读性能平均提升 2 倍,写性能平均提升 3 倍。
实践小结
“沉舟侧畔千帆过,病树前头万木春”,我们作为 OceanBase Cloud 的较早期用户,在使用过程中与 OceanBase 的同学一起进行了 OceanBase 周边设施的共建,共同推进 OceanBase 生态通用解决方案的落地,这段经历弥足珍贵。
在测试过程中,我们主要遇到的问题是 OceanBase 数据库功能的兼容性验证、业务数据的平滑迁移与回切能力。在实践中,对于赔付系统内的读写等 DML 操作进行了比较全面的验证,并使用 MySQL 驱动的方式进行 OceanBase 的数据库访问,SQL 运行良好,无兼容性问题。
我们评估了赔付系统的业务数据量和 QPS 等系统指标,提出暂时避开 OMS 体系,业务侧自己实现数据的全增量同步,并通过 MAC 系统来验证 XDB 和 OceanBase 的数据一致,通过将 XDB 和 OceanBase 数据同步到 ODPS,然后在 MAC 上建立对账任务的方式进行核验,以及时发现不一致数据并排查原因。
业务应用系统内实现的 XDB 和 OceanBase 的同步双写(低频业务,后台业务性能问题权重不高),主写 XDB,从写 OceanBase。业务流程强依赖主写,对于从写,如果有操作失败的情况会通过sunfire的监控报警及时发现并修复。待两侧数据多日核验一致后,切换为主写 OceanBase,从写 XDB,并保持稽核能力和回切能力,以便 OceanBase 有异常时可快速切换到 XDB。