事务
事务:并发控制和原子提交
事务其存在以下性质:
- Atomic(原子性):要不执行、要不不执行,不会出现执行到一半的状态。
- Consistent(一致性):通常由用户程序保证,保证执行事务后的数据是正确的。
- Isolated(隔离性):事务执行的中间状态是隐藏的,不会被其他事务看到。其意味着可序列化。
- Durable(持久化):需要将数据写入到非易失的存储种。
可序列化:存在两个事务,若并发执行的所有结果,都存在一个单线程执行的结果(一次只执行一个事务)与之对应,则执行过程是可序列化的。
可序列化的好处是:只要事务不使用系统的数据,就允许并行执行事务。
并发控制主要分为两种策略:
- 悲观并发控制:在执行事务之前,必须拿到其相关数据的所有锁
- 乐观并发控制:将数据写入到临时区域。在事务执行完毕后,检查是否收到了干扰,若存在需要 Abort 当前事务并重试,若不存在则表示完成事务。
两阶段锁
1,概述
两阶段锁是一种常见的悲观并发控制。
其存在以下规则:
- 在执行任何数据的读写之前,必须拿到锁
- 执行完毕后才能释放锁
考虑其他规则:
- 修改完成后直接释放锁:其他事务将会读取到一个未执行完成事务的数据,并且当前事务中断了,则需要撤回操作。导致其他事务读取一个错误的数据。
- 需要操作时才获取锁:非常容易造成死锁。
2,分布式执行
在分布式系统中,需要操作的数据可能在多个服务器中。因此在执行事务前需要在多个服务器中进行加锁。我们使用一台服务器作为事务协调者(TC)实现。
每个事务都会被TC赋予一个唯一的事务ID(TID)。同时在存储数据的服务器(WS)中会维护一张表,记录锁被哪个事务持有。
执行事务的流程如下:
- TC 收到 Client 的事务请求,TC 要求 WS 获取锁。
- 执行事务,TC 向 WS 发送 Put 或 Get 信息。
- TC 向 WS 发送 Prepare 信息。 WS 判断是否可以执行此操作。若允许回复 Yes,否则回复 No。
- 若 TC 收到所有 WS 回复 Yes 信息,则表示事务可以提交,发送 Commit 请求。否则发送 Abort 信息,表示回滚事务。
- 若 WS 收到了 Commit 信息,提交事务。返回 ACK 信息。
- 若 WS 收到了 Abort 信息,取消事务。返回 ACK 信息。
3,故障恢复
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 信息,可以删除该事务信息。