jpaの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

85
Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 1 JJUG CCC 2014 Spring Java EE 7対応! JPAの同時実行制御とロック 2014/05/18 ()ウチダ人材開発センタ 多田真敏

Upload: masatoshi-tada

Post on 16-Apr-2017

7.508 views

Category:

Software


3 download

TRANSCRIPT

Page 1: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 1

JJUG CCC 2014 Spring

Java EE 7対応!JPAの同時実行制御とロック

2014/05/18

(株)ウチダ人材開発センタ 多田真敏

Page 2: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

このセッションについて

Twitterハッシュタグ : #ccc_r15

JPAの同時実行制御とロックについて、基礎から詳しく説明します

同時実行制御とロックにおいて、JPA実装とDBMSを変えたときの、挙動の違いや注意点を説明します

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 2

Page 3: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

アジェンダ

まずは自己紹介

同時実行制御とロックとは?

JPAにおける同時実行制御とロック

今回のサンプルプログラム

EclipseLink×MySQLの場合

EclipseLink×PostgreSQLの場合

Hibernate×MySQLの場合

Hibernate×PostgreSQLの場合

まとめ

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 3

Page 4: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 4

まずは自己紹介

Page 5: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

あんた、誰?

IT研修インストラクター、社会人7年目。三十路。

専門はJava、.NET、ネットワーク、業務知識など。

中小企業診断士、SJC-WC、応用情報技術者。

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 5

Page 6: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

Twitter・ブログ

Twitter : @suke_masa

ブログ : Java EE 事始め!http://masatoshitada.hatenadiary.jp/

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 6

Page 7: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 7

同時実行制御とロックとは?

Page 8: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

ロックとは?

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 8

empno ename sal

101 Nishida 500000

102 Nohira 285000

103 Kiyama 245000

T1 T2

複数のトランザクションから同時に実行されたとき、データの不整合が起こらないように行・表・DBにロックをかける、DBMSの機能

○ ×

Page 9: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

ロックなしの場合に起こる不整合

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 9

empno ename sal

101 Nishida 500000

102 Nohira 285000

103 Kiyama 245000

本当は520000にならないとおかしい!

2つのトランザクションから同時に、empno=101のsalを10000プラスする

① T1:101のsalを検索(500000)

② T2:101のsalを検索(500000)

③ T1:salを①+10000に更新(510000)

④ T2:salを②+10000に更新(510000)

Page 10: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

READロックとWRITEロック

① READロック(共有ロック)他のトランザクションはREADロックは取得できるが、WRITEロックは取得できない

②WRITEロック(占有ロック、排他ロック)他のトランザクションはREADロックもWRITEロックも取得できない

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 10

先のトランザクション

READロック WRITEロック

後のトランザクション

READロック ○ ×

WRITEロック × ×

Page 11: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 11

JPAにおける同時実行制御とロック

Page 12: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

楽観的ロックと悲観的ロック

楽観的ロック(OPTIMISTIC)更新対象の行に対して、他のトランザクションからの更新は無いという前提に立つ

パフォーマンスは悲観的ロックと比較して高い

悲観的ロック(PESSIMISTIC)更新対象の行に対してロックをかけ、他のトランザクションからの更新をDBMSが防ぐ

パフォーマンスは楽観的ロックと比較して低い

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 12

楽観的ロック → DBMSのロック機能を利用しない悲観的ロック → DBMSのロック機能を利用する

Page 13: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

バージョニング

「バージョン」を表す列を対象テーブルに追加

エンティティのバージョンフィールドには@Versionアノテーションを付加

バージョンの型はint(Integer)、short(Short)、long(Long)、java.sql.Timestampが使用可能

更新時にバージョンの値が更新(インクリメントなど)される

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 13

Page 14: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

LockModeType

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 14

ロックモード 説明

OPTIMISTIC 楽観的ロック

OPTIMISTIC_FORCE_INCREMENT

楽観的ロック。バージョンの値をインクリメントする

PESSIMISTIC_READ 悲観的ロック。READロックを取得する

PESSIMISTIC_WRITE 悲観的ロック。WRITEロックを取得する

PESSIMISTIC_FORCE_INCREMENT

悲観的ロック。WRITEロックを取得し、バージョンの値をインクリメントする

NONE ロックしない(デフォルト)

※READ・WRITEは非推奨

Page 15: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

LockModeTypeの指定方法

EntityManagerインターフェイス<T> T find(Class<T> entityClass, Object primaryKey, LockModeType lockMode)void lock(Object entity, LockModeType lockMode)void refresh(Object entity, LockModeType lockMode)

Query(TypedQuery)インターフェイスQuery setLockMode(LockModeType lockMode)

NamedQueryアノテーションlockmode属性

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 15

Page 16: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

金魚本では・・・

「4.5 同時実行」は6ページしかない

悲観的ロックについては0.5ページ程度

Java EE 5までは楽観的ロックしかなかった

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 16

Page 17: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

参考資料

Java EE 7 Tutorial42. Controlling Concurrent Access to

Entity Data With Rocking

JSR-338 Java Persistence 2.13.4 Locking and Concurrency

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 17

Page 18: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

SPECIAL THANKS!!

O社認定講師&データベーススペシャリストのY部長

外語大卒の後輩Kちゃん

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 18

Page 19: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 19

今回のサンプルプログラム

Page 20: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

実験環境

