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;
第二步, 事务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 #
并发性能差,一般不用。