【每日鲜蘑】数据库隔离级别、脏读、幻读、锁等
在
线上环境
中,偶尔会发生一些令人“我靠”
的问题,其实一部分问题源于数据库
。当开发环境
、测试环境
没有出现的问题在线上环境
中却偶然出现时,这也是解决问题的一个方向。
脏读
数据库隔离级别为
读未提交
的时候,可能发生脏读
。读未提交
指当会话 A 的数据库操作尚未commit
时,会话 B 可以读取到这个未提交的数据。而此时如果会话 A 因为某些原因rollback
了,那么会话 B 读取的数据就是错误的,也就是脏读
。当隔离级别提高到读已提交
时,则可以避免脏读
。
不可重复读
简单的理解就是
多次读取结果不一致
,在会话 A 中多次相同的操作读取的数据是不一致的。比如,在会话 A 的两次读取操作直接,会话 B 对此数据进行了提交,那么会话 A 第一次读取的是之前的数据,第二次读取的是之后的数据。通过隔离级别可重复读
来解决这个问题。
幻读
不可重复读
的特殊场景,不可重复读
主要是指在读取某条记录时发生,而幻读指的是范围。比如,会话 A 第一查询年龄大于 18 的人,发现没有数据,但当第二次进行查询时,却查询到了数据。在串行化
的隔离级别下,不会发生幻读
。
数据库锁
排他锁
又称为 X 锁,写锁。一个事务对数据对象 O 加了
排他锁
,就可以对 O 进行读取和更新。加锁期间其它事务不能对 O 加任何锁。
共享锁
又称为 S 锁,读锁。一个事务对数据对象 O 加了
共享锁
,可以对 O 进行读取操作,但是不能进行更新操作。加锁期间其它事务能对 O 加共享锁
,但是不能加排他锁
。
隔离级别
读未提交
如果一个事务已经开始写操作,那么其他事务则不允许同时进行写操作,但允许其他事务读此行数据。
- 写操作不加锁;
- 读操作不加锁;
读已提交
读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。
- 写操作加
排他锁
,保持到事务结束; - 读操作加锁
共享锁
,此次查询结束后立即释放共享锁
,保证读取的数据都是已提交的数据。
可重复度
读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。保证了在同一个事务中多次读取同样数据的结果是一样的。
- 写操作加
排他锁
; - 读操作加
共享锁
,保持到事务结束。不妨碍其他事务读,但其它事务无法修改这些数据,无法锁住insert
的数据(造成幻读
); InnoDB
中,SELECT
、UPDATE
、DELETE
操作的不可重复读
问题可以通过MVCC(多版本并发控制)
来解决,但是INSERT
操作的幻读
问题需要通过MVCC
+Next-Key Locks
来解决。
串行化
极大的降低数据库的并发能力。
读用读锁,写用写锁,读锁和写锁互斥
- 写操作加
排他锁
; - 读操作加
排他锁
;
总结
- 在
InnoDB
引擎中,对于索引的扫描,不仅锁住扫描到的索引,而且还锁住这些索引覆盖的范围,因此这个范围是内插入数据是不允许的。 - 使用
select @@tx_isolation;
查询数据库的隔离级别。 - 隔离级别越高(
读未提交
->读已提交
->可重复读
->串行化
),越能保证数据的完整性和一致性,对并发性能的影响也会越大。 Mysql
默认的级别是可重复读
,优先考虑把数据库系统的隔离级别设为读已提交
。
本文使用 mdnice 排版
转载自:https://juejin.cn/post/6844904103278182414