可重复读机制下,在备份时,会创建一个read view来隔离读与写操作,实现可重复读,但view不是和表一起变的吗,怎么保证隔离
(没学完时产生的疑问,已解决,留作纪念)
解释:
误认为read view是视图。read view 不是传统的视图,而是在事务执行期间,对数据库中活跃事务(未提交的事务)状态的一个快照。它记录了生成该视图时系统中活跃事务的列表,以及这些事务对数据修改的情况等相关信息。只要不更新快照,无论底层如何变化,都不改变。
read view实现了与表数据变化的解耦,这是实现可重复读的关键。
表级锁的问题
三种表级锁的作用
| 锁 | 允许 | 禁止 |
|---|---|---|
| 表共享读锁 | 多个读线程 | 写线程 |
| 表独占写锁 | 持锁的读写线程 | 其他读线程和写线程 |
| 共享MDL锁 | 多个读多个读元数据线程 | 写元数据线程 |
| 排他MDL锁 | 持锁的写元数据线程 | 其他读元数据和写元数据线程 |
| 意向共享锁(IS) | 1. 多个获取行共享锁的线程 2. 获取其他IS的线程 | 获取表独占写锁的线程 |
| 意向排他锁(IX) | 获取行排他锁的线程 | 获取表共享读锁的线程 |
注意:
能否访问某个表,不取决于“那个表是否被别人锁”,而是取决于“你自己有没有锁它”。
元数据锁(MDL)相关问题
1. 如何自动加锁的
- 对表CRUD时,自动加MDL读锁(共享)
- 对表结构改变时,自动加MDL写锁(排他)
把握关键:MDL是避免DDL和DML相互干扰,保证在对表CRUD时,表的结构不改变.
2.何谓元数据
暂时可简单理解为表结构
严格来说,元数据是关于数据的数据,它提供了对数据库中数据对象的描述、定义和管理信息,帮助数据库系统和用户理解、操作和维护数据。
包括表结构、索引、视图、用户和权限、数据库实例等相关数据。
意向锁
意向锁的目的是什么
为了避免DML在执行时,加的行锁与表锁的冲突,在InnoDB中引入了意向锁,使得表锁不用检查每行数据是否加锁,使用意向锁来减少表锁的检查。
行级锁
RR隔离级别下,使用了MVCC与临键锁(行锁和间隙锁的组合),为什么还会有幻读?
先说下MVCC与临键锁怎么解决大部分幻读
- MVCC(对快照读)
在RR级别下,MVCC解决了快照读(就是普通的select)的幻读问题,因为在事务开启时,会生成一个readview(之后一直用这个),readview加undolog版本链使每次快照读都找到事务开始时的数据。 - next-key lock(对当前读)
当前读(除了普通select以外的语句,因为这些语句的执行都要先获得到数据,包括 select … for update,下面以select… for update为例)
因为是当前读,快照读的read view就没用了,会出现幻读问题。
于是就有了间隙锁,再与行锁组合,把如下图的id大于2的部分锁住,这样插入语句就会阻塞,直到事务A提交。

两种方法小结:
快照读是利用MVCC的read view与版本链,使每次读的都是本事务开启时的数据
当前读则是利用临键锁让事务B的修改语句语句阻塞,直到事务A提交。
那RR隔离机制下还有什么情况会导致幻读呢?
- 事务A快照读查id=5,发现没有,事务B插入id=5并提交(这时A是查不到id=5的),这时事务A又自己更新了id=5,导致版本链的这条新的记录的事务id变成了事务A的id了,导致后面快照读可以读到这条id=5的数据。导致了幻读。所以MVCC不能完全避免幻读

