「JDBCによる悲観ロックの落とし穴」の版間の差分
提供: tknotebook
| 45行: | 45行: | ||
'''レコードが排他ロックするまでのわずかな隙間時間に、レコードのロックがない期間が存在するのです。'''これではうまく動きません。 | '''レコードが排他ロックするまでのわずかな隙間時間に、レコードのロックがない期間が存在するのです。'''これではうまく動きません。 | ||
| − | + | 対処方法は3つあります。 | |
'''1. Isolation Level を REPEATABLE READ 以上にする。''' | '''1. Isolation Level を REPEATABLE READ 以上にする。''' | ||
| − | '''2. 以下のように、カーソルを使って行を更新する。''' | + | '''2. カーソルを取得せず update account balance=balance-100 where ... を使う''' |
| + | |||
| + | '''3. 以下のように、カーソルを使って行を更新する。''' | ||
rsA = s.executeQuery("select name, balance from account where name='A' for update"); | rsA = s.executeQuery("select name, balance from account where name='A' for update"); | ||
2016年7月30日 (土) 04:01時点における版
メインページ>コンピュータの部屋#Java>Java Tips
あるあるですが、JDBCを使って悲観ロックでレコードを更新する場合、たまにこんなコードを見かけることがあります。
更新するテーブルの定義
CREATE TABLE ACCOUNT ( NAME varchar(12) PRIMARY KEY NOT NULL, BALANCE int NOT NULL )
更新するコード
rsA = s.executeQuery("select name, balance from account where name='A' for update");
if (rsA.next()) {
balanceA = rsA.getInt("balance");
logger.debug("balanceA = " + balanceA);
} else {
throw new Exception("行がありません");
}
s.executeUpdate(String.format("update account set balance=%d where name='A'", balanceA - 100));
logger.debug("update A");
conn.commit();
実は Isolation level が READ COMMITED では、これは悲観ロックになっていないのです。
コードでは、
- 更新モードでカーソルを取得
- カーソルの行の値を取得し更新ロックをかける。
- 行の値を更新し排他ロックをかける。
となっていて問題なさそうに見えますが、問題点は更新ロックの寿命です。
多くのDBでは、行ロックの更新ロックは、Isolation Level が READ COMMITED の場合、カーソルの指す行のみがロックされ、 カーソルが別の行へ移ると元の行の更新ロックは解除されます。カーソルがクローズされれば当然更新ロックは残りません。
Statement オブジェクトは executeXXX を実行すると、カーソルをクローズするため、上記のコードでは レコードが排他ロックするまでのわずかな隙間時間に、レコードのロックがない期間が存在するのです。これではうまく動きません。
対処方法は3つあります。
1. Isolation Level を REPEATABLE READ 以上にする。
2. カーソルを取得せず update account balance=balance-100 where ... を使う
3. 以下のように、カーソルを使って行を更新する。
rsA = s.executeQuery("select name, balance from account where name='A' for update");
if (rsA.next()) {
balanceA = rsA.getInt("balance");
logger.debug("balanceA = " + balanceA);
rsA.updateInt("balance", balanceA - 100);
rsA.updateRow();
logger.debug("update A");
} else {
throw new Exception("行がありません");
}
conn.commit();