位置: 编程技术 - 正文

细说MySQL死锁与日志二三事(mysql死锁的例子)

编辑:rootadmin

推荐整理分享细说MySQL死锁与日志二三事(mysql死锁的例子),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:mysql死锁的处理方法,mysql死锁的例子,mysql死锁的情况,mysql死锁的例子,mysql死锁分析,mysql解决死锁的4种基本方法,mysql解决死锁的4种基本方法,mysql解决死锁的4种基本方法,内容如对您有帮助,希望把文章链接给更多的朋友!

最近线上 MySQL 接连发生了几起数据异常,都是在凌晨爆发,由于业务场景属于典型的数据仓库型应用,白天压力较小无法复现。甚至有些异常还比较诡异,最后 root cause 分析颇费周折。那实际业务当中咱们如何能快速的定位线上 MySQL 问题,修复异常呢?下文我会根据两个实际 case,分享下相关的经验与方法。

Case1:部分数据更新失败

某天渠道同学反馈某报表极个别渠道数据为 0,大部分渠道数据正常。这个数据是由一个统计程序每天凌晨例行更新的,按理来说,要么全部正常,要么全部失败,那会是什么原因导致极个别数据异常呢?

首先我们能想到的自然是根据统计任务日志来看了,但是看了统计程序打印的日志没有发现诸如 SQL update 失败的异常描述,那当时的数据库究竟发生了什么呢?在查看 MySQL-server 日志之前,习惯性的看了下数据库状态:

恰好看到了凌晨这个 update 发生了死锁:

篇幅所限,上下文我这里省略了很多,从这段日志里可以看到,TRANSACTION 1 和 TRANSACTION 2 分别持有一定数量的行锁,然后又等待对方的锁,最后 MySQL 检测到 deadlock ,然后选择回滚了 TRANSACTION 1:Innodb目前处理死锁的方法是将持有最少行级排他锁的事务进行回滚。

那这里就有 3 个问题了:

1、innodb 行锁不是只锁一行?

因为这张表是 innodb 引擎的,InnoDB 支持行锁和表锁。而InnoDB行锁是通过给索引上的索引项加锁来实现的,这一点MySQL与Oracle不同,后者是通过在数据块中对相应数据行加锁来实现的。InnoDB这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁,会把所有扫描过的行都锁定!在实际应用中,要特别注意InnoDB行锁的这一特性,不然的话,可能导致大量的锁冲突,从而影响并发性能。由于MySQL的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但是如果是使用相同的索引键,是会出现锁冲突的。当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;另外间隙锁也会锁多行,InnoDB除了通过范围条件加锁时使用间隙锁外,如果使用相等条件请求给一个不存在的记录加锁,InnoDB也会使用间隙锁!

话都说到这了,那就看下咱们业务表的索引情况:

可以看到这张表的索引极不合理:有3个索引,但是 update 却没有完全的用上索引,导致 update 没有精确的用上索引,需要锁定多行范围数据,从而引发死锁。

知道原理后,咱们再精心构建一个四字段的组合索引即可让 update 精准的走 innodb 索引,实际上,我们更新索引后,这个死锁问题即得到了解决。

注:innodb不仅会打印出事务和事务持有和等待的锁,而且还有记录本身,不幸的是,它可能超过innodb为输出结果预留的长度(只能打印1M的内容且只能保留最近一次的死锁信息),如果你无法看到完整的输出,此时可以在任意库下创建innodb_monitor或innodb_lock_monitor表,这样innodb status信息会完整且每s一次被记录到错误日志中。如:create table innodb_monitor(a int)engine=innodb;,不需要记录到错误日志中时就删掉这个表即可。

2、回滚为什么只有部分 update 语句失败

回滚的话,为什么只有部分 update 语句失败,而不是整个事务里的所有 update 都失败?

这是因为咱们的 innodb 默认是自动提交的:

在多个 update 或 insert 语句情况下,每执行完一条 SQL,innodb 就立即 commit 一次以持久化变更,同时释放锁,这也正是本例中死锁回滚事务后只有极个别语句失败的原因。