JPA実装EclipseLink 2.5.2-M1Hibernate Entity Manager 4.3.1.Final

DBMSPostgreSQL 8.4.19MySQL 5.6.16

その他Windows 7 SP1(x64)JDK 7u45Eclipse 4.3.1

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 20

2×2=4通り

Page 21: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

使用するテーブル

empno(PK) ename sal version

101 Nishida 500000 1

102 Nohira 285000 1

103 Kiyama 245000 1

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 21

emp2

empno=101のレコードを検索後、そのレコードのsalを10000プラスする処理を、2スレッドから同時実行

バージョン列

Page 22: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

エンティティクラス

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 22

@Entitypublic class Emp2 implements Serializable {

@Idprivate Integer empno; private String ename;private Integer sal;@Versionprivate Integer version;// setter/getter, equals(), hashCode(), toString()

}

version列と対応するフィールド

Page 23: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

スレッドクラス

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 23

public class UpdateThread extends Thread {@Overridepublic void run() {

String threadName = Thread.currentThread().getName();EntityManagerFactory factory =

Persistence.createEntityManagerFactory("emp");EntityManager manager = factory.createEntityManager();EntityTransaction tx = manager.getTransaction();tx.begin();Emp2 emp = manager.find(Emp2.class, 101, LockModeType.NONE);int sal = emp.getSal();System.out.println(threadName + " 検索直後 " + emp);try {

Thread.sleep(1000);} catch (InterruptedException e) { e.printStackTrace(); }

ここでLockModeTypeを指定

Page 24: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

スレッドクラス

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 24

try {emp.setSal(sal + 10000);System.out.println(threadName + " flush直前 " + emp);manager.flush();System.out.println(threadName + " flush直後 " + emp);tx.commit();System.out.println(threadName + " commit.");System.out.println(threadName + " commit直後 " + emp);

} catch (Exception e) {System.out.println(threadName

+ " 例外発生:" + e.getClass().getName());e.printStackTrace();tx.rollback();System.out.println(threadName + " rollback.");

}

Page 25: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

スレッドクラス

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 25

System.out.println(threadName + " 終了 " + emp);manager.close();factory.close();

}}

Page 26: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

メインクラス

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 26

public class Main {public static void main(String[] args) {

Thread thread1 = new UpdateThread();thread1.setName("T1");Thread thread2 = new UpdateThread();thread2.setName("T2");thread1.start();thread2.start();

}}

マルチスレッドで同時実行

Page 27: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 27

EclipseLink×MySQLの場合

Page 28: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

NONE

ロックしない(デフォルト)

バージョニングは不要なので、@Versionをコメントアウト

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 28

@Entitypublic class Emp2 implements Serializable {・・・// @Version コメントアウトprivate Integer version;・・・

}

Page 29: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

NONEの実行ログ

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 29

[EL Fine]: sql: SELECT EMPNO, ENAME, SAL, VERSION FROM EMP2 WHERE (EMPNO = ?)bind => [101]

[EL Fine]: sql: SELECT EMPNO, ENAME, SAL, VERSION FROM EMP2 WHERE (EMPNO = ?)bind => [101]

T1 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1]T2 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1]T1 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T2 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1][EL Fine]: sql: UPDATE EMP2 SET SAL = ? WHERE (EMPNO = ?) bind => [510000, 101][EL Fine]: sql: UPDATE EMP2 SET SAL = ? WHERE (EMPNO = ?) bind => [510000, 101]T1 flush直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T2 flush直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T1 commit.T1 commit直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T1 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T2 commit.T2 commit直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T2 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]

Page 30: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

実行の様子

① T1が検索→sal=500000

② T2が検索→sal=500000

③ T1がsal=500000+10000で更新

④ T2がsal=500000+10000で更新

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 30

salは最終的に510000になる

Page 31: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

OPTIMISTIC

楽観的ロック

バージョニング必須

バージョンが無い場合はPersistenceException

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 31

@Entitypublic class Emp2 implements Serializable {・・・@Version // 必須private Integer version;・・・

}

Page 32: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

OPTIMISTICの実行ログ

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 32

[EL Fine]: sql: SELECT EMPNO, ENAME, SAL, VERSION FROM EMP2 WHERE (EMPNO = ?)bind => [101]

[EL Fine]: sql: SELECT EMPNO, ENAME, SAL, VERSION FROM EMP2 WHERE (EMPNO = ?)bind => [101]

T2 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1]T1 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1]T1 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T2 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1][EL Fine]: sql: UPDATE EMP2 SET SAL = ?, VERSION = ? WHERE ((EMPNO = ?) AND (VERSION = ?)) bind => [510000, 2, 101, 1][EL Fine]: sql: UPDATE EMP2 SET SAL = ?, VERSION = ? WHERE ((EMPNO = ?) AND (VERSION = ?)) bind => [510000, 2, 101, 1]T1 flush直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]T1 commit.T1 commit直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]T1 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]T2 例外発生:javax.persistence.OptimisticLockExceptionT2 rollback.T2 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=2](以下、スタックトレース)

Page 33: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

実行の様子

① T1が検索→sal=500000, version=1

② T2が検索→sal=500000, version=1

③ T1がsal=500000+10000, version=1+1で更新→コミット

④ T2がsal=500000+10000, version=1+1で更新→バージョンの値が既に変更されているため、OptimisticLockExceptionが発生してロールバック

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 33

