MySQL 事务并发执行时的一致性问题


MySQL 是一个客户端/服务器架构的软件。对于同一个服务器来说,可以有多个客户端与之连接。每个客户端与服务器建立连接后,就形成了一个会话。每个客户端都可以在自己的会话中向服务器发出请求语句,一个请求语句可能是某个事务的一部分。服务器可以同时处理来自多个客户端的多个事务。
在某个事务访问某个数据时,如要求其他试图访问相同数据的事务进行限制 , 让它们进行排队。当该事务提交之后,其他事务才能继续访问这个数据。这样可以让并发执行的事务的执行结果与串行执行的结果一样,我们把这种多个事务的执行方式称为可串行化执行。

事务并发执行时遇到的一致性问题

脏写(Dirty Write)

如果一个事务修改了另一个未提交事务修改过的数据,就意味着发生了脏写现象。我们可以把脏写现象简称为 PO 。假设现在事务 T1 和 T2 并发执行 , 它们都要访问数据项 x (这里可以将数据项 x 当作一条记录的某个字段).那么 PO 对应的操作执行序列如下所示 :

P0:w1[x]...w2[x]...((c1 or a1) and (c2 or a2) in any order)

其中 w1[x]表示事务 T1 修改了数据项 x 的值。w2[x] 表示事务 T2 修改了数据项 x 的值,C1 表示事务 T1 的提交 (Commit),a1 表示事务 T1 的中止 (Abort),c2 表示事务 T2 的提交,a2 表示事务 T2 的中止,…表示其他的一些操作。从 P0 的操作执行序列中可以看出,事务 T2 修改了未提交事务 T1 修改过的数据, 所以发生了脏写现象。
脏写现象也可能破坏原子性和持久性。比方说有 x 和 y 这两个数据项,它们初始的值都是 0。 两个并发执行的事务 T1 和 T2 有下面的操作执行序列:

w1[x=2]w2[x=3]w2[y=3]c2a1

现在的问题是 T1 中止时, 需要将它对数据库所做的修改回滚到该事务开启时的样子,也就是将数据项 x 的值修改为 0 . 但是此时 T2 已经修改过数据项 x 并且提交了,如果要将 T1 回滚的话,相当于要对 T2 对数据库所做的修改进行部分回滚(部分回滚是指只回滚对 x 做的修改,而不回滚对 y 做的修改),这就影响到了事务的原子性。如果要将 T2 对数据库所做的修改全部回滚的话 , 那么明明 T2 己经提交了,它对数据库所做的修改应该具有持久性,怎么能让一个未提交的事务将 T2 的持久性破坏掉呢?

脏读(Dirty Read)

如果一个事务读到了另一个未提交事务修改过的数据,就意味着发生了脏读现象,我们可以把脏读现象简称为 P1。 假设现在事务 T1 和 T2 并发执行,它们都要访问数据项 x。 那么 P1 对应的操作执行序列如下所示 :

P1:w1[x]...r2[x]...(( c1 or a1) and (c2 or a2) in any order)

脏读现象也可能引发一致性问题。比方说事务 T1 和 T2 中要访问 x 和 y 这两个数据项,我们的一致性需求就是让 x 的值和 y 的值始终相同,x 和 y 的初始值都是 0. 现在并发执行事务 T1 和 T2,它们的操作执行序列如下所示 :

w1[x=1]r2[x=1]r2[y=0]c2w1[y=1]c1

很显然 T2 是一个只读事务,依次读取 x 和 y 的值。可是由于 T2 读取的数据项 x 是未提交事务 T1 修改过的值,所以导致最后读取 x 的值为 1,Y 的值为 0 。虽然最终数据库状态还是一致的(最终变为了 x=1,y=1),但是 T2 却得到了一个不一致的状态。数据库的不一致状态是不应该暴露给用户的。P1 代表的事务的操作执行序列其实是一种脏读的广义解释,针对脏读还有一种严格解释。为了与广义解释进行区分,我们把脏读的严格解释称为 A1。 A1 对应的操作执行序列如下所示 :

A1:w1[x]...r2[x]...(a1 and c2 in any order)

也就是 T1 先修改了数据项 x 的值,然后 T2 又读取了未提交事务 T1 针对数据项 x 修改后的值,之后 T1 中止而 T2 提交。这就意味着 T2 读到了一个根本不存在的值,这也是脏读的严格解释。很显然脏读的广义解释是覆盖严格解释包含的范围的。

不可重复读(Non-Repeatable Read)

如果一个事务修改了另一个未提交事务读取的数据 ,就意味着发生了不可重复读现象,或者叫模糊读 ( Fuuzzy Read) 现象。我们可以把不可重复读现象简称为 P2。假设现在事务 T1 和 T2 并发执行,它们都要访问数据项 x。那么 P2 对应的操作执行序列如下所示:

