分布式数据库读操作一致性
问题描述这张图片直观地展示了分布式事务中一个非常经典且棘手的痛点全局读原子性Global Read Atomicity缺失导致的“部分可见性”问题。通俗点说它反映了在分布式环境下即便使用了 XA 协议如果不配合全局快照Global Snapshot用户在某一时刻可能会观察到“钱凭空消失了”的中间状态。1. 问题的物理过程拆解我们按照时间轴来看看这笔“转账”发生了什么初始状态 (t0t0t0)节点 A 有 2000节点 B 有 1000。总和 3000。准备阶段 (t3t3t3-t4t4t4)节点 A 和节点 B 都完成了XA PREPARE。此时物理日志已落盘锁已拿住但数据尚未正式对外界可见。部分提交阶段 (t5t5t5)节点 A 执行了XA COMMIT。此时节点 A 的数据正式更新为180018001800。灾难发生的瞬间 (t7t7t7)节点 A 的读 (t6t6t6)读到了更新后的值180018001800因为已提交。节点 B 的读 (t7t7t7)由于节点 B 此时还处于PREPARED状态尚未执行XA COMMIT。在 MySQL 的 RC/RR 隔离级别下读操作无法看到未提交的数据因此读到的依然是旧值100010001000。结果在这个时间点外界查询到的总和是1800100028001800 1000 2800180010002800。消失的 200 去哪了它卡在了分布式事务的“时间差”里节点 A 已经扣了钱但节点 B 还没来得及把钱加进去。2. 这个问题的本质是什么这个问题被称为“Read Skew”读偏斜在分布式场景下的变种。本地事务的局限性单机数据库MySQL的隔离级别RC/RR只能保证“本地原子性”。它只关心自己节点上的事务提没提它并不知道节点 A 和节点 B 属于同一个全局事务。缺乏全局一致性位点在t5t5t5到t8t8t8这段时间内分布式事务处于“中间态”。如果系统没有一个全局的“发令官”来统一所有节点的可见性那么读请求就会因为不同节点的提交时间差看到不一致的数据视图。3. 物理操作上的后果数据不一致瞬时虽然t8t8t8之后数据最终会恢复一致总和 3000但在高并发的业务场景下这种瞬时的不一致可能导致极大的问题比如风控系统误判余额不足或财务报表统计错误。隔离级别降级即便你在单机上用了最高级别的 RR在分布式环境下由于缺乏全局版本管理读操作实际上退化到了连 RC 都不如的水平。解决方案全局时间戳 全局快照读一、 核心机制TSO 与 MVCC 的物理绑定在分布式系统中解决读一致性的核心在于建立一套逻辑参考系。全局时间戳服务 (TSO)物理实现通常为一个高可用的中心化组件如 GTM/TSO 节点。产出物分配单调递增的 64 位整数作为逻辑时钟。双位点机制Start_TSStart\_TSStart_TS事务开启时获取用于界定可见性范围。Commit_TSCommit\_TSCommit_TS事务提交时获取用于标记数据的物理生效时刻。多版本并发控制 (MVCC) 扩展传统的单机 MVCC 记录的是本地事务 IDTRX_ID。分布式 MVCC数据行Row及其 Undo Log 必须携带或关联Commit_TSCommit\_TSCommit_TS。每一行数据的元数据变更为[Data, Create_Commit_TS, Delete_Commit_TS]。二、 物理执行流程详尽步进假设存在写事务TwT_wTw跨 A、B 节点和读事务TrT_rTr。1. 写事务TwT_wTw执行阶段获取Start_TSStart\_TSStart_TSTwT_wTw向 TSO 申请开始时间戳假设为 100。本地修改在 A、B 节点修改数据此时数据页记录标记为TwT_wTw的临时标识或锁定状态。XA PREPAREA、B 节点执行物理预提交Redo/Binlog fsync。2. 写事务TwT_wTw决策与读事务TrT_rTr发起获取Commit_TSCommit\_TSCommit_TSTwT_wTw向 TSO 申请提交时间戳假设为 110。写入全局日志Proxy 将Commit_TS110Commit\_TS110Commit_TS110写入全局事务表如gtid_log_t。此动作为逻辑生效点。读事务TrT_rTr启动同时另一个会话发起读请求向 TSO 申请Read_TS105Read\_TS105Read_TS105。3. 全局快照读的节点内执行指令下发Proxy 将Read_TS105Read\_TS105Read_TS105发送至所有存储节点。节点 A 过滤已物理提交A 节点已执行XA COMMIT最新行记录显示Commit_TS110Commit\_TS110Commit_TS110。判定逻辑比较Commit_TS(110)Read_TS(105)Commit\_TS(110) Read\_TS(105)Commit_TS(110)Read_TS(105)。物理动作A 节点判定该记录属于“未来可见”立即通过Undo Log向前追溯寻找Commit_TS≤105Commit\_TS \le 105Commit_TS≤105的上一个物理版本并返回。节点 B 过滤物理未提交处于 PrepareB 节点尚未执行XA COMMIT数据页被锁住处于PREPARED状态。判定逻辑读请求查询全局事务状态本地缓存或远程查询。物理动作获知该事务的Commit_TS110Commit\_TS110Commit_TS110。由于110105110 105110105读请求直接忽略此未提交版本读取其 Undo Log 中的旧值并返回。结论TrT_rTr最终拿到的 A、B 节点数据均对齐在TS≤105TS \le 105TS≤105的时空点。三、 特殊情况与注意事项1. 读等待机制 (Read Wait)现象读请求Read_TS105Read\_TS105Read_TS105到达节点 B 时发现记录处于PREPARED状态但该事务正处于“申请Commit_TSCommit\_TSCommit_TS”的过程中。处理此时 B 节点无法判断该事务最终的Commit_TSCommit\_TSCommit_TS是否小于 105。物理动作读请求必须阻塞等待直到该事务获取并确定Commit_TSCommit\_TSCommit_TS。如果最终Commit_TS≤Read_TSCommit\_TS \le Read\_TSCommit_TS≤Read_TS读新值否则读 Undo 旧值。2. 垃圾回收 (Garbage Collection / Purge) 的约束物理要求在分布式快照读下Undo Log 不能在事务提交后立即删除。规则系统必须维护一个Global Minimum Read Timestamp (GMRT)。只有当一个版本的所有Commit_TSCommit\_TSCommit_TS都小于全集群当前最老的活跃Read_TSRead\_TSRead_TS时对应的 Undo Log 才能被 Purge 线程物理清理。3. TSO 网络延迟与时钟偏斜风险如果各节点之间存在严重的网络延迟可能导致Read_TSRead\_TSRead_TS获取过慢使得读请求因版本回溯过深而产生大量随机 I/O读取远古 Undo 记录。优化高性能实现通常采用本地时钟缓存或混合逻辑时钟 (HLC)来减少对中心 TSO 节点的同步请求次数。4. 事务状态缓存注意存储节点如果频繁查询全局事务表GTT会产生巨大开销。物理实现上存储节点会通过Transaction Status Cache缓存已决断的Commit_TSCommit\_TSCommit_TS只有在缓存未命中时才发起跨节点查询。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2592293.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!