Page 34: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

実行後のバージョン値

ロールバックされたエンティティも、バージョン値がインクリメントされる

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 34

T1 commit.T1 commit直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]T1 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]T2 例外発生:javax.persistence.OptimisticLockExceptionT2 rollback.T2 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]

Page 35: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

バージョン値変更の検知方法

UPDATE文のWHERE句に主キーとバージョン列を指定している→バージョン値が既に変更されていた場合、更新行数はゼロになる

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 35

[EL Fine]: sql: UPDATE EMP2 SET SAL = ?, VERSION = ? WHERE ((EMPNO = ?) AND (VERSION = ?))

bind => [510000, 2, 101, 1]

Page 36: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

OPTIMISTIC_FORCE_INCREMENT

楽観的ロック。バージョン値を強制的にインクリメントする

バージョニング必須

EclipseLinkの場合、挙動はOPTIMISTICと全く同じ

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 36

Page 37: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

OPTIMISTIC_FORCE_INCREMENTの実行ログ

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 37

[EL Fine]: sql: SELECT EMPNO, ENAME, SAL, VERSION FROM EMP2 WHERE (EMPNO = ?)bind => [101]

T2 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1]T1 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1]T2 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T1 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1][EL Fine]: sql: UPDATE EMP2 SET SAL = ?, VERSION = ? WHERE ((EMPNO = ?) AND (VERSION = ?)) bind => [510000, 2, 101, 1]T2 flush直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=2][EL Fine]: sql: UPDATE EMP2 SET SAL = ?, VERSION = ? WHERE ((EMPNO = ?) AND (VERSION = ?)) bind => [510000, 2, 101, 1]T2 commit.T2 commit直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]T2 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]T1 例外発生:javax.persistence.OptimisticLockExceptionT1 rollback.T1 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]

Page 38: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

PESSIMISTIC_READ

悲観的ロック。READロックを取得→EclipseLinkではSELECT FOR UPDATE文が実行され、WRITEロックを取得する

バージョニング不要

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 38

@Entitypublic class Emp2 implements Serializable {・・・// @Version コメントアウトprivate Integer version;・・・

}

Page 39: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

PESSIMISTIC_READの実行ログ

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 39

[EL Fine]: sql: SELECT EMPNO, ENAME, SAL, VERSION FROM EMP2 WHERE (EMPNO = ?) FOR UPDATE bind => [101][EL Fine]: sql: SELECT EMPNO, ENAME, SAL, VERSION FROM EMP2 WHERE (EMPNO = ?) FOR UPDATE bind => [101]T1 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1]T1 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1][EL Fine]: sql: UPDATE EMP2 SET SAL = ? WHERE (EMPNO = ?) bind => [510000, 101]T1 flush直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T1 commit.T1 commit直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T1 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T2 検索直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T2 flush直前 Emp2 [empno=101, ename=Nishida, sal=520000, version=1][EL Fine]: sql: UPDATE EMP2 SET SAL = ? WHERE (EMPNO = ?) bind => [520000, 101]T2 flush直後 Emp2 [empno=101, ename=Nishida, sal=520000, version=1]T2 commit.T2 commit直後 Emp2 [empno=101, ename=Nishida, sal=520000, version=1]T2 終了 Emp2 [empno=101, ename=Nishida, sal=520000, version=1]

Page 40: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

実行の様子

① T1が検索、WRTIEロックを取得→sal=500000

② T2が検索しようとするが、T1がWRITEロックを取得しているので、DB内の待ち行列に入る

③ T1がsal=500000+10000で更新→コミットしたらロック解放

④ T2が待ち行列から出て検索、WRITEロックを取得→sal=510000

⑤ T2がsal=510000+10000で更新→コミットしたらロック解放

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 40

Page 41: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

SQLログと実際の実行順序は違う

SQLログは、JPAがDBMSに発行した「つもり」のSQLが表示される

実際には、発行されてもDBMS内の待ち行列に入っている場合がある

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 41

[EL Fine]: sql: SELECT EMPNO, ENAME, SAL, VERSION FROM EMP2 WHERE (EMPNO = ?) FOR UPDATE bind => [101][EL Fine]: sql: SELECT EMPNO, ENAME, SAL, VERSION FROM EMP2 WHERE (EMPNO = ?) FOR UPDATE bind => [101]T1 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1]・・・T1 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T2 検索直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]・・・

Page 42: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

PESSIMISTIC_WRITE

悲観的ロック。WRITEロックを取得→EclipseLinkではPESSIMISTIC_READと挙動は同じ

バージョニング不要

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 42

Page 43: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

PESSIMISTIC_WRITEの実行ログ

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 43

[EL Fine]: sql: SELECT EMPNO, ENAME, SAL, VERSION FROM EMP2 WHERE (EMPNO = ?) FOR UPDATE bind => [101]T2 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1][EL Fine]: sql: SELECT EMPNO, ENAME, SAL, VERSION FROM EMP2 WHERE (EMPNO = ?) FOR UPDATE bind => [101]T2 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1][EL Fine]: sql: UPDATE EMP2 SET SAL = ? WHERE (EMPNO = ?) bind => [510000, 101]T2 flush直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T1 検索直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T2 commit.T2 commit直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T2 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T1 flush直前 Emp2 [empno=101, ename=Nishida, sal=520000, version=1][EL Fine]: sql: UPDATE EMP2 SET SAL = ? WHERE (EMPNO = ?) bind => [520000, 101]T1 flush直後 Emp2 [empno=101, ename=Nishida, sal=520000, version=1]T1 commit.T1 commit直後 Emp2 [empno=101, ename=Nishida, sal=520000, version=1]T1 終了 Emp2 [empno=101, ename=Nishida, sal=520000, version=1]

