NVIDIA 初创加速计划,免费加速您的创业启动 了解详情
写点什么

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:19664

评论

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

王兴的失败观

池建强

成功 王兴 创业失败启示录

一千座5G工厂的花苞

脑极体

机器学习 Machine Learning- 吴恩达Andrew Ng 第5~15课总结 John 易筋 ARTS 打卡 Week 47

John(易筋)

ARTS 打卡计划

SSL / TLS协议解析!什么是SNI? SNI 识别?

明儿

云厂商下一块必争之地就是它了!

Serverless Devs

Serverless 云原生

前端项目上传图片,压缩,拍照图片旋转解决方案

Vue js canvas axios

来了!这份阿里P7大佬梳理的Java注解和反射精髓笔记,信息量过大

飞飞JAva

Java

异步编程的几种方式,你知道几种?

xcbeyond

Java 异步编程 5月日更

从SPACE矩阵,看5G究竟是否在走向成功?

脑极体

强!上线3天获10w浏览量,京东T8纯手码Redis缓存手册,我粉了

飞飞JAva

redis

凭借师兄甩给我的通关秘籍,顺利拿到字节Offer

学Java关注我

Java 编程 架构 面试

阿里P7大佬!王者级讲解ConcurrentHashMap源码,码农:太透彻了

牛哄哄的java大师

Java ConcurrentHashMap

云原生下的灰度体系建设

阿里巴巴云原生

容器 运维 云原生 k8s 监控

Java程序员如何在“黄金五年”实现最大价值?

学Java关注我

Java 编程 架构 互联网 计算机

常见流媒体服务器方案对比分析

liuzhen007

音视频 5月日更

Nginx如何配置Http、Https、WS、WSS?

冰河

nginx 负载均衡 反向代理 https HTTP

实战排查由于系统负载引起的服务响应异常

Coder的技术之路

高并发 性能调优 线上问题

GreenPlum中的资源队列

数据社

greenplum 5月日更

被解救的代码 - 代码即服务时代来了!

Serverless Devs

阿里云 Serverless 云原生

云原生的进一步具象化

阿里巴巴云原生

大数据 容器 云原生 监控 中间件

边缘计算与云计算的故事

攻城先森

云计算 边缘计算 5月日更

如何判断企业赚不赚钱?

石云升

创业 财务分析 5月日更

网络攻防学习笔记 Day10

穿过生命散发芬芳

5月日更 网络攻防

☕【Java技术之旅】如何彻底认识AQS的原理(上篇)

洛神灬殇

Java AQS JVM JUC 5月日更

太赞了!美团大牛强推的Spring事务笔记,上线仅1天就获赞上万

飞飞JAva

Java 事务spring

重磅!数字人民币接入支付宝!

CECBC

数字人民币

高德 Serverless 平台建设及实践

Serverless Devs

阿里云 Serverless 云原生

差点扛不住了,阿里巴巴支付宝面试 5 轮暴击,终获 Offer

Java架构师迁哥

工业制造业亟需数字化转型,区块链可以发挥哪些价值?

CECBC

区块链

Yii2反序列化RCE 新POP链

Thrash

数据工作者必备工作技能:数据治理

博文视点Broadview

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