使用Knex手动锁定表

4
我有一个表,当添加新行时,所有现有行都需要更新。实际上,在一个“插入”完成之前,我不想允许另一个“插入”发生。因此,我猜普通的行级锁定或读取锁定是不够的,想要在事务期间直接锁定表。
在Knex中手动锁定表(ACCESS EXCLUSIVE模式)的方法是什么?我的猜测是这将涉及:
1. 运行原始SQL (锁定和解锁) 并确保使用相同的连接进行事务。 2. 是否有更好的解决方案?
另一种我正在探索的方法是使用作业队列,并让单个工作器插入记录(使用原始SQL连接)。但我被告知这在多节点场景下不起作用。
3个回答

1
如果您正在使用MySQL,那么这根本不可能,因为knex不允许您选择要使用哪个连接。确保两个特定查询在同一个连接池中执行的唯一方法是将它们嵌套在knex事务中。但是MySQL文档说: LOCK TABLES不是事务安全的,并且在尝试锁定表之前隐式提交任何活动事务。如果您的DB驱动程序是Postgres,则将LOCK TABLE查询放入事务中可以解决问题,但MySQL无法实现此目标。

1
使用knex时,除非您正在使用事务,否则无法选择将查询发送到哪个连接。所有发送到同一事务的查询也会发送到同一连接。
要发送锁定命令,需要使用knex.raw
1. 开始事务 2. 将原始SQL锁定查询发送到事务 3. ??? 4. 利润
锁定的具体实现取决于您使用的数据库。通常,当事务提交时,锁定会自动释放。

好主意。使用现有的事务机制来保持相同的连接并处理提交/回滚,然后手动决定如何使用knex.raw锁定表。我会尝试这种方法。 - Yuan Ren

1
你可以使用数据库锁来实现这一点,例如在mysql中使用GET_LOCK
具体而言,您可以在knex中使用事务,以确保您有一个特定的连接来处理,或者您可以使用acquireConnection并将连接设置为GET_LOCK查询。

http://knexjs.org/#Builder-connection

例子:

let conn;
try {
  conn = await knex.client.acquireConnection();

  try {
    let lockRes = await knex.select(knex.raw('GET_LOCK(?, ?) as res', ['lock_name', 15])).connection(conn);

    if (!lockRes.length || lockRes[0]['res'] !== 1) {
      // Did not acquire lock
      return;
    }

    // Your code here
  }
  finally {
    await knex.select(knex.raw('RELEASE_LOCK(?)', ['lock_name'])).connection(conn);
  }
}
finally {
  conn && await knex.client.releaseConnection(conn);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接