Page 44: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

PESSIMISTIC_FORCE_INCREMENT

悲観的ロック。バージョン値を強制的にインクリメントする

チュートリアルにはWRITEロックかREADロックか記述されていない→JSRには「exclusive lock(=WRITEロック)」と書いてある

バージョニング必須

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 44

Page 45: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

PESSIMISTIC_FORCE_INCREMENTの実行ログ

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 45

[EL Fine]: sql:SELECT EMPNO, ENAME, SAL, VERSION FROM EMP2 WHERE (EMPNO = ?) FOR UPDATE bind => [101][EL Fine]: sql: SELECT EMPNO, ENAME, SAL, VERSION FROM EMP2 WHERE (EMPNO = ?) FOR UPDATE bind => [101]T1 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1]T1 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1][EL Fine]: sql: UPDATE EMP2 SET SAL = ?, VERSION = ? WHERE ((EMPNO = ?) AND (VERSION = ?)) bind => [510000, 2, 101, 1]T1 flush直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]T2 検索直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]T1 commit.T1 commit直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]T1 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]T2 flush直前 Emp2 [empno=101, ename=Nishida, sal=520000, version=2][EL Fine]: sql: UPDATE EMP2 SET SAL = ?, VERSION = ? WHERE ((EMPNO = ?) AND (VERSION = ?)) bind => [520000, 3, 101, 2]T2 flush直後 Emp2 [empno=101, ename=Nishida, sal=520000, version=3]T2 commit.T2 commit直後 Emp2 [empno=101, ename=Nishida, sal=520000, version=3]T2 終了 Emp2 [empno=101, ename=Nishida, sal=520000, version=3]

Page 46: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

実行の様子

① T1が検索、WRTIEロックを取得→sal=500000, version = 1

② T2が検索しようとするが、T1がWRITEロックを取得しているので、DB内の待ち行列に入る

③ T1がsal=500000+10000, version=1+1で更新→コミットしたらロック解放

④ T2が待ち行列から出て検索、WRITEロックを取得→sal=510000, version=2

⑤ T2がsal=510000+10000, version=2+1で更新→コミットしたらロック解放

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 46

Page 47: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 47

EclipseLink×PostgreSQLの場合

Page 48: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

EclipseLink×MySQLと挙動は同じ

以下、ログのみ記載

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 48

Page 49: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

NONEの実行ログ

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 49

[EL Fine]: sql: SELECT EMPNO, ENAME, SAL, VERSION FROM EMP2 WHERE (EMPNO = ?)bind => [101]

[EL Fine]: sql: SELECT EMPNO, ENAME, SAL, VERSION FROM EMP2 WHERE (EMPNO = ?)bind => [101]

T2 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1]T1 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1]T2 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T1 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1][EL Fine]: sql: UPDATE EMP2 SET SAL = ? WHERE (EMPNO = ?) bind => [510000, 101][EL Fine]: sql: UPDATE EMP2 SET SAL = ? WHERE (EMPNO = ?) bind => [510000, 101]T2 flush直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T1 flush直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T2 commit.T2 commit直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T2 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T1 commit.T1 commit直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T1 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]

Page 50: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

OPTIMISTICの実行ログ

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 50

[EL Fine]: sql: SELECT EMPNO, ENAME, SAL, VERSION FROM EMP2 WHERE (EMPNO = ?)bind => [101]

[EL Fine]: sql: SELECT EMPNO, ENAME, SAL, VERSION FROM EMP2 WHERE (EMPNO = ?)bind => [101]

T1 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1]T2 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1]T1 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T2 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1][EL Fine]: sql: UPDATE EMP2 SET SAL = ?, VERSION = ? WHERE ((EMPNO = ?) AND (VERSION = ?)) bind => [510000, 2, 101, 1][EL Fine]: sql: UPDATE EMP2 SET SAL = ?, VERSION = ? WHERE ((EMPNO = ?) AND (VERSION = ?)) bind => [510000, 2, 101, 1]T1 flush直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]T1 commit.T1 commit直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]T1 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]T2 例外発生:javax.persistence.OptimisticLockExceptionT2 rollback.T2 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=2](以下、スタックトレース)

Page 51: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

OPTIMISTIC_FORCE_INCREMENTの実行ログ

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 51

[EL Fine]: sql: SELECT EMPNO, ENAME, SAL, VERSION FROM EMP2 WHERE (EMPNO = ?)bind => [101]

[EL Fine]: sql: SELECT EMPNO, ENAME, SAL, VERSION FROM EMP2 WHERE (EMPNO = ?)bind => [101]

T1 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1]T2 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1]T2 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T1 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1][EL Fine]: sql: UPDATE EMP2 SET SAL = ?, VERSION = ? WHERE ((EMPNO = ?) AND (VERSION = ?)) bind => [510000, 2, 101, 1][EL Fine]: sql: UPDATE EMP2 SET SAL = ?, VERSION = ? WHERE ((EMPNO = ?) AND (VERSION = ?)) bind => [510000, 2, 101, 1]T1 flush直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]T1 commit.T1 commit直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]T1 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]T2 例外発生:javax.persistence.OptimisticLockExceptionT2 rollback.T2 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]

