脏读、不可重复读、幻读以及解决办法
Laiyong Wang Lv6

脏读:读到其他事务未提交的数据;
不可重复读:前后读取的数据不一致;
幻读:前后读取的记录数量不一致。

上一篇文章写的有快照读实现的 mvcc:http://laiyong.wang/2024/05/27/mysql-readView
其实现的事务的隔离级别:

  • 读已提交 可解决 脏读 问题
  • 可重复读 可解决 不可重复读 问题
  • 可重复读 解决了快照读(普通 select 语句)的幻读问题,但是当前读(select … for update 等语句)的幻读问题未解决

当前读的定义:
mysql 里除了普通查询是快照读,其他都是当前读,比如 update、insert、delete,这些语句执行前都会查询最新版本的数据,然后再做进一步的操作

mysql如何解决当前读的幻读问题:
通过 next-key lock(记录锁+间隙锁)方式解决了幻读,因为当执行 select … for update 语句的时候,会加上 next-key lock,如果有其他事务在 next-key lock 锁范围内插入了一条记录,那么这个插入语句就会被阻塞,无法成功插入,所以就很好了避免幻读问题

什么是间隙锁+记录锁?
举个例子: select name from wanglaiyong where id > 520 for update; 那么 id (520,+∞)就被加上锁了,这是个悲观锁,别的事务想插入啥的需要等待

上面说的都是避免幻读的原理,即使我们设置了可重复读的隔离级别,还是会产生幻读

  • 快照读的幻读
    事务A开启事务查询 id = 520 的数据,显示没有,事务 B 插入了一条 id = 520 的数据并提交,事务A 基于当前读的快照,再次查询,两次查询结果相同,都是没有,但是插入时会报错,因为数据库的约束问题,这也是一种幻读

  • 当前读的幻读
    跟快照读的幻读很类似
    事务A开启事务查询 id = 520 的数据,显示没有,事务 B 插入了一条 id = 520 的数据并提交,事务A 再次查询,使用当前读,将会查询到数据,两次查询结果不一致,幻读了

  • 怎么避免幻读
    在开启事务之后,马上执行 select … for update 这类当前读的语句,这样它会对记录加 next-key lock,从而避免其他事务插入一条新记录。

$li-margin-bottom = 0.8rem $post-tool-button-width = 2.5rem .post-tools-container { padding-top var(--component-gap) .post-tools-list { li { margin-bottom $li-margin-bottom &:last-child { margin-bottom 0 } } li.tools-item { position relative box-sizing border-box width $post-tool-button-width height $post-tool-button-width color var(--text-color-3) font-size 1.2rem background var(--background-color-1) border-radius 50% box-shadow 2px 2px 5px var(--shadow-color) cursor pointer &:hover { box-shadow 2px 2px 8px var(--shadow-hover-color) } i { color var(--text-color-3) } &:hover { color var(--background-color-1) background var(--primary-color) i { color var(--background-color-1) !important } } &.toggle-show-toc { display none } &.go-to-comments { .post-comments-count { position absolute top 0 right -1rem display none align-items center justify-content center box-sizing border-box min-width 1.1rem height 1.1rem padding 0 0.2rem color var(--badge-color) font-size 12px background var(--badge-background-color) border-radius 0.4rem +keep-tablet() { display none !important } } } } li.status-item { width $post-tool-button-width height $post-tool-button-width color var(--text-color-3) font-size 1.6rem cursor pointer &.post-lock { cursor default .fa-lock-open { display none color var(--keep-success-color) } &.decrypt { cursor pointer .fa-lock-open { display block } .fa-lock { display none } } } } } }