循环插入 MySql 数据库,无死锁

Insert to MySql Database in loop without deadlock

我正在使用 nodejs (AdonisJs) 创建一个脚本来更新我的数据库 (Mysql)。像这样:

const trx = await Database.beginTransaction();
try {
    const async = use('async');

    await async.eachOfLimit(arr, 50, async item => {
        `select * from table1 where id = 1 LOCK IN SHARE MODE`
        `insert into ....`
        `another query...`
    });
    trx.commit();
} catch (err) {
    trx.rollback();
}

问题是 eachOfLimit 运行ning 并行查询,所以上面的代码抛出错误:Deadlock found when trying to get lock; try restarting transaction。如果我 运行 它与 for of 同步循环,它工作正常。 如何解决这个问题或我的脚本的任何想法。抱歉我的英语不好。

顺便说一句,我使用 LOCK IN SHARE MODEFOR UPDATE 来避免在许多查询 INSERT 运行 时重复,即使我使用 findOrCreate 检查行是否存在

您可以尝试使用 "FOR UPDATE" 而不是 "LOCK IN SHARE MODE" 来防止对同一行的锁定。

来自dev.mysql.com

SELECT ... 锁定共享模式

在读取的任何行上设置共享模式锁。其他会话可以读取这些行,但在您的事务提交之前不能修改它们。如果其中任何行被另一个尚未提交的事务更改,您的查询将等待该事务结束,然后使用最新值。

SELECT ...更新

对于搜索遇到的索引记录,锁定行和任何关联的索引条目,就像您为这些行发出 UPDATE 语句一样。其他事务被阻止更新这些行,执行 SELECT ... LOCK IN SHARE MODE,或读取某些事务隔离级别的数据。一致读取忽略读取视图中存在的记录上设置的任何锁。 (无法锁定旧版本的记录;通过在记录的内存副本上应用撤消日志来重建它们。)

我找到了一个临时解决方案:使用 INSERT IGNORE 而不是 INSERT。 所以我删除了 SELECT 查询并使用 INSERT IGNORE.

const language = await Database.raw(trx('languages').insert({
      ...languageItem,
      created_at: this.getTimeNow(),
      updated_at: this.getTimeNow()
}).toString().replace('insert', 'INSERT IGNORE'));

我的脚本运行没有死锁。