※ ChatGPTを利用し、要約された質問です(原文:2人のユーザーが同時アクセス時のトランザクション)
2人のユーザーが同時アクセス時のトランザクションで発生する問題と解決方法
このQ&Aのポイント
2人のユーザーが同時アクセスした際に、mysqlデータベースにおいてAtomicity(原子性)が保たれない現象が発生することがあります。
問題の現象は、同じレコードに対して行われるトランザクションの途中で中途半端な値を読み込んでしまうことです。
この問題は排他ロックをかけることやトランザクションのタイムアウトを設定することで解決することができます。
AユーザーとBユーザーがいてmysqlデータベース(innoDB)に同時アクセスします。
(実際にはphpから行っています)
このときAtomicity(原子性)が保たれていないように見える現象が3割程度の確率で発生します。
以下がその問題の現象が発生する流れです。
error_logで出力したものです。
beginTransactionとcommitTransactionは、実際にはphpのpdoのメソッドを呼んでいます。
テーブル1 : state (初期値 0)
テーブル2 : answer (初期値 0)
stateに対しては常に行の排他ロックをかけている(必ずテーブル2に対するアクセス前)
(念のため、stateに対するアクセスはもちろん同じレコードに対してです。
answerに対するアクセスももちろん同じレコードに対してです。)
B : beginTransaction 前
B : beginTransaction 後
B : answer=0を読み込み確認
B : state=0を読み込み確認
B : answer=1を書き込む
B : state=3を書き込む
A : beginTransaction 前
A : beginTransaction 後
B : commitTransaction 前 :state=3を読み込み確認
B : commitTransaction 前 :answer=1を読み込み確認
A : state=3を読み込み確認 // 問題
A : answer=0を読み込み確認 //
A : commitTransaction 前 :state=3を読み込み確認
A : commitTransaction 前 :answer=0を読み込み確認
B : commitTransaction 後 :state=3を読み込み確認
B : commitTransaction 後 :answer=1を読み込み確認
A : commitTransaction 後 :state=3を読み込み確認
A : commitTransaction 後 :answer=1を読み込み確認
問題はAユーザー側の途中の処理でstate=3とanswer=0という、中途半端な組み合わせの値を読み込んでしまっていることです。
あるべき状態はAユーザー側で、state=0とanswer=0あるいはstate=3とanswer=1と確認できることです。
一体何が起こっているのでしょうか。
また解決方法はどのようなものでしょうか。
直接の答えでなくてもヒントだけでも頂けると助かります。
お礼
stateがFOR UPDATEにより最新のものを読み込んでいるのに対し、answerはスナップショットを読んでいてその違いが原因でした。 ありがとうございました。
補足
stateはSELECT FOR UPDATEにより行ロックをかけているので「A : state=3を読み込み確認 // 問題」に来た時点でBのCOMMITは既に完了したものと推測しています。もし完了していなければ待たされているはずです。 answerは確かにロックしていませんが、この場合順番がstateのほうが先なので問題ないと思っています。 という考えなのですがいかがでしょうか。 (リンクはとても参考になりました) ただちょっと思ったのはBのCOMMITがmysqlの内部的に行われているときstateのロックは外れたけど、まだCOMMIT作業は終わってなくてanswerを読んでしまっている、つまり全然アトミックでない、というまずあり得なさそうな想像もしています。 あとはトランザクションの開始がAとBでかぶっているのでその辺に問題があるのかなどです。