前言
快照读(Snapshot Read)是数据库管理系统中一种特殊的读取机制,主要用于实现多版本并发控制(MVCC, Multi-Version Concurrency Control)策略,尤其是在MySQL的InnoDB存储引擎中。其核心目的是在高并发环境下提供一致性的非锁定读取,以提高读操作的性能。
知识点
先来张图:
-
多版本并发控制(MVCC):MySQL InnoDB 引擎使用MVCC来管理事务并发时的数据版本。每个事务看到的数据是由其开始时数据库状态的快照组成,而不是最新的数据状态。如此以来,事务之间就不会因为读操作而相互阻塞。
-
事务ID与版本号:每当一个新的事务开始时,MySQL会分配给它一个唯一的事务ID。在执行更新操作时,InnoDB不会直接修改原有数据行,而是将更改保存在新的记录中,并标记新记录的事务ID(或版本号),同时保留旧版本的记录。
-
Undo Logs(回滚日志):为了支持快照读,InnoDB使用Undo Logs来保存数据的旧版本。当事务需要查看某个数据的历史版本时,可以通过Undo Logs回滚到该事务开始时的数据状态。
-
Read View(读视图):在可重复读(Repeatable Read)隔离级别下,事务第一次执行快照读操作时,会创建一个Read View。这个Read View记录了当前活跃事务的列表以及它们的事务ID。之后的快照读都将依据这个Read View来决定数据的可见性:事务能看到所有在它之前提交的事务所做的更改,但看不到在其之后开始的事务所做的更改。
-
数据可见性判断:在进行快照读时,会根据Read View和Undo Logs来判断数据的可见性。如果数据的最新版本是由一个在当前事务启动后才开始的事务所修改的,则会通过Undo Logs回溯到该数据的一个旧版本,确保读取的是一个一致性的视图。
-
隔离级别影响:在不同的事务隔离级别下,快照读的行为有所不同。例如,在读已提交(Read Committed)隔离级别下,每次快照读都会创建一个新的Read View,而在可重复读(Repeatable Read)级别下,整个事务周期内只创建一个Read View。
通过这些机制,快照读使得事务能够在不加锁的情况下读取数据,降低了锁的竞争,提高了系统的并发性能,同时也保证了事务间的隔离性和数据一致性。
一个例子
假设我们有一个表users
,包含两列:id
和name
,初始数据如下:
id | name |
---|---|
1 | Alice |
2 | Bob |
假设有两个并发事务T1和T2:
-
事务T1开始:事务T1开始执行,此时MySQL为T1分配了一个事务ID(假设为100)。
-
T1执行快照读:T1想要读取用户Alice的信息,由于这是T1的第一次读操作,InnoDB为T1创建一个Read View。此时没有其他活动事务,所以Read View中记录的活跃事务列表为空。
-
事务T2开始并更新数据:接着,事务T2开始(事务ID为200),T2决定将Alice的名字改为Carol,并提交这个更改。InnoDB不会直接覆盖原记录,而是创建一个新的版本记录,标记其由事务200修改,同时保留原始记录(Alice)作为Undo Log的一部分。
-
T1再次执行快照读:现在,T1再次读取用户Alice的信息。由于T1已经有了自己的Read View(里面没有活动事务ID大于100的事务),它不会看到T2所做的更改。InnoDB通过Undo Logs回滚到T1开始时的状态,因此T1看到的仍然是Alice,而不是Carol。这就是快照读的效果——T1看到的是一个一致性的数据视图,仿佛T2的更改从未发生过。
在这个例子中,快照读确保了事务T1在多次读取同一条记录时,结果保持一致(即“可重复读”),同时避免了T1和T2之间的读写冲突,提高了并发处理能力。