SlideShare a Scribd company logo
禍つSQL

OSS開発者: CPython, mysqlclient-python, go-sql-driver/mysql,
msgpack-python



ISUCON6優勝、8予選落ち、9決勝fail

稲田 直哉

Twitter: @methane

Github: methane

Kラボラトリー

3
2019-04-23、「禍つヴァールハイト」配信開始。
しかし、S-in 後にパーティー機能のロック問題が顕在化。
パーティー機能がよく利用されていた時間限定クエストを
暫定的に24時間オープンに。
GWが終わり、ロックとの本格的な戦いが始まる。
あらすじ

4
MySQL のエキスパートではないです。
解説に間違いがあったらごめんなさい。
利用している MySQL は Aurora の MySQL 5.7 です。
まえおき

5
序章

6
最大4人で一時的なチームを組んで共闘する機能。
パーティー

7
partyテーブル (抜粋)

column type 備考
id bigint primary key
member_counter int メンバー数
8
party_memberテーブル (抜粋)

column type 備考
party_id bigint party.id
player_id int メンバーのユーザーID
(party_id, player_id) の複合PK

player_id は unique key

9
1. party と party_member をロック
(SELECT ... FOR UPDATE)
2. 新しい party_member を INSERT
3. party.member_counter を UPDATE
4. COMMIT
パーティー参加

10
パーティー参加のAPIがロック待ちタイムアウト
(innodb_lock_wait_timeout = 50sec)でエラーになる。
障害内容

11
第一章

パーティーチャット

12
1. party と party_member をロック
(SELECT ... FOR UPDATE)
2. 新しい party_member を INSERT
3. party.member_counter を UPDATE
4. パーティーチャットに参加
5. COMMIT
パーティー参加

13
チャット機能は別サービスで実装されている。
コミット前にそのサービスのAPI呼び出しをしていた。
→API呼び出しを commit 後に移動
パーティーチャット

14
結果

15
解決...?

● innodb_lock_wait_timeout は 50sec
● チャットサービスのAPIは遅くても1秒程度
● 名前解決等に時間がかかってた形跡もなし
本当にこれがタイムアウトの原因だったのか?
16
報告

17
第二章

招待デッドロック

18
大量の人が同一のパーティーの
ロックを待っている。
パーティーが満員になるまで
パーティー一覧に表示され続け
る。
一度詰まると被害が増える。
調査

19
招待

20
● パーティー作成時に、ギルドメンバーやフォローしているユーザーに
招待を送っていた。
● 相互招待を避けるために招待対象全員のユーザーをロックしていた。
● 相互フォロワーやギルドメンバーが同時にパーティーを作るとデッド
ロックする。
○ 巻き添えで他のフォロワーやギルドメンバーもロックされタイム
アウト大量発生。
招待

21
相互招待を避ける必要がなくなっていた。
→ユーザーのロックを外して検証開始。
招待

22
時限クエスト復活

第一章の改善結果をもとに時限クエストが復活することに
23
緊急デプロイ

24
解決

25
翌週

2019-05-12 13:10:21 0x2b3f75a197002019-05-12 13:10:21 0x2b3f75a19700
*** (1) TRANSACTION:
TRANSACTION 2056616651, ACTIVE 0 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 6 lock struct(s), heap size 1136, 4 row lock(s)
MySQL thread id 151527458, OS thread handle 47340841101056, query id 7830734758
10.0.3.60 user update
INSERT INTO party_member (party_id, player_id, job_no, alive_updated, created) VALUES
(...)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED: ...
26
第三章

Insertion Deadlock

27
ギャップロック(1)



party_id player_id
1 10
1 20
2 11
3 12
party_id=2 を SELECT FOR UPDATE すると、
party_id=2 のレコードを追加できないように前後の
ギャップもロックする。



結果 party_id=1 や party_id=3 のレコードも player_id
次第で insert がロック待ちになる。

party_member

28
ギャップロック(2)



party_id player_id
1 10
1 20
2 11
3 12
party_id=1 と party_id=2 に同時に参加しようとする
と、両方ともロックは成功する。



ギャップロックは insert ロックと競合するが、ギャップ
ロック同士は競合しない。

party_member

29
デッドロック

party_id player_id
1 10
1 20
2 11
3 12
(1, 25) も (2, 5) も insert できずデッドロックになる



この場合片方のトランザクションが成功しもう片方は
Deadlock found エラーになる。

