速来报名!AICon北京站鸿蒙专场~ 了解详情
写点什么

Mysql 锁:灵魂七拷问

  • 2020-03-15
  • 本文字数:1892 字

    阅读完需:约 6 分钟

Mysql 锁:灵魂七拷问

一、缘起

假设你想给别人说明,Mysql 里面是有锁的,你会怎么做?


大多数人,都会开两个窗口,分别起两个事务,然后 update 同一条记录,在发起第二次 update 请求时,block,这样就说明这行记录被锁住了:


二、禁锢

问题来了,貌似只有显式的开启一个事务,才会有锁,如果直接执行一条 update 语句,会不会加锁呢?


比如直接执行:


update t set c = c + 1 where id = 1;
复制代码


这条语句,前面不加 begin,不显式开启事务,那么 Mysql 会不会加锁呢?


直觉告诉你,会。


但是为什么要加锁?


给你五秒钟,说出答案。



学过多线程和并发的同学,都知道下面这段代码,如果不加锁,就会有灵异事件:


i++;
复制代码


开启十个线程,执行 1000 次这段代码,最后 i 有极大可能性,会小于 1000。


这时候,用 Java 的套路,加锁:


synchornize {  i++;}
复制代码


问题解决。


同理,对于数据库,你可以理解为 i,就是数据库里的一行记录,i++ 这段代码,就是一条 update 语句,而多线程,对应的就是数据库里的多个事务。


既然对内存中 i 的操作需要加锁,保证并发安全,那么对数据库的记录进行修改,也必须加锁。


这道理很简单,但是很多人,未曾想过。

三、释然

为什么大家都喜欢用第一部分里的例子来演示 Mysql 锁?


因为开两个事务,会 block,够直观。


那么问题又来了,为什么会 block,或者说,为什么 Mysql 一定要等到 commit 了,才去释放锁?


执行完一条 update 语句,就把锁释放了,不行吗?


举个例子就知道 Mysql 为什么要这么干了:



一开始数据是:{id:1,c:1};


接着事务 A 通过 select … for update,进行当前读,查到了 c=1;


接着它继续去更新,把 c 更新成 3,假设这时候,事务 A 执行完 update 语句后,就把锁释放了;


那么就有了第 4 行,事务 B 过来更新,把 c 更新成 4;


结果到了第 5 行,事务 A 又来执行一次当前读,读到的 c,竟然是 4,明明我上一步才把 c 改成了 3…


事务 A 不由的发出怒吼:我为什么会看到了我不该看,我也不想看的东西?!


事务 B 的修改,居然让事务 A 看到了,这明目张胆的违反了事务 ACID 中的 I,Isolation,隔离性(事务提交之前,对其他事务不可见)。


所以,结论:Mysql 为了满足事务的隔离性,必须在 commit 才释放锁。

四、自私的基因

有人说,如果我是读未提交( Read Uncommited )的隔离级别,可以读到对方未提交的东西,是不是就不需要满足隔离性,是不是就可以不用等到 commit 才释放锁了?


非也。


还是举例子:



事务 A 是 Read Committed,事务 B 是 Read Uncommitted;


事务 B 执行了一条 update 语句,把 c 更新成了 3


假设事务 B 觉得自己是读未提交,就把锁释放了


那这时候事务 A 过来执行当前读,读到了 c 就是 3


事务 A 读到了别的事务没有提交的东西,而事务 A,还说自己是读已提交,真是讽刺


根因在于,事务 B 非常自私,他觉得自己是读未提交,就把锁释放了,结果让别人也被“读未提交”


显然,Mysql 不允许这么自私的行为存在。


结论:就算你是读未提交,你也要等到 commit 了再释放锁。

五、海纳百川

都知道 Mysql 的行锁,分为 X 锁和 S 锁,为什么 Mysql 要这么做呢?


这个简单吧,同样可以类比 Java 的读写锁:


It allows multiple threads to read a certain resource, but only one to write it, at a time.


允许多个线程同时读,但只允许一个线程写,既支持并发提高性能,又保证了并发安全。

六、凤凰涅磐

最后来个难点的。


假设事务 A 锁住了表 T 里的一行记录,这时候,你执行了一个 DDL 语句,想给这张表加个字段,这时候需要锁表吧?但是由于表里有一行记录被锁住了,所以这时候锁表时会 block。


