共计 3285 个字符,预计需要花费 9 分钟才能阅读完成。
自动写代码机器人,免费开通
这篇文章主要介绍了 innodb 如何实现事务隔离级别,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让丸趣 TV 小编带着大家一起了解一下。
前言
之前的文章 mysql 锁机制详解中我们详细讲解了 innodb 的锁机制,锁机制是用来保证在并发情况下数据的准确性,而要保证数据准确通常需要事务的支持,而 mysql 存储引擎 innodb 是通过锁机制来巧妙地实现事务的隔离特性中的 4 种隔离级别。
事务 ACID 特性,其中 I 代表隔离性(Isolation)。隔离性是指,多个用户的并发事务访问同一个数据库时,一个用户的事务不应该被其他用户的事务干扰,多个并发事务之间要相互隔离。
我们都知道事务的几种性质,数据库中的一致性和隔离性等是实现事务的基本思想,在系统有大量的并发访问的情况下,了解和熟练应用数据库的本身的事务隔离级别,对于写出健壮性,并发处理能力强的代码还是起关键的作用。
1. 事务之间如何互相干扰
一个事务是如何干扰其他事务呢?举个例子,有如下表:
create table lock_example(id smallint(10),name varchar(20),primary key id)engine=innodb;
表中有如下数据:
1, zhangsan
2, lisi
3, wangwu
demo1:
事务 A,先执行,处于未提交的状态:
insert into t values(4, zhaoliu
事务 B,后执行,也未提交:
select * from t;
如果事务 B 能够读取到 (4, zhaoliu) 这条记录,说明事务 A 就对事务 B 产生了影响,这种影响叫做“读脏”,即读到了未提交事务操作的记录。
demo2:
事务 A,先执行:
select * from t where id=1;
结果集为
1,zhangsan
事务 B,后执行,并且提交:
update t set name=xxx where id=1;
commit;
事务 A,再次执行相同的查询:
select * from t where id=1;
结果集为:
1, xxx
这次是已提交事务 B 对事务 A 产生的影响,这种影响叫做“不可重复读”,即一个事务内相同的查询,却得到了不同的结果。
demo3:
事务 A,先执行:
select * from t where id
结果集为:
NULL
事务 B,后执行,并且提交:
insert into t values(4, zhaoliu);
commit;
事务 A,首次查询了 id 3 的结果为 NULL,于是想插入一条为 4 的记录:
insert into t values(4, xxoo);
结果集为:
Error : duplicate key!
你可能会想。。。你 TM 在逗我?查了 id 3 为空集,insert id= 4 时又告诉我 PK 冲突?→_→
这次是已提交事务 B 对事务 A 产生的影响,这种影响叫做“幻读”。
如上,并发的事务可能导致其他事务出现读脏、不可重复读、幻读。为了避免如上情况出现,innodb 又做了哪些努力呢?
2. InnoDB 实现了哪几种事务的隔离级别?
InnoDB 实现了四种不同事务的隔离级别:
读未提交(Read Uncommitted)
读提交(Read Committed, RC)
可重复读(Repeated Read, RR)
串行化(Serializable)
不同事务的隔离级别,实际上是一致性与并发性的一个权衡与折衷。
3. 四种事务的隔离级别,innodb 如何实现?
InnoDB 使用不同的锁策略 (Locking Strategy) 来实现不同的隔离级别。
a. 读未提交(Read Uncommitted)
这种事务隔离级别下,select 语句不加锁,也不是快照读。
SELECT statements are performed in a nonlocking fashion.
此时,可能读取到不一致的数据,即“读脏”。这是并发最高,一致性最差的隔离级别。
b. 读提交(Read Committed, RC)
普通 select 是快照读;
加锁的 select, update, delete 等语句,除了在外键约束检查 (foreign-key constraint checking) 以及重复键检查 (duplicate-key checking) 时会封锁区间,其他时刻都只使用记录锁;
间隙锁 (gap lock)、临建锁(next-key lock) 在该级别下失效;
此时,其他事务的插入依然可以执行,就可能导致,读取到幻影记录。该级别是最常使用的。而且如果是不上锁的 select,可能产生不可重复读。
该级别下是通过快照读来防止读脏的。因为在该级别下的快照读总是能读到最新的行数据快照,当然,必须是已提交事务写入的,所以可能产生不可重复读。
c. 可重复读(Repeated Read, RR)
这是 InnoDB 默认的隔离级别,在 RR 下:
普通的 select 使用快照读(snapshot read),这是一种不加锁的一致性读(Consistent Nonlocking Read),底层使用 MVCC 来实现;
加锁的 select(select … in share mode / select … for update), update, delete 等语句,它们的锁,依赖于它们是否在唯一索引 (unique index) 上使用了唯一的查询条件(unique search condition,此时使用记录锁),或者范围查询条件(range-type search condition,此时使用间隙锁或临键锁);
在唯一索引上使用唯一的查询条件,会使用记录锁 (record lock),而不会封锁记录之间的间隔,即不会使用间隙锁(gap lock) 与临键锁(next-key lock);
范围查询条件或者是非唯一索引,会使用间隙锁与临键锁,锁住索引记录之间的范围,避免范围间插入记录,以避免产生幻影行记录,以及避免不可重复读;
在该级别下
通过快照读以及锁定区间来实现避免产生幻读和不可重复读;
某个事务首次 read 记录的时间为 T,未来不会读取到 T 时间之后已提交事务写入的记录,以保证连续相同的 read 读到相同的结果集,这可以防止不可重复读;
RR 下是通过间隙锁,临键锁来解决幻影读问题;
d. 串行化(Serializable)
这种事务的隔离级别下,所有 select 语句都会被隐式的转化为 select … in share mode,也就是默认上共享读锁(S 锁)。
所以,如果事务 A 先执行如下 sql 之后,会尝试获取所查询行的 IS 锁(和别的 IS、IX 锁是兼容的),这时别的事务也能获取这些行的 IS 锁甚至是 S 锁,但是如果接下来,事务 A 如果 update 或 delete 其中的某些行,这时就获取了 X 锁,别的事务即便是执行普通的 select 语句也会阻塞,因为它们尝试获取 IS 锁,但是 IS 锁和 X 锁是互斥的,这样就避免了读脏、不可重复读以及幻读,所有事务就只能串行了。
select ... ;
这是一致性最好的,但并发性最差的隔离级别。高并发量的场景下,几乎不会使用上述 a 和 d 这两种隔离级别。
4. 总结
并发事务之间相互干扰,就可能导致事务出现读脏,不可重复读,幻读等问题。
InnoDB 实现了 SQL92 标准中的四种隔离级别:
读未提交:select 不加锁,可能出现读脏;
读提交(RC):普通 select 快照读,锁 select /update /delete 会使用记录锁,可能出现不可重复读;
可重复读(RR):普通 select 快照读,锁 select /update /delete 根据查询条件等情况,会选择记录锁,或者间隙锁 / 临键锁,以防止读取到幻影记录;
串行化:select 隐式转化为 select … in share mode,会被 update 与 delete 互斥;
InnoDB 默认的隔离级别是 RR,用得最多的隔离级别是 RC
感谢你能够认真阅读完这篇文章,希望丸趣 TV 小编分享的“innodb 如何实现事务隔离级别”这篇文章对大家有帮助,同时也希望大家多多支持丸趣 TV,关注丸趣 TV 行业资讯频道,更多相关知识等着你来学习!
向 AI 问一下细节