快照隔离与可重复读的关系是什么?
共 4516字,需浏览 10分钟
·
2024-06-13 18:52
来源:juejin.cn/post/7069985361396498462
推荐:https://t.zsxq.com/YRPg5
前言
随着业务迭代,我们会更多地面临分布式环境带来的挑战。
❝数据库软件或硬件出现问题(写过程中)
应用程序崩溃(多个读写操作中)
网络延迟异常导致数据库链接
并发读写问题
多个客户端之间的竞争条件下出现的异常
事务的引入简化了上述问题的解决,但需要我们正确运用,因此理解事务就变得尤为重要,本文旨在对事务隔离级别简单说明。
相关理论
ACID:Atomicity、Consistency、Isolation、Durability
CAP:Consistency、Availability、Partition tolerance
BASE:Basically Available、Soft state、Eventually consistency
详解 CAP 定理 Consistency(一致性)、 Availability(可用性)、Partition toleranc
事务隔离级别
未提交读(Read Uncommited)
脏读:一个事务读到了另一个事务未提交的数据
已提交读(Read Commited)
读数据:读已经提交的数据(没有脏读)
-
没有解决不可重复读(read skew, nonrepeatable read) 问题,如下图
❝1位置Alice查询
select balance from accounts where id = 1
2位置Alice认为
Account1 + Account2 = 900
,但是她总共有1000!
即使两个并发事务全部提交,Alice依然能看到正确的数据,但这“暂时的不一致”(temporary inconsistency)也是不可接受的。
-
为了解决上述脏读、不可重复读的问题,快照隔离(snapshot isolation)技术被广泛应用于关系型数据库存储引擎。
快照隔离与可重复读(Snapshot Isolation and Repeatable Read)
-
快照隔离简单来讲就是每个事务都去读取一致的快照(已提交的) ,也就是1的时间节点Alice会查到数据500。 -
实现快照隔离的方式就是多版本并发控制(MVCC, multi-version concurrency control)。 -
如果在Read commited级别,MVCC只需要维护(已提交版本、未提交版本)
新增created by
与deleted by
,记录每个数据快照的创建事务ID与删除事务ID(update可以认为是delete & insert)。
一个小坑:快照隔离在不同的数据库有不同的实现命名,《Designing Data Intensive Applications》原文如下:
❝In Oracle it is called serializable, and in PostgreSQL and MySQL it is called repeatable read
-
为了更加清楚地说明,下文就用可重复读(Repeatable Read)级别代替快照隔离
幻读问题
提交读与可重复读对只读(read-only)事务支持我们已经讨论过了,那么对写事务,特别是并发的两个写事务之间会出现什么问题呢?
可以看到,Alice事务和Bob事务分别对shift_id=1234 and on_call=true
的数据进行了筛选,并且分别对数据进行了更新,导致最后的数据出现了错误。
幻读问题特征
-
使用SELECT语句使用特定条件查询出多条数据; -
依赖上个SELECT语句的结果,决定接下来的操作; -
决定后,写数据并提交事务。
核心在于,B事务插入或删除了,满足A事务SELECT查询条件的数据,导致A事务出现”幻觉“。
串行化(Serializability)
针对上述幻读场景,需要更强的事务隔离级别约束,就是串行化。
串行化级别的实现,取决于各个数据库存储引擎的选型,但主要有以下三种:
-
串行化运行 -
2PL(Two Phase Locking)机制 -
乐观并发控制,例如串行化快照隔离
InnoDB如何解决”幻读“
InnoDB采用2PL机制解决”幻读“,并且在可重复读(RR) 级别实现,具体实现方式为next-key locking
。
2PL简介
多个并发事务允许读取相同数据,仅当这个数据没有在写入中:
❝A事务已经读取数据,B事务并发地写入该数据,B必须等A提交或者中断后执行;
A事务已经写入数据,B事务并发的读取该数据,B必须等A提交或者中断后执行。
-
这里的”数据“包含范围查询出的区间
NEXT-KEY
next-key锁是一种行锁与gap锁的结合,gap锁可以对索引值范围内的行数据进行加锁。
❝A next-key lock is a combination of a record lock on the index record and a gap lock on the gap before the index record.
如果我们拥有一个索引,并且值为10, 11, 13, 20;那么,next-key锁可能的加锁范围如下所示。
❝(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)
结合Bob和Alice的例子,Bob已经读取shift_id=1234 and on_call=true
的行数据,那么next-key会对shift_id=1234
的范围数据进行加锁,Alice写入必须等待Bob提交完成或者中断,避免幻读问题产生。
参考
-
Designing Data Intensive Applications -
https://tech.meituan.com/2014/08/20/innodb-lock.html -
https://dtm.pub/guide/start.html -
https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html
AI 总结
快照隔离(Snapshot Isolation)和可重复读(Repeatable Read)是两种数据库事务隔离级别,它们之间有一些相似性和联系,但也有关键的区别。以下是对这两种隔离级别及其关系的详细解释:
可重复读(Repeatable Read)
可重复读隔离级别确保在一个事务内多次读取同一行数据时,其值始终一致,即使其他事务修改了这行数据。在实现可重复读的过程中,数据库系统通常会采取如下措施:
-
读取同一行数据时:事务会锁定读取的行,直到事务结束。这防止了其他事务修改或删除这些行。 -
防止不可重复读:当一个事务读取了一行数据,其他事务不能更新或删除这行数据,直到第一个事务结束。
可重复读的主要问题是幻读(Phantom Read),即在同一个事务内,两次相同的查询结果集不一致,原因是其他事务插入或删除了行。
快照隔离(Snapshot Isolation)
快照隔离是通过多版本并发控制(MVCC)实现的一种隔离级别。在快照隔离下,每个事务在开始时会获取数据的一致性快照,这个快照基于事务开始时的数据状态。这意味着:
-
事务读取的数据版本一致:在事务执行期间,即使其他事务对数据进行了修改,当前事务读取的数据仍然是事务开始时的状态。 -
写入冲突检测:如果两个事务尝试修改同一行数据,其中一个事务会被拒绝并回滚,以避免更新丢失。
关系与区别
-
相似性:
-
两者都确保了事务内的读取操作一致。 -
都可以防止不可重复读问题。 -
区别:
-
实现方式:可重复读通常通过加锁机制实现,锁定读取的行。而快照隔离通过多版本并发控制实现,事务读取的是开始时的快照。 -
幻读问题:在可重复读隔离级别下,幻读问题可能依然存在。而快照隔离通过事务快照来避免幻读。 -
写冲突处理:在快照隔离下,事务之间的写冲突会导致一个事务回滚,而可重复读一般通过锁机制避免写冲突。
最后,留一个问题,InnoDB彻底解决了幻读问题吗?