15.7.2.1 事务隔离级别

InnoDB支持四种隔离级别,分别是:

  • REPEATABLE READ 可重复读
  • READ COMMITTED 读已提交
  • READ UNCOMMITTED 读未提交
  • SERIALIZABLE 串行化 默认隔离级别是可重复读。

在InnoDB中,每种隔离级别的加锁策略不同。使用可重复读可以得到高度的一致性,如果更重视并发,可以采用读已提交。

REPEATABLE READ #

是默认隔离级别。在这个隔离级别下,同一个事务内的Consistent reads读取由第一次read建立的快照。

在可重复隔离级别下,快照是在第一次读取数据时建立的。而在读已提交隔离级别下,每次读取数据都会建立新的快照。

READ COMMITTED #

此隔离级别下,每次一致性读,都会建立新的快照。

锁定读(SELECT … FOR UPDATE,UPDATE,DELETE),InnoDB只锁定索引记录,不会锁定记录之前的间隙。只有在外键约束检查 和重复键检查时会用到间隙锁。

因为禁用了间隙锁,其它事务可能会在间隙中插入新的记录,所以可能会发生幻读。

在RC隔离级别下,binlog格式只支持ROW格式。如果你设置的是MIXED,server会自动使用ROW格式。

在RC级别下,还要下面两个要注意的点:

  • 对于UPDATE或DELETE语句,InnoDB只会锁定它要更新或删除的记录。在MySQL校验where条件后,不符合的记录的锁会被释放。这极大的降低了死锁的概率。
  • 对于UPDATE语句,如果一条记录已经被锁定了,InnoDB会使用 半一致性读,给MySQL返回最新的已提交记录,MySQL可以评估出这条记录是否符合where条件。如果符合,MySQL会再次读取这条记录,这时InnoDB要么锁定这条记录,要么等待。

例1 update用到了索引 #

第一步, 新建表并插入测试数据:

create table t7 (id int(11) not null auto_increment,a int,b int,c int,primary key(id),index(b));
insert into t7 (a,b,c) values(1,2,3),(2,2,4),(3,3,5),(2,4,6);

事务1,更新记录:

begin;
update t7 set c=11 where b=2 and c=3;

此时我们查看相关锁信息:

select * from performance_schem.data_locks\G;

可以看到,事务1获取到了聚簇索引、二级索引b上的锁。

第二步, 事务2,更新记录:

begin;
update t7 set c=21 where b=2 and c=4;

发生阻塞。

查看相关锁信息

select * from performance_schem.data_locks\G;

可以看到,事务2在等待二级索引b上的锁。

例2 update没有用到索引 #

当前数据库中的记录为:

第一步, 事务1,更新语句:

begin;
update t7 set c=11 where a=2 and c=4;

此时我们查看相关锁信息:

select * from performance.data_locks\G;

结果为:

可以看到,事务1只获取到了聚簇索引上的锁。

第二步, 事务2,更新语句:

begin;
update t7 set c=11 where a=2 and c=4;

结果:

执行成功,并没有阻塞。

我们再来查看一下锁信息:

select * from performance.data_locks\G;

结果为:

可以看到,事务2获取到了聚簇索引上的锁。事务1和事务2没有锁冲突,所以事务2执行成功。

更新记录前,会先获取相关表的IX锁。以后不再分析。

READ UNCOMMITTED #

会导致脏读,一般不用。

SERIALIZABLE #

并发性能差,一般不用。