Page 52: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

PESSIMISTIC_READの実行ログ

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 52

[EL Fine]: sql: SELECT EMPNO, ENAME, SAL, VERSION FROM EMP2 WHERE (EMPNO = ?) FOR UPDATE bind => [101]T2 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1][EL Fine]: sql: SELECT EMPNO, ENAME, SAL, VERSION FROM EMP2 WHERE (EMPNO = ?) FOR UPDATE bind => [101]T2 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1][EL Fine]: sql: UPDATE EMP2 SET SAL = ? WHERE (EMPNO = ?) bind => [510000, 101]T2 flush直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T2 commit.T2 commit直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T2 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T1 検索直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T1 flush直前 Emp2 [empno=101, ename=Nishida, sal=520000, version=1][EL Fine]: sql: UPDATE EMP2 SET SAL = ? WHERE (EMPNO = ?) bind => [520000, 101]T1 flush直後 Emp2 [empno=101, ename=Nishida, sal=520000, version=1]T1 commit.T1 commit直後 Emp2 [empno=101, ename=Nishida, sal=520000, version=1]T1 終了 Emp2 [empno=101, ename=Nishida, sal=520000, version=1]

Page 53: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

PESSIMISTIC_WRITEの実行ログ

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 53

[EL Fine]: sql: SELECT EMPNO, ENAME, SAL, VERSION FROM EMP2 WHERE (EMPNO = ?) FOR UPDATE bind => [101]T1 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1][EL Fine]: sql: SELECT EMPNO, ENAME, SAL, VERSION FROM EMP2 WHERE (EMPNO = ?) FOR UPDATE bind => [101]T1 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1][EL Fine]: sql: UPDATE EMP2 SET SAL = ? WHERE (EMPNO = ?) bind => [510000, 101]T1 flush直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T2 検索直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T1 commit.T1 commit直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T1 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T2 flush直前 Emp2 [empno=101, ename=Nishida, sal=520000, version=1][EL Fine]: sql: UPDATE EMP2 SET SAL = ? WHERE (EMPNO = ?) bind => [520000, 101]T2 flush直後 Emp2 [empno=101, ename=Nishida, sal=520000, version=1]T2 commit.T2 commit直後 Emp2 [empno=101, ename=Nishida, sal=520000, version=1]T2 終了 Emp2 [empno=101, ename=Nishida, sal=520000, version=1]

Page 54: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

PESSIMISTIC_FORCE_INCREMENTの実行ログ

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 54

[EL Fine]: sql: SELECT EMPNO, ENAME, SAL, VERSION FROM EMP2 WHERE (EMPNO = ?) FOR UPDATE bind => [101]T2 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1][EL Fine]: sql: SELECT EMPNO, ENAME, SAL, VERSION FROM EMP2 WHERE (EMPNO = ?) FOR UPDATE bind => [101]T2 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1][EL Fine]: sql: UPDATE EMP2 SET SAL = ?, VERSION = ? WHERE ((EMPNO = ?) AND (VERSION = ?)) bind => [510000, 2, 101, 1]T2 flush直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]T1 検索直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]T2 commit.T2 commit直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]T2 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]T1 flush直前 Emp2 [empno=101, ename=Nishida, sal=520000, version=2][EL Fine]: sql: UPDATE EMP2 SET SAL = ?, VERSION = ? WHERE ((EMPNO = ?) AND (VERSION = ?)) bind => [520000, 3, 101, 2]T1 flush直後 Emp2 [empno=101, ename=Nishida, sal=520000, version=3]T1 commit.T1 commit直後 Emp2 [empno=101, ename=Nishida, sal=520000, version=3]T1 終了 Emp2 [empno=101, ename=Nishida, sal=520000, version=3]

Page 55: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 55

Hibernate×MySQLの場合

Page 56: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

NONE

発行されるSQLが微妙に異なるが、最終結果はEclipseLinkと同じ

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 56

Page 57: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

NONEの実行ログ

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 57

Hibernate: select emp2x0_.empno as empno1_0_0_, emp2x0_.ename as ename2_0_0_, emp2x0_.sal as sal3_0_0_, emp2x0_.version as version4_0_0_ from Emp2 emp2x0_ where emp2x0_.empno=?Hibernate: select emp2x0_.empno as empno1_0_0_, emp2x0_.ename as ename2_0_0_, emp2x0_.sal as sal3_0_0_, emp2x0_.version as version4_0_0_ from Emp2 emp2x0_ where emp2x0_.empno=?T1 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1]T2 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1]T2 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T1 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]Hibernate: update Emp2 set ename=?, sal=?, version=? where empno=?Hibernate: update Emp2 set ename=?, sal=?, version=? where empno=?T2 flush直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T1 flush直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T2 commit.T2 commit直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T2 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T1 commit.T1 commit直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T1 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]

Page 58: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

OPTIMISTIC

ロールバックされたエンティティのバージョンはインクリメントされない

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 58

Page 59: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

OPTIMISTICの実行ログ

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 59

