系列文章

一、原来一条select语句在MySQL是这样执行的《死磕MySQL系列 一》

二、一生挚友redo log、binlog《死磕MySQL系列 二》

三、MySQL强人“锁”难《死磕MySQL系列 三》

获取MySQL各种学习资料

前言

下边两幅图还熟悉吧!就是第三期文章中的前言,但上一期文章并未提及死锁,只是引出了全局锁、表锁的概念。本期文章将继续聊聊锁的内容。

Lock wait timeout exceeded; try restarting transaction

Deadlock found when trying to get lock; try restarting transaction

一、行锁

行锁的锁粒度最小,发送锁冲突的概率最低,并发度也最高。

问题:MySQL的所有存储引擎都支持行锁吗?

不是的,MySQL中只有Innodb存储引擎才支持行锁,其它的并不支持,MyIsam存储引擎也只支持表锁。

所以Myisam存储引擎只能使用表锁来解决并发,表锁开销小,加锁快,锁定粒度大,发生锁冲突的概率最高,并发度最低。

问题:锁粒度指的是什么?

这种名词不能只记名字,需要知道其代表的含义。锁粒度指的是加锁的范围。

上期文章讲的全局锁锁的是整库、表锁锁定的全表、行锁指的是锁定某一行或某个范围的数据。

问题:如何加行锁?

Innodb存储引擎在执行update、delete、insert语句时会隐式加排它锁,而对于select不会加任何锁。

同样也可以手动加锁。

共享锁:select * from tableName where id = 100 lock in share more

排它锁:select * from tableName where id = 100 for update

共享锁、排它锁也被称之为读锁、写锁。读锁与读锁之间不互斥,读锁与写锁、写锁与写锁之间是互斥的。

问题:为什么要加锁?

MySQL事务的四大特性分别是原子性、隔离性、一致性、持久性,当你了解完事务的四大特性之后就发现都是为了保证数据一致性为最终目的的。

常说一句话有人地方就有江湖,放在MySQL中是有锁的地方就有事务。

所以说加锁就是为了保证当事务结束后,数据库的完整性约束不被破坏,从而确保数据一致性。

二、两阶段锁

问题:两阶段锁是什么?

说实话,这个名字属实很唬人,猛然间你有没有想到另一个名词两阶段提交。这里回忆一下,两阶段提交是确保redo log跟binlog同时提交成功,若有一方提交失败则回滚。

在Innodb存储引擎中,行锁是在需要的时候加上的,但并不是不需要了就直接释放的,而是要等到事务结束才释放。

案例:解释两阶段锁

上图中MySQL1客户端开启事务并执行了两条update语句,紧接着MySQL2开启另一个事务执行update语句,那么此时MySQL的更新语句会执行成功吗?

答案肯定是不能的。

这个结论取决于MySQL1事务在执行完两条 update 语句后,持有哪些锁,以及在什么时候释放。你可以验证一下:MySQL2事务 update 语句会被阻塞,直到MySQL1事务 执行 commit 之后,才能继续执行。

万事有因必有果,有头必有尾,锁是开启事务后添加的也需提交事务后解除。

现在你理解了两阶段锁,那么试想一下对你在写代码有什么帮助吗?

三、理解死锁

这幅图是咔咔在2019年画的,当时用这种方式来解释死锁对于一部分伙伴来说属实有点绕。

错误的理解:之前在一个博文中看到对死锁是这样解释的

现实中这样的案例比比皆是,家里有两个小孩,给老大冲了一杯奶,这时老二过来也想喝。但奶嘴只有一个,此时老二只能处于等待状态,让老大先喝完。这个就是死锁。

不要把锁等待跟死锁一同对待,锁等待是,一个事务中的语句添加了共享锁,另一个事务开启了排它锁。此时就需要等待共享锁的释放,这个过程是锁等待。而死锁是两个事务互相等待对方。

四、优化你的代码尽量防止死锁

知道两阶段锁后,在以后的代码实现中要把最可能造成锁冲突也就是死锁的语句放到最后边。

问题:如何理解放到最后边这句话?

这样一个业务场景。

每到中午吃饭时间都是好几个人一起出去,吃饭得付钱吧!复现一下这个流程。

1.你给商家付了10块钱,这笔钱从你的余额中扣。

2.给商家的账户添加10元。

3.记录一条交易日志。

在这个过程中可得知进行了两次update操作,一次insert操作。使用为了保证交易的原子性数据的一致性此时必须得把三个操作放到一个事务。

在这三个操作中最容器造成锁冲突的就是第2步给商家的账户添加钱。

所以在编码过程中需要把第2步放到最后一步执行,保证在同样结果下锁住的时间最短。这样可以在编码的程度上尽量保证事务之间锁等待,提高事务并发度。

五、解释死锁的两种方案

第一种方式

MySQL已经给咱们提供好了,使用参数innodb_lock_wait_timeout来设置超时时间。若等待时间超过设置的值则返回超时错误。