需要注意的是,通常还有另外一种情况也可能导致部分语句回滚,需要格外留意。在 innodb 里有个参数叫:innodb_rollback_on_timeout

官方手册里这样描述:

细说MySQL死锁与日志二三事(mysql死锁的例子)

In MySQL 5.1, InnoDB rolls back only the last statement on a transaction timeout by default. If ?innodb_rollback_on_timeout is specified, a transaction timeout causes InnoDB to abort and roll back the entire transaction (the same behavior as in MySQL 4.1). This variable was added in MySQL 5.1..

解释:这个参数关闭或不存在的话遇到超时只回滚事务最后一个Query,打开的话事务遇到超时就回滚整个事务。

3、怎样降低 innodb 死锁几率?

死锁在行锁及事务场景下很难完全消除,但可以通过表设计和SQL调整等措施减少锁冲突和死锁,包括:

尽量使用较低的隔离级别,比如如果发生了间隙锁,你可以把会话或者事务的事务隔离级别更改为 RC(read committed)级别来避免,但此时需要把 binlog_format 设置成 row 或者 mixed 格式

精心设计索引,并尽量使用索引访问数据,使加锁更精确,从而减少锁冲突的机会;

选择合理的事务大小,小事务发生锁冲突的几率也更小;

给记录集显示加锁时,最好一次性请求足够级别的锁。比如要修改数据的话,最好直接申请排他锁,而不是先申请共享锁,修改时再请求排他锁,这样容易产生死锁;

不同的程序访问一组表时,应尽量约定以相同的顺序访问各表,对一个表而言,尽可能以固定的顺序存取表中的行。这样可以大大减少死锁的机会;

尽量用相等条件访问数据,这样可以避免间隙锁对并发插入的影响;

不要申请超过实际需要的锁级别;除非必须,查询时不要显示加锁;

对于一些特定的事务,可以使用表锁来提高处理速度或减少死锁的可能。

Case2:诡异的 Lock wait timeout

连续几天凌晨6点和早上8点 都分别有一个任务失败,load data local infile 的时候报 Lock wait timeout exceeded try restarting transaction innodb 的 Java SQL 异常,和平台的同学沟通得知,这是我们自己的业务数据库的 Lock 时间太短或者锁冲突的问题。但是回头一想不应该啊?这不一直好好的吗?而且基本都是单表单任务,不存在多人冲突。

甭管谁的问题,那咱们还是先看自己的数据库有没有问题:

默认 lock 超时时间 s,这个时间真心不短了,估计调了也没用,事实上确实死马当活马医的试了下没用。。。

而且这次 SHOW ENGINE INNODB STATUSG 也没出现任何死锁信息,然后又将目光转向 MySQL-server 日志,希望能从日志里看一看那个时刻前后数据究竟在做什么操作。这里先简单的介绍下MySQL日志文件系统的组成:

(a) error 日志:记录启动、运行或停止 mysqld 时出现的问题,默认开启。 (b) general 日志:通用查询日志,记录所有语句和指令,开启数据库会有 5% 左右性能损失。 (c) binlog 日志:二进制格式,记录所有更改数据的语句,主要用于 slave 复制和数据恢复。 (d) slow 日志:记录所有执行时间超过 long_query_time 秒的查询或不使用索引的查询,默认关闭。 (e) Innodb日志:innodb redo log、undo log,用于恢复数据和撤销操作。

从上面的介绍可以看到,目前这个问题的日志可能在 d 和 b 中,看了下 d 中没有,那就只能开启 b 了,但 b 对数据库的性能有一定损耗,由于是全量日志,量非常巨大,所以开启一定要谨慎:

我这里只是每天在出问题的前后半小时开启下全量日志,结果没有发现任何 MySQL-client 请求到我们的业务数据库!该日志格式如下,记录了所有的连接与命令:

那问题基本确定了,客户端请求都没到我们这边就抛出了上述的异常,和平台方再三沟通确认下,最后平台方查证是因为在执行插入前他们需要先从 SQL task 表取出 SQL 和更新 task 状态,结果这张表由于在整点存在大量 insert 和 update 并发,导致部分 SQL 等待 lock 超时了。。。

