文章目录

  • 四种隔离级别的实现
  • 行锁
  • MVCC实现


四种隔离级别的实现

结论:

  • 对于RR和RC级别隔离,InnoDB使用MVCC+行锁实现。
  • 对于Serializable,使用表锁实现。

具体实现:

  • 在可重复读(RR)的隔离级别下,事务启动时得到一个事务ID,整个事务存在期间只能看到小于等于这个事务ID的版本数据。(MVCC实现
  • 在读提交(RC)的隔离级别下,每个SQL执行时,得到一个事务ID,这个SQL只能看到小于等于这个事务ID的版本数据。(MVCC实现
  • 在串行化(Serializable)的隔离级别下,使用的加表锁的方式实现,应该是悲观锁,读写冲突的时候,一个事务必须等待另一个事务的完成。因此并发性降低。(表锁实现
  • 在读未提交(RU)的隔离级别下,直接返回记录的最新值。(不用实现
    补充说明:在MVCC的多版本中,肯定是看不到其他事务未提交的数据,但是自己事务中更新的数据,可以实时看到。

行锁

InnoDB使用行锁来实现并发,多个事务可以并发修改不同的行。

当多个事务修改同一行的时候,即多个事务要获得某一行的行锁,后获得的就要等待先获得的事务释放锁。

行锁是在SQL开始执行的时候获取,并不是事务开始的时候获取。而且行锁一旦获得,是在事务结束后才释放的,并不是在SQL结束后释放。

MVCC实现

每个事务启动时,得到一个事务ID trx_id,这个值是全局分配的,而且递增。

保存多个版本: 当多个事务更新同一行记录的时候,每个事务更新的数据记录都会保留,但是事务ID不同,记录中会有一个隐藏字段记录这个事务ID。这就是多版本实现的机制。

**读取特定的版本:**假设一个事务启动,事务ID为1000,那么它只能读到事务ID<=1000的版本中最新版本数据,这叫做快照读,实现了RR和RC级别的隔离。数据多版本肯定不是保存无限多个版本,当一些事务提交后,就可以删除更早的版本了。

使用行锁更新:但是,写的时候就不一样了,由于写数据是当前读,即直接读某一行在内存或者数据库文件中数据,不读快照数据了,这里需要加行锁。这个最开始,我也不能理解,既然是MVCC实现,我写我的,你写你的,有什么关系呢?假设某个字段 money = 100,A事务对它加100,B事务也对它加100,如果不加锁,最后值可能是200。这样话,其中一个更新就丢失了。因此,写操作必须串行,而且拿到锁后,必须读某一行的书,不读副本,这就是当前读,这样保证A、B事务结束后,money的值为300。这样就存在一个现象,事务ID小的那个版本可以是当前数据的最新版本。

参见下图案例

假设:事务隔离级别为RR,k初始值为1,事务ID分别为A 110,B 120,C 130。其中 start transaction with consistent snapshot 表示立即开启一个事务,begin/start transaction 表示执行到第一语句才开始一个事务。

结论:那么A读到的为1,B读到的为3。

分析:C更新后,B的更新会阻塞,直到C的事务提交。B会当前读数据,读出C更新的k值为2,然后更新k为3。假设有一个ID为121的事务,它读的k值应该是2。此时B更新后的k是当前最新版本的数据。

hyperf mysql事务 mysql事务实现_hyperf mysql事务