在MySQL8.0版本中此值默认为50s,意味着当出现死锁以后,被锁住的线程需要50s才会自动退出,然后其它线程才会继续执行。这个等待时间一般是无法接受的。

但设置时间太短会造成很多锁等待的语句直接返回超时,造成严重误伤。

重要的话再说一遍:“不要把锁等待跟死锁一同对待,锁等待是,一个事务中的语句添加了共享锁,另一个事务开启了排它锁。此时就需要等待共享锁的释放,这个过程是锁等待。而死锁是两个事务互相等待对方。

第二种方式

另一个种方式,同样MySQL也给提供了一个参数innodb_deadlock_detect,默认值为on,意思是当发现死锁后,MySQL主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。

检测死锁的流程是当一个事务被堵住时,就要看它所在的线程是否被别的线程锁住,如若没有则继续找下一个线程进行检测,最后判断是否出现了循环等待,也就是死锁。

过程示例:新来的线程F,被锁了后就要检查锁住F的线程(假设为D)是否被锁,如果没有被锁,则没有死锁,如果被锁了,还要查看锁住线程D的是谁,如果是F,那么肯定死锁了,如果不是F(假设为B),那么就要继续判断锁住线程B的是谁,一直走知道发现线程没有被锁(无死锁)或者被F锁住(死锁)才会终止

问题:平时在开发中使用那种方案呢?

存在必合理,一般情况还是采用第二种方式,这种方式在有死锁时是能够快速进行处理的。

作为开发者肯定听过一句这样的话要么用空间换时间,要么用时间换空间。两者只可兼一种。

这种方式虽可以非常迅速的处理死锁问题,同样也会带来额外的负担。

思考:带来了那些额外的负担?

假设你负责的业务都需要更新同一行数据。

此时按照第二种方式,当发现死锁后,主动回滚死锁链条的某一个事务,那么,每一个进来被堵住的线程,都要判断是不是由于自己的加入导致死锁,这个时间复杂度是O(n)的操作。

假设有1000个线程都在更新同一行,操作的数据量是100W,检测出来死锁消耗资源还不怕,若最终检测结果没有死锁,这个期间消耗的CPU资源是非常高的。

就如何解决这种问题再进行谈论一下。

六、如何解决热点数据的更新

为什么要聊这个问题

使用了第二种方案来解决死锁,热点数据死锁检测会非常消耗CPU(每一个进来被堵的线程都会检测是不是由于自己的加入导致的死锁,有可能是锁等待,但还是需要做判断,所以非常消耗CPU),所以针对这个问题进行简单讨论一下。

咔咔在其它资料中看到有三种方案。

1.关闭死锁检测
2.控制并发度
3.修改MySQL源码对于更新同一行数据,在进入引擎之前排队。这样就不会出现大量的死锁检测

方案一:关闭死锁检测不考虑

这种方式会出现大量的超时,降低了用户体验,一般情况死锁不会对业务产生严重错误,毕竟出现死锁,数据大不了回滚即可。

方案二:控制并发度

可以把商家账户分散多个,所有的账户之和为账户余额。

例如分了10个子账户,那么出现更新同一行数据的概率就降低了10倍,这种方式在业务处理时需要简单处理一下。防止账户余额为0时用户发起退款的逻辑处理。

这种方式还是很建议大家使用的,从设计上降低死锁发生。

方案三:修改MySQL源码

大多数公司连DBA都没有,何谈存在可以修改MySQL源码的人,这种对于企业的成本是非常大的,而且也没那个必要。

修改MySQL源码想要实现的功能是当更新同一行数据时,在进入存储引擎之前排队。

这种方案用队列完全可以解决,所以并不需要从根上解决这个问题。

七、总结

本期从行锁出发引出了两阶段锁,明白了事务提交后才会释放锁。

死锁的产生,如何从代码的角度来减少死锁的产生。

MySQL也给提供了两种方案来解决死锁问题,对于这两种方案咔咔也给了不同的观点。根据自己的情况来使用。

在这期文章中并没有演示死锁案例,在后边的文章中咔咔会给大家列举几种典型的死锁案例。

坚持学习、坚持写作、坚持分享是咔咔从业以来所秉持的信念。愿文章在偌大的互联网上能给你带来一点帮助,我是咔咔,下期见。