MySQL 日志分析脚本

由于凌晨是数据仓库的业务高峰,很多问题都是在这个时候爆发,一些诡异的问题往往是过了这个村就没这个店了,白天无法复现。如何能捕获我们关心的日志,便于快速的定位问题,这个是重中之重,这里我写了个小脚本,crontab 部署,可以选择时间范围开启,每分钟采样一次日志,需要说明的是 general log 没事别轻易开启,否则对数据库性能损耗较大。

标签: mysql死锁的例子

本文链接地址:https://www.jiuchutong.com/biancheng/348674.html 转载请保留说明!

上一篇:解决“无法启动mysql服务 错误1069”的方法(无法启动diagnostic policy service服务)

下一篇:寻找sql注入的网站的方法(必看)(寻找sql注入漏洞存在哪几个关键点)

  • 纳税义务发生时间 纳税期限
  • 个税扣除方法叫什么
  • 财务软件怎么样从捷软系统导出数据
  • 新成立的销售公司有哪些
  • 个人独资企业需要交什么税
  • 增值税零申报附加税怎么报
  • 房产税从租计征12%是年税率吗
  • 应付账款从质保开始算吗
  • 减免的教育费附加和地方教育费附加怎么做分录
  • 银行退回服务费怎么写分录
  • 自营工程领用原材料
  • 补交以前年度房产税如何记账
  • 企业员工无偿使用合法吗
  • 进项税额转出后企业所得税怎么处理?
  • 进项借方有余额怎么调账
  • 自然人税收申报显示申报失败:未选择纳税人
  • 成本费用分配的原则方法
  • 固定资产处置流程
  • 资金筹集业务的账务处理重点笔记
  • 哪些科目适用于借方多栏式
  • 试运营收入账务处理
  • 研发成功的产品卖出去怎么做账
  • 期末增值税如何结转
  • 如何防止程序被关闭
  • windows11怎么创建宽带拨号连接图标
  • 车船税收费标准2023年多少钱
  • win10怎么更换版本
  • php字符串赋值
  • 购物卡怎么给
  • 电脑密码解除步数怎么弄
  • 巴拉诺维奇市
  • 企业亏损注销税务会不会来查账
  • 关于固定资产抵扣的税务规定
  • 预加载的目的是什么
  • 未取得合法凭证税前扣除
  • 利息收入发票能抵扣吗
  • gridviewcolumn
  • 自制半成品属于原材料还是库存商品
  • 自然语言处理属于人工智能的哪个领域
  • trace命令详解
  • 未抵扣进项税额转出完整分录
  • 免税后的商品有什么优势?
  • 无形资产和固定资产减值准备可以转回吗
  • 前端面试题汇总.pdf
  • 帝国cms界面
  • python国内下载地址
  • 公司捐赠给个人公司要交税吗
  • 不开增值税发票的销售收入报税的操作流程是?
  • 对公账户分类及区别
  • sql 分组排名
  • 发票票种核定发票种类怎么选
  • 建筑企业印花税应税凭证名称
  • 权益法核算投资收益纳税调整
  • 公司前期装修费属于开办费吗
  • 个人所得税税前扣除凭证
  • 收到客户预付款会计分录
  • 固定资产报废处理中,最终的净损益应作为
  • 发票联遗失的证明怎么开
  • 资产减值损失和资产减值准备的区别
  • 会计凭证后面需要打勾的是
  • 帮客户支付的机票计入什么科目
  • 会计各种凭证的填写示例
  • mysql osc
  • Win7系统如何关闭自动更新
  • macbook系统快捷键
  • win8怎么玩帝国时代2
  • Linux常用命令的实验总结
  • centos的安装步骤
  • win7系统怎么设置电源
  • 摄像头挡住了
  • jQuery基于ajax操作json数据简单示例
  • python整数数字
  • js内存条
  • jquery 图片预览
  • nodejs怎么调试
  • python set集合取值
  • 事件传播路径
  • JavaScript中的数据类型
  • JavaScript的函数中只能写一条代码语句
  • 广告业发票税率是多少 可以抵扣多少
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设