Hibernate: select emp2x0_.empno as empno1_0_0_, emp2x0_.ename as ename2_0_0_, emp2x0_.sal as sal3_0_0_, emp2x0_.version as version4_0_0_ from Emp2 emp2x0_ where emp2x0_.empno=?Hibernate: select emp2x0_.empno as empno1_0_0_, emp2x0_.ename as ename2_0_0_, emp2x0_.sal as sal3_0_0_, emp2x0_.version as version4_0_0_ from Emp2 emp2x0_ where emp2x0_.empno=?T1 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1]T2 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1]T1 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T2 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]Hibernate: update Emp2 set ename=?, sal=?, version=? where empno=? and version=?Hibernate: update Emp2 set ename=?, sal=?, version=? where empno=? and version=?T1 flush直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]Hibernate: select version from Emp2 where empno =?T2 例外発生:javax.persistence.OptimisticLockExceptionT1 commit.T1 commit直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]T1 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]T2 rollback.T2 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]

Page 60: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

実行後のバージョン値

ロールバックされたエンティティは、バージョン値がインクリメントされない

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 60

T2 例外発生:javax.persistence.OptimisticLockExceptionT1 commit.T1 commit直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]T1 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]T2 rollback.T2 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]

Page 61: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

謎のSELECT文

T1がcommit時に発行している→コミット前に、他のトランザクションからバージョンが変更されていないかどうかを確かめるためと思われる

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 61

T1 flush直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]Hibernate: select version from Emp2 where empno =?T2 例外発生:javax.persistence.OptimisticLockException

Page 62: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

OPTIMISTIC_FORCE_INCREMENT

flush時とcommit時の2回、バージョンがインクリメントされる

「謎のSELECT文」によるバージョン確認は無い

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 62

Page 63: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

OPTIMISTIC_FORCE_INCREMENTの実行ログ

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 63

Hibernate: select emp2x0_.empno as empno1_0_0_, emp2x0_.ename as ename2_0_0_, emp2x0_.sal as sal3_0_0_, emp2x0_.version as version4_0_0_ from Emp2 emp2x0_ where emp2x0_.empno=?Hibernate: select emp2x0_.empno as empno1_0_0_, emp2x0_.ename as ename2_0_0_, emp2x0_.sal as sal3_0_0_, emp2x0_.version as version4_0_0_ from Emp2 emp2x0_ where emp2x0_.empno=?T2 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1]T1 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1]T1 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T2 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]Hibernate: update Emp2 set ename=?, sal=?, version=? where empno=? and version=?Hibernate: update Emp2 set ename=?, sal=?, version=? where empno=? and version=?T2 flush直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]Hibernate: update Emp2 set version=? where empno=? and version=?T1 例外発生:javax.persistence.OptimisticLockExceptionT2 commit.T2 commit直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=3]T2 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=3]T1 rollback.T1 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]

Page 64: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

実行後のバージョン値

flush時・commit時の2回、バージョンがインクリメントされる

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 64

T2 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]Hibernate: update Emp2 set ename=?, sal=?, version=? where empno=? and version=?T2 flush直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]Hibernate: update Emp2 set version=? where empno=? and version=?T2 commit.T2 commit直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=3]

Page 65: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

PESSIMISTIC_READ

SELECT・・・LOCK IN SHARE MODE文が実行され、READロックが取得される→T1とT2が互いにロック解放待ちとなり、デッドロック発生→片方はロールバックされ、もう片方のみがコミットされる

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 65

Page 66: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

PESSIMISTIC_READの実行ログ

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 66

Hibernate: select emp2x0_.empno as empno1_0_0_, emp2x0_.ename as ename2_0_0_, emp2x0_.sal as sal3_0_0_, emp2x0_.version as version4_0_0_ from Emp2 emp2x0_ where emp2x0_.empno=? lock in share modeHibernate: select emp2x0_.empno as empno1_0_0_, emp2x0_.ename as ename2_0_0_, emp2x0_.sal as sal3_0_0_, emp2x0_.version as version4_0_0_ from Emp2 emp2x0_ where emp2x0_.empno=? lock in share modeT2 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1]T1 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1]T1 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T2 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]Hibernate: update Emp2 set ename=?, sal=?, version=? where empno=?Hibernate: update Emp2 set ename=?, sal=?, version=? where empno=?T1 flush直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]ERROR: Deadlock found when trying to get lock; try restarting transactionT1 commit.T1 commit直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T1 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T2 例外発生:javax.persistence.PersistenceExceptionT2 rollback.T2 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]

Page 67: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

PESSIMISTIC_WRITE

SELECT FOR UPDATE文が実行され、WRITEロックが取得される→片方のトランザクションがコミット・ロック解放後、もう片方のトランザクションも実行される

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 67

Page 68: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

PESSIMISTIC_WRITEの実行ログ

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 68

Hibernate: select emp2x0_.empno as empno1_0_0_, emp2x0_.ename as ename2_0_0_, emp2x0_.sal as sal3_0_0_, emp2x0_.version as version4_0_0_ from Emp2 emp2x0_ where emp2x0_.empno=? for updateHibernate: select emp2x0_.empno as empno1_0_0_, emp2x0_.ename as ename2_0_0_, emp2x0_.sal as sal3_0_0_, emp2x0_.version as version4_0_0_ from Emp2 emp2x0_ where emp2x0_.empno=? for updateT1 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1]T1 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]Hibernate: update Emp2 set ename=?, sal=?, version=? where empno=?T1 flush直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T2 検索直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T1 commit.T1 commit直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T1 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]Hibernate: update Emp2 set ename=?, sal=?, version=? where empno=?T2 flush直後 Emp2 [empno=101, ename=Nishida, sal=520000, version=1]T2 commit.T2 commit直後 Emp2 [empno=101, ename=Nishida, sal=520000, version=1]T2 終了 Emp2 [empno=101, ename=Nishida, sal=520000, version=1]

