栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

MySQL实战45讲 笔记(7)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

MySQL实战45讲 笔记(7)

行锁功过:怎么减少行锁对性能的影响?
  • 什么是行锁?
  • 两阶段锁
  • 死锁和死锁检测

写在最前,本篇文章来源于对MySQL实战45讲的笔记,主要是为了帮助自己理解。如果同时还能对其他人有所裨益,那就更好不过了。如果有谬误的地方,还请不吝指出。

本文并非对文章的直接复制,并且肯定有理解不到位的情况,如果希望系统地学习,还是要去官网支持原作者。

注意:最好拥有一定的MySQL基础再来看本系列文章,可以去b站搜索动力节点的mysql基础教程,或者翻看我做的走进MySQL系列(笔记做的并不是特别详尽,仅作为参考)

什么是行锁?

顾名思义,行锁就是指,对某一行进行更新时,这一行的记录会被锁住,等到更新完成以后,其它事务才能对这一行进行更新。

行锁是针对一条记录的。

**MySQL的行锁是在引擎层实现的,而MyISAM不支持行锁。**只能使用表锁。

两阶段锁

行锁是什么时候取得和释放的呢?

取得肯定是在更新时,然而释放锁却是在事务结束以后。这也就是所谓的两阶段锁协议。

为什么呢?
因为不确定这个更新是否一定会被提交,万一要回滚呢?
如果这时候被另一个事务获取到锁,并更新了的话,即使这个事务回滚了,更新依旧被保留了下来。

根据这个特点,在实际情况中,我们尽量将并发度高的语句放在事务最后执行,这样操作和释放锁的间隔时间会被压到最短。最大程度减少了事务间的锁等待。

死锁和死锁检测

行锁还会导致死锁,怎么出现的?
联想一下java中出现死锁的情况

  • 事务A对记录1更新;同时,事务B对记录2更新;
  • 事务A更新完成,并请求记录2的行锁;事务B更新完成,并请求记录1的行锁。但由于AB现在还未提交,双方都无法取得;
  • 要想获得记录2的行锁,必须等待事务B提交,然而事务B提交必须等到事务B获取记录1的行锁。此时死锁就产生了。

两种解决方案:

  1. 超时等待:通过innodb_lock_wait_timeout来设置,默认值50s。如果出现死锁,要等待50s。
  2. 死锁检测:发现死锁后,主动回滚死锁链条中的某一个事务,使得其它事务继续执行。通过设置innodb_deadlock_detect 为on开启逻辑。

50s显然不是一个能接受的数字,那死锁检测又有什么问题呢?
每一个事务被锁的时候,都要去看它所依赖的线程有没有被锁住,最后判断是否出现了循环等待(死锁)。比如:A被B锁住,A就去查看锁住B的线程,如此直到判断出为死锁,或者直到链条的最后一环才会终止

如果有多个事务更新同一行记录会怎么样?
这显然不会发生死锁,但由于行锁阻塞,线程依旧会判断是否由于自己的加入造成了死锁。

最直观的想法肯定是去检查现在持有这个锁的线程是否被锁住了,如果没被锁住,那说明可以安心获取锁,因为不会发生死锁。但可惜的是,由于行锁等待会形成一个队列,即使当前线程释放锁,也不一定轮得上这个线程,对于这个线程来说,问题还在于:自己获取锁以后,会不会和我后面的线程造成冲突呢。所以它要去访问处于自己后面的线程以查看是否会出现死锁。所以总的复杂度为O(n^2)。

如果有1000个并发线程更新同一行,那么死锁检测就是100万量级,CPU利用率很高,但却执行不了几个事务。

那怎么解决这类问题呢?

  1. 如果确保不会出现死锁,可以临时关掉死锁检测。风险是,死锁可以回滚,通过业务重试后就没问题了,是业务无损的。但如果关掉死锁检测,且出现了死锁,会长时间等待超时,是业务有损的。
  2. **控制并发度。**比如,使得一行记录同一时间最多只有10个线程更新,那么死锁检测的成本很低。可以通过客户端做并发控制,但如果客户端很多,即使每个客户端并发线程很少,汇总到服务端后,峰值也会很高。
    所以,并发控制最好在服务端,思路是对于同行的更新,不在引擎内等待,即不让他进行死锁检测,而是在外部进行等待。

除此之外,还可以将一行数据放在多个记录上(即子账户的概念),当需要总数据时,可以汇总这些子记录,以得到总体值。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/282531.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号