繁体   English   中英

SQL/Java 并发事务死锁

[英]SQL/Java concurrent transaction deadlock

我有一个方法boolean addIntegerColumnToDatabases(String tableName, String columnName, Connection ... conns)在那里我有一个 SQL 连接的集合。

对于每个 SQL 连接,我执行 schema-update-query

BEGIN
  ALTER TABLE <b>tableName</b> ADD COLUMN <b>columnName</b> int4
COMMIT

由于此方法必须是 ACID,因此我喜欢并行执行此操作。

例如,我有两个连接(C1、C2):

C1:开始

C2:开始

C1: ALTER TABLE tableName ADD COLUMN columnName int4

C2: ALTER TABLE tableName ADD COLUMN columnName int4

C1:提交

C2:提交

这是代码:

Statement[] s = new Statement[conns.length];
int i =0;
for(Connection c:conns) {
  c.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
  c.setAutoCommit(false);
  s[i++]=c.createStatement();
}
for(Statement st:s) {
  st.executeUpdate("ALTER TABLE "+tableName+" ADD COLUMN "+columnName+" int4;");
}
for(Connection c:conns) {
  c.commit();
}
return true;

只要连接在不同的数据库上,这就会起作用。

问题

如果 C1 和 C2 连接到同一个数据库,C2 会等待 C1 在 Postgres 端提交,C1 会在 Java 端等待 C2。 瞧,我们陷入了僵局。

由于集群/平衡、ipv4/6 和 dns 问题等问题,我没有 100% 的机会检查连接是否到同一个数据库系统。

如何确保return false; 如果执行有两个连接到同一个数据库而不执行任何模式更改?

使用填充了 GUID 的帮助表database_instance

在 schema-update-query 之前,获取 GUID 并将其锁定在本地字典中。

或者简单地在本地锁定表,无论它位于哪个数据库上:

var tablename = new object();
lock(tablename)
{
   <your query>
}

您可以为此使用咨询锁

咨询锁使用调用者提供的任意数字并存在,直到它被显式释放或连接关闭。 您可以将表名转换为其 oid并将该数字用作锁标识符。

类似的东西(没有错误处理或清理!)

ResultSet st.executeQuery("select pg_try_advisory_lock('" + tableName + "'::regclass::oid::bigint)");
rs.next();
boolean locked = rs.getBoolean(1);
if (locked) {
  // do your migration
  .....

  // once you are done with the table, release the lock immediately
  // alternatively keep it open and it will be released automatically 
  // when the connection is closed
  st.execute("select pg_advisory_unlock('" + tableName + "'::regclass::oid::bigint)");
} else {
  // handle the conflict
}

也许您想将该功能移动到它自己的方法中。


或者,您可以尝试在迁移开始时使用一些硬编码数字获取一个锁(独立于要修改的表)。

嗯,我还有一个想法。

我可以首先通过简单地执行ROLLBACK而不是COMMIT来衡量每个数据库的预期时间消耗。

可以说

  1. 𝗧 是一个数据库的BEGINCOMMIT之间的单次消耗,并且
  2. 𝗧𝗮 是所有时间消耗的数量。
  3. 𝗧𝗺 是所有测量时间消耗max(𝗧𝗮)的最大值,最高值。

然后我将看门狗超时设置为

2 * 𝗧𝗺

如果看门狗阻止我们有重复的数据库连接,并且显然必须返回false

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM