首页 > 数据库 >mysql中行锁、两阶段锁协议、死锁以及死锁检测

mysql中行锁、两阶段锁协议、死锁以及死锁检测

时间:2022-11-21 13:01:41浏览次数:50  
标签:事务 行锁 中行 mysql 并发 死锁 线程 检测


行锁

MySQL的行锁都是在引擎层实现的,但是 MyISAM 不支持行锁,意味着并发控制只能使用表锁,同一张表任何时刻只能被一个更新在执行,影响到业务并发度。InnoDB 是支持行锁的,这也是 MyISAM 被 InnoDB 替换的重要原因之一。

行锁就是针对数据库中表的行记录的锁,这很好理解,比如事务 A 更新了一行,而这时候,事务 B 也要更新一行,则必须等事务 A 的操作完成后才能更新。

两阶段锁 (Two-Phase Locking――2PL)

在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,需要等事务结束时才释放,这就是两阶段锁协议,分为加锁阶段和解锁阶段,所有的 lock 操作都在 unlock 操作之后。

两段锁协议规定所有的事务应遵守的规则:
  ① 在对任何数据进行读、写操作之前,首先要申请并获得对该数据的封锁。
  ② 在释放一个封锁之后,事务不再申请和获得其它任何封锁。
  即事务的执行分为两个阶段:
  第一阶段是获得封锁的阶段,称为扩展阶段。
  第二阶段是释放封锁的阶段,称为收缩阶段。

 

 

遵循两段锁协议的事务有可能发生死锁。
 

  此时事务T1 、T2同时处于扩展阶段,两个事务都坚持请求加锁对方已经占有的数据,导致死锁。
  为此,又有了一次封锁法。一次封锁法要求事务必须一次性将所有要使用的数据全部加锁,否则就不能继续执行。因此,一次封锁法遵守两段锁协议,但两段锁并不要求事务必须一次性将所有要使用的数据全部加锁,这一点与一次性封锁不同,这就是遵守两段锁协议仍可能发生死锁的原因所在。

 

死锁

如下图所示,事务 A 在等待事务 B 释放 id = 2 的行锁,而事务 B 在等待 事务 A 释放 id = 1 的行锁,事务 A 和事务 B 在互相等待对方的资源释放,就是进入了死锁状态。

mysql中行锁、两阶段锁协议、死锁以及死锁检测_死锁检测

在并发系统中,不同线程出现循环资源依赖,涉及的线程都在等待别的线程释放资源时,就会导致这几个线程进入无限等待的状态,成为死锁。

当进入死锁状态时,有下列 2 种策略:

  1. 通过 innodb_lock_wait_timeout 来设置超时时间,InnoDB 中默认值是 50s,第一个被锁住的事务 A 等待超过 50s 才会超时退出,其他事务才能得以执行,对于在线服务来说,这个等待时间往往是无法接受的。如果设置太短 1s,可能有的事务只是简单的锁等待,就被退出了,会出现很多误伤。
  2. 通过设置 innodb_deadlock_detect = on,发起死锁检测,发现死锁之后主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。比如回滚事务 A,让事务 B 继续执行。

死锁检测

正常情况下使用第 2 种策略的,但是快速发现死锁并进行处理,也是有额外负担的。你可以想象一下这个发现死锁的过程:每当一个事务被锁的时候,就要看看他依赖的线程有没有被别人锁住,判断是否出现了循环等待,也就是死锁。

假设有 1000 个并发线程,都要同时更新同一行,第 1 个线程来的时候检测数是 0;第 2 个线程来的时候,需要检测【线程1】有没有被别人锁住;第 3 个线程来的时候,需要检测【线程1,线程2】有没有被其他线程锁住,以此类推,第 n 个线程来的时候,检测数是 n - 1,所以总的检测数是 0 + 1 + 2 + 3 + 。。。+ (n - 1) = n(n -1)/2,所以时间复杂度应该是 O(n²)。

也就是 1000 个并发线程同时操作同一行,那么死锁检测操作就是 100 万这个量级的,虽然最终检测的结果是没有死锁,但是这期间要消耗大量的 CPU 资源,就会看到 CPU 利用率很高,但是每秒却执行不了几个事务。

那么怎么处理这种热点行更新导致的性能问题呢?

  1. 1、关掉死锁检测,等待超时
  2. 如果你能确定这个业务一定不会出现死锁,可以临时把死锁关掉,这种操作带有一定风险,因为业务设计的时候一般不会把死锁当成一个严重错误,毕竟出现死锁了,就回滚,然后通过业务重试一般就没有问题了,这是业务无损的,而关掉死锁检测意味着可能出现大量超时,这是业务有损的。
  3. 2、控制并发度
  4. 比如同一行最多只有 10 个线程在更新,这样死锁检测的成本很低,一个直接的想法就是在客户端做并发控制。可是如果客户端有 600 个,即使每个客户端控制到只有 5 个线程,汇总到数据库服务端以后,峰值并发数也有可能达到 3000。因此这个并发控制要在服务端,比如引入中间件来实现,在进入引擎之前排队。
  5. 3、热点key拆分
  6. 将一行改成逻辑上的多行来处理,比如影院的账户余额等于 10 行记录的值总和,这样每次给影院账户加金额的时候,随机选取其中一条记录来加,冲突概率变为原来的1/10,减少锁的等待个数,也就减少了死锁检测的 CPU 消耗。这个方案看上去是无损的,但是需要根据业务逻辑做详细设计。如果账户余额减少,比如退票,这个时候就要考虑当一部分行记录变为 0 的时候,代码要有特殊处理。

 

 

标签:事务,行锁,中行,mysql,并发,死锁,线程,检测
From: https://blog.51cto.com/u_6353447/5873556

相关文章

  • MySQL45讲笔记
    MySQL45https://funnylog.gitee.io/mysql45/原系列目录:01讲基础架构:一条SQL查询语句是如何执行的主要介绍MySQL分为Server层和存储引擎层两部分:Server层包括连接......
  • 5分钟搞定 SQL Server 到 MySQL 数据迁移和同步
    简述SQLServer是一个值得信赖的老牌数据库系统,自从1988年由Microsoft、Sybase和Ashton-Tate三家公司共同推出之后就一直不断迭代更新。而如今我们提到SQLServer......
  • mysql hint介绍
    在mysql中,hint指的是“查询优化提示”,会提示优化器按照一定的方式来生成执行计划进行优化,让用户的sql语句更具灵活性;Hint可基于表的连接顺序、方法、访问路径、并行度......
  • mysql优化
    思路:1、尝试单表查询,验证索引是否正常试了一下单表查询B是可以走主键索引,正常,排出索引问题2、尝试优化SQL修改了一下SQL,将leftjoin分别改为innerjoin,join和子查询,几......
  • MySQL联结表
    简介保存数据时往往不会将所有数据保存在一个表中,而是在多个表中存储。联结表就是从多个表查询数据,其实就是多表操作。联结(JOIN)是一种机制,用来在一条SELECT语句中关......
  • mysql5.7以上的启停命令
    1、启动mysqlserversystemctlstartmysqld#启动程序systemctlenablemysqld#开机自运行systemctlstatusmysqld#查看状态2、查看初始密码......
  • 原来用 MySQL 也可以做全文检索
    我是风筝,公众号「古时的风筝」,专注于Java技术及周边生态。文章会收录在JavaNewBee中,更有Java后端知识图谱,从小白到大牛要走的路都在里面。有朋友聊到他们的系统......
  • 基于Docker 部署 MySQL 主从复制
    Docker的安装可参考这篇文章:Linux安装Docker;这里的主从复制是基于GTID(GlbalTransationIdentifier)全局事务标识符的。GTID是MySQL5.6新加入的一项技术,GTID是......
  • MYSQL创建与root一样权限用户
    1.创建账号createuserbackupdb@'localhost'identifiedby'123456';#或createuserbackupdb@'%'identifiedby'123456';2.设置权限grantusageon*.*to......
  • Linux自动备份MySql数据库
    1.创建备份数据库文件的根目录:#mysql备份文件目录cd/data/db/mysql2.创建各个数据库的备份文件目录  3.编写shell脚本3.1 在usr/sbin目录下分别创建数据......