BOSS 直聘是在全球范围内首创互联网“直聘”模式的在线招聘产品,目前已经成为了中国最大的招聘平台。本文谈到的 BOSS 直聘的业务场景主要是通过数据库对招聘过程中的聊天记录信息进行存储,数据量极大,且每天都有 5 亿到 10 亿的增量数据。和招聘相关聊天记录往往呈现流水型特征,写入一段时间后即不会再次访问或更新,写多读少。
面对快速增长的在线数据,尤其是访问频率很低甚至为 0 的历史聊天记录,其占用的在线业务库的存储空间达到 PB 级别,造成了大量硬件资源浪费,堆高了企业的 IT 成本。同时,随着数据量的增加,在线数据库体积臃肿、查询效率逐步降低,给后续数据变更、扩展造成阻碍。
为了解决这些问题,我们需要对历史聊天记录进行冷热数据分离。热数据所在的在线库是多个 MySQL 集群,采用分库分表的方式,每月定期清理过期数据,滚动写入历史归档库。
团队进行这次数据库选型的主要目的就是对超大容量的归档库进行选型,参加选型的几个数据库为:MySQL、ClickHouse、OceanBase、某开源分布式数据库(以下简称为 DB-U),主要从存储成本、高可用这两个方面对归档库进行评估。
(一)存储成本
我们的归档库需要保留三到五年的历史聊天数据,必须解决大容量存储的成本问题。首先我们对 MySQL、ClickHouse、OceanBase、DB-U(某开源分布式数据库) 分别创建一张相同的用于存储用户历史消息的表,表结构如下:
然后分别写入 1 亿行相同的单副本数据,并对其磁盘的使用情况进行对比:
数据库 |
磁盘使用量 |
MySQL |
130GB |
DB-U(某开源分布式数据库) |
60GB |
ClickHouse |
30GB |
OceanBase |
22GB |
可以清楚地看到,基于列存进行存储的 ClickHouse 和拥有超高压缩率的 OceanBase 这两款数据库的存储成本明显低于 MySQL 和 DB-U,所以我们分别对 ClickHouse 和 OceanBase 的存储引擎进行了调研。
ClickHouse 存储引擎调研
ClickHouse 存储成本低的原因显而易见,就是因为它的存储引擎是基于列存的。相比行存存储引擎,ClickHouse 同一列中的数据属于同一类型,压缩效果显著。列存往往有着高达十倍甚至更高的压缩比,节省了大量的存储空间,降低了存储成本。
不过历史归档库一般都是写多读少的场景,像 ClickHouse 这种纯列存的存储引擎在这里并不能发挥出查询性能好的优势,相反列存引擎写入性能差的劣势还被放大了。
OceanBase 存储引擎调研
1. 存储引擎架构
OceanBase 的存储引擎基于 LSM-Tree 架构,将数据分为基线数据(放在 SSTable 中)和增量数据(放在 MemTable/SSTable 中)两部分,其中基线数据是只读的,一旦生成就不再被修改;增量数据支持读写。
数据库 DML 操作插入、更新、删除等首先写入内存里的 MemTable,所以在写入性能上就相当于内存数据库的写入性能,正好适合我们历史归档库写多读少的场景。等到 MemTable 达到一定大小时转储到磁盘成为增量的 SSTable(上图中红色箭头部分),转储到磁盘上的过程是批量的顺序写,相比 B+ 树离散的随机写,会大大提高写盘的性能。
当增量的 SSTable 达到一定规模的时候,会触发增量数据和基线数据的合并,把增量数据和基线数据做一次整合,基线数据在合并完成之后就不会发生变化了,直到下一次合并。同时每天凌晨的业务低峰期,系统也会自动进行每日合并。
但是 LSM-Tree 的架构也存在一个问题,就是读放大(上图中绿色箭头部分)。在进行查询时,需要分别对 SSTable 和 MemTable 进行查询,并将查询结果进行一次归并,然后再将归并后的查询结果返回 SQL 层。OceanBase 为了减小读放大带来的影响,在内存实现了多级的缓存,例如 Block Cache 和 Row cache,来避免对基线数据频繁的进行随机读。
2. 数据压缩技术
在这样的存储架构下, OceanBase 的数据压缩集中发生在 compaction 过程中 SSTable 的写入时,数据的在线更新与压缩得到了解耦。
OceanBase 支持不感知数据特征的通用压缩 ( compression ) 和感知数据特征并按列进行压缩的数据编码 ( encoding )。这两种压缩方式是正交的,也就是说,可以对一个数据块先进行编码,然后再进行通用压缩,来实现更高的压缩率。
批量落盘的特性使其采用更激进的压缩策略。OceanBase 是行列混存的微块存储格式( PAX ),充分利用了同一列数据的局部性和类型特征,在微块内部对一组行以列存的方式存储,并针对数据特征按列进行编码。变长的数据块和连续批量压缩的数据也可以让 OceanBase 通过同一个 SSTable 中已经完成压缩的数据块的先验知识,对下一个数据块的压缩进行指导,在数据块中压缩尽量多的数据行,并选择更优的编码算法。
与部分在 schema 上指定数据编码的数据库实现不同, OceanBase 选择了用户不感知的数据自适应编码,在给用户带来更小负担的同时降低了存储成本。从历史归档库角度而言,也不需要针对数据做出过多压缩与编码相关的配置调整。
(二)高可用和稳定性
除了存储成本以外,我们还对归档库选型中的候选者 ClickHouse 和 OceanBase 的高可用能力和稳定性进行了对比。
ClickHouse
我们将 ClickHouse 拟作历史库选型对其进行了充分测试:通过 Replication(复制)来实现集群中不同服务器之间自动同步数据的功能,以此确保数据的高可用性和容错性;使用 ZooKeeper 来协调复制过程,跟踪所有副本的状态,并确保它们保持一致。Replication 和 ZooKeeper 保证了在不同的物理设备上有多个数据副本,减少了数据丢失的风险。
不过在使用 ClickHouse 的过程中,我们发现它的高可用方案在大数据量的场景下会存在一些问题。主要由于原生的 Replication 方案有太多的信息存在 ZooKeeper 上,而为了保证服务,一般会有一个或几个副本,但 ZooKeeper 不支持线性扩展,受单机服务能力限制,,当归档库集群的数据量持续增长时,整个服务很快会不可用。
实际上在 ClickHouse 使用时,大家往往都把 ZooKeeper 当成了多种服务的结合,而不仅仅把它当作一个 Coordinate service。例如常见做法中,还会把它当作 Log Service,很多行为日志等数字的信息也会存在 ZooKeeper 上;还会作为表的 catalog service,像表的一些 schema 信息也会在 ZooKeeper 上做校验,这就会导致 ZooKeeper 上接入的数量与数据总量会成线性关系。按照我们归档库的数据增长速度做预估,ClickHouse 搭配 ZooKeeper 无法支撑三到五年全量归档数据需求。
除此以外,ClickHouse 的复制功能高度依赖于 ZooKeeper。但 ZooKeeper 是一个外部的协调服务,本身 的配置和维护增加了额外的复杂性,而且如果 ZooKeeper 自身出现问题,可能会影响到 ClickHouse 的复制过程。同时,这种高可用方案还增加了问题排查的链路长度和定位问题的难度,恢复过程也变得比较复杂,需要手动进行干预。我们在使用 ClickHouse 的过程中,很容易出现丢数据的情况。
OceanBase
OceanBase 是原生的分布式数据库,原生就可以保证多个数据副本之间的一致性,它们利用了基于 Paxos 分布式一致性协议保证了在任一时刻只有当多数派副本达成一致时,才能推选一个 Leader,保证主副本的唯一性来对外提供数据服务。也就是说,OceanBase 通过多副本和 Paxos 协议来保证数据库的高可用。
相比 MySQL 和 ClickHouse 的高可用方案方案,OceanBase 的高可用方案降低了我们的运维难度和业务变更难度。而且 OceanBase 的多地多副本架构和 Paxos 一致性协议,还能够支持数据副本分别存储在同城和异地,实现异地容灾。
因为 OceanBase 具备分布式特性,所以数据存储原生就具备了动态扩容的能力。当归档库的数据量持续增长时,只需要我们的 DBA 执行几条命令,就可以对机器的硬件或者整个集群的节点数进行扩容。在集群增加新节点之后,数据会自动在新、老节点之间完成负载均衡的过程,可以做到业务无感知的平滑扩容,保证业务扩容时不停机。同时还节省了业务量猛增后的数据库扩容和迁移成本,极大降低了数据库容量不足造成的各种风险。
对 OceanBase 进行扩容时,无论是增加单机的容量,还是增加 zone 内的节点数,亦或是为了保证更高的可用性而增加新的 zone,都可以直接通过白屏化的 OCP 工具来完成。下面就是我们把一个单副本的集群扩展成三 zone 三副本时的一张 OCP 截图:
相较于黑屏执行命令的方式,我们的 DBA 同学反馈使用 OCP 来进行 OceanBase 的部署和运维会更加方便,推荐大家使用。
综上所述:相比 MySQL 和 ClickHouse,在一致性方面,OceanBase 原生就有着强一致的存储保证,而不是去用最终一致性的妥协换取其他方面的能力,而且也不需要通过配置各种复杂的周边组件来对一致性进行保证。在高可用方面,OceanBase 的多副本容灾技术面向单个集群,事务日志持久化并在多个副本之间同步日志数据,基于 Paxos 协议保证日志数据在多数派副本持久化成功,整体上对用户提供了少数派故障时 RPO = 0,RTO < 8s 的高可用能力。在整个测试过程中,OceanBase 的表现相比 MySQL、 ClickHouse和DB-U,也要更加稳定。
综合考量各种数据库的存储成本、高可用能力、运维难度等方面之后,我们最终选择了 OceanBase 作为我们的历史归档库。
我们目前的在线库是主从结构的 MySQL,用于存储热数据,一般是最近一个月内的用户聊天记录;历史库是几个由 OCP 接管的 OceanBase 集群。每个月我们都会通过一个自研的 DTS 工具从 MySQL 在线库定期归档过期数据到通过 OceanBase 搭建的历史库,整体架构如下图:
我们现在已经用 OCP 接管了 8 个 OceanBase 归档业务集群,超过 20 个租户。在线库 MySQL 分表超过万张,仍然在源源不断地通过 App 按照用户的 ID hash 向 MySQL 写入数据,过期的历史数据现在会直接新导入到归档库 OceanBase 中。
我们曾经用过的一个旧 ClickHouse 归档库集群目前仍然提供部分历史数据的读取功能,不过由于考虑到 ClickHouse 的稳定性和数据安全问题,该归档集群会逐步被 OceanBase 替换掉。
首先,OceanBase 通过数据库内核的高压缩的能力,帮助我们轻松完成冷数据归档,并且节省了超过 70% 的存储资源。
其次,OceanBase 是原生的分布式系统,有着良好的扩展性。而且还可以对用户提供少数派故障时 RPO = 0,RTO < 8s 的高可用能力,让数据库在使用过程中更加稳定。
最后,OceanBase 自带一个智能化的白屏 OCP 平台工具,降低了我们 DBA 同学的部署和运维的门槛。OCP 对集群、租户、主机、软件包这些资源对象进行一个全生命周期管理,包括管理,安装、运维、性能监控、配置、升级这些功能。并且除了默认的监控告警之外,现在 OCP 还支持自定义告警,比如我们可以定制磁盘、内存达到怎样一个水位线的时候就会进行报警,满足了定制化的告警需求。除此以外,OCP 还支持备份恢复和在运维过程中可以支持进行一些自动化的诊断功能。
(一)在线库分布式能力支持
我们的在线库目前依然使用的是 MySQL,在 MySQL 中进行分库分表明显比在分布式数据库中使用单表的业务复杂度要高很多,而且数据一致性难以保证,当多个数据表或数据库之间的数据关联较为复杂时,维护数据的一致性难度也会增加很多。
除了一致性问题以外,在线库的运维难度也很高,需要对多个数据库或数据表进行管理和维护,增加了系统的故障排查和维护的难度。而且在分库分表的场景下,历史问题数据追溯问题是一个普遍存在的问题,由于数据被分散存储在多个数据库或数据表中,导致历史数据的追溯变得困难。
现在依然有很多上层业务都依赖在线库 MySQL,这些上层业务很多都是根据 MySQL 分库分表进行的设计和实现,所以在线库从 MySQL 替换成 OceanBase 还需要花一些时间。
但引入 OceanBase 数据库后,完善了 DB 侧对原生分布式库表的支持能力,对于大存储量、改造分库分表逻辑难度比较大的业务提供了更便捷的可行方案。
(二)使用 ODC 与 Binlog Server 等工具
我们了解到白屏化的 ODC 工具从 4.2.2 版本开始,就已经提供了从 MySQL 到 OceanBase 和从 OceanBase 到 OceanBase 的灵活数据归档能力,支持了多种维度的自动化任务配置。考虑到 OceanBase 的高压缩率及 ODC 的数据归档功能,用 OceanBase 做历史库方案就变得非常简单。
BOSS 直聘目前内部使用的 RDS 平台可以直接调用自研的 DTS 工具进行数据归档,所以业务同学目前会继续保持原有的 DTS 归档方法,当出现 DTS 解决不了的归档问题时,会利用 ODC 对 DTS 缺失的能力进行补位。后续, 我们会继续研究 ODC 的其他功能完善 DB 侧的支持能力。
另外,由于在线业务的逐步接入,下游数据仓库等业务也提出了基于 Binlog 订阅等需求,OceanBase 4.2.1 版本提供了 Binlog Service,对于分库分表类业务的下游接入可以直接通过该服务来提供,降低了下游需要逐个 MySQL 集群订阅 Binlog 的复杂度。
(三) 最佳实践探索
新数据库的引入对于 DBA 的考验也有所增加,在保证数据库稳定的前提下,也要充分对硬件、服务配置等维度进行合理的选型和持续优化,以便更好地挖掘OceanBase潜力。我们将持续与OceanBase团队一起实践和探讨,找到 OceanBase 最有效率和最具成本效益的使用方式,为业务的快速和稳定发展提供强有力的支撑。