解决线上数据库死锁,就是这么简单!

2021-02-26    分类: 网站建设

前几天,线上发生了一次数据库死锁问题,这一问题前前后后排查了比较久的时间,这个过程中自己也对数据库的锁机制有了更深的理解。

updateFundStreamId 执行的时候使用到的是 PRIMARY 索引。

updateStatus 执行的时候使用到的是 idx_seller_transNo 索引。

通过执行计划,我们发现 updateStatus 其实是有两个索引可以用的,执行的时候真正使用的是 idx_seller_transNo 索引。这是因为 MySQL 查询优化器是基于代价(cost-based)的查询方式。

因此,在查询过程中,最重要的一部分是根据查询的 SQL 语句,依据多种索引,计算查询需要的代价,从而选择最优的索引方式生成查询计划。

我们查询执行计划是在死锁发生之后做的,事后查询的执行计划和发生死锁那一刻的索引使用情况并不一定是相同的。

但是,我们结合死锁日志,也可以定位到以上两条 SQL 语句执行的时候使用到的索引。

即 updateFundStreamId 执行的时候使用到的是 PRIMARY 索引,updateStatus 执行的时候使用到的是 idx_seller_transNo 索引。

有了以上这些已知信息,我们就可以开始排查死锁原因及其背后的原理了。

通过分析死锁日志,再结合我们的代码以及数据库建表语句,我们发现主要问题出在我们的 idx_seller_transNo 索引上面:

  1. KEY `idx_seller_transNo` (`seller_id`,`fund_transfer_order_no`(20)) 

索引创建语句中,我们使用了前缀索引,为了节约索引

    那么为什么 fund_transfer_order_no 的前 20 位相同会导致死锁呢?

    加锁原理

    我们就拿本次的案例来看一下 MySQL 数据库加锁的原理是怎样的,本文的死锁背后又发生了什么。

    我们在数据库上模拟死锁场景,执行顺序如下:

    我们知道,在 MySQL 中,行级锁并不是直接锁记录,而是锁索引。索引分为主键索引和非主键索引两种:

    • 如果一条 SQL 语句操作了主键索引,MySQL 就会锁定这条主键索引。
    • 如果一条语句操作了非主键索引,MySQL 会先锁定该非主键索引,再锁定相关的主键索引。

    主键索引的叶子节点存的是整行数据。在 InnoDB 中,主键索引也被称为聚簇索引(Clustered Index)。

    非主键索引的叶子节点的内容是主键的值,在 InnoDB 中,非主键索引也被称为非聚簇索引(Secondary Index)。

    所以,本文的示例中涉及到的索引结构(索引是 B+ 树,简化成表格了)如图:

    死锁的发生与否,并不在于事务中有多少条 SQL 语句,死锁的关键在于:两个(或以上)的 Session 加锁的顺序不一致。

    文章标题:解决线上数据库死锁,就是这么简单!
    文章链接:https://www.cdcxhl.com/news/103129.html

    成都网站建设公司_创新互联,为您提供App开发动态网站外贸网站建设关键词优化网站策划电子商务

    广告

    声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联

    网站托管运营