質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

ドメイン駆動設計

ドメイン駆動設計(Domain-driven design, DDD)とは、ソフトウェアの設計手法、および設計思想や哲学のことです。ドメインモデル構築の際に、設計上の判断を決定する枠組みとドメイン設計に関して議論するボキャブラリを提供するものです。

Q&A

解決済

2回答

745閲覧

異なるトランザクションでのEntity更新処理によるデータ不整合の対策

rashild

総合スコア24

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

ドメイン駆動設計

ドメイン駆動設計(Domain-driven design, DDD)とは、ソフトウェアの設計手法、および設計思想や哲学のことです。ドメインモデル構築の際に、設計上の判断を決定する枠組みとドメイン設計に関して議論するボキャブラリを提供するものです。

0グッド

0クリップ

投稿2023/04/11 01:17

異なるトランザクションでのEntity更新処理によるロストアップデート?の対策

※DBの想定としてはMySQLでデフォルトのRepeatable Readです。
質問内容
・複数のリクエストでほぼ同時にEntityを取得し、それぞれ更新処理をする際にロストアップデートが起きるのではないか

例:銀行口座に50万円があったとして、ほぼ同時に

  1. 口座の持ち主が30万円引き出す。
  2. 銀行が40万円の自動振替を行う

を行った場合に、異なるトランザクションで口座残高が50万円のEntityが2つ作成され
それぞれEntityへの操作を行うことで口座残高が変更されたEntityがRepositoryを介してDBに保存されます。
この時に期待する処理は上記2つの処理が直列的に実行され、自動振替を行う際に残高がマイナスになりエラーが発生することです。

ですが2つの処理は同時に残高50万円の状態のEntityを保持しているため、更新時には
1.残高20万円で保存される
2.残高10万円で保存される
となり元々ある残高以上の取引が行われてしまい、最終的に残高10万円でDBに保存されてしまいます。

考えた対策

口座を更新する際には排他ロックをかけることで複数のリクエストに参照させないことで防ぐことができると思うのですが、ロックすることで待機時間が増えてしまうということもあり他の方法を考えています。

他に方法があれば教えていただきたいです。
よろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

実際の銀行の処理は煩雑なので参考にはなりませんが簡素化して以下

SQL

1create table bank (user varchar(10) primary key,zandaka int unsigned); 2insert into bank values 3('a',500000), 4('b',500000), 5('c',0);

上記情報に対して、以下2つのクエリを発行します
先行分を実行中に後行分を実行。

SQL

1start transaction; 2select 1 from bank where user='a' limit 1 for update; 3select sleep(5); 4update bank set zandaka=zandaka-200000 where user='a'; 5commit; 6 7start transaction; 8update bank set zandaka=zandaka-100000 where user='b'; 9update bank set zandaka=zandaka-400000 where user='a'; 10commit;

上記、先行分は5秒後に実行されてaの値は30万になりますが
後行分はaのzandakaがマイナスになるためrollbackされてbの引き出しもなかったことになります

排他ロックをかけることで複数のリクエストに参照させないことで防ぐことができると思うのですが、ロックすることで待機時間が増えてしまう

InnoDBはレコードロックのためaユーザーだけの処理をするかぎり他のbやcの処理に影響はありませんのでご心配には及ばないと思います
仮に上記後行処理がbだけ引き出す処理であれば、aの処理がおわる前にbの処理はコミットされます

投稿2023/04/11 03:39

編集2023/04/11 03:48
yambejp

総合スコア114839

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

rashild

2023/04/12 02:53

ご返答ありがとうございます。 回答としては レコードロックなので待機時間を気にせず、排他ロックを使用しても問題ないです。 ということでしょうか? 確かにその通りだと思います。 正直業務で意図的にロックを使用したことがなかったため、回答をいただいて確信が持てました。 さらに排他ロックであれば、7行目からの後行処理で更新処理の前にSELECTでaの口座を取得する際に すでにロックされていた処理が実行された後の値(30万円)を取得できるはずなので Entityへの詰め替え時にも正しい値が代入できると思いました。
guest

0

残高そのものを設定[残高←差引残高]するのではなく、計算式[残高←(残高-引落金額)]による更新を、[残高-引落金額 > 0]の条件で実行し、更新されなければエラーとして扱えば良いかと思います。

投稿2023/04/11 02:27

編集2023/04/11 03:59
sazi

総合スコア25195

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

rashild

2023/04/12 03:14

ご返答ありがとうございます。 思いつかない方法でした。 確かにSQLを実行する場合には、残高項目をunsigned intにすればエラーが発生し不整合を防げると思います。 少し気になることがあるとすれば、私はDDDでの開発を行っており 可能な限り値のバリデーションはEntityやValueObject等を使用してシステム内で完結させたいと思っていたため DBの型定義でエラーが起きることが新鮮でした。 ※更新後のレコードの値をEntityに詰め替える時にエラーが起きればDBには依存していないのかもしれません。 参考にさせてもらいます。
rashild

2023/04/15 11:37

その後、調べてみたのですが MySQLのデフォルトのトランザクション分離レベルであるRepeatable Readでは トランザクション中に他のトランザクションのコミットによるデータの変更(ファジーリード) は反映されないようで、今回の場合 計算式[残高←(残高-引落金額)] = [10←50-40] となってしまい、今回の質問の解決にはなりませんでした。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問