共计 2810 个字符,预计需要花费 8 分钟才能阅读完成。
本篇内容主要讲解“分布式数据库对 2PC 的优化方法是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让丸趣 TV 小编来带大家学习“分布式数据库对 2PC 的优化方法是什么”吧!
两阶段提交 (2PC)
两阶段提交协议主要有 2 种,一种是应用层的 TCC,比如阿里巴巴的 seata 就实现了 TCC 模式,这种模式的特点是每个服务都需要提供 try/confirm/cancel 这 3 个实现,这 3 个实现需要在业务代码中实现,对业务侵入高。
今天我分享的是面向资源的 2PC 协议,最早由 Jim Gray 提出,整个事务分为 2 个阶段,prepare 阶段和 commit 阶段,这 2 个阶段由协调节点和 DB 资源管理器协作完成。
这里我们还是以经典的电商系统为例,整个系统分为订单、账户和库存 3 个服务,我们收到客户的购买请求后,协调节点需要协调订单服务生成订单,账户服务扣减商品款,库存服务扣减商品库存,假如这 3 个服务的数据库在不同切片上,这个协调过程具体如下:
1.prepare 阶段
协调节点向所有服务发送 prepare 请求,每个服务收到 prepare 请求后会尝试执行本地事务,但不会真正提交本地事务。这个尝试执行的过程会检查到是否具备执行事务的条件,比如资源是否被锁定等,当所有服务都尝试执行成功后会给协调节点返回一个 yes,如下图:
2.commit/rollback 阶段
如果 prepare 阶段所有服务有返回了 yes,那么协调节点就会通知各个服务执行 commit 操作,这时各个服务就会真正的提交本地事务。如下图:
如果 prepare 阶段有服务返回了 no,协调节点就需要通知所有服务进行本地事务回滚。
2PC 存在问题
上面我们简单地分析了 2PC 协议的执行过程,那么 2PC 有什么问题呢?
1. 性能问题
本地事务在 prepare 阶段锁定资源,比如账户服务要扣减 xiaoming 这个账户的金额 100 元,那必须把 xiaoming 这个账户先锁定。这样如果有其他事务也要修改 xiaoming 这个账户,就必须等待前面的事务完成。这样就造成了延迟和性能下降。
2. 协调节点单点故障
协调节点是单节点的,如果发生故障,整个事务会一直阻塞。比如第一个阶段 prepare 成功了,但是第二个阶段协调节点发出 commit 指令之前宕机了,所有服务的数据资源处于锁定状态,后面的事务只能等待。
3. 数据不一致
如果第一阶段 prepare 成功了,但是第二阶段 commit 的时候,如果协调节点通知库存服务失败了,这样就相当于生成了订单,扣减了账户,但是没有扣减库存。这导致了数据的不一致。
Percolator 模型
主流的 NewSQL 数据库,比如 TiDB,是用 Percolator 模型来解决的。如下官网链接:
https://pingcap.com/blog-cn/percolator-and-txn/
Percolator 模型来自于 Google 论文:
《Large-scale Incremental Processing Using Distributed Transactions and Notifications》
原文可以看下面连接,网上也有好多翻译版的:
https://www.cs.princeton.edu/courses/archive/fall10/cos597B/papers/percolator-osdi10.pdf
Percolator 的前提是本地事务的数据库支持多版本并发控制协议,也就是 mvcc。现在主流数据库比如 mysql、oracle 都是支持的。
a) 初始阶段
还是看上面我们提到的经典电商案例,初始阶段,我们假设订单数量是 0,账户服务是 1000,库存服务是 100,客户下了 1 个订单后,订单服务增加 1 个订单,账户服务扣除金额 100,库存服务扣除商品数量 1。各个切片的初始数据如下表:
: 前面的是时间戳或者数据版本,后面是数据值。这 3 张表中,第一条记录不保存真正的数据,而是保存了指向真正数据的指针,比如订单表中,6 这个版本的数据指向了 5 个版本的数据,订单数量是 0。
b)prepare 阶段
在 prepare 阶段,协调节点向每个服务发送了 prepare 命令,这 3 张表分别进入了 prepare 阶段。在 prepare 阶段,Percolator 定义了主锁的概念,每个分布式事务只能有一个服务获得主锁,比如本案例的订单服务,其他服务的锁指向这个主锁的指针,如下表:
prepare 阶段,每个服务会写日志,并且根据时间戳记录事务的私有版本,这样其他事务就不能操作这三条数据了。
c)commit 阶段
在 commit 阶段,协调节点只需要跟订单服务通信,因为订单服务拥有 primary lock,也就是说协调节点只跟拥有 primary lock 的切片通信。这时数据如下表:
这时我们注意到除了 order 服务的锁没有了,而且增加了版本 8 指向版本 7,说明订单服务已经没有私有版本了,但是账户服务和库存服务的私有版本还在。Percolator 的独特之处就是在这里,它会启动异步线程来更新账户服务和库存服务。最终数据如下表:
因为协调节点只需要跟获取 primary lock 的切片进行通信,要么成功要么失败这样就避免了 commit 时节点不能全部成功导致的数据不一致问题。
而 prepare 阶段记录了日志,如果某个切片 commit 失败,可以根据日志进行再次 commit,这样就保证了数据最终一致。
如果协调节点宕机了,异步线程可以做资源的释放工作,避免了因单点故障通信失败造成的资源不能释放。
这里我们要注意 2 点:
primary lock 的选择是随机的,比如本例中并不一定会选择订单服务
协调节点发送 commit 后订单服务先提交成功,这时如果其他事务要读取账户服务和库存服务的 2 条数据,虽然 2 条数据上面还有 lock,但是查找 primary@order.bal 发现已提交,所以是可以读取的。
总结
2PC 协议有 3 个问题,性能问题、单点故障和数据不一致。
Percolator 模型简化了协调节点和切片的通信流程,让协调节点只跟其中一个 primary 切片通信,一方面,减少了通信开销,另一方面,避免了因为单点故障,commit 阶段部分节点通信失败导致的数据不一致问题。
Percolator 在 prepare 阶段记录了日志,这样即使协调节点故障了,恢复后也可以根据日志来做事务恢复。
Percolator 使用异步线程来做资源的释放工作,这样即使协调节点故障了,也不用担心资源得不到释放。
知名的 NewSQL 数据库 TiDB 就是参照 Percolator 模型来对 2PC 协议进行优化的。
但是我们要知道,2PC 的性能问题还是存在的,好在主流的分布式数据库都做了优化,性能损耗只会越来越小。
到此,相信大家对“分布式数据库对 2PC 的优化方法是什么”有了更深的了解,不妨来实际操作一番吧!这里是丸趣 TV 网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!