mysql官方文档翻译02-一致性非锁定读与一致性锁定读
【1】一致性非锁定读
参考资料: innodb一致性读
一致性读定义: 一致性读是一种读操作,它使用快照信息根据某个时间点呈现查询结果,不考虑其他事务同时执行的变更。
-
如果查询的数据被另一个事务修改,则根据撤销日志内容重建原始数据。这种技术避免了一些锁定问题,这些问题可能会通过强行当前事务等待其他事务完成而降低并发度。
-
在可重复读隔离级别下,快照的时间是根据第一次读取操作执行的时间点。而读已提交隔离级别下,快照的时间会重置为每次一致性读操作执行的时间点。
-
在读已提交或可重复读隔离级别下,一致性读是innodb处理select语句的默认模式;因为一致性读不会在访问的表上设置任何锁,因此其他会话可以自由修改那些同时执行一致性读的数据库表。
-
更多技术细节,参见 一致性非锁定读
【1.1】一致性非锁定读详情
1.一致性读:一致性读意味着innodb使用多版本控制技术向查询请求展示数据库在某时刻的快照。一致性读
查询请求可以看到时间点之前事务提交的变更结果,而且无法看到时间点之后或未提交事务的变更结果。
有个例外是查询请求可以看到同一个事务中早期sql语句执行的变更结果。该例外会导致如下异常:如果你更新几行数据,select语句可以查看数据行被更新后的最新版本,但该语句也可能看到任何行的老版本。如果其他会话同时更新相同表,异常情况意味着你可能看到该表在数据库中从不出现过的状态。
2.如果事务隔离级别为可重复读(mysql默认隔离级别),则同一个事务中所有一致性读操作都会读取第一次读取时建立的快照。
提交当前事务后发出新查询请求,你可以查询到更新(更新鲜)的快照。
3.在读已提交隔离级别下,事务中的每次一致性读都会设置和读取它本身最新快照。
4.一致性读是Innodb在读已提交和可重复读隔离级别中处理select语句的默认模式。
一致性读不会给它访问的表加任何锁,因此其他会话可以自由修改同时被执行一致性读的表。
5.假设你运行在可重复读隔离级别中。
当你发出一个一致性读(即普通select语句),innodb会为你的事务分配一个时间点,以决定查询请求可以看到的数据库状态。【给事务分配时间点】
如果另一个事务删除了一行且在你的时间点之后就提交事务,你不能看到被删除行的变更。新增与更新操作的情况类似。
6.您可以通过提交事务来推进您的时间点,然后再执行另一个 SELECT 或使用一致性快照启动新的事务。这被称为多版本并发控制。
【1.2】一致性非锁定读代码实践
1.在以下例子中,会话A可以看到会话B插入的行,当且仅当B提交插入操作且A也提交,因此时间点推进到会话B提交之后。【注意:事务隔离级别为可重复读】
时间线 | 会话A | 会话B |
---|---|---|
1 | set autocommit=0; | set autocommit=0; |
2 | select * from user_tbl; 结果为空 | |
3 | insert into user_tbl(name) values(‘tom01’) | |
4 | select * from user_tbl; 结果为空 | |
5 | commit | |
6 | select * from user_tbl; 结果为空 | |
7 | commit | |
8 | select * from user_tbl; 存在name=tom01的行 | |
2.如果你想查看数据库最新状态,要么使用读已提交隔离级别,要么使用锁定读(如 select * from table_name for share);
在读已提交隔离级别下,事务中每次一致性读都会设置和读取它本身最新快照。使用for share会触发锁定读:select操作会阻塞直到包含最新行的事务结束。locking reads
3.一致性读不适用于某些ddl语句;
- 一致性读不适用于drop table。 因为mysql不会使用已经被删除的表以及innodb会销毁该表;
- 一致性读不适用于alter table操作,该操作会生成原始表的临时副本,当新建副本完成后会删除原始表。当在同一个事务重新发起一致性读,新表中的行是不可见的,因为事务快照拍摄时,这些数据行不存在。在这种情况下,事务会返回一个错误信息:表定义已变更,请重试事务。
4.像INSERT INTO … SELECT, UPDATE… (SELECT), 以及CREATE TABLE …SELECT 这些select子句没有指定for update或者for share,读取类型会有变化:
- 默认情况,innodb会给这些语句加更强的锁,而select语句部分执行效果与读已提交类似,其中每次一致性读,即使在同一个事务中,它也会设置并读取它自己最新快照;
- 为在上述场景中执行非锁定读,可以设置隔离级别为读未提交或读已提交,以避免对所选表中所读行设置锁;
【2】一致性锁定读
参考资料: https://dev.mysql.com/doc/refman/8.4/en/innodb-locking-reads.html
1.如果你在同一个事务里先查询数据,然后插入或更新关联数据,常规的select语句并不能提供足够防护。其他事务可以更新或删除你刚刚查询的数据行。
innodb支持两类提供额外安全的锁定读取,包括 SELECT … FOR SHARE , SELECT … FOR UPDATE
【3.1】SELECT FOR SHARE
1.SELECT FOR SHARE : 在读取行上设置共享锁;其他会话可以读取这些行,但不能修改直到加锁的事务提交。如果这些行中任意一行被另一个事务修改但没有提交,你的查询会等待直到修改数据的事务结束,然后你的查询会使用最新值。
2.SELECT…FOR SHARE 需要select权限;
3.SELECT…FOR SHARE语句不会获取mysql授权表上的读锁;
【3.2】SELECT FOR UPDATE
1.对于搜索遇到的索引记录,锁定这些行以及关联的索引条目,这与你发出update语句类似。
- 其他事务被阻止更新这些行,被阻止执行SELECT FOR SHARE,或者被阻止读取某些事务隔离级别下的数据。
- 一致性读忽略读视图中存在的记录上设置的任何锁。(记录的老版本无法被锁定;它们是通过对记录的内存副本应用撤销日志来重构的)
2.SELECT FOR UPDATE需要select权限,且至少需要 delete, lock tables, 或update权限中的一个;
当事务提交或回滚时,所有被 FOR SHARE 和 FOR UPDATE 设置的锁都会被释放。
【注意】锁定读只在禁用自动提交时有效(要么使用 START TRANSACTION 开启事务 或设置autocommit=0)