Sequelize 事务大并发下形成的死锁问题

Author: bugall
Wechat: bugallF
Email: 769088641@qq.com
Github: https://github.com/bugallmysql

  • 环境git

  • Mysql 5.6 InnoDBgithub

一.声明

这并非sequelize的bug,在涉及到connection pool的时候都有可能出现这个问题sql


二.原由

咱们有个须要事物的业务场景,上线之初一直运行正常,但是在晚上高峰的时候一直会有逻辑错误的问题,刚开始以为是逻辑有问题。
后来查看innodb才发现出现大量的锁,主表的某些数据行持有锁不释放,其它的sql一直等待,直到业务服务器报deadlock。由于主表其它业务模块也会用到,因此是一个比较紧急的状况数据库


三.错误的代码

代码能够简写为:json

DBSequelize.transaction({autocommit:false,isolationLevel:'SERIALIZABLE'}).then(function(t){
    return db.Tuser.create({...},{transaction:t});
}).then(function(trans){
    return db.TuserRelation.update({...},{where:{...},transaction:t})
}).then(function(trans){
    return db.Twork.update({...},{where:{...}})
}).then(function(result){
    t.commit(res.json(result));
})

大概意思就是若是用户注册了一个帐号,就在帐号关系里增长一个记录这两个操做是原子的。没并发的状况下,这个逻辑执行是没有问题的,可是一旦有并发就会出问题。服务器


四.分析错误

首先在建立事务的时候咱们指定了两个参数”不自动提交“,”一致性读“。(咱们这里但愿对一条数据读的时候加X锁,事务级别可是问题不大,最可能是影响数据库的并发性能,
对于主表来讲加X锁是一个很愚蠢的作法),问题关键是出如今不自动提交设置这里。
刚开始我对事务理解就是:只有咱们主动执行commit的时候才会把buffer cache的脏数据写入磁盘并释放事务里持有的锁。
可是真是状况不是这样的。在mysql官方文档中找到了关于autocommit transaction相关描述session

By default, MySQL runs with autocommit mode enabled. This means that as soon as you execute a statement that updates (modifies) a table, MySQL stores the update on disk to make it permanent. The change cannot be rolled back.

To disable autocommit mode implicitly for a single series of statements, use the START TRANSACTION statement:

START TRANSACTION;
SELECT @A:=SUM(salary) FROM table1 WHERE type=1;
UPDATE table2 SET summary=@A WHERE type=1;
COMMIT;

With START TRANSACTION, autocommit remains disabled until you end the transaction with COMMIT or ROLLBACK. The autocommit mode then reverts to its previous state.

这里有几个概念能够明确:并发

1. autocommit的设定是针对session的(一个数据库链接),若是你对这个这个链接设定了autocommit=1那么这个链接
   执行的全部sql都会自动commit,反之不会自动提交必须手动commit提交
2. 不管链接设置的autocommit是什么状态,在执行事务时候事务里的autocommit状态都会被隐式的设置为0,当事物执行
   完成后autocommit会被设置为原先的状态(执行事务前)

因此咱们的问题出如今建立事务时候的{tautocommit=false}这个参数实际上是对链接设置的,而不是事务。
在并发的状况下,sequelize链接池的链接是会复用的,假如一个执行了事务的链接去执行其它的DML语句,这些语句是不会提交的
也就是说数据行的锁不释放,其它请求设计到该条数据的时候将会被阻塞,直到超时。性能


五.解决

咱们只需在事务参数中设定autocommit=true就好了。须要注意的时候在Isolation的级别是一致性读的时候,由于该级别违反了MVCC规范,因此这时候的autocommit=true/false是要分状况讨论的。这里不是sequelize的问题,在用到connection pool对innodb进行操做的时候应该注意这个状况。

相关文章
相关标签/搜索