party_member

30
リトライする
→不安、面倒
party ロックをしているから party_member での排他制御は
いらないはず。
→party_member をロックなしの select にする。
解決策?

31
第四章

Lost Update

32
MySQL のデフォルトのトランザクション分離レベル
トランザクション中は同じクエリは同じ結果を返す
トランザクションの最初のクエリを実行した時のバージョン
を記憶し、SELECTはそのバージョンの結果を返す。
※SELECT * ... FOR UPDATE は最新の情報を返す
 そのトランザクションがINSERTやUPDATEした行を
SELECTしたときも最新の情報を返す
Repeatable Read

33
1. BEGIN
2. SELECT * FROM party WHERE id=?
3. バリデーション等
4. SELECT * FROM party WHERE id=? FOR UPDATE
5. INSERT INTO party_member ...
6. SELECT * FROM party_member WHERE party_id=?
7. UPDATE party SET member_count=?
8. COMMIT
パーティー参加

34
● party をロックする前にクエリを実行していた
● SELECT * FROM party_member はロック前のバージョン
になる
● 同じ party に同時に参加しようとすると、 SELECT *
FROM party_member が1つ少ない行を返す
○ member_counter に1つ少ない値をSETしてしまう!!
ロストアップデート

35
/* 3人目 */
BEGIN
...
SELECT party ... FOR UPDATE
INSERT party_member
SELECT party_member ... (3人)
UPDATE party SET member_counter=3
COMMIT
ロストアップデート

/* 4人目 */
BEGIN
...
SELECT party ... FOR UPDATE
(ロック待ち)
.
.
.
INSERT party_member
SELECT party_member ... (3人!!)
UPDATE party SET member_counter=3
COMMIT
36
● BEGIN 直後に SELECT party ... FOR UPDATE する
○ 今は良いけどあとでエンバグしそう🙄
● member_counter = member_counter+1
○ SELECT party_member が減らせる👍
○ いろんな箇所で触っているので保留
解決案(1)

37
● READ COMMITTED にする?
○ SELECT が最新情報を返す👍
○ ギャップロックいらない👍
○ でも副作用がこわいので保留🤔
● Redisで作り直す!
○ パーティー機能を大幅強化したい
■ Redisにした方が簡単そう
解決案(2)

38
終章

39
● ロック中に外部API呼び出しするのは避けよう
● ロック中に他のロックをするのに気をつけよう
● ギャップロックこわい、ロストアップデートこわい
○ とにかく試そう
● READ COMMITTED や Redis も検討しよう
まとめ


More Related Content

PPTX
Gossip事始め
PDF
イルカさんチームからゾウさんチームに教えたいMySQLレプリケーション
ODP
PDF
MySQLerの7つ道具
PPTX
TDEで透過的暗号化
PPT
関西オープンソース 2008 30days Albumの裏側
PDF
サーバーが完膚なきまでに死んでもMySQLのデータを失わないための表技
PDF
What is visor_fs_201207_customize
Gossip事始め
イルカさんチームからゾウさんチームに教えたいMySQLレプリケーション
MySQLerの7つ道具
TDEで透過的暗号化
関西オープンソース 2008 30days Albumの裏側
サーバーが完膚なきまでに死んでもMySQLのデータを失わないための表技
What is visor_fs_201207_customize

What's hot (14)

PDF
MySQL 5.7の次のMySQLは
KEY
The Chef integrations Z Cloud(Joyent)
PPT
YAPC Asia 2010 30days Albumの裏側 後日談
PDF
How to backup your mroonga database?
PDF
Handlerさんコンニチワ
PDF
Maatkitの紹介
PDF
RancherでMesosクラスタをデプロイしてみる的ななにか
PPTX
Rancherで簡単に作るk8s環境 Kubernetes meetup tokyo #4 LT kubernetes on rancher
PDF
MySQL Casual Talks in Fukuoka vol.2
PDF
MySQL 5.7にやられないためにおぼえておいてほしいこと
PDF
CloudFoundry 2 on Apache CloudStack 4.2.1
PPTX
Kubernetesできること
PDF
マイクラ自動化枠第2回資料
PDF
マイクラ自動化枠第1回資料
MySQL 5.7の次のMySQLは
The Chef integrations Z Cloud(Joyent)
YAPC Asia 2010 30days Albumの裏側 後日談
How to backup your mroonga database?
Handlerさんコンニチワ
Maatkitの紹介
RancherでMesosクラスタをデプロイしてみる的ななにか
Rancherで簡単に作るk8s環境 Kubernetes meetup tokyo #4 LT kubernetes on rancher
MySQL Casual Talks in Fukuoka vol.2
MySQL 5.7にやられないためにおぼえておいてほしいこと
CloudFoundry 2 on Apache CloudStack 4.2.1
Kubernetesできること
マイクラ自動化枠第2回資料
マイクラ自動化枠第1回資料
Ad

