社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  DATABASE

MySQL,InnoDB究竟如何巧妙实现,4种事务的隔离级别(第9讲,超硬核)

架构师之路 • 1 月前 • 118 次点击  

《数据库架构100讲》

9. InnoDB四种隔离级别


事务ACID特性,其中I代表隔离性(Isolation)

什么是事务的隔离性?
隔离性是指,多个用户的并发事务访问同一个数据库时,一个用户的事务不应该被其他用户的事务干扰,多个并发事务之间要相互隔离。

一个事务怎么会干扰其他事务呢?
咱们举例子来说明,假设有InnoDB表:

t(id PK, name);


表中有三条记录:

1, shenjian

2, zhangsan

3, lisi


case 1:读脏
事务A,先执行,处于未提交的状态:

insert into t values(4, wangwu);


事务B,后执行,也未提交:

select * from t;


如果事务B能够读取到(4, wangwu)这条记录,事务A就对事务B产生了影响,这个影响叫做“读脏”,读到了未提交事务操作的记录。

case 2:不可重复读
事务A,先执行:

select * from t where id=1;


结果集为:

1, shenjian


事务B,后执行,并且提交:

update t set name=xxoo where id=1;

commit;


事务A,再次执行相同的查询:

select * from t where id=1;


结果集为:

1, xxoo


这次是已提交事务B对事务A产生的影响,这个影响叫做“不可重复读”,一个事务内相同的查询,得到了不同的结果。

case 3:幻读
事务A,先执行:

select * from t where id>3;


结果集为:

NULL


事务B,后执行,并且提交:

insert into t values(4, wangwu);

commit;


事务A,首次查询了id>3的结果为NULL,于是想插入一条为4的记录:

insert into t values(4, xxoo);


结果集为:

Error : duplicate key!


事务A的内心OS是:你TM在逗我,查了id>3为空集,insert id=4告诉我PK冲突?

这次是已提交事务B对事务A产生的影响,这个影响叫做“幻读”。

可以看到,并发的事务可能导致其他事务:
(1)读脏;
(2)不可重复读;
(3)幻读;

InnoDB实现了哪几种事务的隔离级别?
按照SQL92标准,InnoDB实现了四种不同事务的隔离级别:
1. 读未提交(Read Uncommitted);
2. 读提交(Read Committed, RC);
3. 可重复读(Repeated Read, RR);
4. 串行化(Serializable);

不同事务的隔离级别,实际上是一致性与并发性的一个权衡与折衷

InnoDB的四种事务的隔离级别,分别是怎么实现的?
InnoDB使用不同的锁策略(Locking Strategy)来实现不同的隔离级别。

一,读未提交(Read Uncommitted)
这种事务隔离级别下,select语句不加锁。
画外音:官方的说法是

SELECT statements are performed in a nonlocking fashion.


此时,可能读取到不一致的数据,即“读脏”。这是并发最高,一致性最差的隔离级别。

二,串行化(Serializable)
这种事务的隔离级别下,所有select语句都会被隐式的转化为select ... in share mode.

这可能导致,如果有未提交的事务正在修改某些行,所有读取这些行的select都会被阻塞住。
画外音:官方的说法是

To force a plain SELECT to block if other transactions have modified the selected rows.


这是一致性最好的,但并发性最差的隔离级别。

在互联网大数据量,高并发量的场景下,几乎不会使用上述两种隔离级别

三,可重复读(Repeated Read, RR)
这是InnoDB默认的隔离级别,在RR下:

1. 普通的select使用快照读(snapshot read),这是一种不加锁的一致性读(Consistent Nonlocking Read),底层使用MVCC来实现,具体的原理在《InnoDB的高并发是因为MVCC(第5讲)》中有详细的描述;

2. 加锁的select(select ... in share mode / select ... for update)updatedelete等语句,它们的锁,依赖于它们是否在唯一索引(unique index)上使用了唯一的查询条件(unique search condition),或者范围查询条件(range-type search condition)
    - 在唯一索引上使用唯一的查询条件,会使用记录锁 (record lock),而不会封锁记录之间的间隔,即不会使用间隙锁(gap lock)与临键锁(next-key lock)
    - 范围查询条件,会使用间隙锁临键锁,锁住索引记录之间的范围,避免范围间插入记录,以避免产生幻影行记录,尽量避免不可重复的读
画外音:这一段有点绕,多读几遍。

关于记录锁,间隙锁,临键锁的更多说明,详见《MySQL,InnoDB的select为什么会阻塞insert?(第8讲)》。

四,读提交(Read Committed, RC)
这是互联网最常用的隔离级别,在RC下:

1. 普通读是快照读;

2. 加锁的select, update, delete等语句,除了在外键约束检查(foreign-key constraint checking)以及重复键检查(duplicate-key checking)时会封锁区间,其他时刻都只使用记录锁;

此时,其他事务的插入依然可以执行,就可能导致,读取到幻影记录。

总结
1. 并发事务之间相互干扰,可能导致事务出现读脏不可重复度幻读等问题;
2. InnoDB实现了SQL92标准中的四种隔离级别;
- 读未提交:select不加锁,可能出现读脏;
- 读提交(RC):普通select快照读,锁select /update /delete 会使用记录锁,可能出现不可重复读;
- 可重复读(RR):普通select快照读,锁select /update /delete 根据查询条件情况,会选择记录锁,或者间隙锁/临键锁,以防止读取到幻影记录;
- 串行化select隐式转化为select ... in share mode,会被update与delete互斥;
3. InnoDB默认的隔离级别是RR,用得最多的隔离级别是RC;


知其然,知其所以然。
思路比结论更重要。


==全文完==

有架构合集吗?
流量从10万到10亿,80个架构问题
关于即时通讯架构的一切!

Q4内测了一个新产品,看普通人起号能否复制:
复盘来了,4周,人均41W+
内测成绩不错,4周的时间人群你全网流量41W+,1月份启动二期点击阅读原文一起参与!

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/191631