如何在 java 应用程序中设置锁定模式

How to SET LOCK MODE in java application

我正在开发一个 Java Web 应用程序,它使用 Weblogic 连接到 Informix 数据库。在应用程序中,我们有多个线程在 table.

中创建记录

失败并抛出以下错误的情况经常发生:

java.sql.SQLException: Could not do a physical-order read to fetch next row....
Caused by: java.sql.SQLException: ISAM error: record is locked.

我假设当记录被锁定时两个线程都在尝试插入或更新。

我做了一些研究,发现有一个选项可以设置数据库,而不是抛出错误,它应该等待锁被释放。

SET LOCK MODE TO WAIT;
SET LOCK MODE TO WAIT 17;

我认为 JDBC 中没有使用此设置的选项。如何在我的 java 网络应用程序中使用此设置?

您始终可以使用 createStatement() 直接发送 SQL,然后发送准确的 SQL。

更多 'normal'/现代解决此问题的方法是结合 MVCC、t运行saction 级别 'SERIALIZABLE'、重试和 运行dom 退避。

不过,我不知道 Informix 是否接近那个先进水平。诸如 Postgres 之类的现代数据库(mysql 就 MVCC/serializable/retry/backoff 和 t运行 安全性而言不算作现代数据库)。

在原始 JDBC 中执行 MVCC/Serializable/Retry/Backoff 非常复杂;使用 JDBI 或 JOOQ 等库。

MVCC:一种机制,t运行saction 是基础数据的浅层克隆。 2 个单独的 t运行saction 可以在相同的 table 中读取和写入相同的记录,而不会互相妨碍。在您提交 t运行saction 之前,事情不会 'saved'。

SERIALIZABLE:一个 t运行saction 级别(也称为隔离级别),设置 table 和 jdbcDbObj.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); - 最安全的级别。如果您知道版本控制系统是如何工作的:您要求数据库积极地对所有内容进行变基,以便将整个提交链排序为一长串事件:每个 t运行saction 就像它已完成一样在上一个 t运行saction 完成后。实现这个级别最简单的方法是全局锁定所有的东西。当然,这对多线程性能非常不利。实际上,好的数据库引擎(例如 postgres)比这更聪明:多个线程可以同时 运行 t运行 操作,而不仅仅是被冻结和等待锁;数据库引擎反而会检查 t运行saction 所做的事情(不仅是写,还有读)是否与同时的 t运行saction 没有冲突。如果是,则全部允许。如果不是,除了一个同时的 t运行saction 之外的所有操作都会抛出重试异常。这是唯一可以让您安全地完成这一系列事件的级别:

  1. 获取 isaace 的银行账户余额。
  2. 获取 rzwitserloot 银行账户的余额。
  3. 从isaace的号码中减去€10,-,如果余额不足则失败。
  4. 将 €10,- 添加到 rzwitserloot 的号码。
  5. 将isaace的新余额写入数据库。
  6. 将rzwitserloot的新余额写入数据库。
  7. 提交 t运行操作。

任何低于 SERIALIZABLE 的级别都会默默地使作业失败;如果多个线程同时执行上述操作,则不会出现 SQL 异常,但 isaace 和 rzwitserloot 的余额总和将随时间变化(钱丢失或创建 – 在步骤 1 和 2 与步骤 5/6 之间/ 7,另一个线程设置了新余额,但是由于5/6/7的更新,这些新余额都丢失了。使用可序列化,这不会发生。

重试:智能数据库解决问题的方法是通过检查整个 t运行saction 不受在 此 t运行saction 打开后提交给数据库的任何 t运行saction 的影响。如果答案是肯定的(某些选择会有所不同),则 t运行 操作失败。这个错误的要点是告诉代码 运行 t运行saction 只是..从顶部开始并再次执行。这次很可能不会发生冲突,它会起作用。假设冲突可能发生但通常不会发生,所以最好假设 'fair weather'(没有锁,只做你的事情),然后检查,然后在发生冲突的奇异场景中重试,而不是试图锁定行和 tables。请注意,例如以太网以相同的方式工作(假设天气晴朗,之后恢复错误)。

BACKOFF:重试的一个问题是计算机过于一致:如果 2 个线程互相妨碍,它们可能都失败,都再次尝试,只是再次失败,永远。解决方案是线程在 运行dom 的时间内转动拇指,以确保在某个时候,两个冲突的重试器之一'wins'.

换句话说,如果你想做'right'(看银行账户例子),也相对'fast'(不是全局锁定),得到一个可以做这个的DB,并使用 JDBI 或 JOOQ;否则,您必须将代码写入 运行 lambda 块中的所有数据库内容,捕获 SQL 异常,检查 SqlState 以查看它是否指示您应该重试(sqlstate 代码是 DB -特定于引擎),如果是,则重新运行那个lambda,在等待指数增长的时间后,还包括一个运行dom因子。这相当复杂,这就是为什么我强烈建议您依靠 JOOQ 或 JDBI 来为您解决这个问题。

如果您还没有准备好使用该级别的数据库,只需声明并发送“SET LOCK MDOE TO WAIT 17;”作为 SQL 直接声明,在打开任何连接的开始。如果您使用的是连接池,通常可以在一个地方将 SQL 语句配置为 运行 在连接开始时。

Informix JDBC 驱动程序允许您在连接到服务器时自动设置锁定等待模式。

只需通过数据源或连接URL传递以下参数

IFX_LOCK_MODE_WAIT=17

JDBC 的值为

  • (-1) 永远等待
  • (0) 不等待(默认)
  • (> 0) 等这么多秒

https://www.ibm.com/support/knowledgecenter/SSGU8G_14.1.0/com.ibm.jdbc.doc/ids_jdbc_040.htm

连接conn = DriverManager.getConnection ( "jdbc:Informix-sqli://cleo:1550: IFXHOST=cleo;PORTNO=1550;user=rdtest;password=my_passwd;IFX_LOCK_MODE_WAIT=17";);