jOOQ同時制御-Optimicロック/persimicロック


1.同時制御


なぜ必要なのか

  • スプリングフレームワークは基本的にマルチスレッド環境であるため、異なるリクエストにより開発者が望んでいない結果になる可能性があります.
  • 💡 ソリューション

  • アプリケーション側で同時性(書き込みベース)を制御できますが、この文書ではJavaネイティブクエリービルダーjOOQを使用してデバイス側の同時性(トランザクションベース)を制御します.
  • 2.同期制御前の問題


    🔱 50トピックごとに1回記事を表示

  • を掛けない
  •     public class NoLockWorker implements Runnable{
            private CountDownLatch countDownLatch;
    
            public NoLockWorker(CountDownLatch countDownLatch){
                this.countDownLatch = countDownLatch;
            }
    
            @Override
            public void run() {
                postService.plusHitById(BASE_ID);
                countDownLatch.countDown();
            }
        }
        
        ------------------------------------------------------------------
    
        public void plusHitById(Long id){
            context.transaction(configuration ->{
                final Post post = DSL.using(configuration)
                                    .selectFrom(POST)
                                    .where(POST.ID.eq(id))
                                    .fetchOneInto(Post.class);
    
                final Long hit = post.getHit();
    
                DSL.using(configuration)
                        .update(POST)
                        .set(POST.HIT, hit + 1L)
                        .where(POST.ID.eq(id))
                        .execute();
            });
        }
  • 結果

    テストに失敗しました
    =>hitは50個ですが、50個のマルチスレッドの複数のトランザクションが同時に1つのROWに集中しているため、開発者はhitが5になることを望んでいません.
  • 3. Optimistmic Lock / Pessimistic Lock


    🔒 Optimistic Lockとは?

  • トランザクションが競合しないと仮定します.(楽観)
  • バージョン、タイムスタンプなどの列を使用して、別のトランザクションがバージョンで更新された場合、例外(更新がある場合はバージョン1が追加されます)
  • が発生します.
  • 更新後のロックは、リアルタイムでデータを表示する必要がある業務に有利である

    🔏 Persimisic Lockとは?

  • トランザクションが常に競合していると仮定すると、私たちは先に失敗します.(悲観的なやり方)
  • データベースで提供されるRockを使用します.Persimisic Lockにはさまざまなタイプがあるので、ビジネスごとに
  • を選択することをお勧めします.
  • Optimical Lockと比較して、より信頼性の高い方法ですが、リアルタイムのデータ・トラフィックとの同期性は一致しません.
  • 4.Optimical Lockで同期を制御する


    吸い取りVersion列の追加


  • データベース(Postテーブル)


  • Gradle
    =>jOOQ設定では、Gradleで使用するバージョン列を指定できます.

  • DslContextの設定時にwith ExecuteWithOptimisticsLock(true)オプションを追加します.
    また、OptimisticsLockが競合によって異常が放出されると、コネクタを噛み続ける場合があり、try with resourcesを使用して競合が発生した場合は直ちにコネクタを閉じます.
        public void plusHitOptimisticById(Long id){
            try(Connection connection = DriverManager.getConnection(URL, USERNAME, PASSWORD)){
                DSL.using(connection, SQLDialect.MYSQL, new Settings().withExecuteWithOptimisticLocking(true))
                        .transaction(configuration -> {
                            PostRecord postRecord = DSL.using(configuration)
                                                      .fetchOne(POST, POST.ID.eq(id));
    
                            postRecord.setHit(postRecord.getHit() + 1L);
                            postRecord.store();
                        });
            } catch (SQLException e) {
                log.info("error code : {}, error : {}", e.getErrorCode(), e);
            }
        }
  • 結果

    テストに合格する
    =>同じバージョンで複数のトランザクションが競合している場合は、前のトランザクションのみが正常に更新され、残りのトランザクションに例外が発生します.例外を取得し、リフレッシュして同期を制御します.
  • 5.Persimisic Lockを使用して同期を制御する


    Persimisic Lock for Update


    forUpdateには、他のトランザクションのCRUDが許可しないという特徴があります.
  • DslContextトランザクションの対応するデータ・クエリー・セクションに、ForUpdateというメソッドを追加します.
  • 結果

    テストに合格する
    =>スレッドごとにトランザクションがあり、ROWにアクセスして順番にクエリ->更新し、hitが50の結果を得ることができます.
  • に感銘を与える


    💡


    同期性を制御する必要がある場合は、アプリケーション・レベルで制御するか、データベース・レベルで制御するかを考慮する必要があります.
    データベース・レベルで制御する場合は、ビジネスに適したロックを考慮して選択できますが、Optimical Lockを使用する場合は、それらを構成する方法を考慮する必要があります.
    今回は个人的に勉强していた时も、私たちの部分を会社のプロジェクトに応用したので、もっと面白かったです.

    ソース


    https://www.jooq.org/doc/latest/manual/sql-execution/crud-with-updatablerecords/optimistic-locking/