Page 69: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

PESSIMISTIC_FORCE_INCREMENT

SELECT FOR UPDATE文が実行され、WRITEロックが取得される→片方のトランザクションがコミット・ロック解放後、もう片方のトランザクションも実行される

OPTIMISTIC_FORCE_INCREMENTと同様、flush時・commit時の2回、バージョンがインクリメントされる

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 69

Page 70: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

PESSIMISTIC_FORCE_INCREMENTの実行ログ

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 70

Hibernate: select emp2x0_.empno as empno1_0_0_, emp2x0_.ename as ename2_0_0_, emp2x0_.sal as sal3_0_0_, emp2x0_.version as version4_0_0_ from Emp2 emp2x0_ where emp2x0_.empno=? for updateHibernate: select emp2x0_.empno as empno1_0_0_, emp2x0_.ename as ename2_0_0_, emp2x0_.sal as sal3_0_0_, emp2x0_.version as version4_0_0_ from Emp2 emp2x0_ where emp2x0_.empno=? for updateHibernate: update Emp2 set version=? where empno=? and version=?T1 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=2]T1 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]Hibernate: update Emp2 set ename=?, sal=?, version=? where empno=? and version=?T1 flush直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=3]Hibernate: update Emp2 set version=? where empno=? and version=?T1 commit.T1 commit直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=3]T1 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=3]T2 検索直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=4]T2 flush直前 Emp2 [empno=101, ename=Nishida, sal=520000, version=4]Hibernate: update Emp2 set ename=?, sal=?, version=? where empno=? and version=?T2 flush直後 Emp2 [empno=101, ename=Nishida, sal=520000, version=5]T2 commit.T2 commit直後 Emp2 [empno=101, ename=Nishida, sal=520000, version=5]T2 終了 Emp2 [empno=101, ename=Nishida, sal=520000, version=5]

Page 71: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 71

Hibernate×PostgreSQLの場合

Page 72: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

Hibernate×PostgreSQL

PESSIMISTIC_FORCE_INCREMENTのみ、MySQLの場合と挙動が異なる

READロックはSELECT FOR SHARE文

上記以外は同じなので、ログのみ記載

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 72

Page 73: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

NONEの実行ログ

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 73

Hibernate: select emp2x0_.empno as empno1_0_0_, emp2x0_.ename as ename2_0_0_, emp2x0_.sal as sal3_0_0_, emp2x0_.version as version4_0_0_ from Emp2 emp2x0_ where emp2x0_.empno=?Hibernate: select emp2x0_.empno as empno1_0_0_, emp2x0_.ename as ename2_0_0_, emp2x0_.sal as sal3_0_0_, emp2x0_.version as version4_0_0_ from Emp2 emp2x0_ where emp2x0_.empno=?T1 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1]T2 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1]T1 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T2 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]Hibernate: update Emp2 set ename=?, sal=?, version=? where empno=?Hibernate: update Emp2 set ename=?, sal=?, version=? where empno=?T1 flush直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T2 flush直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T1 commit.T1 commit直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T1 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T2 commit.T2 commit直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T2 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]

Page 74: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

OPTIMISTICの実行ログ

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 74

Hibernate: select emp2x0_.empno as empno1_0_0_, emp2x0_.ename as ename2_0_0_, emp2x0_.sal as sal3_0_0_, emp2x0_.version as version4_0_0_ from Emp2 emp2x0_ where emp2x0_.empno=?Hibernate: select emp2x0_.empno as empno1_0_0_, emp2x0_.ename as ename2_0_0_, emp2x0_.sal as sal3_0_0_, emp2x0_.version as version4_0_0_ from Emp2 emp2x0_ where emp2x0_.empno=?T2 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1]T1 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1]T2 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T1 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]Hibernate: update Emp2 set ename=?, sal=?, version=? where empno=? and version=?Hibernate: update Emp2 set ename=?, sal=?, version=? where empno=? and version=?T1 flush直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]Hibernate: select version from Emp2 where empno =?T1 commit.T1 commit直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]T1 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]T2 例外発生:javax.persistence.OptimisticLockExceptionT2 rollback.T2 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]

Page 75: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

OPTIMISTIC_FORCE_INCREMENTの実行ログ

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 75

Hibernate: select emp2x0_.empno as empno1_0_0_, emp2x0_.ename as ename2_0_0_, emp2x0_.sal as sal3_0_0_, emp2x0_.version as version4_0_0_ from Emp2 emp2x0_ where emp2x0_.empno=?Hibernate: select emp2x0_.empno as empno1_0_0_, emp2x0_.ename as ename2_0_0_, emp2x0_.sal as sal3_0_0_, emp2x0_.version as version4_0_0_ from Emp2 emp2x0_ where emp2x0_.empno=?T1 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1]T2 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1]T2 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T1 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]Hibernate: update Emp2 set ename=?, sal=?, version=? where empno=? and version=?Hibernate: update Emp2 set ename=?, sal=?, version=? where empno=? and version=?T1 flush直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]Hibernate: update Emp2 set version=? where empno=? and version=?T1 commit.T1 commit直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=3]T1 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=3]T2 例外発生:javax.persistence.OptimisticLockExceptionT2 rollback.T2 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]

