【华为云MySQL技术专栏】TaurusDB 全局一致性,保障客户数据强一致
1. 背景介绍
随着信息技术的飞速发展,企业业务场景变得日益复杂多样,数据量呈爆炸式增长。传统单一节点数据库,已难以满足大规模数据处理、高并发读写以及高可用性方面的需求,多节点、多副本数据库应运而生,其中数据一致性问题成为关键难点。
在无法保证最终一致性的情况下,主备复制延迟会导致从不同节点查询到的结果不同。对于金融、游戏等对数据延迟敏感的行业而言,只读节点的数据延迟可能导致交易数据错误、业务逻辑混乱等严重后果,直接影响企业运营的准确性与稳定性。
TaurusDB是一款基于存算分离架构的云原生数据库,同样面临着数据一致性的挑战。为解决这一问题,TaurusDB通过在代理层和数据库内核之间构建一套高效的一致性协议,实现了对全局一致性的支持,为用户提供可靠、高效的数据管理解决方案。
本文将从全局一致性的使用方法、原理介绍以及性能分析等方面,对TaurusDB的这一特性进行详细介绍。
2. 使用方法
全局一致性,是指在多节点、多副本的数据库环境中,保证发往集群任意节点的读请求,都可以获得强一致性的结果。
也就是说,无论事务在哪个节点执行,所有客户端都能看到最新的数据,就像系统只有一个单一的数据库副本一样。所有客户端无论访问哪个节点,都能立即看到最新写入的数据,从而确保了数据在整个系统范围内的准确性、完整性和连贯性,避免了数据读取不一致的问题。
TaurusDB的全局一致性功能需要用户创建数据库代理[i] ,并通过数据库代理访问数据库实例,如图1所示。其中,数据库代理是TaurusDB和应用服务之间的网络代理服务,负责代理应用服务访问TaurusDB的所有请求。后文中,proxy均指数据库代理。
图1 代理访问路由到主/只读节点[ii]
TaurusDB支持在创建数据库代理时设置一致性级别,也支持在数据库代理创建成功后修改一致性级别。
1. 当用户通过管控界面,修改代理的一致性级别,选择使用全局一致性,并设置超时时间、配置分发策略后,proxy节点会识别到此功能已开启。此后,每当代理节点向 TaurusDB 内核新建一个会话连接时,就会在会话连接上打开开关和设置超时时间。2. 由于代理的实例往往是采用多proxy架构,即不同proxy负责代理不同的业务,每个代理上各自配置各自的一致性级别。这样好处是可以按照不同业务灵活使用全局一致性。
图2 全局一致性开关选择
对于全局一致性,还需要在TaurusDB实例的“参数修改”页面,将参数‘innodb_rds_trx_use_commit_lsn’设置为`ON`。该参数为TaurusDB内核层全局一致性的功能开关,默认为`OFF`,只有将其设置为 `ON`,全局一致性功能才能完整生效。
在代理的“参数修改”页面中,提供了两个与全局一致性相关的参数,具体如下所示。
表1 数据库代理参数说明
3. 原理介绍
前置技术背景
TaurusDB只读节点和主节点共享底层的存储数据,但为了保证只读节点内存中缓存数据的一致性,主节点与只读节点通信后,只读节点仍需要从Log Store中读取主节点生成的Redo log(Redo 日志),来更新内存中的缓存数据。这是导致只读复制延迟的由来,也是主节点和只读节点读取数据存在延迟的关键因素。
只读延迟指标:指在主节点更新数据后,多少时间后,只读节点才能得到这个最新数据。
只读节点通过读取Redo日志来进行缓存数据更新,已更新的Redo日志的LSN(日志序列号),称为visible lsn,表示只读节点能读取数据页的最大LSN。
对于主节点来说,所有数据修改产生的Redo日志,经过刷盘后的LSN,称为flushed to disk lsn(刷盘LSN),表示主节点能访问的数据页的最大LSN。只读节点只能读取已刷盘的Redo日志,同时也是只读节点能读取的最大LSN。
只读延迟的计算:
只读延迟,其实就是只读节点visible lsn,相对于主节点flushed to disk lsn的延迟。比如,在t1时刻:主节点flushed to disk lsn=100, 只读节点visible lsn=80, 经过一段时间,只读节点回放Redo日志后,在t2时刻:主节点 flushed to disk lsn=130,只读节点visible lsn=100。
此时,我们可以计算出只读延迟为:t2-t1,因为经过t2-t1的时间后,只读节点才能读取主节点t1时刻之前提交的数据。
实现原理
参考图1,在全局一致性功能开启之后,当某一个会话数据更新操作执行完成后,主节点的flushed to disk lsn会被更新为最新的LSN,后续读请求进来时,数据库代理会按照代理的路由模式直接路由到预期的节点上。由于存在只读延迟间隙,只读节点上当前visible lsn可能仍滞后于主节点的flushed to disk lsn。
内核层面此时会立即加快只读节点的LSN推进,在毫秒级内完成只读节点和主节点的LSN同步。同步完成后,再对该读请求进行响应,保证只读节点能读到,查询请求下发之前所有在主节点上已经提交的更新。
整体流程
图3展示了全局一致性整体工作流程。图中将整体框架划分为三个层面,分别为用户面、proxy层面以及TaurusDB内核层面,并且详细描述了从全局一致性功能的开启到生效,再到内核层控制只读节点全局一致性的细节。
图3 全局一致性整体流程示意
下面将从不同层面按照时间流向,对全局一致性功能的整体流程进行说明:
(1)用户面向代理层发送业务SQL请求。
(2)代理层将更新业务SQL请求路由到读写节点node0,主机刷完Redo日志后,将LSN推进到70。
(3)代理层将此时的查询业务SQL请求路由到只读节点node1,并在当前连接中附上全局一致性开关和超时时间标识。
(4)node1收到SQL请求后,等待自身LSN从60推进到70后,返回查询结果。断开连接后,全局一致性开关参数失效。
(5)代理层将查询业务SQL请求路由到只读节点node2,并在当前连接中附上全局一致性和超时时间标识。
(6)node2收到SQL请求后,等待此时自身LSN从48推进到70后,返回查询结果。断开连接后,全局一致性开关参数失效。
如果查询业务 SQL 请求等待超时,TaurusDB 的只读节点会向 proxy 返回错误码。此时,proxy 会根据用户配置的超时分发策略,自动将请求发送到主节点(即写节点,确保总是能读到最新的数据),或者向用户返回读失败错误码。
4. 技术实现介绍
内核层整体实现分三个阶段完成
-
fetch阶段
只读节点获取主节点最新flushed to disk lsn。
图4 获取主机flushed to disk lsn示意
当只读节点启动时,会开启一个常驻线程handle_slave_fetch_lsn。只读节点发起读请求时,采用生产者-消费者模型驱动读业务运行,具体流程如下:
步骤一:
将该读事务搁置到等待队列中,并使用条件通知机制,立即通知handle_slave_fetch_lsn线程,该读事务随即进入条件等待。
步骤二:
fetch线程得到事件通知后,立即遍历等待队列列表。如果队列非空,则把等待队列中的事务转移到另一个待通知队列中,并移出等待队列,然后向主机发送获取最新的flushed to disk lsn的消息请求。
步骤三:
当主节点请求响应后,fetch线程遍历待通知列表,并逐个唤醒。只读事务从而退出等待状态,顺利拿到主节点当前最新flushed to disk lsn。
步骤四:
只读节点判断获取的flushed to disk lsn是否比上次保存的LSN大。如果变大了,则说明主节点有了新的更新。此时需要等待只读节点推进,进入advance阶段。否则直接返回数据,无需等待只读节点推进。
-
advance阶段
只读节点回放Redo,推进visible lsn到主节点最新flushed to disk lsn。
当只读节点仍落后主节点,即只读事务无法满足立即返回读响应时,则通知handle_slave_sync线程立即获取主机最新的Redo信息进行回放,该读事务会再次进入条件等待状态。
图5 只读节点推进流程示意
只读节点Redo回放线程集在完成Redo日志回放后,会更新只读节点的一系列内存数据,同时更新只读节点视图信息,最后唤醒等待中的只读事务。
只读事务会判断只读节点是否已推进到了主机flushed to disk lsn,如果已经完成了推进,则退出等待,否则继续等待。
关键等待流程代码如下:
auto stop_condition_wait_lsn = [&trx, wait_timeout](bool wait) {
uint64_t flush_lsn = atm_load(trx->flush_lsn);
uint64_t visible_lsn = atm_load(trx_sys->read_consistent_lsn);
if (visible_lsn >= flush_lsn) {
return true;
}
if (wait) {
auto start_time = trx->start_wait_time;
auto now = std::chrono::steady_clock::now();
ulong duration = std::chrono::duration_cast<std::chrono::milliseconds>(
now - start_time).count();
if (duration > wait_timeout) {
atm_store(&trx->is_wait_timeout, true);
return true;
}
}
return false;
};
os_event_wait_for(trx_sys->read_consistent_lsn_event, max_spins, timeout,
stop_condition_wait_lsn);
-
response阶段
只读节点在超时时间内完成推进,并且满足visible_lsn>= flush_lsn,则返回成功。
如果只读节点在超时时间内没有推进到主机的数据,则返回DB_WAIT_LSN_TIMEOUT给proxy层。此时,proxy通过用户配置的consistTimeoutPolicy参数,自动将请求发送到主节点(即写节点,确保总是能读到最新的数据)或者向用户返回读失败错误码。
性能分析
在以下性能测试中,我们使用的实例规格及配置如下:
实例规格:8U32GB
Proxy规格:8个节点*(4U8G)
Proxy路由模式:负载均衡
打开一致性读开关
在不同并发下,sysbench OLTP测试数据(RO只读场景下),如图6所示:
图6 只读场景下QPS统计数据
小结:在只读场景下,低并发时,全局一致性的QPS相比最终一致性QPS,劣化基本在10%左右,而在高并发下,全局一致性的QPS相比最终一致性QPS ,劣化基本在15%以内。
在不同并发下,sysbench OLTP测试数据(RW读写场景下),如图7所示:
图 7 读写场景下QPS统计数据
小结:在读写场景下,全局一致性的QPS相比最终一致性QPS ,劣化基本在15%以内。
5. 总结
本文从背景介绍、使用方法、原理介绍以及性能分析等方面,对TaurusDB的全局一致性特性内容做了详细的介绍。该特性在多节点数据库系统的基础上,配合proxy,实现了所有的读操作,无论在哪个节点进行访问,都能看到一致的数据,且性能高效,满足了客户对读请求强一致性的诉求。
[i]数据库代理详细介绍参考:https://support.huaweicloud.com/usermanual-taurusdb/taurusdb_11_0016.html
[ii]一致性介绍参考:https://support.huaweicloud.com/usermanual-taurusdb/taurusdb_11_0041.html
- 点赞
- 收藏
- 关注作者
评论(0)