跳至主要內容

MySQL事务与MVCC

小刘Learning大约 5 分钟

什么是事务?

事务就是多条SQL语句,要么全部成功,要么全部失败

事务的4大特性:ACID,即原子性,一致性,隔离性,持久性

  • 原子性:一个事务中的所有操作,要么全部成功,要么全部失败
  • 一致性:事务让数据库从一个一致性状态到另一个一致性状态,数据库的状态与业务规则是保持一致的,比如转账,不论怎么操作,钱的总额是不变的
  • 隔离性:事务与事务之间是隔离的,一个事务不会影响另一个事务
  • 持久性:事务提交后,对数据库的改变应该是持久的,即使发生崩溃也能够恢复

事务并发带来的问题

  • 脏读:一个事务对数据进行了修改,但是并没有提交,在另一个事务中能够看到这种修改,称为脏读
  • 丢失修改:一个事务中对数据进行了修改,另一个事务中也对这个数据进行了修改,导致第一个事务的修改结果被覆盖掉了,称为丢失修改
  • 不可重复读:在一个事务中重复读同一个数据时,因为另一个事务修改了这个数据,导致前后两次重复读取的结果不同,称为不可重复读
  • 幻读:一个事务在读取数据时,另一个事务插入或者删除了一些记录,导致这个事务在读取的时候发现多了或者少了一些记录,像是产生幻觉一样,称为幻读;幻读产生的原因是因为某个事务读取了某个范围的记录,之后其他事务在该范围内插入了新的记录,该事务再次读取这个范围的记录时,可以读取到新插入的记录

不可重复读和幻读的区别:二者的侧重点不同,不可重复读侧重于读取一条记录,发现这条记录的某些列不一样;幻读侧重于插入或者删除一些记录

事务的隔离级别

  1. 读未提交:允许读取到其他事务修改了但未提交的数据
  2. 读已提交:允许读取到其他事务已经提交的数据
  3. 可重复读:对同一字段的重复读取的结果是一样的,除非数据被本事务进行了修改,默认隔离级别
  4. 串行化:所有的事务依次逐个执行,事务之间不会有干扰

什么是MVCC?

MVCC,多版本并发控制,是一种用来解决读写冲突的无锁并发控制,在并发读写数据库时,可以做到读操作不用阻塞写操作,写操作不用阻塞读操作,提高了数据库并发读写的能力,同时还可以解决脏读,幻读,不可重复读等问题

MVCC实现原理

为什么说MVCC是无锁的并发控制呢?这也MVCC的实现原理有关,MVCC的实现依赖于数据库的隐藏字段undologread view隐藏字段

  • 隐藏主键:如果数据没有主键,MySQL会自动生成一个主键
  • 回滚指针:指向这条记录的上一个版本,配合undolog使用
  • 最近修改事务id:记录创建这条记录或者最后一次修改这条记录的事务的id

read view

read view是事务进行快照读(普通select语句)的时候产生的读视图,它用来记录并维护系统当前活跃的事务id(还没提交的事务的id) read view最大的作用是用来做可见性判断,当事务进行快照读的时候,对该记录生成一个read view,把它当作条件去判断当前事务能够看到哪个版本的数据,有可能看到的是最新的数据,也有可能看到的是一个历史版本

read view有三个重要的属性:

  • 事务id列表:read view生成时刻,系统中活跃的事务id
  • 最小事务id:事务id列表中最小的事务id
  • 最大事务id:read view生成时刻,尚未分配的下一个事务id
事务1事务2事务3事务4
开始开始开始开始
..................修改且已提交
进行中快照读进行中
..................

上面的例子中,事务2为当前事务,则当事务2进行快照读,生成read view时,各个属性都列举如下:

  • 当前事务id:2
  • 最近修改事务id:4
  • 事务id列表:1,3
  • 最小事务id:1
  • 尚未分配最大事务id:5

read view比较规则

  1. 如果最近修改事务id<最小事务id,则当前事务能看到最近修改事务id所在的记录,否则进入下一个判断
  2. 如果最近修改事务id>=尚未分配最大事务id,则代表最近修改事务是在read view生成之后才出现的,那么当前事务肯定不可见,因为当前事务版本更老,否则进入下一个判断
  3. 判断最近修改事务id是否在事务id列表中,如果在,则说明read view生成时刻,最近修改事务还没有提交,仍然处于活跃状态,是不可见的,如果不在,则说明最近修改事务在read view生成之前就已经提交了,是可见的

RC,RR级别下快照读的区别

在读已提交和可重复读级别下,read view生成的时机不同,从而造成快照读结果不同

  1. 在RC隔离级别下,每个普通select语句(快照读)都会生成一个read view,这也是在RC隔离级别下,能看到其他事务提交的更新的原因
  2. 在RR隔离级别下,第一个快照读语句会生成一个read view,之后的查询操作都重复使用这个read view