MySQL

MySQL

MVCC

基本解释

AI解释:

MySQL中的多版本并发控制(MVCC)是一种用于管理数据库并发操作的技术,允许多个事务并发地执行而不会相互阻塞,从而提高了数据库的性能和效率。MVCC的基本思想是通过维护数据的多个版本,来实现事务隔离和数据一致性。

前提:事务隔离级别(4种)、并发问题(3个)

多版本并发控制是Innodb引擎特有的技术,只要为了提高数据库并发性能,处理读写冲突,这里的读指的是快照读,不是当前读,当前读就是加锁(悲观锁),MVCC相当于乐观锁。

快照读:就是我们平常使用select查询

select * from user where id = 1

当前读:就是使用锁

selct * from user where id = 1 SHARE MODE; 共享锁
selct * from user where id = 1 FOR UPDATE; 排他锁

具体实现

  1. 隐藏字段

    • trx_id : 当前事务id select事务id都是0
    • roll_pointer : undo log中记录的指针 这个指针可以找到undo log中的各个版本数据
  2. undo log

    事务日志 :相当于每个事务操作后会记录不同版本,不同规则可以查到不同版本的数据,其中roll_pointer就是找到每个版本的记录

    image-20240721182404099

  3. ReadView
    事务启动时,InnoDB创建一个Read View,用于确定哪些事务的修改是当前事务可见的。Read View包含以下信息:

    • creator_trx_id ,创建这个 Read View 的事务 ID。

    • trx_ids ,表示在生成ReadView时当前系统中活跃的读写事务的 事务id列表 。

    • up_limit_id ,活跃的事务中最小的事务 ID。

    • low_limit_id ,表示生成ReadView时系统中应该分配给下一个事务的 id 值。low_limit_id 是系

      统最大的事务id值,这里要注意是系统中的事务id,需要区别于正在活跃的事务ID。

ReadView规则

  • 如果trx_idcreator_trx_id相同,说明就是当前事务进行的操作,可以读
  • 如果trx_ids小于up_limit_id,说明是已经提交的事务,可以读
  • 如果trx_id大于low_limit_id,说明记录(事务)是在创建ReadView之后改的,不能读
  • 如果trx_idup_limit_idlow_limit_id范围里
    • 如果trx_id正好在ReadViewtrx_ids里,说明事务还没提交,不能读
    • 如果trx_id不在trx_ids里,说明事务都已提交,可以读

解决的问题

读已提交

每次select都生成一个ReadView,所以会出现不可重复读和幻读问题

可重复读

只在第一次select生成一个ReadView,所以可以解决不可重复读

例子

现在有两个事务10,20在执行

# Transaction 10
BEGIN;
UPDATE student SET name="李四" WHERE id=1;
UPDATE student SET name="王五" WHERE id=1;
# Transaction 20
BEGIN;
# 更新了一些别的表的记录
...

此刻,表student 中 id 为 1 的记录得到的版本链表如下所示:

image-20240721185542928

# 使用READ COMMITTED隔离级别的事务
BEGIN;
# SELECT1:Transaction 10、20未提交
SELECT * FROM student WHERE id = 1; # 得到的列name的值为'张三'
# 之后,我们把 事务id 为 10 的事务提交一下:

# Transaction 10
BEGIN;
UPDATE student SET name="李四" WHERE id=1;
UPDATE student SET name="王五" WHERE id=1;
COMMIT;

# 然后再到 事务id 为 20 的事务中更新一下表 student 中 id 为 1 的记录:
# Transaction 20
BEGIN;
# 更新了一些别的表的记录
...
UPDATE student SET name="钱七" WHERE id=1;
UPDATE student SET name="宋八" WHERE id=1;

此时undo log版本链:

image-20240721185840597

读已提交 READ COMMITTED 查找id=1

# 使用READ COMMITTED隔离级别的事务
BEGIN;

# SELECT1:Transaction 10、20均未提交
SELECT * FROM student WHERE id = 1; # 得到的列name的值为'张三'

# SELECT2:Transaction 10提交,Transaction 20未提交
SELECT * FROM student WHERE id = 1; # 得到的列name的值为'王五'
  • 如果trx_idcreator_trx_id相同,说明就是当前事务进行的操作,可以读
  • 如果trx_id小于up_limit_id,说明是已经提交的事务,可以读
  • 如果trx_id大于low_limit_id,说明记录(事务)是在创建ReadView之后改的,不能读
  • 如果trx_idup_limit_idlow_limit_id范围里
    • 如果trx_id正好在ReadViewtrx_ids里,说明事务还没提交,不能读
    • 如果trx_id不在trx_ids里,说明事务都已提交,可以读
读已提交

每次select都会创建ReadView -----trx_id

select1: 10和20都未提交

这里trx_id是有三个(20,10,8),creator_trx_id是0,up_limit_id是10,low_limit_id 是20,trx_ids是[10,20]

​ 用上面的规则,自己理解一下 其实就是只能访问比活跃事务小的 或者在范围里切不相等的所以只有8符合,可以读到trx_id=8那个版本

select2: 10事务提交 20未提交

重新生成ReadView trx_id是[20],其他相同

​ 这里trx_id是有三个(20,10,8),creator_trx_id是0,up_limit_id是10,low_limit_id 是20,trx_ids是[20]

​ 这里20不可以,10直接就可以了,所以可以读到trx_id = 10这个版本

可重复读

只有第一次select时生成一个ReadView

# 使用REPEATABLE READ隔离级别的事务
BEGIN;

# SELECT1:Transaction 10、20均未提交
SELECT * FROM student WHERE id = 1; # 得到的列name的值为'张三'

# SELECT2:Transaction 10提交,Transaction 20未提交
SELECT * FROM student WHERE id = 1; # 得到的列name的值仍为'张三'

这里不详细解释了,相当于两次读trx_ids都是[10,20] 所以读到的都是trx_id等于8那个版本

@TODO
//MySQL可重复读也解决了幻读(后续扩展)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值