MySql获取\u锁以实现并发安全升级

MySql获取\u锁以实现并发安全升级,mysql,node.js,concurrency,locking,mutex,Mysql,Node.js,Concurrency,Locking,Mutex,在node.js中使用mysql db编写API,我正在实现一个相当标准的模式: If exists then update else insert 这当然可以正常工作,直到对api发出多个同时请求,此时请求2上的If exists可以在插入请求1之前执行,导致两条记录而不是一条记录 我知道解决这个问题的一种方法是确保db有一个约束或键来防止重复记录,但在这种情况下,决定我们是否应该进行插入或更新的规则更加复杂,因此检查需要在代码中完成 这听起来像是使用互斥锁/锁的好例子。我需要将其分发,因为

在node.js中使用mysql db编写API,我正在实现一个相当标准的模式:

If exists then update
else insert
这当然可以正常工作,直到对api发出多个同时请求,此时请求2上的
If exists
可以在插入请求1之前执行,导致两条记录而不是一条记录

我知道解决这个问题的一种方法是确保db有一个约束或键来防止重复记录,但在这种情况下,决定我们是否应该进行插入或更新的规则更加复杂,因此检查需要在代码中完成

这听起来像是使用互斥锁/锁的好例子。我需要将其分发,因为api可能有多个实例作为池/场的一部分运行

我提出了以下实现:

try {
     await this.databaseConnection.knexRaw().raw(`SELECT GET_LOCK('lock1',10);`);
     await this.databaseConnection.knexRaw().transaction(async (trx) => {
        const existing = await this.findExisting(id);
        if (existing) {
          await this.update(myThing);
        } else {
          await this.insert(myThing);
        }
     });

  } finally {
    await this.databaseConnection.knexRaw().raw(`SELECT RELEASE_LOCK('lock1');`);
}

这一切似乎都很好,我的测试现在只生成一个插入。虽然这看起来有点暴力/手工。作为mysql和node的新手(我来自c#和sql server的背景),这种方法合理吗?有更好的方法吗?

明智吗?主观的

技术上安全吗?它可能是--
GET\u LOCK()
是可靠的--但不是您编写的那样

您忽略了
GET_LOCK()
的返回值,如果您获得了锁,则返回值为1;如果超时过期而您没有获得锁,则返回值为0;在某些故障情况下,返回值为NULL

如前所述,您将等待10秒,然后再进行工作,因此,不安全

这假设您只有一个MySQL主机。如果您有多个主节点或Galera,那么它将不起作用,因为它跨越所有节点。(Galera集群是一个由可写主机组成的高可用性MySQL/MariaDB/Percona集群,可同步复制,最多可在n个节点中的(ceil(n/2)-1个节点发生故障/隔离后生存)


最好是使用查找并锁定相关行,这会锁定找到的行,或者在某些情况下,锁定它们存在时的间隙,阻止尝试捕获相同锁的其他事务,直到您回滚或提交。。。但如果这不实际,则使用
GET_LOCK()
是有效的,这取决于上面关于返回值的观点。

请注意,即使使用
SELECT。。。对于更新
和可序列化的事务,间隙未锁定,因为两个并发事务将能够同时选择间隙;一个不会等待另一个。只有在插入时,才会发生等待,如果两个TX尝试插入同一行,则会发生死锁,其中一个事务将被强制回滚。所以如果你想序列化你的
选择。。。用一个可能不存在的行插入
,并希望避免死锁,那么尽管有点麻烦,
GET_LOCK()
是我知道的唯一解决方案。