Files
rikako-note/mysql/mysql底层/mvcc.md
2023-01-10 21:59:13 +08:00

49 lines
5.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 多版本并发控制
* ## 多版本并发控制解决的问题:
* 在多个事务并发访问资源时,可能会发生并发安全问题。为了解决多个事务同时对资进行读写的问题,可以采用如下解决方案:
* 多事务同时对资源进行读取:并发安全
* 多事务同时对资源进行写操作:非并发安全,可以对写操作进行加锁来解决并发安全问题
* 多事务同时对资源进行读和写操作:非并发安全
1. 对读操作和写操作都加锁:性能较差
2. 通过MVCC机制来解决并发读写的问题对写操作加锁并用MVCC机制来使读操作读到先前的值
* ## 多版本并发控的概念:
* MVCC通过数据库行的多个版本管理来实现数据库的并发控制。当一个写事务正在更新数据行的值时通过MVCC机制可以在另一个事物对数据行进行读操作时读取到被写事务更新之前的值。
* 通过MVCC机制可以更好的解决事务在读-写操作同时发生时的性能问题。MVCC可以避免在写事务时另一个读事务必须等待当前写事务释放排他锁而是可以通过MVCC读取资源被写事务修改之前的值。
* ## mysql中读操作的种类
* 当前读:读取数据库中当前的值,为了解决并发安全问题,需要对读操作进行加锁操作,可以尝试加排他锁或者共享锁,当当前事务对资源进行写操作时,读事务会阻塞直到写事务释放锁:
```mysql
# 为select语句加上共享锁
select * from user where... lock in share mode
# 为select语句加上排他锁
select * from user where... for update
```
* 快照读mysql中默认select语句的读取方式当当前事务对资源进行读操作时如果另一个事务正在对资源进行写操作那么读操作并不会阻塞而是会读取资源被写事务修改之前的值
* ## MVCC原理
* 隐藏字段对于使用innodb存储引擎的表其聚簇索引包含两个隐藏字段
1. trx_id每个事务对每条记录进行更改时该trx_id字段都会记载最后一次修改该行记录的事务id
2. roll_pointer指向undo log中关于修改该条数据的记录
* ReadViewReadView是MVCC机制中事务对数据进行快照读时产生的一个读视图。
* ReadView原理
* 当事务开启时会产生当前数据库系统的一个快照innodb为每个事务构造了一个数组用来维护当前系统中活跃事务的ID活跃事务为当前开启了但是尚未提交的事务id
* ReadView中保存了如下信息
* creator_trx_id创建当前ReadView的事务id
* trx_ids记录创建失误时当前mysql系统中活跃的事务id集合(已经开始但是还没有被提交的事务id)
* up_limit_idtrx_ids中最小的事务id
* low_limit_id表示生成ReadView中当前系统应该分配给下一个事务的id
* MVCC仅针对读已提交和可重复读的情况在读已提交的情况下事务中每执行一次select操作ReadView都会重复生成而在可重复读的隔离级别下事务仅仅会在第一次select操作时生成ReadView
* MVCC细节
* 当select语句想要对一条记录中的数据进行读取时首先会查看记录的trx_id是是否对当前事务的读操作是可见的判断事务是否可见的标准如下所示
* 如果记录的trx_id大于low_limit_id那么说明在创建ReadView时对记录进行修改的事务还没有被创建当然修改对当前读事务来说是不可见的
* 如果trx_id小于up_limit_id那么说明对该记录进行修改的事务id小于创建ReadView时最小的活跃事务id在创建ReadView时修改记录的事物已经被提交修改对当前事务来说可见
* 如果trx_id位于up_limit_id和low_limit_id之间那么
* trx_id如果与trx_ids中保存的某个活跃事务id相同那么说明在创建ReadView时修改事务的id尚未被提交修改对当前失误不可见
* 如果trx_id与trx_ids中每个活跃事物id都不相同那么修改事务在创建ReadView事物之时已经被提交修改对当前事务可见
* 根据上述规则如果想要读取的记录trx_id对当前事务来说可见那么获取该事务id所对应的数据值如果trx_id对当前ReadView来说不可见那么沿着roll_pointer沿着undo log向前寻找知道找到对当前ReadView可见的事务id如果undo log中所有的事务id对当前ReadView来说都不可见那么对当前事物来说数据表中该条记录并不可见
* ## MVCC与幻读问题
* MVCC仅在读已提交和可重复读的情况下起作用而关于幻读问题MVCC仅在可重复读的隔离级别下解决。
* 在可重复读的隔离级别下,ReadView仅仅在第一次select语句时生成故而在同一事务中多次读取之间其他事务插入了新数据那么该新数据对应的trx_id在readView创建时应该处于活跃或者未创建的状态故而对ReadView所对应事物来说即使其他事务插入了新数据那么新插入的数据也不可见。
* 而在读已提交的隔离级别下ReadView在每次读操作的情况下都会被创建故而在两次读操作之间如果新的数据被插入那么新插入的数据对后一次读操作创建的ReadView来说是已经提交的数据是可见的。故而MVCC在读已提交的隔离级别下并不能够解决幻读的问题。