惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

L
LangChain Blog
V
V2EX
爱范儿
爱范儿
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Martin Fowler
Martin Fowler
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Apple Machine Learning Research
Apple Machine Learning Research
WordPress大学
WordPress大学
有赞技术团队
有赞技术团队
宝玉的分享
宝玉的分享
Last Week in AI
Last Week in AI
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
罗磊的独立博客
小众软件
小众软件
Vercel News
Vercel News
博客园 - 司徒正美
阮一峰的网络日志
阮一峰的网络日志
V
Visual Studio Blog
J
Java Code Geeks
P
Proofpoint News Feed
MongoDB | Blog
MongoDB | Blog
B
Blog
美团技术团队
量子位

博客园 - 何锦彬

没开电脑! 只用手机和QQ聊天, 让openClaw帮我"手搓"个AI新闻网站 AI编程从 “猜你想要” 到 “精准生成”, 基于Qoder的Spec驱动开发初探. Java团队Cursor最佳实践:3分钟构建「零泄漏」AI开发环境 再不用手写Commit!AI自动总结代码变更,Git提交效率 MCP赋能,给Cursor插上“外挂翅膀”:实战操作数据库 一个老程序员, 两个小时能用corsur做出什么样的东西 会用 AI 的工程师,效率已经拉开差距了 - “ 我们曾经引以为傲的编码能力,正在被改写。” 1-1, 一个简单的mysql 安装教程,基于mysql 5.7解压版本. Hystrix 服务的隔离策略对比,信号量与线程池隔离的差异 为什么阿里的dubbo注册中心要放弃zookeeper, 而用Nacos? 和spring cloud/boot 学习如何管理自己的组件 利用jmeter+JAVA对RPC的单接口(dubbo接口等)进行性能测试 spring mvc中,直接注入的HttpServletRequst是否安全呢? 基础篇系列,JAVA的并发包 - 锁 撸基础篇系列,JAVA的NIO部分 1, 本地缓存的实现以及遇到的问题 JAVA的那些数据结构实现总结,实现,扩容说明 JAVA中的数据结构 - 真正的去理解红黑树 对把JDK源码的一些注解,笔记
1-2 【包子mysql系列】, 对mysql的innoDB加锁分析
何锦彬 · 2018-11-21 · via 博客园 - 何锦彬

innoDB的事务,是基于锁来实现的,用到事务不自然就会用到锁,而如果对锁理解的不通透,很容易造成线上问题。

数据库加锁的分析,和事务的引擎,隔离级别,索引,主键索引都有关系,

如果去考虑引擎和各种隔离级别的话,就会很复杂了,所以下面都是基于innoDB和RR的隔离级别进行分析:

表结构:

内容:

1 , 根据主键更新

如果根据主键来行数

事务A

事务B

 

update user set name='ce1' where id='1';

update user set name='ce3' where Id='3';

同时执行,都成功

update user set name='ce1' where id='1';

update user set name='ce3' where userId='10003';

B更新失败,直至:Lock wait timeout

结论,如果根据非主键来更新,会把整个表进行锁定,无法 进行更新操作。

注:只要是根据主键索引来更新,哪怕事务A没命中主键,也不会锁定整个表

2,根据非索引非主键更新

事务A

事务B

 

update user set name='ce1' where userId='10001';

update user set name='ce3' where Id='3';

或者

update user set name='ce3' where userId='10003'

都会失败,如果非索引,直接锁表

3, 如果在userId 列上加入普通唯一索引

修改成

再更新

事务A

事务B

 

update user set name='ce1' where userId='10001';

update user set name='ce3' where Id='3';

或者

update user set name='ce3' where userId='10003'

都会成功,如果有唯一索引,也是能成功行数,互相不影响

4, 如果在userId 列上加入普通非唯一索引 (重点探讨)

把userId改成非唯一索引:

记录内容如下:

+----+--------+------+

| id | userId | name |

+----+--------+------+

| 1 | 10001 | ce1 |

| 2 | 10002 | ce2 |

| 3 | 10001 | ce3 |

| 4 | 10004 | ce4 |

+----+--------+------+

再相同操作

事务A

事务B

 

update user set name='ce1' where userId='10001';

update user set name='ce3' where Id='3';

B失误执行失败,显然id=3的这行也被锁住了

其实最终还是按主键锁住的记录 id=1和id=3的记录

非唯一索引与普通索引,更一步的区别是GAP锁

gap锁是用于解决幻读的存在,演示

把记录修改成,如:

id为pk. userId为Normal key

A事务

B事务

结果

begin;

update user set name='ce22' where userId='100020';

   
 

insert into user (userId,name) values('100021','tttt');

直至事务失败超时

1, 首先GAP锁针对的是insert操作

2, 当更新userId='100020'时,会锁住两边的记录区间,防止幻读的存在。

3, 锁是作用在普通索引上,但由于索引是由B+树存储,那么锁住的是两边的区间,防止insert

GAP锁为什么不是锁住一条记录,而是锁住一个区间呢? 

附上疑问: https://www.oschina.net/question/867417_2289606

其实:

GAP锁是解决幻读存在的,如当 delete时就必须锁住区间了

A事务

B事务

 

begin;

delete from user where userId='888888';

   
 

insert into user (userId,name) values('100021','tttt');

OK, 可以插入

 

insert into user (userId,name) values('100041','tttt');

插入超时

可见,这个GAP锁,锁住的是100040~无穷大 的记录

死锁的产生分析

1, 两条语句产生的死锁

id = pk, userId= key

最简单的。两条语句互相更新等待

begin;

update user set name='ce1' where userId='100010';

begin;

update user set name='ce2' where userId='100020';

update user set name='ce2' where userId='100020';

update user set name='ce1' where userId='100010';

 

最简单的死锁

2, 由于gap锁,删除一台不存在的记录

如,先删除一条记录,然后插入一条记录, 如果记录GAP锁冲突,两个事务容易互为死锁。如:

A事务

B事务

begin;

delete from user where userId='100020';

(Query OK, 1 row affected)

begin;

delete from user where userId='565656';

insert into user (userId,name) values('100041','tttt');

 

insert into user (userId,name) values('100019','tttt');

结果直接抛出:

ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

分析:

A事务 delete 加入gap锁【100010,100020】, 第二段【100020,100030】

B事务 delete加入gap 锁【100040,无穷大】

然后A事务插入,获取插入意向锁时B事务的GAP锁被阻塞

B事务插入,获取插入意向锁时时被A事务的GAP锁阻塞

结果死锁