More from KLab Inc. / Tech (20)

PDF
【公開用】モバイルオンラインゲーム開発を支える早く、安く、使いやすいサーバインフラ構築
PDF
モバイルオンラインゲームのアプリ外課金の導入と運用方法について
PDF
デバイスファーム 「AirLab」 による 自動QAテストの実績と機械学習が拓く次世代QAの可能性
PDF
2024年5月17日 先駆的科学計算フォーラム2024 機械学習を用いた新たなゲーム体験の創出の応用
PDF
大規模モバイルオンラインゲーム開発における チーム組成とワークフロー最適化
PDF
運用中の大規模オンラインゲームで 8年ぶりにPHPバージョンアップをした話
PDF
AirLab導入でテストコストの大幅削減と品質向上! 数十台の端末を一斉に全自動テストできる社内DeviceFarmについてご紹介
PDF
生成AIが切り拓く新しいゲームの創り方・遊び方
PDF
表も裏もすべて見せます! KLab謹製大規模オンラインゲームの リアルタイムチャットマイクロサービス
PDF
モバイルオンラインゲームでの大規模観戦とチート対策 〜自社製リアルタイム通信システム「WSNet2」の事例〜
PDF
他業界からゲーム業界へ転向したときの話
PDF
KLabのゲーム開発を支える開発環境
PDF
ゲーム開発を知らない人にも分かるKLabのゲーム開発運営
PDF
「リアルISUCON」としてのモバイルオンラインゲーム開発
PDF
ゴリラテスト モバイルゲームのUIを自動的に検出・操作する モンキーテスト
PDF
モバイルアプリの高速で安定したビルドを支えるJenkins運用術
PDF
『ラブライブ!スクールアイドルフェスティバル ALL STARS』を支えるビルドパイプライン 〜より安定したサービス提供を目指して〜
PPTX
KLabのチャットシステム インフラ変遷
PPTX
Ganglia のUIにGrafanaを追加する話
PPTX
KLabのインフラエンジニア 〜 こんな感じで働いてます 〜
【公開用】モバイルオンラインゲーム開発を支える早く、安く、使いやすいサーバインフラ構築
モバイルオンラインゲームのアプリ外課金の導入と運用方法について
デバイスファーム 「AirLab」 による 自動QAテストの実績と機械学習が拓く次世代QAの可能性
2024年5月17日 先駆的科学計算フォーラム2024 機械学習を用いた新たなゲーム体験の創出の応用
大規模モバイルオンラインゲーム開発における チーム組成とワークフロー最適化
運用中の大規模オンラインゲームで 8年ぶりにPHPバージョンアップをした話
AirLab導入でテストコストの大幅削減と品質向上! 数十台の端末を一斉に全自動テストできる社内DeviceFarmについてご紹介
生成AIが切り拓く新しいゲームの創り方・遊び方
表も裏もすべて見せます! KLab謹製大規模オンラインゲームの リアルタイムチャットマイクロサービス
モバイルオンラインゲームでの大規模観戦とチート対策 〜自社製リアルタイム通信システム「WSNet2」の事例〜
他業界からゲーム業界へ転向したときの話
KLabのゲーム開発を支える開発環境
ゲーム開発を知らない人にも分かるKLabのゲーム開発運営
「リアルISUCON」としてのモバイルオンラインゲーム開発
ゴリラテスト モバイルゲームのUIを自動的に検出・操作する モンキーテスト
モバイルアプリの高速で安定したビルドを支えるJenkins運用術
『ラブライブ!スクールアイドルフェスティバル ALL STARS』を支えるビルドパイプライン 〜より安定したサービス提供を目指して〜
KLabのチャットシステム インフラ変遷
Ganglia のUIにGrafanaを追加する話
KLabのインフラエンジニア 〜 こんな感じで働いてます 〜
Ad

禍つSQL