事务

事务:并发控制和原子提交

事务其存在以下性质:

  • Atomic(原子性):要不执行、要不不执行,不会出现执行到一半的状态。
  • Consistent(一致性):通常由用户程序保证,保证执行事务后的数据是正确的。
  • Isolated(隔离性):事务执行的中间状态是隐藏的,不会被其他事务看到。其意味着可序列化。
  • Durable(持久化):需要将数据写入到非易失的存储种。

可序列化:存在两个事务,若并发执行的所有结果,都存在一个单线程执行的结果(一次只执行一个事务)与之对应,则执行过程是可序列化的。

可序列化的好处是:只要事务不使用系统的数据,就允许并行执行事务。


并发控制主要分为两种策略:

  • 悲观并发控制:在执行事务之前,必须拿到其相关数据的所有锁
  • 乐观并发控制:将数据写入到临时区域。在事务执行完毕后,检查是否收到了干扰,若存在需要 Abort 当前事务并重试,若不存在则表示完成事务。

两阶段锁

1,概述

两阶段锁是一种常见的悲观并发控制。

其存在以下规则:

  • 在执行任何数据的读写之前,必须拿到锁
  • 执行完毕后才能释放锁

考虑其他规则:

  • 修改完成后直接释放锁:其他事务将会读取到一个未执行完成事务的数据,并且当前事务中断了,则需要撤回操作。导致其他事务读取一个错误的数据。
  • 需要操作时才获取锁:非常容易造成死锁。

2,分布式执行

在分布式系统中,需要操作的数据可能在多个服务器中。因此在执行事务前需要在多个服务器中进行加锁。我们使用一台服务器作为事务协调者(TC)实现。

每个事务都会被TC赋予一个唯一的事务ID(TID)。同时在存储数据的服务器(WS)中会维护一张表,记录锁被哪个事务持有。

执行事务的流程如下:

  1. TC 收到 Client 的事务请求,TC 要求 WS 获取锁。
  2. 执行事务,TC 向 WS 发送 Put 或 Get 信息。
  3. TC 向 WS 发送 Prepare 信息。 WS 判断是否可以执行此操作。若允许回复 Yes,否则回复 No。
  4. 若 TC 收到所有 WS 回复 Yes 信息,则表示事务可以提交,发送 Commit 请求。否则发送 Abort 信息,表示回滚事务。
  5. 若 WS 收到了 Commit 信息,提交事务。返回 ACK 信息。
  6. 若 WS 收到了 Abort 信息,取消事务。返回 ACK 信息。

3,故障恢复

支付请求时序图.png
A号点:在服务器回复 Yes/No 信息之前故障
如果 B 发现自己无法发送 Yes 信息,或者发送 Yes 之前故障。B 可以单方面执行 Abort 事务。
若 B 崩溃重启了,故障后 B 是无法知道任何有关事务的信息的,对于不知道事务的 Prepare 请求回复 No,并要求执行 Abort 事务。


B 号点:参与者发送 Yes,但是在服务器收到 Commit / Abort 信息之前崩溃
在服务器回复 Yes 给 Prepare 信息之前,必须持久化事务的中间状态,以 Log 的形式保存事务信息。
因此在服务器重启后,可以知道自己在执行事务,并且回复了 Yes 信息。
在这里服务器必须一致持有锁,直到收到 TC 发送的 Commit 或 Abort 信息。因为参与者不知道该事务的状态。


C 号点:服务器处理完 Commit 后崩溃
B 处理 Commit 后会释放锁,并且删除有关事务的 Log 信息。
对于收到的 Commit 信息,回复 ACK 信息。


TC 在发送 Commit 之前崩溃
因为没有任意参与者会 Commit 事务。因此可以直接 Abort。


TC 在发送 Commit 之后崩溃
TC 在收到 Prepare 信息的 Yes/No 投票后,会将结果和事务ID持久化,然后才发送 Commit 信息。
当重启后,TC 会重发 Commit 消息或者 Abort 消息。


TC 发送 Prepare 信息,但没有收到所有的 Yes/No 信息
TC 可以重新发送 Prepare 信息。或者单方面执行 Abort 信息。
Abort 后会删除其事务信息,若收到参与者信息会直接回复 Abort。


在 TC 收到了所有参与者发送的 ACK 信息,可以删除该事务信息。