mysql 锁与MVCC的原理

本文探讨了锁在并发操作中的关键作用,如操作金额、库存管理和事务隔离。重点讲解了myisam和innodb表锁的区别,以及共享锁和排他锁在不同场景的应用。MVCC的原理和在ReadCommitted和RepeatableRead隔离级别中的作用被详细阐述,通过实例分析了不同索引条件下查询锁定策略。
摘要由CSDN通过智能技术生成

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:什么场景下用共享锁、排他锁?

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值