Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

My sqlで遭遇したトランザクションとロックのお話take2 2

1,409 views

Published on

Published in: Technology
  • Be the first to comment

My sqlで遭遇したトランザクションとロックのお話take2 2

  1. 1. MySQLで遭遇したトランザク ションとロックのお話take2 小林拓
  2. 2.  トランザクション  排他制御(ロック)  楽観ロック  悲観ロック 前提知識
  3. 3.  トランザクション処理は、既知の一貫した状態のデ ータベースを維持するよう設計されており、相互依 存のある複数の操作が全て完了するか、全てキャン セルされることを保証する。(Wikipediaより) トランザクション
  4. 4. トランザクションの例 •G減少 •所持数増加 購入
  5. 5. トランザクションの例 •G減少 •所持数増加 購入 エラー なかったこ とにしたい!!
  6. 6. トランザクションの例 •G減少 •所持数増加 購入 セット 全て反映 ⇒commit 全て元に戻す ⇒rollback or
  7. 7. トランザクション実装 •SQL BEGIN ~ COMMIT 又は ROLLBACK •Rails ActiveRecord::Base.transaction do ~ end
  8. 8. 排他制御(ロック) 参考 : http://www.slideshare.net/kuromoyo/20140717-37115076
  9. 9. 基本構成 DBサーバー アプリケーショ ンサーバー
  10. 10. 悲観ロックと楽観ロック
  11. 11. 楽観ロック Rollback
  12. 12. 悲観ロック
  13. 13. Id lock_version 1 0 楽観ロックの実装 •Railsの場合 usersテーブル lock_versionというカラムを追加する 楽観ロックが適用されるとき ActiveRecord::StaleObjectError というExceptionが発生して Rollbackする ※楽観ロックの実装は本来処理上で手動で実装させるもの
  14. 14. 楽観ロックある時とない時の違い UPDATE `users` SET `money` = 4, `updated_at` = '2015-02-25 11:07:28', `lock_version` = 3 WHERE (`users`.`id` = 2 AND `users`.`lock_version` = 2) 楽観ロックがないときの SQL 楽観ロックがあるときの SQL UPDATE `users` SET `money` = 4, `updated_at` = '2015-02-25 11:07:28’ WHERE (`users`.`id` = 2)
  15. 15. 悲観ロックの実装 •Railsの場合 •SQL BEGIN SELECT `users` WHERE (`users`.`id` = 2) FOR UPDATE … COMMIT または ROLLBACK user = User.find_by!(id: 2) (あらかじめインスタンスを取得しておく) ActiveRecord::Base.transaction do user.lock! end
  16. 16. ロックをかけ忘れると http://blogos.com/article/36121/ gree ドリランド 事件 ↓記事
  17. 17.  READ UNCOMMITED  READ COMMITED  REPEATABLE READ  SERIALLIZABLE トランザクション分離レベル <= Oracle, PostgreSQL, SQL Severのデフォルト <= MySQLのデフォルト 参考: http://d.hatena.ne.jp/fat47/20140212/1392171784 下に行くほど不都合な読み込み現象が発生しなくなるが、 パフォーマンスが落ちる
  18. 18. Id money 1 400 例(Rails+MySQL) User.transaction do user = User.find_by(id: 1) user.money += 400 user.save! end BEGIN; SELECT * FROM users WHERE id = 1; UPDATE users SET users.money = 800 WHERE id = 1; COMMIT; usersテーブル SQLをだす
  19. 19. 例リクエスト リクエスト BEGIN; SELECT * FROM users WHERE id = 1; UPDATE users SET users.money = 800 WHERE id = 1; COMMIT; BEGIN; SELECT * FROM users WHERE id = 1; UPDATE users SET users.money = ? WHERE id = 1; COMMIT このとき、?に入る値はいくつ でしょう? ほぼ同時にリクエストが飛んできた。 このとき実行されたSQLは以下の通り
  20. 20. 例について考察  答えは800でした  片方のトランザクションで既にcommitしてあるから、 SELECT * FROM users WHERE id = 1;これで取得できる値は users.money = 800なはず。だから800 + 400 = 1200。よっ て?の値は1200だ!!
  21. 21. READ UNCOMMITED
  22. 22. READ COMMITED
  23. 23. READ COMMITED
  24. 24. こうなると思ってました
  25. 25. REPEATABLE READ
  26. 26. こうなりました
  27. 27. 教訓 必ずtransactionの外で一度select文を走らせ てデータをとるようにしましょう!! REPEATABLE READではトランザクション が開始された時点で参照される値は同じ になる
  28. 28. REPEATABLE READ
  29. 29. SERIALLIZABLE 複数トランザクションのSQLが入り混じら ないように、強制的にトランザクションを 順序付けて処理します。 今まで出てきたすべての問題は発生しませ んが、この分離レベルは読み取るすべての 行にロックをかけます。 ちなみに

×