【问题标题】:Insert to MySql Database in loop without deadlock在没有死锁的循环中插入到 MySql 数据库
【发布时间】:2020-06-03 09:52:24
【问题描述】:

我使用 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 并行运行查询,所以上面的代码抛出错误:Deadlock found when trying to get lock; try restarting transaction。如果我使用for of 同步循环运行它,它工作正常。 如何为我的脚本解决这个问题或任何想法。对不起我的英语不好。

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

【问题讨论】:

    标签: mysql node.js async-await transactions deadlock


    【解决方案1】:

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

    来自dev.mysql.com

    选择...锁定共享模式

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

    选择 ... 更新

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

    【讨论】:

    • 我已经尝试过FOR UPDATE,仍然是同样的错误。我认为这里的问题是eachOfLimit 同时运行多个查询SELECT,所以它们锁定了同一行。
    【解决方案2】:

    我找到了一个临时解决方案:使用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'));
    

    我的脚本运行没有死锁。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多