P2: r1[x]...w2[x]...((c1 or a1) and (c2 or a2) in any order)  

不可重复读现象也可能引发一致性问题。比方说事务 T1 和 T2 中要访问 x 和 y 这两个数据项,我们的一致性需求就是让 x 的值和 y 的值始终相同,x 和 y 的初始值都是 0。现在并发执行事务 T1 和 T2。它们的操作执行序列如下所示:

r1[x=O]w2[x=1]w2[y=1]c2r1[y=1]c1  

很显然 Tl 是一个只读事务,依次读取 x 和 y 的值.可是由于 T1 在读取数据项 x 后,T2 接着修改了数据项 x 和 y 的值,并且提交,之后 T1 再读取数据项 y。这个过程中虽未发生脏写和脏读(因为 T1 读取 y 的值时,T2 已经提交),但最终 T1 得到的 x 的值为 0, Y 的值为 1。很显然这是一个不一致的状态,这种不一致的状态是不应该暴露给用户的。
P2 代表的事务的操作执行序列其实是一种不可重复读的广义解释,针对不可重复读还有一种严格解释。为了与广义解释进行区分,我们把不可重复读的严格解释称为 A2。A2 对应的操作执行序列如下所示:

A2:r1[x]...w2[x]...c2...r1[x]...c1

也就是 T1 先读取了数据项 x 的值,然后 T2 又修改了未提交事务 T1 读取的数据项 x 的值,之后 T2 提交,然后 T1 再次读取数据项 x 的值时会得到与第一次读取时不同的值。这也是不可重复读的严格解释。很显然不可重复读的广义解释是覆盖严格解释包含的范围的。

幻读(Phantom)

如果一个事务先根据某些搜索条件查询出一些记录,在该事务未提交时,另一个事务写入一些了符合那些搜索条件的记录(这里的写入可以指 INSERT、 DELETE、UPDATE 操作),就意味着发生了幻读现象。我们可以把幻读现象简称为 P3。假设现在事务 T1 和 T2 并发执行, 那么 P3 对应的操作执行序列如下所示 :

P3:r1[P]...w2[y in P]...((c1 or a1) and (c2 or a2) any order)

其中 r1[P] 表示 T1 读取一些符合搜索条件 P 的记录,w2[y in P]表示 T2 写入一些符合搜索条件 P 的记录。
幻读现象也可能引发一致性问题。比方说现在符合搜索条件 P 的记录条数有 3 条。我们有一个数据项 z 专门表示符合搜索条件 P 的记录条数,它的初始值当然也是 3。我们的一致性需求就是
让 z 表示符合搜索条件 P 的记录数.现在并发执行事务 T1 和 T2,它们的操作执行序列如下所示:

r1[P]w2[insert y to P]r2[z=3]w2[z=4]c2r1[z=4]c1

T1 先读取符合搜索条件 P 的记录,然后 T2 插入了一条符合搜索条件 P 的记录,并且更
新数据项 z 的值为 4。然后 T2 提交,之后 T1 再读取数据项 z。z 的值变为了 4,这与 T1 之前
实际读取出的符合搜索条件 P 的记录条数不合,不符合一致性需求。
P3 代表的事务的操作执行序列其实是一种幻读的广义解释,针对幻读还有一种严格解释。
为了与广义解释进行区分,我们把幻读的严格解释称为 A3, A3 对应的操作执行序列如下所示:

A3:r1[p]...w2[y in P]...c2...r1[p]...c1

也就是 T1 先读取符合搜索条件 P 的记录,然后 T2 写入了符合搜索条件 P 的记录。之后 T1 再读取符合搜索条件 P 的记录时,会发现两次读取的记录是不一样的。

MySQL 中的幻读

上面关于脏写、脏读、不可重复读、幻读的讨论均属于理论范畴,不涉及具体数据库。对于 MySQL 来说,幻读强调的就是一个事务在按照某个相同的搜索条件多次读取记录时,在后读取时读到了之前没有读到的记录。这个「后读取到的之前没有读到的记录」可以是由别的事务执行 INSERT 语句插入的,也可能是别的事务执行了更新记录键值的 UPDATE 语句而插入的。这些之前读取时不存在的记录也可以被称为幻影记录。假设 T1 先根据搜索条件 P 读取了一些记录,接着 T2 删除了一些符合搜索条件 P 的记录后提交,如果 T1 再读取符合相同搜索条件的记录时获得了不同的结果集,我们就可以把这种现象认为是结果集中的每一条记录分别发生了不可重复读现象。

参考资料

  • 《A critique of ANSI SQL isolation levels》
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值