InnoDB事务加锁分析

  
  

本文首发于体内互联网技术微信公众号
链接:https://mp.weixin.qq.com/s/S7MhlsZveBHRSQhq5aTIJA
何志创

     

一般大家对数据库事务的了解可能停留在事务的酸特性以及事务4种不同的隔离级别层面上,而对于事务4种不同隔离级别如何实现了解相对较少。

  

本文以MySQL数据库InnoDB引擎为例,为大家分析InnoDB数据库引擎对默认的隔离级别可重复读(RR)的具体实现。

  

整文知识点介绍:事务4种隔离级别,不同隔离级别解决的问题,MVCC,锁的类型,加锁案例分析,阅读完整文相信大家对事务隔离级别的具体实现有了一定的认识。

  

一、事务的隔离级别

  

<强> 1,4种隔离级别

  

<强>(1)未提交读(读未提交):一个事务读取到其他事务未提交的数据,是级别最低的隔离机制;

  

<强>(2)提交读(读):一个事务读取到其他事务提交后的数据,

  

<强>(3)可重复读(可重复读):一个事务对同一份数据读取到的相同,不在乎其他事务对数据的修改。

  

<>强(4)序列化(序列化):事务串行化执行,隔离级别最高,牺牲了系统的并发性。

  

<强> 2,不同隔离级别解决的问题

  

若不考虑事务的隔离级别,则事务的并发会造成以下问题:

  

<强>(1)脏读:事务一个读取了事务B更新的数据,然后B回滚操作,那么一个读取到的数据是脏数据。

  

<强>(2)不可重复读:事务多一次读取同一数据,事务B在事务多一次读取的过程中,对数据作了更新并提交,导致事务多一次读取同一数据时,结果不一致。

  

<强>(3)幻读:同一事务中对同一范围的数据进行读取,结果却多出了数据或者少了数据,这就叫幻读。(如同一事务对id<10的范围进行2次查询,第一次出现id=8、9的两条数据,第二次出现id=7, 8, 9的3条数据)。

  

不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表。

  

不同的隔离级别针对上述3个问题的解决能力,如下表:

  

 InnoDB事务加锁分析

  

二,MVCC

  

上文提到InnoDB默认的隔离级别是可重复读(RR), InnoDB是通过MVCC(多版本并发控制)来实现可重复读的,下面为大家介绍MVCC。

  

<强> 1,概念

  

在InnoDB中,给每行增加两个隐藏字段来实现MVCC,一个用来记录数据行的创建时间,另一个用来记录行的过期时间(删除时间)。在实际操作中,存储的并不是时间,而是事务的版本号,每开启一个新事务,事务的版本号就会递增。

  

于是乎,默认的隔离级别(可重复读)下,增删查改变成了这样:

  

<强>(1)选择

  
      <李>读取创建版本小于或等于当前事务版本号,并且删除版本为空或大于当前事务版本号的记录。这样可以保证在读取之前记录是存在的。   
  

<强>(2)插入

  
      <李>将当前事务的版本号保存至行的创建版本号。   
  

<强>(3)更新

  
      <李>新插入一行,并以当前事务的版本号作为新行的创建版本号,同时将原记录行的删除版本号设置为当前事务版本号。   
  

<>强(4)删除

  
      <李>将当前事务的版本号保存至行的删除版本号。   
  

<强> 2,快照读和当前读

  

<强>(1)快照读:读取的是快照版本,也就是历史版本;

  

<强>(2)当前读:读取的是最新版本。

  

普通的选择就是快照读,而更新、删除、插入、选择……锁在分享模式,选择…更新是当前读。

  

<强>(3)结论:强如果隔离级别是可重复读,那么在同一个事务中的所有普通选择读读到的都是事务第一个读到的快照,如此实现了可重复读;而对于当前读(更新、删除、插入、选择……锁在分享模式,选择…更新),InnoDB通过加锁来实现可重复读,且InnoDB加锁同时解决了幻读问题。

  

三,锁的类型

  

InnoDB引入以下三种锁类型:

  
      <李>   

    <强>记录锁(记录锁):在索引记录上加锁,即行锁,锁住当前行。

      李   <李>   

    <强>差距锁(间隙锁):在索引记录之间加锁,或者在第一个索引记录之前加锁,或者在最后一个索引记录之后加锁。

    InnoDB事务加锁分析