Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mysql 5.7 でdeadlockが発生 #630

Open
nobuhiko opened this issue Nov 10, 2022 · 17 comments · Fixed by #1097
Open

mysql 5.7 でdeadlockが発生 #630

nobuhiko opened this issue Nov 10, 2022 · 17 comments · Fixed by #1097
Milestone

Comments

@nobuhiko
Copy link
Contributor

$objQuery->delete($table, $where, array($order_id));

mysql 5.7 でdeadlockがまれに発生する可能性がある、らしい
https://www.slideshare.net/yuyamada777/delete-77702365

@nanasess
Copy link
Contributor

行の存在を確認してから DELETE するのが良い感じですかね

@nobuhiko
Copy link
Contributor Author

そうみたいです

@nanasess nanasess added this to the 2.x milestone Dec 16, 2024
nobuhiko added a commit to nobuhiko/ec-cube2 that referenced this issue Dec 23, 2024
@seasoftjapan
Copy link
Contributor

デッドロックを避け DELETE が走らなかったところで、片方のトランザクションの INSERT は通常キー重複で落ちる気がします。EC-CUBE の動作上はシステムエラーなので少なくともフロントでは差が無い気がします。むしろ落ちずに通ってしまう事があったら、その方が大問題なので、その観点では MySQL は PostgreSQL より安全方向に振った標準設定であり、#1097 はそれを殺す変更になる気がします。

安全方向に振る価値が無く、デッドロックを避けるのが最優先であれば、PostgreSQL 同様に READ COMMITTED にする方がスマートだと思います。

SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

#1097 では対応できない DELETE 以外の行ロックとの組み合わせにも対応します。

@seasoftjapan seasoftjapan reopened this Dec 24, 2024
@seasoftjapan
Copy link
Contributor

上とは別の観点で、#1097 の手法で行くにしても、PostgreSQL を巻き込まない配慮は可能そうに思います。

@nanasess
Copy link
Contributor

@seasoftjapan キー重複での INSERT は置いておいて、 DELETE の行を count() で確認すること自体に害は無さそうですが、いかがでしょうか?

@seasoftjapan
Copy link
Contributor

@nanasess PostgreSQLが対象外となれば、当座黙認できる認識です。

MySQLは、後々時間ある時にでも手がけるかもしれませんが、別チケットでOKです。

@nanasess
Copy link
Contributor

@seasoftjapan DELETE の前に、 DELETE と同じ WHERE の条件で COUNT をコールすると、 PostgreSQL でどんな悪影響が考えられますでしょうか?

@seasoftjapan
Copy link
Contributor

@nanasess 無駄な実行の観点です。

@seasoftjapan
Copy link
Contributor

あと、count() ではなく、exists() を使った方が良いですね。

@seasoftjapan
Copy link
Contributor

@nobuhiko
| @seasoftjapan おっしゃっていることが全くわかりません。 INSERT は落ちませんよ。

想定シナリオが相違していたようです。
ご指摘ありがとうございます。

参照先記事のデッドロック例で双方のセッションが delete from lock_test where id = 100 だったため、registerShipping() に置き換えると $order_id が同一という想定で考えていました。通常の操作では発生しないと思うので、多重要求や決済システム絡みでWEBブラウザーとWEBフックが同時に動いたケースなど。

しかし、実際には $order_id は異なっていても生じ得ると理解いたしました。異なる場合には、INSERT はキー重複せず落ちないですね。

@nobuhiko
Copy link
Contributor Author

@seasoftjapan
ちなみに空DELETEは無駄な実行ではないのでしょうか?

@nobuhiko
Copy link
Contributor Author

@seasoftjapan
発生条件としては、新規受注で、registerShippingは必ず空DELETEを行うので受注が多く発生するサイトやバッチ処理で受注を作ったりするとネクストキーロックでdeadlockが発生し、あとからの受注が作られなくなります。

@seasoftjapan
Copy link
Contributor

@seasoftjapan ちなみに空DELETEは無駄な実行ではないのでしょうか?

無駄ですね。しかし、SELECT するのとコストに差は無い認識でもあります。
よって、空 DELETE の場合はコストに大差はなく、実際に削除が必要な場合のコストが増えた認識です。

@seasoftjapan
Copy link
Contributor

別の観点で、トランザクションを開いていない場合、MySQL でも無駄となりそうです。

ここまでに挙げた課題について、改善できそうな部分は追って PR しようと思います。

@seasoftjapan
Copy link
Contributor

#1097 のコメントで、同一インスタンスで setOrder() など → select() など → delete() の流れで実行するとコケる問題が上がりましたが、#1116 で回避を見込んでいます。

@seasoftjapan
Copy link
Contributor

備忘録: PostgreSQL では、REPEATABLE READ でも SERIALIZABLE でも、このケースのデッドロックは発生しなかった。

seasoftjapan added a commit that referenced this issue Jan 5, 2025
- トランザクション分離レベルを意識して、無駄な SQL 実行を避ける。#630 (comment)
- 幾らかコストが低い exists() を使う。#630 (comment)
- SC_DB_DBFactory のテストが無さそうなので、今回追加したメソッドの他、ごく基本的な部分のみ実装する。
seasoftjapan added a commit that referenced this issue Jan 7, 2025
- トランザクション分離レベルを意識して、無駄な SQL 実行を避ける。#630 (comment)
- 実行コストが幾らか低い傾向の exists() を使う。#630 (comment)
- SC_DB_DBFactory のテストが無さそうなので、今回追加したメソッドの他、ごく基本的な部分を実装する。
- 削除件数を返さない不具合を修正。
- アノテーションを補完。
seasoftjapan added a commit that referenced this issue Jan 8, 2025
mysql 5.7 でdeadlockが発生 #630
@seasoftjapan
Copy link
Contributor

備忘録: 後続の改善案。今のところ、直ぐやる必要はなく、別チケットで良いと考える。

  • 現在の分離レベルを変数に持たせる。
  • 現在の分離レベルは、実装済みの DBMS 固定ごとのソース直書きに加えて、設定により以下のいずれかも選択可能とする。
    • 頭一発で現在のリアルな分離レベルを取得する。
    • ソース記載の定数値より優先して、分離レベルを指定する。
  • 分離レベルの変更に対応する。
    • 設定に基づき頭一発実行だけなら簡単そう。途中変更にも対応すると、相手方への影響も考慮が必要かも (デッドロックでなく通常のロック待ちで問題ないのかも)。
  • exists() を実行すべきかの判定結果 (若しくは判定条件の一部) は、キャッシュできそう。ただ、一般的な運用の範囲では、効果はかなり薄そう。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants