Oceanbase 读写分离方案探索与优化
作者简介:许金柱,携程资深 DBA,专注于分布式数据库研究及运维。
台枫,携程高级 DBA,主要负责 MySQL 和 OceanBase 的运维。
【前言】
读写分离,是一种将数据库的查询操作和写入操作分离的方案,目的是为了降低读写操作的相互影响并提升资源利用率。在携程,读写分离的应用场景非常普及,只读的业务场景主要包括线上业务的读请求、缓存的拉取,大数据 ETL 取数等。
OceanBase 数据库天然支持读写分离的功能,即通过 OBProxy 代理服务和 OBServer 的配置修改即可实现业务的读写分离策略;然而 OceanBase 读写分离方案存在与业务耦合度过高、匹配不够灵活等问题。因此,我们在 OceanBase 已有的读写分离方案下继续探索并优化更符合当前业务场景的解决方案。
一、OceanBase 原生的读写分离策略
OceanBase 数据库在读取数据时,提供了两种一致性级别:强一致性和弱一致性。
强一致性指的是读取最新数据,请求路由给主副本;弱一致性不要求读取最新数据,请求优先路由给备副本。
基于此,通过应用侧为执行的 SQL 添加 SQL Hint 来显性开启弱一致性读就可以实现基于注释的读写分离,同时也衍生出了如下三种常用的读写分离策略:
策略一、备优先读
通过修改 OBproxy 的路由策略为 follower_first ,并将业务读流量指定到该 OBproxy 从而保证读请求优先访问 follower 副本。
优点: 配置相对简单,只修改 OBproxy 的配置,无需修改 observer 的配置;读流量均摊到全部 follower 副本上。
缺点: 读请求可能存在跨 zone 访问;在不调整副本 leader 的情况下,同 zone 或同 server 下不同副本的 leader 和 follower 可能共存,不能完全实现 zone 级别或者 server 级别的读写隔离。
高可用: 当两个 follower 副本中的一个宕机时,配置读写分离的 OBproxy 将优先路由到另一个 follower 副本。
策略二、LDC(Logical Data Center)只读型副本
新增一个只读副本,为其添加 idc 标签,并将 OBproxy 的参数 proxy_idc_name 指向该 idc。指定读流量访问该 OBproxy,通过 LDC 配置仅访问只读副本。
优点: 读写完全隔离,读扩展性好。
缺点: 只读型副本只同步数据,不参与分布式选举,需要额外增加一个副本。对读请求不大的场景存在资源浪费。
高可用: 当只读副本宕机时,配置读写分离的 OBproxy 将自动把流量切到其他 follower 副本上。
适用场景: 业务的读请求远大于写请求,且大部分读请求对实时性要求不高或者有大量的 AP 分析场景。
策略三、LDC(Logical Data Center)全功能型副本
将一个全功能型副本添加 idc 标签,驱逐该副本上的 leader,并将 OBproxy 的参数 proxy_idc_name 指向该 idc。指定读流量访问该 OBproxy ,通过 LDC 配置仅访问该副本,完全分离读写流量
优点: 读写完全隔离,且相对于策略二不需要额外资源
缺点: leader 分布集中在两个 zone 中,有可能会降低写入性能
高可用性: 当指定 idc 侧的 follower 副本宕机时,配置读写分离的 OBproxy 将自动路由到其他 follower 副本上。
适用场景: 读写比较均衡,存在少量 AP 场景
在满足读写分离需求的同时,我们也关心在使用这些策略时的高可用性:
当只读节点宕机时,配置读写分离的 OBproxy 将自动修改路由,优先将读流量路由到其他 follower 副本上。在使用策略二或者策略三时,每一个 partition 只有单个的 readonly 或者 follower 副本承担读压力,因此切换到新的 follower 副本上的压力是可以负担的。
当配置读写分离的 OBproxy 出现问题时,由于 OBproxy 是一组无状态的服务,因此只需要依赖数据库访问中间件的判活来修改应用访问 OBproxy 的入口即可实现高可用。
二、携程针对 OceanBase 在读写分离场景的探索
上面三种策略,在不同场景下各有优劣,也都能达成读写分离的效果,但相较于前两种策略,我们更倾向于使用策略三。携程的业务场景中,大部分数据库的读写相对均衡,同时包含少量的 etl 取数、BI 查询等场景,与策略三的适用场景更贴合,且成本相对较低。
同时,我们也关注到 OceanBase 现有的读写分离策略中存在一个共性问题:应用侧必须为 SQL 添加弱一致性读的 SQL Hint,否则即使在 OBproxy 做了相应配置,仍然不能达到读写分离的效果,所有读写压力将集中在 leader 副本上。这涉及到用户的代码改造且使读写分离策略与应用的耦合度高。因此,我们需要在已有方案的基础上,根据当前业务场景探索并实践出一个灵活的、与应用耦合度低的读写分离策略。
携程目前已接入 OceanBase 近百套集群,租户约 140 个,同时越来越多基于 MySQL 数据库的业务计划迁移至 OceanBase 数据库。对于读写分离场景,迁移 OceanBase 成本较大。
针对需要保证 OceanBase 数据库适配当前读写分离场景的需求,我们首先梳理了携程业务目前读写分离的主要适用场景:
ETL 取数:Zeus 定时作业,需要将线上订单相关数据同步至 Hive 或 ES
BI 查询 Offline 报表,为业务部门提供分析数据
MySQL 业务中已存在的读写分离场景(如缓存拉取): 读请求远大于写请求的产品库场景,要求在延迟阈值内,将读请求路由到只读节点
我们看到携程现有的读写分离场景包含 OLTP 和 OLAP ,并且 db 涉及的业务线可能比较复杂很难简单分辨。
同时,读写分离改造涉及到增加 SQL Hint /*+READ_CONSISTENCY(WEAK) */ 的问题 。 SQL Hint 在传统的关系型数据库中是一种通过注释的方式改写 SQL 执行计划的设计,而在 OceanBase 中它同样有类似功能,并且提供了 sql 选择副本访问策略的能力。为了使弱一致性读生效,需要对有读写分离需求应用中的 SQL 做如下改造:
MySQL 原版 SQL
SELECT * FROM test
OceanBase 改造读写分离的 SQL
SELECT /*+READ_CONSISTENCY(WEAK) */ * FROM test
综上,在携程的业务上线或业务迁移过程中,容易遇到以下问题:
现有业务迁移过程不透明,涉及业务层面的代码改造。
代码和读写分离策略耦合,造成技术债。
DBA 需要大量重新审核查询语句和业务行为,给出改造评估建议。
针对这些痛点,我们希望可以不使用 SQL Hint ,直接指定这些 SQL 进行弱一致性读来达到读写分离的目的。我们开始调研能否在 OceanBase 代码层寻求解决方案。通过对源码研究,我们尝试在 OBProxy 组件上进行改造,最终实现不涉及用户代码改造前提下的自动开启弱一致性读功能。代码的改造包含以下两个方面:
功能 1、自动为访问本 OBProxy 的所有连接开启弱一致性读策略
功能 2、自动为访问本 OBProxy 的指定帐号(只读账号)开启弱一致性读策略
我们通过新增两个参数 enable_weak_read 和 weak_read_user_list 在建连时进行判断来分别实现上述两个功能。enable_weak_read 参数开启时,将为访问该 OBProxy 的所有连接开启弱一致性读;weak_read_user_list 参数则针对账号维度来指定部分账号进行弱一致性读。
以上弱一致性读策略均基于会话级别开启,通过配置 LDC 和 read(slave) 域名,进行读、写隔离,分担主库压力,自动实现读写分离策略,不需要人工干预。
三、携程针对 OceanBase 读写分离场景的实践和测试
对于 OBproxy 进行改造后,为了验证其功能性,我们将其与 LDC(Logical Data Center)全功能型副本读写分离策略相结合,进行了如下实验。
场景一:
leader 副本集中在 zone1, OBproxy 指定账号开启弱一致性读并且绑定 idc2(zone2+zone3)
实验结果:zone1 的流量消失,流量被 zone2 和 zone3 分摊
场景二:
Leader 副本集中在 zone1 和 zone3, OBproxy 开启对所有连接开启弱一致性读,并绑定 idc2(zone2)
实验结果:zone1 和 zone3 的流量消失,流量全部由 zone2 承担
四、携程选择的 OceanBase 读写分离场景
在携程现有的业务读写分离场景中,我们往往通过区分业务的账号来识别业务的行为,因此我们将 OBproxy 上设置针对部分账号开启弱一致性读并将其中一个 zone 和 proxy 绑定来将这部分请求路由到指定 zone 上实现读写分离。
优点:
1、同一个集群的 OBproxy 配置统一(不开启弱一致性读时 LDC 路由不生效),便于 obproxy 的横向扩展和快速切换应用流量的指向。
2、按照账号维度配置灵活度高,在部分极端场景方便人工介入。
版权声明: 本文为 InfoQ 作者【OceanBase 数据库】的原创文章。
原文链接:【http://xie.infoq.cn/article/c0b96ae8ec6e208039ad689ca】。文章转载请联系作者。
评论