- 事务A中先快照读,再当前读(期间B插入了数据),因为快照读不会加上临键锁,导致后面B插入数据的时候,不会阻塞,使得当前读可以查到新数据。
如何避免:开启事务后,尽快使用当前读,加上next-key lock。
为什么读已提交不能解决不可重复读
MVCC中,读已提交时,快照读在事务开始后,每次的读操作都会生成一个新的快照,所以此线程改变数据后,生成的快照也变了,自然之后查的不是事务开始时的数据。
锁与MVCC等是怎么解决并发事务问题的
TODO
并发事务可能会引发脏读(Dirty Read)、不可重复读(Non - Repeatable Read)和幻读(Phantom Read)这三个主要问题,数据库通常通过多种机制来解决这些问题,以下分别介绍:
1. 脏读的解决
- 锁机制:
- 共享锁与排他锁:使用共享读锁(S锁)和排他写锁(X锁)。当一个事务要读取数据时,获取共享锁,多个事务可以同时持有共享锁进行读取。而当一个事务要修改数据时,获取排他锁,此时其他事务不能获取任何锁,包括共享锁,从而防止其他事务读取到未提交的数据。例如,在银行转账场景中,事务T1对账户A进行取款操作,会先获取账户A数据的排他锁,在T1未提交前,事务T2若尝试读取账户A的数据,由于T1持有排他锁,T2无法获取共享锁,也就不会读到T1未提交的修改,避免了脏读。
- 行级锁与表级锁:无论是行级锁还是表级锁,都遵循上述共享锁和排他锁的规则。行级锁粒度更细,能在一行数据上进行锁定,并发性能相对较高;表级锁粒度大,对整个表进行锁定,虽然并发性能低,但实现简单。在实际应用中,数据库系统会根据具体情况选择合适的锁粒度来解决脏读问题。
- 事务隔离级别:
- 读已提交(Read Committed)及以上级别:在“读已提交”隔离级别下,一个事务只能读取到其他事务已经提交的数据。这意味着当一个事务读取数据时,数据库系统会检查数据的提交状态,只有已提交的数据才对当前事务可见。例如,事务T1修改了数据但未提交,此时事务T2在“读已提交”隔离级别下读取数据,看不到T1未提交的修改,从而避免了脏读。“可重复读(Repeatable Read)”和“串行化(Serializable)”隔离级别同样能避免脏读,因为它们对读操作的限制更严格。
2. 不可重复读的解决
- 事务隔离级别:
- 可重复读(Repeatable Read):在该隔离级别下,事务在开始时会生成一个数据快照,之后在事务执行期间,所有的读操作都基于这个快照。这就保证了在同一个事务内多次读取相同数据时,结果是一致的,不会因为其他事务的修改提交而改变。例如,事务T开始执行并读取账户余额为1000元,在事务T执行过程中,事务T’修改并提交了账户余额,但由于T处于“可重复读”隔离级别,T再次读取账户余额时,依然是1000元,不会出现不可重复读的情况。
- 串行化(Serializable):这是最严格的隔离级别,它通过强制事务串行执行,即一个事务执行完后另一个事务才开始,从而完全避免了不可重复读问题。在这种级别下,不存在并发事务对同一数据的竞争,也就不会出现一个事务内两次读取数据不一致的情况。但由于完全串行执行,性能较低,一般用于对数据一致性要求极高且并发量较低的场景。
- MVCC(多版本并发控制):
- 原理:MVCC通过为数据库中的数据行维护多个版本来实现并发控制。在“可重复读”隔离级别下,MVCC机制结合事务开始时生成的快照,使得事务内的读操作始终读取到快照对应的版本数据,而不受其他事务并发修改的影响。例如,数据初始版本为V1,事务T1对其修改后生成版本V2并提交,此时事务T2在“可重复读”隔离级别下读取数据,根据MVCC机制,T2读取的依然是V1版本的数据,从而避免了不可重复读。
3. 幻读的解决
- 事务隔离级别:
- 串行化(Serializable):如前文所述,串行化隔离级别强制事务串行执行,完全杜绝了并发事务之间的干扰。在这种情况下,不会出现幻读问题,因为不存在并发事务插入或删除数据导致当前事务两次查询结果集不同的情况。例如,事务T在查询某个范围内的数据,由于没有其他并发事务可以同时操作数据,所以T再次查询时,结果集不会发生变化,避免了幻读。
- 锁机制增强:
- 间隙锁(Next - Key Lock):在“可重复读”隔离级别下,MySQL使用间隙锁来解决幻读问题。间隙锁锁定的是索引记录之间的间隙,或者第一条记录之前、最后一条记录之后的间隙。例如,一个表中有索引值为1、3、5的记录,当事务T1查询索引值在1到5之间的数据并加锁时,不仅会锁住1、3、5这三条记录,还会锁住(1, 3)、(3, 5)这两个间隙,以及小于1和大于5的间隙。这样,其他事务就无法在这些间隙中插入新的数据,从而避免了幻读。假设事务T1查询年龄在20到30岁之间的用户,使用间隙锁后,其他事务不能在这个年龄区间对应的索引间隙中插入新用户记录,T1再次查询时,结果集就不会因为新插入的数据而改变。
- MVCC:使用快照与undolog的版本链,保证快照读所读取的一直是事务开启时的数据(但不能完全解决)。
readview和MVCC分析问题:
其实原理就一条,trx_id在m_ids里面就说明这条记录对应的事务还没提交,就不可以访问,如果没在m_ids里面且不大于max_trx_id就说明提交了,就可以访问了
自己改的可见,过去的已提交可见,未来的不可见,活跃的不可见

1095

被折叠的 条评论
为什么被折叠?



