为了账号安全,请及时绑定邮箱和手机立即绑定

mysql 锁与MVCC的原理

标签:
MySQL

Q:为什么要用锁?
1、操作金额、红包、用户余额、订单状态的时候
2、判断一条记录存不存在,不存在的话执行操作
3、减商品库存的时候(并发大的时候,很容变负数)

图片描述

myisam 表锁

innodb 行锁

共享锁(S锁):假设事务T1对数据A加上共享锁,那么事务T2可以读数据A,不能修改数据A。
select * from table where id = ? lock in share mode

排他锁(X锁):假设事务T1对数据A加上共享锁,那么事务T2不能读数据A,不能修改数据A。
select * from table where id = ? for update
update、delete、insert

记录锁:行锁 (锁索引)
gap锁:间隙锁,锁定一个范围,但不包含记录本身
next-key锁:Gap Lock+Record Lock,锁定一个范围、索引之间的间隙,并且锁定记录本身;目的是为了防止幻读
图片描述

MVCC

事务隔离机制

name 脏读 不可能重复读 幻读
Read uncommitted
Read committed ×
Repeatable read × ×
Serializable × × ×

这里一定要区分 不可重复读 和 幻读:

不可重复读的重点是修改:
同样的条件的select, 你读取过的数据, 再次读取出来发现值不一样了
例子:A事务结算时,查询金额足够刚好500,去结算流程,此时B事务刚好减去金额200,A事务在结算最后扣除金额时发现不够

幻读的重点在于新增或者删除:
同样的条件的select, 第1次和第2次读出来的记录数不一样
例子:A事务插入一条name='xiao’的记录,判断不存在,继续下面的逻辑;此时B事务插入一条name="xiao"的记录并提交,A事务就报错了

如果没有MVCC的话,实现RC和RR的方式分别是:
RC:更新的时候加锁,其他事务任何读都要等待
RR:读的时候加锁,其他事务只能读不能更新

MVCC相关原理

Mutli-Version Concurreny Control,多版本并发控制,读不加锁,读写不冲突。应用于 Read Commited 和 Repeatable read 两个事务隔离级别。

快照读: 普通的select,不加锁,读取记录的可见版本。
当前读:select…lock in share mode/for update、update、insert、delete,读取记录最新版本,并加锁。

MVCC的实现原理主要是依赖记录中的 3个隐式字段,undo日志 ,Read View 来实现的。所以我们先来看看这个三个point的概念
地址:https://blog.csdn.net/SnailMann/article/details/94724197

隐藏字段

图片描述

undo日志

undo log主要分为两种:
insert undo log
代表事务在insert新记录时产生的undo log, 只在事务回滚时需要,并且在事务提交后可以被立即丢弃
update undo log
事务在进行update或delete时产生的undo log; 不仅在事务回滚时需要,在快照读时也需要;所以不能随便删除,只有在快速读或事务回滚不涉及该日志时,对应的日志才会被purge线程统一清除

比如一个有个事务插入persion表插入了一条新记录,记录如下,name为Jerry, age为24岁,隐式主键是1
图片描述

现在来了一个事务1对该记录的name做出了修改,改为Tom
图片描述

来了个事务2修改person表的同一个记录,将age修改为30岁
图片描述

Read View

Read View就是事务进行快照读操作的时候生产的读视图(Read View),在该事务执行的快照读的那一刻,会生成数据库系统当前的一个快照,记录并维护系统当前活跃事务的ID(当每个事务开启时,都会被分配一个ID, 这个ID是递增的,所以最新的事务,ID值越大)

我们可以把Read View简单的理解成有三个全局属性

trx_list
一个数值列表,用来维护Read View生成时刻系统正活跃的事务ID
up_limit_id
记录trx_list列表中事务ID最小的ID
low_limit_id
ReadView生成时刻系统尚未分配的下一个事务ID,也就是目前已出现过的事务ID的最大值+1

首先比较DB_TRX_ID < up_limit_id, 如果小于,则当前事务能看到DB_TRX_ID 所在的记录,如果大于等于进入下一个判断
接下来判断 DB_TRX_ID 大于等于 low_limit_id , 如果大于等于则代表DB_TRX_ID 所在的记录在Read View生成后才出现的,那对当前事务肯定不可见,如果小于则进入下一个判断
判断DB_TRX_ID 是否在活跃事务之中,trx_list.contains(DB_TRX_ID),如果在,则代表我Read View生成时刻,你这个事务还在活跃,还没有Commit,你修改的数据,我当前事务也是看不见的;如果不在,则说明,你这个事务在Read View生成之前就已经Commit了,你修改的结果,我当前事务是能看见的

在RC隔离级别下,是每个快照读都会生成并获取最新的Read View;
在RR隔离级别下,则是同一个事务中的第一个快照读才会创建Read View, 之后的快照读获取的都是同一个Read View

RC的情况:

事务A 事务B
开启事务 开启事务
快照读(无影响)查询金额为500 快照读查询金额为500
更新金额为400 ×
提交事务 ×
- select 快照读金额为400
- select lock in share mode当前读金额为400

RR的情况:

事务A 事务B
开启事务 开启事务
快照读(无影响)查询金额为500 ×
更新金额为400 ×
提交事务 ×
- select 快照读金额为500
- select lock in share mode当前读金额为400

案例分析

select * from t1 where id = 10

delete from t1 where id = 10

组合一:id主键
图片描述

组合二:id唯一索引
图片描述

组合三:id非唯一索引+RC
图片描述

组合四:id非唯一索引+RR
图片描述

组合五:id无索引+RC
图片描述

组合六:id无索引+RR
图片描述

Q:select * from t1 where id > 10 for update ?
图片描述

Q:什么场景下用共享锁、排他锁?

共享锁:我查完之后我自己不改,我也不希望别人去改。比如:查询商品价格并且生成订单

排他锁:我查完之后我自己还要改,不允许别人读。比如:修改订单状态,修改余额

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
PHP开发工程师
手记
粉丝
42
获赞与收藏
356

关注作者,订阅最新文章

阅读免费教程

  • 推荐
  • 1
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消