You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

多线程批量插入MySQL表时出现死锁问题求助

搞定多线程插入带唯一约束表的死锁问题

嘿,我来帮你拆解下为啥你的8个QT线程插入数据时会触发死锁,以及怎么解决这个问题:

首先得明白死锁的根源:你的source_names表给source_name加了唯一索引,InnoDB在处理唯一约束检查的时候,会给待插入值对应的间隙加上Next-Key Lock(间隙锁+记录锁)。当多个线程同时插入不同的source_name值时,它们锁定的范围可能交叉,再加上线程之间的锁请求顺序不一致,就很容易出现循环等待的死锁场景。

下面给你几个实用的解决思路,按推荐程度排序:

  • 统一插入顺序:让所有线程先把要插入的source_name按字典序排好,再按相同的顺序执行插入。这样所有线程的锁请求顺序完全一致,就不会出现循环等待的死锁了。比如每个线程先对自己的500条数据做排序,再依次插入,这个改动成本很低,效果却很好。

  • 改用批量插入+冲突处理:别再单条插入了,改成批量插入,同时用INSERT ... ON DUPLICATE KEY UPDATE或者INSERT IGNORE语法。比如每个线程执行:

    INSERT INTO source_names (source_name) 
    VALUES ('name_1'), ('name_2'), ..., ('name_500')
    ON DUPLICATE KEY UPDATE id = id;
    

    这样InnoDB会一次性处理批量的唯一性检查,锁的粒度和顺序更可控,能大幅降低死锁概率。ON DUPLICATE KEY UPDATE后面的id=id是个无意义的操作,只是为了语法合法,不会修改现有数据,还能避免重复值抛出错误。

  • 缩短事务持有时间:检查下你的插入逻辑是不是每个插入都开了独立事务?如果是长事务的话,锁会被持有更久,死锁概率自然更高。尽量让每个插入(或批量插入)的事务尽快提交,做完就释放锁。

  • 调整事务隔离级别(按需使用):把数据库的事务隔离级别降到READ COMMITTED,这个级别下InnoDB的间隙锁会失效,只会加记录锁。你可以在每个线程连接数据库后执行:

    SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
    

    不过要注意,这个隔离级别会导致不可重复读,如果你的业务场景对这个不敏感的话,这是个有效的办法。

  • 应急方案:降低并发或加全局锁:如果上面的方法暂时不好实现,可以先把线程数从8个降到4个,或者在QT里给插入操作加个全局QMutex,让插入串行执行。不过这会牺牲性能,只能作为临时过渡方案。

内容的提问来源于stack exchange,提问作者morteza ali ahmadi

火山引擎 最新活动