这是我参与「第五届青训营 」伴学笔记创作活动的第 13 天。
1. 什么是分布式事务
分布式系统会把一个应用系统拆分为可独立部署的多个服务,因此需要服务与服务之间远程协作才能完成事务操作。
这种分布式系统环境下由不同的服务之间通过网络远程协作完成事务称之为分布式事务。
分布式事务产生的情景:
- 跨应用进程产生分布式事务
- 跨数据库实例产生分布式事务
- 多服务访问同一个数据库实例
2. 事务
事务是区别数据库和文件系统的重要特性之一。 事务会把数据库从一种一致状态转换为另一种一致状态。在数据库提交工作时,可以确保要么所有修改都已经保存了,要么所有修改都不保存。
2.1. ACID
- 原子性:整个事务是不可分割的工作单位。要么都完成,要么都不完成。
- 一致性:事务将数据库从一种一致状态转变为另一种一致状态。(在一致性状态下,所有事务对同一个数据的读取结果都是相同的)
- 隔离性(并发控制、可串行化、锁):一个事务提交之前对其他事务是不可见的。
- 持久性:事务一旦提交,其结果是永久性的。持久性保证事务系统的高可靠性。
事务是由一条或者一组 SQL 语句组成的,事务是访问并更新数据库中各种数据项的一个程序执行单元。
ACID 靠什么保证的呢?
原子性:由undo log日志保证,它记录了需要回滚的日志信息,事务回滚时撤销已经执行成功的sql。
一致性:一般由代码层面来保证。另一说法:原子性和隔离性保证了一致性。
隔离性:由 MVCC 来保证。
持久性:由 内存 + redo log来保证,mysql修改数据同时在内存和redo log记录这次操作,事务提交的时候通过redo log刷盘,宕机的时候可以从redo log恢复。
2.2. 事务隔离级别
读未提交 Read uncommitted:如果一个事务读取到了另一个未提交的事务修改过的数据。 读已提交 Read committed:一个事务能读到另一个已经提交的事务修改过的数据。 可重复读 Repeatable read:一个事务只会读取该事务开始时的记录快照版本。 串行化 serializable:不允许 读-写、写-读 的并发操作。
2.3. 并发一致性问题
- 丢失修改:指一个事务的更新操作被另外一个事务的更新操作替换
- 脏读:当前事务可以读到另一个事务还未提交的数据
- 不可重复读:同一个事务内,两只执行同一条语句查询的结果不一致。
- 幻影读:同一个事务内,两次使用 count 计数的结果不一致。
不可重复读和幻读的区别:
避免不可重复读需要锁行就行;避免幻影读则需要锁表。
不可重复读重点在于update;而幻读的重点在于insert
| 脏读 | 不可重复读 | 幻影读 | |
|---|---|---|---|
| 读未提交 | Y | Y | Y |
| 读已提交 | Y | Y | |
| 可重复读 | Y | ||
| 串行化 |
3. CAP 理论
- C:Consistency (强一致性)
- A:Availability (高可用性)
- P:Partition tolerance (分区容错性)
CAP理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求。
因此,根据CAP原理将NoSQL数据库分成了满足CA原则、满足CP原则和满足AP原则三大类:
- CA:单点集群,满足—致性,可用性的系统,通常在可扩展性上不太强大。
- CP:满足一致性,分区容忍必的系统,通常性能不是特别高。
- AP:满足可用性,分区容忍性的系统,通常可能对一致性要求低一些。
4. BASE 理论
- Basically Available(基本可用):分布式系统在出现故障时,允许损失部分可用功能,保证核心功能可用。
- Soft state(软状态):由于不要求强一致性,所以 BASE 允许系统中存在中间状态(也叫软状态),这个状态不影响系统可用性,待数据最终一致后状态改为“成功”状态。
- Eventually consistent(最终一致性):最终一致是指经过一段时间后,所有节点数据都将会达到一致。
5. 分布式事务解决方案
5.1. 2PC
两阶段提交协议,是将整个事务流程分为两个阶段:准备阶段(Prepare phase)、提交阶段(commit phase)。二阶段提交是一种强一致性设计。
准备阶段(Prepare phase):事务管理器给每个参与者发送 Prepare 消息,每个数据库参与者在本地执行事务,并写本地的 Undo/Redo 日志,此时事务没有提交。 提交阶段(Commit phase):如果事务管理器收到了参与者的执行失败或者超时消息时,直接给每个参与者发送回滚(Rollback)消息;否则,发送提交(Commit)消息。参与者根据事务管理器的指令执行提交或者回滚操作,并释放事务处理过程中使用的锁资源。
2PC 是一种尽量保证强一致性的分布式事务,因此它是同步阻塞的。 而同步阻塞就导致长久的资源锁定问题,总体而言效率低。 并且存在单点故障问题,在极端条件下存在数据不一致的风险。
5.2. 3PC
引入了超时机制
准备阶段(CanCommit):事务参与者状况检查,资源预留。预提交阶段的引入起到了一个统一状态的作用。 预提交阶段(PreCommit):同 2PC 的准备阶段。 提交阶段(DoCommit):同 2PC 的提交阶段。
5.3. TCC
2PC 和 3PC 都是数据库层面的,而 TCC 是业务层面的分布式事务。TCC可以跨数据库、跨不同的业务系统来实现事务。
预处理 Try:业务检查(一致性)及资源预留(隔离)。此阶段仅是一个初步操作,它和后续的 Confirm 一起才能真正构成一个完整的业务逻辑。 确认 Confirm:业务确认操作(只要 Try 成功,Confirm 一定成功)。 撤销 Cancel:实现一个与 Try 相反的操作即回滚操作
TM 首先发起所有的分支事务的 Try 操作,任何一个分支事务的Try操作执行失败,TM 将会发起所有分支事务的 Cancel 操作。 若 Try 操作全部成功,TM 将会发起所有分支事务的 Confirm 操作。 其中 Confirm/Cancel 操作若执行失败,TM 会进行重试。
TCC 异常处理
- 空回滚:在没有调用 Try 方法的情况下,调用了二阶段的 Cancel 方法,Cancel 方法需要识别出这是一个空回滚,然后直接返回成功。 出现原因:当一个分支事务所在服务宕机或网络异常,分支事务调用记录为失败,这个时候其实是没有执行 Try 阶段。当故障恢复后,分布式事务进行回滚则会调用二阶段的 Cancel 方法,从而形成空回滚。
- 幂等:由于 Confirm 和 Cancel 失败需进行重试,因此需要实现为幂等。
- 悬挂:对于一个分布式事务,其二阶段 Cancel 接口比 Try 接口先执行。