开源实践 | 携程在OceanBase的探索与实践
写在前面:选型考虑
携程于1999年创立,2016-2018年全面推进应用 MySQL 数据库,前期线上业务、前端技术等以 SQL Server 为主,后期数据库逐步从 SQL Server 转到开源 MySQL 数据库。随着技术多元化以及业务的不断发展,MySQL 逐渐无法满足携程需求,主要体现在:业务数据模型呈现多元化,在异地多活部署、运维成本、资源弹性管理以及应用稳定性等方面对数据库技术提出了新的要求,MySQL 方案在单机性能瓶颈表现明显,同时分库分表方案带来运维复杂度和维护成本也随之增加。于是从2018年开始,携程开始引入 Mongo、HBase 等技术栈,同时探索通过OceanBase 社区版方案来替换原有MySQL方案。
经前期测试与后期实践,事实证明OceanBase技术特性及架构相对符合携程项目需求。OceanBase的异地多机房多写、大促弹性扩容、高可用切换对业务影响小都非常符合携程需求。同时在性能和成本方面取得了不错的收益:在性能方面,OceanBase 方案读性能平均提升 2 倍,写性能平均提升 3 倍;在成本方面,OceanBase 方案节省 2/3 存储资源,很大程度上降低了硬件成本。
本文内容主要分享携程在OceanBase社区版的探索,将从以下三个方面展开分析:OceanBase 自动化部署、MySQL 实例迁移 OceanBase 以及 OceanBase 方案收益。
一、基于OBD工具自动化部署环境
OBD (OceanBase Deployer)是 OceanBase 开源软件的安装部署工具,同时也是包管理器,可以用来管理 OceanBase 所有的开源软件。一开始,携程利用 OceanBase 开发的OBD 组件快速进行了部署,由于业务需要非常灵活的运维操作,比如需要扩容 OBServer 时,该操作需要通过命令行执行 ALTER 语句实现,暂不支持通过 OBD 来完成集群扩容,同时综合业务自身需求:
1、携程有自己的运维监控平台,需要将OceanBase的元数据、监控数据、以及任务流很好地融入到携程监控平台、自动运维平台中;
2、携程有着完整的企业dal层、数据库多环境(开发、测试、生产)上传发布流程和部署平台,OceanBase需要兼容甚至还需要和MySQL互相巧妙的兼容,而OBD当前版本无法支持。
为此,携程选择了基于OBD自动化工具进行二次开发,形成符合业务需求的部署方案,基于两个方面的考虑:开发语言易上手,二次开发成本低。OBD工具是Python写的程序,源码简单易懂,可以直接调试源码并进行二次开发自动化部署程序,大大降低了OceanBase运维嵌入携程平台的难度;融入个性化设置。OBD工具虽然方便,但是一些配置不够灵活,携程的安全策略以及服务器环境限制可能和OBD有所冲突。集群的元数据合并到携程的运维平台至关重要,而二次开发的自动化程序,可以直接定义安装的用户、目录,将服务器、IP、Zone等基础信息直接落到运维平台,为后续自动化运维打下了坚实的基础,和携程运维平台契合的更紧密。
以下是携程基于 OBD 部署工具二次开发的部署运维平台示意图:
在环境部署这里特别提一点,OceanBase的命令可以将整个参数放在启动命令中,包括OBServer和OBProxy。集群部署好后会在OBServer/etc目录下生成一个配置文件OBServer.config.bin,将所有集群信息记录在文件中,这样后续启动或者重启OBServer时,就不需要再带参数启动。OBProxy也是一样,在OBProxy/etc下也会生成OBProxy_config.bin文件,再次启动可以不带参数启动,这点对使用者非常友好,避免不带参数再次启动时,出现参数回滚问题。
二、MySQL实例迁移OceanBase实践
在部署工具改造工作完成之后,正式进入业务和数据的迁移环节。该部分主要从以下几个方面来介绍携程的迁移工作:集群架构设计、迁移准备工作(包括兼容性测试、业务模式评估、回退方案等)、应用迁移以及性能优化。
1、OceanBase 架构如何设计?
在实际环境中,需要根据自身 IDC 机房的部署,匹配适用 OceanBase 的架构设计。 OceanBase 在携程的部署架构如下,当前是一地三中心部署方案:
2、迁移准备工作
1)SQL事务兼容性测试
在携程开始使用时,OceanBase社区版完全兼容MySQL 5.6版本,而实际携程应用的是5.7版本。为此,将MemTable Trace抓取出来的语句在OceanBase上进行兼容性测试,发现大部分都相互兼容。这里需要注意的是:OceanBase不支持主键的删除,主键的修改、跨数据类型的字段的类型修改,但新增字段,扩容字段,没有任何影响。
2)对MySQL实例特点和业务模式进行分析
最值得关注的是,可以直接用MySQL创建表结构的语句在OceanBase 建表,后期简单做下优化和微调就可以,对使用者非常友好。
3)分区表结构改造
在OceanBase里,一张表即使没做分区也默认为分区表,即一个大的分区。使用分区表一方面可以基于数据分区来做集群层面的数据扩展, 另外一方面可以更充分的支持 SQL 并行执行, 充分利用资源,提升集群的性能。同时,好的分区设计机制跟业务息息相关。在实际环境中,MySQL实例的分区机制相对特殊,大量分区表的分区维护需要依赖自动化脚本,因此迁移到 OceanBase 环境前,表结构需要进行改造。换言之,更好地分析与设计OceanBase分区表,可以充分地发挥 OceanBase 的优势。基于以上考虑,对业务表结构进行如下改造:
- 去掉 Max分区:由于OceanBase不支持Max分区的Reorganize,所以MySQL的range分区表转到OceanBase后需要去掉Max分区,直接预留和保留一些分区来保证所有数据落在分区中,风险点是当数据超出分区范围则会导致写入失败。
- 本地索引改造:分区表的分区增加和删除机制,是由脚本自动维护的。在分区维护的时候,删除分区耗时比MySQL要长很多,经过分析研究,原因是表结构在OceanBase创建后二级索引默认是Global索引,在维护分区的时候会重建全局索引,所以耗时比较久。解决方法:创建表结构的时候,二级索引指定为Local,这样在维护分区时可以瞬间完成。
- TableGroup 解决跨 zone 访问:小表到底要不要做分区?根据 OceanBase 开源社区技术专家的意见:小表不建议做分区。为了解决join时候跨zone跨机器查询,rpc count过大导致性能下降问题,比较小的表、做key分区的表join查询比较频繁的表等可以做TableGroup,这样小表或者相同key的数据会在一起,不会跨zone跨机器查询。
4)回退方案
在OceanBase社区版3.1.0版本中,回退的方案并没有很多,针对社区版不支持OMS组件的情况,携程有两种解决办法:
方法一是应用双写,这也是最常用的一种方法。即在业务数据迁移完成之后,业务在写入 MySQL 环境的同时也会写入 OceanBase 环境。经过一段时间的线上验证,确认 OceanBase 方案在性能和稳定上满足业务要求之后,会断开业务写 MySQL 环境的链路,业务读写流量全部接入 OceanBase 环境。双写的缺点是增加开发成本,开发要介入。在社区版 3.1.2 版本中,将会开源的数据同步工具 OMS(OceanBase Migration Service)是 OceanBase 提供的一站式数据传输产品, 提供 MySQL 到 OceanBase 以及 OceanBase 到 MySQL 数据同步能力,数据迁移将会变的更加简单和快捷。
方法二是备份恢复。备份恢复是 OceanBase 数据库高可用特性的核心组件,主要用于保障数据的安全,包括预防存储介质损坏和用户的错误操作等。如果存储介质损坏或者用户误操作而导致了数据丢失,可以通过恢复的方式恢复用户的数据。从社区版3.1.1 开始, 社区版支持物理备份与恢复, 备份和恢复的性能得到大幅提升,可以快速的完成线上数据的全量和增量备份以及在异常情况下的数据恢复,非常好用。
三、上线迁移以及性能优化
1、从 MySQL平滑迁移至 OceanBase
携程自研设计架构是三个IDC,对应着三个IDC机房,也就是物理的机房,其中一个是开源的集群。由于后续资源紧张,对集群进行弹性扩容,每个Zone从最初的一台机器扩容到了两台机器。在 OBProxy 这一层,为了防止访问过于集中以及考虑到高可用问题,多个 OBProxy 做了负载均衡,应用通过负载均衡设备可以将流量平均打在多个OBProxy。还有另外一个方法,即应用通过DNS直接访问,但这种方法用得很少。
如何实现应用安全并且平滑的从MySQL转到OceanBase?携程也做了很多工作和预案。比如,OceanBase的连接串有租户的配置,那么用MySQL的账号直接访问OceanBase是不行的,需要提前在MySQL创建一个带租户的账号,uapp@tt01权限和之前的账号一样,只要修改应用的连接串配置,开发无需重新发布,后续将连接数据库的配置直接切换到OceanBase,几乎可以实现完全兼容。只要在迁移前验证确保所有的应用全部使用临时账号,就可以万无一失。
2、性能优化
在SQL优化方面,迁移之前应用大批次写入 MySQL 环境,单个批次数据量范围在1000条至10000条不等,写入性能不佳。同等数据量时,在 OceanBase 环境中数据写入更快。此外,可以对 SQL 语句进行优化,比如减少多张表的 join查询,SQL语句带上分区条件缩小范围查询,避免跨分区查询。
业务数据大量写入的时候,可以通过调整一些内存相关参数、日志清理参数来保证集群运行的稳定。在这里介绍3个非常有用的参数以及实际操作说明供参考学习:
- enable_merge_by_turn控制是否开启轮转合并。在凌晨做大合并的时候,开启轮转合并是逐个zone进行合并操作,先把Leader切走再做合并,做完再做下一个,切Leader会影响到业务。关闭该参数即所有的zone一起操作,可能会出现大事务冲突问题,携程一般会选择业务低峰期,或者业务影响很小的时候进行合并,保证合并顺利完成的同时尽量缩短用时。
- minor_freeze_times 用于设置多少次小合并触发一次全局合并。在实际业务中,频繁的全局合并可能会影响集群整体性能,通过该参数设置转储条件,尽量减少触发大合并的次数,保证集群性能稳定。
- writing_throttling_trigger_percentage参数用来控制内存限流阀值,在MEMStore占用内存达到该参数默认阀值时,会开启写入限速。针对写入频繁的高写入业务场景可能出现的内存耗尽导致OOM 问题,通过该参数配合writing_throttling_maximum_duration 参数(通过控制内存分配进度来控制写入速度,即指定在触发写入限速后,剩余 memstore 内存分配完所需的时间,默认一小时)使用。如果内存使用在默认值一小时之内达到限流阈值大小,剩下的内存会按照默认一小时平均分配。默认一小时之后内存没有释放,OceanBase 会拒绝应用建立新连接。这是一个保护措施,保证携程的集群在写入量很大时,集群运行依旧稳定。
3、慢SQL优化
在实际业务测试中,出现执行时间很长的 SQL 语句,仔细排查原因发现是因为查询所需的数据分布在不同节点或者机器上,所以 SQL 请求的数据需要跨机器或者跨 zone访问,网络交互次数增加,影响 SQL整体执行时间,在这里介绍一个典型慢 SQL 优化案例。
慢 SQL 信息:
select a.c1,a.c2 from table1 a left join table2 b on a.c0 = b.c0 where b.c1 is null and a.c1 = “XXXXX”;
查看系统表得到该SQL 执行信息:
select * from gv$sql_audit;
针对这个场景尝试两种优化方法:
第一种:使用TableGroup 方式,将查询所需的数据放在一起,查询速度提升明显
Step1:创建 tablegroup
mysql> CREATE TABLEGROUP IF NOT EXISTS tg_1;
Step2:添加对应的表
mysql> ALTER TABLE TABLE1 SET TABLEGROUP tg_1;
mysql> ALTER TABLE TABLE2 SET TABLEGROUP tg_1;
第二种:手工把两个表迁移到同一个节点,效果一样但是相对麻烦
Step1:switch replica
mysql> ALTER SYSTEM SWITCH REPLICA PARTITION_ID='0%0@100000' source='x.x.x.x:2882';
Step2:move replica
mysql> ALTER SYSTEM MOVE REPLICA PARTITION_ID='0%0@100000' source='x.x.x.x::2882';
Step3:避免分区自动在Unit之间重新均衡
mysql> ALTER SYSTEM SET ENABLE_REBALANCE = 'false' [TENANT='tenantname'];
通过以上两种优化方式,RPC_count 从 23331降低至 1 次,优化效果显著。
四、OceanBase 方案在性能和成本方面收益明显
在使用 OceanBase 方案之后,携程在性能和成本方面取得了不错的收益:
1、性能方面,相比 MySQL,OceanBase 方案读性能平均提升 2 倍,写性能平均提升 3 倍,性能提升明显;
2、成本方面,基于数据编码以及存储引擎自带多级压缩技术,使得 OceanBase 方案相比 MySQL 节省 2/3 存储资源,很大程度上降低了硬件成本。
读性能对比:
OceanBase 读性能 | MySQL 读性能 |
---|---|
写性能对比:
OceanBase 写性能 | MySQL 写性能 |
---|---|
写在最后:未来规划
在携程与OceanBase短暂的磨合期内,携程各项测试表明,OceanBase原生分布式数据库的各方面性能均可满足携程的业务需求。当前携程的风控业务系统、会员用户画像等多个业务在测试 OceanBase 社区版。携程非常感谢OceanBase的所有技术支持人员,期待在后续合作中,OceanBase能发挥出更优越的表现。