自用 - mysql事务与锁复习 - 书写中

前几天看到公司一篇关于排除mysql幻读的文章, 发现自己关于 脏读、不可重复读、幻读.....这些涉及【事务】和【锁】知识有些遗忘了。一直在忙业务,没时间复习。今晚终于腾出来一点时间,写一篇文章 帮助自己复习一下。用大白话 来写。

一、幻读涉及知识

什么是幻读

幻读就是说 一个事务中 有两次查询 这两次查询分别查到的符合其查询条件的记录,数量不一致。

第一次查 数量是x。第二次查可能变成了y。y!=x。

这是幻读,强调的是 记录数量的不一致。而不可重复度 强调的是 记录的内容 不一致。记录里面的某个字段,这个字段的值 不一致。

或者说

对于某一个事务 select(快照读)是null 但 之后 insert(当前读)失败。这也是幻读的一种现象。

因为在这个事务 select 和 insert之间,有别的事务 insert 了。那你之后再insert ,且刚好你要insert的东西 和 刚刚那个insert进来的东西一致(比如设置了唯一主键,但两个记录主键一致;或者设置了唯一列,两个记录关于这个列的值相同),那自然会insert失败了。

再或者 select(快照读)与 update/delete。

所以 下面是幻读可能会出现的四种情况:

1 select (快照读)与 select(快照读),两次查询得到的记录数量,不一致。“刚刚我查还是8个,咋这会变成9个了,我是不是出现幻觉了。”

2 select (快照读)与 insert(当前读) ,select为null,但insert失败。“select为null 说明里面没有这条记录啊 但为啥我insert的时候会失败 咋回事 幻觉吗?”

3 select (快照读)与 delete(当前读),select为null,但delete成功,“select为null 说明里面没有这个记录啊 但为啥我delete成功了 幻觉?”

4 select (快照读)与 update(当前读),select为null,但update成功,“select为null 说明里面没有这个记录啊 但为啥我update成功了 幻觉?”

所以,在这里,我要对幻读下一个统一的定义,自创。

幻读,说白了,就是两次“读”之间的矛盾,而且是那种很智障的矛盾,让你觉得你是不是看错了,出现幻觉了。

select (快照读)与 select(快照读):刚刚我查还是8个,咋这会变成9个了,矛盾了。

select (快照读)与 insert(当前读):select为null 说明里面没有这条记录啊 insert失败 说明里面有 ,咋回事啊,矛盾了”

select (快照读)与 delete(当前读):select为null,但delete成功,“select为null 说明里面没有这个记录啊 但为啥我delete成功了 幻觉?”

select (快照读)与 update(当前读):select为null,但update成功,“select为null 说明里面没有这个记录啊 但为啥我update成功了 幻觉?”

select 是快照读? insert/update/delete/select...for update是当前读?

快照读,读的是 快照,通过 MVCC 读取历史版本。

当前读,访问的是最新的数据。

幻读为什么会出现 & 可重复读隔离级别下怎么避免幻读

对于第一种情况: select (快照读)与 select(快照读),两次查询得到的记录数量,不一致

幻读会出现 是因为 A事务的两次查询之间 B事务插入或者删除了一条记录。导致两次查询得到的记录数目不一致了。

怎么避免:针对 select(快照读)

MVCC 。不管你是什么时候 select ,你查到的记录数目 永远是那个数。那自然 两次查询得到的记录数目永远一致。

对于第二种情况:select (快照读)与 insert(当前读),select为null,但insert失败

幻读会出现 是因为 A事务 select与insert之间 B事务insert一条记录。当执行到A事务的insert时,会失败,因为B事务已经插入过了,且,B事务插入进去的这个记录,与A事务要插入进去的记录,主键一致(设置了主键唯一)或某列的值一致(设置了唯一列)。

怎么避免:针对 insert(当前读),不让B事务插入

把事务A的 select 换成 SELECT ... FOR UPDATE,事务A 查的时候 就加锁。比如事务A查询唯一值 100 时,若记录不存在,InnoDB 会加间隙锁,锁定 100 附近的间隙,阻止B事务插入 100,直到事务 A 提交或回滚。那之后事务A的insert会成功吗?答案是会的,因为自己事务加的锁,对自己没效, 同一事务内的锁是“兼容”的。

对于第三种情况:select (快照读)与 delete(当前读),select为null,但delete成功

幻读会出现是因为 A事务 select与delete之间,B事务insert进去了一条记录。所以能delete。

怎么避免:针对delete(当前读),不让B事务插入

把事务A的 select 换成 SELECT ... FOR UPDATE,事务A 查的时候 就加锁。比如事务A查询唯一值 100 时,若记录不存在,InnoDB 会加间隙锁,锁定 100 附近的间隙,阻止B事务插入 100,直到事务 A 提交或回滚。那之后事务A的delete会成功吗?答案是否的,因为都没有这条记录,哪里可以delete。

对于第四种情况:select (快照读)与 update(当前读),select为null,但update成功

幻读会出现是因为 A事务 select与update之间,B事务insert进去了一条记录。所以能update。

怎么避免:针对update(当前读)语句,为了不在A事务执行这个update时,出现幻读的现象。不让B事务插入

把事务A的 select 换成 SELECT ... FOR UPDATE,加了锁,B事务无法insert,那自然后面A事务 update会失败,都没有,咋update

所以概括一下 可重复读隔离级别怎么避免幻读:

MVCC + 临键锁

结合我自己的理解 下面这个小林的说法 我翻译一下:

第一条:怎么确保事务A在第二次select时 不会出现幻读这种现象,是通过MVCC方式解决了幻读,因为可重复度隔离级别下,........

第二条:怎么确保事务A在第二次update/insert/delete/select...for update时,不会出现幻读这种现象,是通过next-key lock(记录锁+间隙锁)方式解决了幻读,......

二、事务篇

三、锁篇

#java##mysql##笔记##自用##学习#
全部评论

相关推荐

评论
1
2
分享

创作者周榜

更多
牛客网
牛客企业服务