那 Mysql 在锁表时,怎么判断表里有没有记录被锁住呢?


最简单暴力的,遍历整张表,遍历每行记录,遇到一个锁,就说明表里加锁了。


这样做可以,但是很傻,性能很差,高性能的 Mysql,不允许这样的做法存在。


Mysql 会怎么做呢?


行锁是行级别的,粒度比较小,好,那我要你在拿行锁之前,必须先拿一个假的表锁,表示你想去锁住表里的某一行或者多行记录。


这样,Mysql 在判断表里有没有记录被锁定,就不需要遍历整张表了,它只需要看看,有没有人拿了这个假的表锁。


这个假的表锁,就是我们常说的,意向锁。


Intention locks are table-level locks that indicate which type of lock (shared or exclusive) a transaction requires later for a row in a table


很多人知道意向锁是什么,但是却不知道为什么需要一个粒度比较大的锁,不知道它为何而来,不知道 Mysql 为何要设计个意向锁出来。


知其然,知其所以然。

七、参考文献


2020-03-15 20:19772

评论

发布
暂无评论
发现更多内容

Java垃圾回收全过程

百度搜索:蓝易云

Java 云计算 Linux 运维 云服务器

k8s说一下deployment,statefulset,daemonset的区别

百度搜索:蓝易云

Linux Kubernetes 运维 Deployment statefulset

听说测试“有手就行 ”?华为20年测试老兵干货分享!

华为云开发者联盟

物联网 华为云 华为云开发者联盟 DTSE Tech Talk 企业号2024年4月PK榜

云起无垠入选《网络安全优质初创企业推荐(2024版)》研究报告

云起无垠

Bigasoft Audio Converter for Mac(音频转换器)v5.8.0.8857中文激活版

iMac小白

蚂蚁可信智能获“2023 吴文俊人工智能科技进步一等奖”

可信AI进展

人工智能

Bigasoft Video Downloader Pro for Mac v3.27中文版

iMac小白

linux IP地址原理,分类,子网划分,VLAN,TRUNK详解

百度搜索:蓝易云

云计算 Linux 运维 IP 云服务器

什么是单元测试,和集成测试有什么区别?

派大星

单元测试 Java 面试题 互联网大厂面试

对 NGINX、Kong 和 Amazon 的 API 管理解决方案进行基准测试:它们能否交付实时 API?

NGINX开源社区

nginx SaaS API GigaOm Kong Cloud

万字长文,聊聊我在京东锦礼成长的这一年

京东零售技术

后端 技术人生 企业号 4 月 PK 榜

淘系接口推荐:淘宝天猫实时商品数据采集接口

tbapi

淘宝商品API接口 淘宝商品详情数据采集

DBeaverUE for Mac(数据库管理软件)v24.0.1旗舰激活版

iMac小白

漫谈测试策略

阿里技术

效率 测试 质量 测试策略

Penpad Season 2 质押突破350ETH,还有望获Scroll生态空投

鳄鱼视界

解密数仓的SQL ON ANYWHERE技术

华为云开发者联盟

大数据 华为云 SQL语句 华为云开发者联盟 企业号2024年4月PK榜

Animate 2024 for mac(An2024)v24.0.2中文激活版

iMac小白

NetShred X for Mac 强大缓存清理工具

iMac小白

Final Draft for Mac 剧本文字处理软件

iMac小白

AnyRec Screen Recorder for Mac(Mac专业的屏幕录制软件)v1.1.22激活版

iMac小白

Dumping grounds

EchoZhou

Expression English

解析基础设施即代码:重新定义云管理

SEAL安全

DevOps 云原生 IaC

MySQL的多层SP中Cursor的m_max_cursor_index相关BUG分析

GreatSQL

昆仑万维发布面向人工智能时代的六条人才宣言

新消费日报

探索元宇宙:数字化未来的新前沿

天津汇柏科技有限公司

元宇宙

淘系接口推荐:淘宝天猫实时商品评论数据采集接口

tbapi

淘宝商品评论接口 淘宝评论API 淘宝商品评论采集

Mysql 锁:灵魂七拷问_文化 & 方法_柳树_InfoQ精选文章