Page 76: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

PESSIMISTIC_READの実行ログ

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 76

Hibernate: select emp2x0_.empno as empno1_0_0_, emp2x0_.ename as ename2_0_0_, emp2x0_.sal as sal3_0_0_, emp2x0_.version as version4_0_0_ from Emp2 emp2x0_ where emp2x0_.empno=? for shareHibernate: select emp2x0_.empno as empno1_0_0_, emp2x0_.ename as ename2_0_0_, emp2x0_.sal as sal3_0_0_, emp2x0_.version as version4_0_0_ from Emp2 emp2x0_ where emp2x0_.empno=? for shareT1 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1]T2 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1]T1 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T2 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]Hibernate: update Emp2 set ename=?, sal=?, version=? where empno=?Hibernate: update Emp2 set ename=?, sal=?, version=? where empno=?T1 flush直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]ERROR: ERROR: deadlock detectedT2 例外発生:javax.persistence.PersistenceExceptionT2 rollback.T2 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T1 commit.T1 commit直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T1 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]

Page 77: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

PESSIMISTIC_WRITEの実行ログ

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 77

Hibernate: select emp2x0_.empno as empno1_0_0_, emp2x0_.ename as ename2_0_0_, emp2x0_.sal as sal3_0_0_, emp2x0_.version as version4_0_0_ from Emp2 emp2x0_ where emp2x0_.empno=? for updateHibernate: select emp2x0_.empno as empno1_0_0_, emp2x0_.ename as ename2_0_0_, emp2x0_.sal as sal3_0_0_, emp2x0_.version as version4_0_0_ from Emp2 emp2x0_ where emp2x0_.empno=? for updateT1 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=1]T1 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]Hibernate: update Emp2 set ename=?, sal=?, version=? where empno=?T1 flush直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T2 検索直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T1 commit.T1 commit直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T1 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=1]T2 flush直前 Emp2 [empno=101, ename=Nishida, sal=520000, version=1]Hibernate: update Emp2 set ename=?, sal=?, version=? where empno=?T2 flush直後 Emp2 [empno=101, ename=Nishida, sal=520000, version=1]T2 commit.T2 commit直後 Emp2 [empno=101, ename=Nishida, sal=520000, version=1]T2 終了 Emp2 [empno=101, ename=Nishida, sal=520000, version=1]

Page 78: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

PESSIMISTIC_FORCE_INCREMENT

SELECT FOR UPDATE文にNOWAITオプションが付加される→後からのトランザクションは、待つことができないため例外発生

NOWAITオプションはMySQLには無い

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 78

Page 79: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

PESSIMISTIC_FORCE_INCREMENTの実行ログ

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 79

Hibernate: select emp2x0_.empno as empno1_0_0_, emp2x0_.ename as ename2_0_0_, emp2x0_.sal as sal3_0_0_, emp2x0_.version as version4_0_0_ from Emp2 emp2x0_ where emp2x0_.empno=? for update nowaitERROR: ERROR: could not obtain lock on row in relation "emp2"Hibernate: select emp2x0_.empno as empno1_0_0_, emp2x0_.ename as ename2_0_0_, emp2x0_.sal as sal3_0_0_, emp2x0_.version as version4_0_0_ from Emp2 emp2x0_ where emp2x0_.empno=? for update nowaitHibernate: update Emp2 set version=? where empno=? and version=?T1 検索直後 Emp2 [empno=101, ename=Nishida, sal=500000, version=2]T1 flush直前 Emp2 [empno=101, ename=Nishida, sal=510000, version=2]Hibernate: update Emp2 set ename=?, sal=?, version=? where empno=? and version=?T1 flush直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=3]T1 commit.T1 commit直後 Emp2 [empno=101, ename=Nishida, sal=510000, version=3]T1 終了 Emp2 [empno=101, ename=Nishida, sal=510000, version=3]

Page 80: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 80

まとめ

Page 81: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

まとめ

ロックモード 相違点

OPTIMISTIC ・EclipseLinkでは、ロールバックされたエンティティのバージョン値もインクリメントされる

OPTIMISTIC_FORCE_INCREMENT

・Hibernateでは、flush時とcommit時の2回、バージョンがインクリメントされる

PESSIMISTIC_READ

・EclipseLinkでは、READロックではなくWRITEロックになる

PESSIMISTIC_FORCE_INCREMENT

・Hibernateでは、flush時とcommit時の2回、バージョンがインクリメントされる・Hibernate×PostgreSQLでは、SELECT FOR UPDATE文にNOWAITオプションが付加

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 81

Page 82: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

開発時の注意

必ずSQLログを出力して、一通りの挙動を確認しておきましょう!

JPAのSQLログの順番は、実際にDB内で実行される順番とは異なる場合があるので、注意しましょう!

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 82

Page 83: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

今回、入れられなかった内容

悲観的ロックのタイムアウト

javax.persistence.lock.timeout

悲観的ロックのスコープ

javax.persistence.lock.scope

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 83

Page 84: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

技術の普及=教育の普及

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 84

教育の普及

技術の普及

Page 85: JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc

Copyright UCHIDA HUMAN DEVELOPMENT all rights reserved. 85