MySQL 事务隔离性的实现

众所周知,数据库有四种隔离级别,那么这个隔离级别是什么用途呢?

大家可能对一个场景比较熟悉:做业务功能的时候,产品大大既要求保证业务的并发量,又要保证数据的安全性、一致性。而且还经常对较为复杂的这类场景感到头痛吧?
你可能会说不会头痛的 哈哈 优秀!

那么问题来了,在java代码或其他语言中保证安全性会使用到各种锁,那么数据库保证安全性的是什么呢?没错,你猜对了,就是锁,他也就是数据库隔离性实现的底层! 隔离就是为了安全!


数据库锁的分类

PS: 我们只考虑 InnoDB 哈

  • 共享锁(S Lock)

  • 排他锁(X Lock)

    共享锁允许多个事务读取同一行数据,可以理解为【共享读】;
    排他锁只允许最多一个事务删除或更新同一行数据, 可以理解为【排他写】。

一个事务获取了一行的 S Lock 时,允许其他事务获取该行的 S Lock, 但不允许其他事务获取该行的 X Lock,
那么如果一个事务获取了一行的 X Lock 时,数据库允许其他事务获取到该行的 Slock吗?是否也需要等待?


一致性非锁定读

上面说到,如果一个事务获取了一行的 X Lock 时,数据库允许其他事务获取到该行的 Slock吗?这个时候是不需要等待的,但是不需要加锁,因为这时候读取的是行的一个快照。
(因为快照是不会被修改的,所以快照是不会加锁的)这种快照读就是一致性非锁定读。每行会有很多快照,那么每次读取的都是哪个快照呢?

其实这就和隔离级别关系很大了,各个隔离级别对应读取的快照如下:

  • RC(读已提交):被锁定行被其他事务最新提交的快照数据
  • RR(可重复读):事务开启时的快照数据

一致性锁定读

一般我们使用的都是一致性非锁定读,但是有些情况会去锁行,以保证数据逻辑一致性,这就是一致性锁定读。

InnoDB 提供了两种方式:

  • 读行加排它锁,不允许其他读写事务获取
  • 读行加共享读,允许读,但不允许写数据

示例如下:

1
2
select ... for update;
select ... lock in shared mode;

数据库的其他锁

  • Gap Lock 间隙锁: 锁定一个范围,但不包含记录本身
  • next-key lock : 也是锁定范围,但包含了记录本身,相当于【行锁 + Gap Lock】

next-key lock 可有效解决 幻读 的出现

RC时,用 for update 进行一致性锁定读某行,事务不关闭,其他事务是可以插入新的行的数据的,所以一致性锁定读期间的多次读取可能会出现 幻读。

next-key lock 是怎么解决幻读的呢?

RR级别下进行上述操作时,加的就是 next-key lock 间隙锁了。锁的范围是 2(正无穷大),当开启 for update 查询时,就对所有行加了 xlock,那么其他事务是不可能有插入数据的情况的。


Serializable 串行化的实现

他隔离级别最高,但串行化不是就说是多个事务的操作按顺序执行的。其实是InNoDB会为每个 selectlock in shared mode ,那么这些行的数据是肯定不会被获取到Xlock了,也就保证了串行化级别的可靠性。