S 锁与 X 锁的爱恨情仇《死磕MySQL系列 四》的更多相关文章

  1. MySQL强人“锁”难《死磕MySQL系列 三》

    系列文章 一.原来一条select语句在MySQL是这样执行的<死磕MySQL系列 一> 二.一生挚友redo log.binlog<死磕MySQL系列 二> 前言 最近数据库 ...

  2. Menu与ActionBar的爱恨情仇

    最近在开发一款音乐播放器,在开发过程中遇到了一点小麻烦,通过android API搞清楚了Menu与ActionBar的爱恨情仇,写了个小Demo祭奠一下那些年我们陷进去的坑,有不对的地方请大神们批评 ...

  3. web移动端fixed布局和input等表单的爱恨情仇 - 终极BUG,完美解决

    [问题]移动端开发,ios下当fixed属性和输入框input(这里不限于input,只要可以调用移动端输入法的都包括,如:textarea.HTML5中contenteditable等),同时存在的 ...

  4. 注解:大话AOP与Android的爱恨情仇

    转载:大话AOP与Android的爱恨情仇 1. AOP与OOP的区别 平时我接触多的就是OOP(Object Oriented Programming面向对象).AOP(Aspect Oriente ...

  5. 除了love和hate,还能怎么表达那些年的“爱恨情仇”?

    实用英语 帮你全面提高英语水平 关注 童鞋们每次刷美剧的时候,相信都会被CP感满满的男女主角虐得体无完肤吧. 可是,一到我们自己表达爱意或者恨意的时候,却苦于词穷,只会用love, like, hat ...

  6. 对json的爱恨情仇

    本文回想了对json的爱恨情仇. C++有风险,使用需慎重. 本文相关代码在:http://download.csdn.net/detail/baihacker/7862785 当中的測试数据不在里面 ...

  7. String、StringBuilder、StringBuffer的爱恨情仇

    第三阶段 JAVA常见对象的学习 StringBuffer和StringBuilder类 (一) StringBuffer类的概述 (1) 基本概述 下文以StringBuffer为例 前面我们用字符 ...

  8. [转帖]探秘华为(一):华为和H3C(华三)的爱恨情仇史!

    探秘华为(一):华为和H3C(华三)的爱恨情仇史! https://baijiahao.baidu.com/s?id=1620703498823290828&wfr=spider&fo ...

  9. Tidyverse|数据列的分分合合,爱恨情仇

    Tidyverse|数据列的分分合合,爱恨情仇 本文首发于“生信补给站”Tidyverse|数据列的分分合合,一分多,多合一 TCGA数据挖掘可做很多分析,前期数据“清洗”费时费力但很需要. 比如基因 ...

随机推荐

  1. 树莓派修改默认pi帐号亲测有效

    # 树莓派修改默认pi帐号亲测有效### 1.我的树莓派机型:3B+,系统:Raspbian桌面标准版,连接的屏幕:电视机..###2.打开树莓派LX终端,快捷键:Ctrl+Alt+t ###3.输入 ...

  2. 使用 VSCode 开发调试 STM32 单片机尝试

    使用 VSCode 开发调试 STM32 单片机尝试 本文记录基于 Windows + DAP-Link 开发 STM32F103C8T6 的实践过程,其他操作系统或芯片应该也只是大同小异的问题. 注 ...

  3. k8s garbage collector分析(2)-处理逻辑分析

    garbage collector介绍 Kubernetes garbage collector即垃圾收集器,存在于kube-controller-manger中,它负责回收kubernetes中的资 ...

  4. Struts2 S2-061 远程命令执行漏洞复现(CVE-2020-17530)

    0x01 漏洞简介 Struts在某些情况下可能存在OGNL表达式注入漏洞,如果开发人员使用了 %{-} 语法进行强制OGNL解析,某些特殊的TAG属性可能会被双重解析.攻击者可以通过构造恶意的OGN ...

  5. deepin(debian)中双网卡上内外网的设置方法(通过NetworkManager运行脚本)

    国产良心操作系统deepin,界面好看,反应速度快,开箱即用,深度商店里有非常多好用的linux.windows软件,其windows软件通过crossover进行运行,还可以运行一些安卓的apk程序 ...

  6. c++ 的学习 第二集函数的重载之3 -利用IDA分析bebug里面

    1. 对项目右击,在文件资源管理器中打开文件夹 2.看debug里面的.exe     这个文件 函数的真实的名字 打开.exe文件就是还是显示,,, 3.debug模式有太多的断点信息还有许多不精简 ...

  7. ARC106E-Medals【hall定理,高维前缀和】

    正题 题目链接:https://atcoder.jp/contests/arc106/tasks/arc106_e 题目大意 \(n\)个员工,第\(i\)个在\([1,A_i]\)工作,\([A_i ...

  8. Dubbo与Zookeeper简单理解

    理论 在<分布式系统原理与范型>一书中有如下定义:"分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像单个相关系统 "; 分布式系统是由一组通过网络进行通信. ...

  9. Docker里面没有你期望的命令、甚至没有yum怎么办?

    分享小知识点 跟大家分享一个实用的小知识点 有时候在docker容器里面不仅没有你期望的那些常用的命令,甚至没有yum命令让你去安装那些常用的命令 怎么办呢?不要慌! 没有yum命令说明这个容器的系统 ...

  10. 微服务Cloud整体聚合工程创建过程

    1.父工程创建及使用 使用idea开发工具,选择File-new- project ,在选项中选择Maven工程,选择jdk版本1.8,勾选maven-archetype-site,点击next,输入 ...