【MySQL篇】MySQL的MVCC是什么😵‍💫,解释清楚什么是脏读、不可重复读、幻读

1,852 阅读4分钟

各种非正常读

脏读

事务A读取到了事务B还未提交的数据,被称之为脏读。

image-20221109174108109

不可重复读

在并发更新操作时,在同一次事务中,前后两次查询到的数据是不一样的。

image-20221109174313125

幻读

在并发新增、修改时,在同一次事务中,前后两次查询到的数据量不一致。用户产生不可预料的问题

image-20221109174740906


事务隔离级别

可以解决脏读、幻读、不可重复读的问题。

隔离级别隔离级别英文脏读幻读不可重复读加锁读说明
读未提交Read UnCommitted (RU)能够读取到其它事物未提交的内容
读已提交Read Committed (RC)只能读取到其它事物提交成功的内容
可重复读Repeatable Read (RR)innoDB除外读取某行后,不允许其它事物操作此行,直到事物结束
序列化Serializable等待其它事物执行完成后,依次执行,性能低,安全性高

MVCC并发版本控制

MVCC全称(Multiversion Concurrency Control)

在MySQL的InnoDB引擎下,RC、RR基于MVCC(多版本并发控制)进行并发事务控制,MVCC是基于数据版本来进行并发事务访问的一种技术。

RR在MVCC下不能保证不产生幻读的问题。因为如果在两次快照读中间穿插了当前读,那么ReadView会被重新生成,因此可能会导致幻读的问题产生。既第一次读到了3条数据,第二次读到了2条数据。

image-20221110111810472

UNDO_LOG

UNDO_LOG(回滚日志)版本链条

image-20221110102518045

MySQL会在确定UNDO_LOG没有其它事务引用时才会删除。

ReadView

ReadView就是快照读SQL执行时,MVCC提取数据的依据。当前读不会使用MVCC。

  • 快照读:就是最基础的查询语句。使用MVCC。
  • 当前读:执行下列语句时读取数据的方式。使用行锁 + 间隙锁。
    • INSERT、UPDATE、DELETE
    • SELECT ... FOR UPDATE
    • SELECT ... LOCL IN SHARE MODE

ReadView属性

  • m_ids:当前活跃的事务id列表
    • 活跃:没有commit、rollback的事务
  • min_trx_id:最小活跃的事务id
  • max_trx_id:预分配的事务id,最大事务编号 +1
  • cerator_trx_id:ReadView创建者的事务编号

ReadView生成时机:

  • 读已提交RC:每次读取都会生成新的ReadView
    • image-20221110104624700
  • 可重复读RR:会复用第一次生成的ReadView
    • 因为所使用的ReadView是一模一样的,所以RR是可重复读的。
    • image-20221110111239484

ReadView提取数据的逻辑

ReadView会根据自己的属性,去到UNDO_LOG中提取数据,提取的顺序由UNDO_LOG的事务号从大到小依次读取,如果当前不满足,就会往下接着找,最终找到符合条件的数据。规则如下。

  • 判断当前事务id是否等于,ReadView创建者的事务id,如果是的话就说明这个事务是自己的,可以访问数据。
  • 判断trx_id < min_trx_id,自己的事务id是否小于最小的活跃事务id,如果是的话说明自己已经被提交了,可以访问。
  • 判断 trx_id > max_trx_id 如果自己的事务id大于ReadView生成时的最大事务ID,那么说明自己还没有提交。不可以访问。
  • 判断 min_trx_id <= trx_id <= max_trx_id,如果当前事务id大于等于最小事务id,并且小于等于预分配事务id,成立的话就去活跃的事务id列表中查找,没找到,说明自己被提交了,那就可以访问,否则就不可以访问。

image-20221110105418155

参考地址:IT老齐


Spring事务

三方操作如何保证原子性

在开发业务的过程中,往往可能遇到这种场景,在数据库事务操作完毕后,给第三方组件、服务发送一条消息、HTTP请求。像这种操作如果直接写在事务内部,则有可能会影响主要业务代码的运行,如果写在外部,则无法保证事务操作与三方操作的原子性。

在Spring中提供了一个功能,事务回调接口