美文网首页我爱编程mysql
MySQL 非主键索引更新引起的死锁

MySQL 非主键索引更新引起的死锁

作者: 杰哥长得帅 | 来源:发表于2018-05-25 00:29 被阅读266次

表结构如下:

CREATE TABLE `user_item` (
  `id` BIGINT(20) NOT NULL,
  `user_id` BIGINT(20) NOT NULL,
  `item_id` BIGINT(20) NOT NULL,
  `status` TINYINT(4) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_1` (`user_id`,`item_id`,`status`)
) ENGINE=INNODB DEFAULT CHARSET=utf-8

SQL 语句如下:

update user_item set status=1 where user_id=? and item_id=?

原因分析

MySQL 的事务支持与存储引擎有关,MyISAM 不支持事务,INNODB 支持事务,更新时采用的是行级锁。这里采用的是 INNODB 做存储引擎,意味着会将 update 语句做为一个事务来处理。前面提到行级锁必须建立在索引的基础,这条更新语句用到了索引 idx_1,所以这里肯定会加上行级锁

行级锁并不是直接锁记录,而是锁索引,如果一条 SQL 语句用到了主键索引,mysql 会锁住主键索引;如果一条语句操作了非主键索引,mysql 会先锁住非主键索引,再锁定主键索引

这个 update 语句会执行以下步骤:
1、由于用到了非主键索引,首先需要获取 idx_1 上的行级锁
2、紧接着根据主键进行更新,所以需要获取主键上的行级锁
3、更新完毕后,提交,并释放所有锁。

如果在步骤 1 和 2 之间突然插入一条语句:update user_item .....where id=? and user_id=?,这条语句会先锁住主键索引,然后锁住 idx_1

蛋疼的情况出现了,一条语句获取了 idx_1 上的锁,等待主键索引上的锁;另一条语句获取了主键索引上的锁,等待 idx_1 上的锁,这样就出现了死锁

解决方法

只要让更新操作中带有主键即可,也就是让获取锁的顺序一致即可:

  1. 先获取需要更新的记录的主键
select id from user_item where user_id=? and item_id=?
  1. 逐条更新
select id from user_item where user_id=? and item_id=?  
for (Long id : idList) {  
    userItemDAO.updateStatus(id, userId, 1);  
}
update user_item set status=? where id=? and user_id=?
  1. 这样貌似解决了,都是对单条进行操作,都是先获取主键上的锁,再获取 idx_1 上的锁
    不过这个解决方案与先前的更新语句不一样,先前的更新语句(update user_item set status=1 where user_id=? and item_id=?)对所有记录的更新在一个事务中,采用循环更新后并不在同一个事务中,所以在 for 循环外面还得开一个事务。伪代码:
public Object doInTransaction(Transaction t) {
    try {
        for (Long id : idList) {
            userItemDAO.updateStatus(id, userId, 1);
        }
        return null;
    } catch (DAOException e) {
        t.rollback();
    } catch (Exception e) {
        r.rollback();
    }
}

总结

在采用 INNODB 的 MySQL 中,更新操作默认会加行级锁,行级锁是基于索引的,在分析死锁之前需要查询一下 mysql 的执行计划,看看是否用到了索引,用到了哪个索引,对于没有用索引的操作会采用表级锁。如果操作用到了主键索引会先在主键索引上加锁,然后在其他索引上加锁,否则加锁顺序相反。在并发度高的应用中,批量更新一定要带上记录的主键,优先获取主键上的锁,这样可以减少死锁的发生

相关文章

  • mysql死锁-非主键索引更新引起的死锁

    [mysql死锁-非主键索引更新引起的死锁] 背景:最近线上经常抛出mysql的一个Deadlock,细细查来,长...

  • MySQL 非主键索引更新引起的死锁

    表结构如下: SQL 语句如下: 原因分析 MySQL 的事务支持与存储引擎有关,MyISAM 不支持事务,INN...

  • MySQL非主键更新

    根据非主键id更新流程 对当前索引加间隙锁或行锁后,若有其他线程通过主键索引进行更新将出现死锁 https://w...

  • 索引

    Mysql索引模型采用B+树 主键索引与非主键索引 从图中不难看出,根据叶子节点的内容,索引类型分为主键索引和非主...

  • MySql优化

    MySqlInnoDB引擎支持事务 非主键索引都会存主键索引值非主键索引检索:先走非主键索引,再走主键索引 表即主...

  • 蓝信移动面经03-26

    自我介绍 mysql 事务机制 acid 引擎索引为什么用B+树主键索引和非主键索引区别select。。。wher...

  • MySQL--索引

    MySQL索引 查看索引 创建索引 创建唯一索引 创建主键索引 删除索引 删除主键 MySQL视图 创建视图 删除...

  • 主键索引就是聚集索引?MySQL 索引类型大梳理

    @[toc]之前松哥在前面的文章中介绍 MySQL 的索引时,有小伙伴表示被概念搞晕了,主键索引、非主键索引、聚簇...

  • MySQL索引

    聚簇索引和非聚簇索引 只有Innodb有聚簇索引,MyISAM引擎没有聚簇索引。 主键一定是聚簇索引,MySQL的...

  • mysql索引(三)聚集索引与非聚集索引(辅助索引)

    Mysql中常用的两个存储引擎innodb和mysiam的索引是不同的。 聚集索引就是以主键创建的索引 非聚集索引...

网友评论

    本文标题:MySQL 非主键索引更新引起的死锁

    本文链接:https://www.haomeiwen.com/subject/vfrsjftx.html