SlideShare a Scribd company logo
MySQLで論理削除と正しく付き合う
⽅法
2015/08/31
yoku0825
論理削除 Casual Talks #1
元のタイトルは
“MySQLの論理削
除はどうして速い
のか”
1/33
DELETEじゃなく
てUPDATEだか
ら、という出落ち
ネタ
2/33
論理削除についてど
う思うか、それぞれ
の⽴場の⼈(︖)に
聞いてみました
3/33
論理削除についてのそれぞれの⾒解
とある企業のDBA
よく考えたら反対する理由があんまりないことに気付いた
MySQL
UPDATEじゃんそれ
InnoDB
うん、よくやるよくやる
4/33
みたいな
5/33
とはいえ書い
てて何となく
思い当たる
6/33
MySQLの都合に沿
って 論理削除すれ
ば 必要悪 っぽいけ
ど
7/33
そうでない場
合は単なる 害
悪
8/33
Using where;
とか Using
filesort; の温床
9/33
という訳でなんか
その辺を整理して
みようと思います
10/33
\こんばんは/
yoku0825@とある企業のDBA
オラクれない-
ポスグれない-
マイエスキューエる-
家に帰ると
妻の夫-
せがれの⽗-
ムスメの⽗-
Twitter: @yoku0825
Blog: ⽇々の覚書
MyNA ML: ⽇本MySQLユーザ会
11/33
※個⼈の⾒解であり、所
属する組織あるいは所属
しない組織の意⾒を代表
するわけがありません
12/33
ちょっと待て︕ そ
のカラムNOT
NULL制約ついて
る︕︖
13/33
( ゚д゚) ハッ
14/33
※個⼈の⾒解であり、所
属する組織あるいは所属
しない組織あるいは
NULLの意⾒を代表する
わけがありません
15/33
個⼈の意⾒
本当はしてほしくない
余計なカラムは増えて欲しくないし-
参照されないデータは⼊っていて欲しくない-
テーブル設計的に汚い-
個⼈的に⼀番好きなのはDELETEトリガーでアーカイブ⽤の
テーブルに追い出しちゃうこと
カジュアルに元テーブルにADD COLUMNしてアーカイブ⽤のテーブ
ルにADD COLUMNを忘れるとトリガーが腐ってひどいことに
-
16/33
よく考えること
UNIQUE制約さん息してない
カラムぶんデータのサイズが⼤きくなる
削除されなかったレコードがゴミとしてたまる
covering index狙いにくい
WHERE狙いのキーはなんとかなるかも知れないけど-
ORDER BY狙いのキーでだいたい死ぬ-
17/33
んー︖
UNIQUE制約さん息してない
でも残念ながらナチュラルキーがしっかり定義してあるテーブルの⽅
が少ない
-
カラムぶんデータのサイズが⼤きくなる
TINYINTなら(1バイト * レコード数) * (1 + セカンダリーインデッ
クス数)
数千万レコード8インデックスくらいならせいぜい数百メガバイト
これが⼗ギガバイト単位になるなら考え直した⽅がいいかも
-
18/33
あれ︖
削除されなかったレコードがゴミとしてたまる
そもそもWEBサービス削除少ない-
みんなだいすきtpcc-mysqlだと1/47より少ない(内部でカーソル回し
てるのでSELECTもうちょっと多いはず)
-
deliveryの中だけで⾒て1/7
体感、もっと少ないと思う
-
100GBのレコードの1%が削除されるとして1GBなら誤差範囲なんじ
ゃ︖ (⼈によるかな)
-
covering index狙いにくい
狙いにくくはあるけどちゃんとインデックス設計してWHEREの先頭
で必ず指定するようにすれば狙っていける
-
19/33
前提
サロゲートキーが振られていて、他にユニーク制約がない
削除フラグのカラムはboolかせめてENUM
削除フラグのカラムは全てのセカンダリーインデックスの先
頭に振られている
MySQLが5.5以降でない (ごめんなさい当⽇「5.5以降であ
ること」みたいに話してましたが逆でした。。)
UPDATEで済ませないといけない理由がある
削除されたデータに運営やバッチがアクセスする-
ユーザートラフィックのくせに削除の対象レコードが数万規模-
20/33
サロゲートキー
ナチュラルキーがある場合に論理削除しちゃうとナチュラル
キーで制約できない
残念ながらナチュラルキーが使われてるケースの⽅が少ない
ナチュラルキー, サロゲートキーは宗教戦争になるので、ウチではサ
ロゲートキーが主流なので忌避する理由にならない、とだけ⾔ってお
きます
-
「 サロゲートキーの毒を⾷らわば論理削除の皿まで 」って感じ-
21/33
削除フラグの型
DATETIME型, TIMESTAMP型は変
NULLABLE⼤前提になる-
“削除されているかどうか”というドメインと”いつ削除されたか”とい
うドメインが1つのカラムで表現される
-
リレーショナルモデル的に間違ってる-
bool型なら”= 0”で等価検索できるのでインデックスが上⼿く使える-
22/33
削除 フラグ︖
というかそもそも論理 削除 とか 削除 フラグなんていうか
ら話が掴みにくくなる
スーパー非表⽰フラグとか表⽰ステータスとかにすればいいんじゃ
ね︖
-
そうすれば、それはレコードの 属性 の⼀つなので、リレーショナル
モデル的に納得がいく
運営のみ閲覧可能フラグとかユーザー非表⽰フラグとかでもいい
ステータスならENUM(ʻNOT̲DELETEDʼ, ʻDELETED̲BY̲USERʼ,
ʻDELETED̲BECAUSE̲DMCAʼ, ʻDELETED̲BECAUSE̲PORNʼ, ..)とか︖
ただし”display̲status <> ʻNOT̲DELETEDʼ“とかやると死ねる。
とはいえそんなクエリー流すの管理画⾯だけであるべきなので、テーブルスキャンく
らいは覚悟しろという気でいる。
-
23/33
削除フラグのインデックス
全てのセカンダリーインデックスの先頭に削除フラグのカラ
ムが存在しなければならない
本当は先頭じゃなくてもいいけど(カーディナリティー2だし)「先
頭じゃなくていい」って⾔って変なところに挟まれるくらいだったら
統⼀して先頭に「確実に」振ってほしい
-
24/33
削除フラグのインデックス
そして全ての(ユーザートラフィックによる)クエリーは
WHERE句の先頭で削除フラグのカラムを等価⽐較しなけれ
ばならない
たとえば”WHERE super̲hidden̲flag= 0 AND thread̲id= ?
ORDER BY comment̲date DESC” なら “KEY
(super̲hidden̲flag, thread̲id, comment̲date)”
-
AND演算⼦は等価交換できるから先頭じゃなくてもいいにしても、
super̲hidden̲flagが”=”演算⼦で評価されてないとこのインデック
スは使えない
だからboolがいい
-
というかユーザートラフィックから参照されるならそれ削除
じゃないよね
25/33
UPDATEとDELETEの違い
UPDATEでもDELETEでもインデックスを使ってレコードを
探す
DELETEの場合、クラスターインデックスのリーフを削除するので場
合によってはリバランスが⾛る
-
UPDATEの場合、クラスターインデックスのリーフが書き換わる
セカンダリーインデックスはチェンジバッファを使って非同期に更新される
DELETEの場合は最終的に全てのセカンダリーインデックスが更新される
UPDATEの場合はそのカラムが含まれるセカンダリーインデックスだけ更新される
super̲hidden̲flagが全部のセカンダリーインデックスの先頭にあると最終的なI/O
量の旨味は少ない
けどどうせ非同期だし意外と目をつむれる
-
26/33
MySQLが5.5 <del>以降</del> より前
チェンジバッファがDELETE, UPDATEに対応するのは
MySQL 5.5以降
チェンジバッファが効くとセカンダリーインデックス(プライマリ
ー, ユニークはダメ)への反映が非同期になる
-
“DELETE確かに遅いけど、そんなに⾔うほどでもなくね︖” と思った
らこれが効いているケースが多い
-
27/33
MySQLが5.5 <del>以降</del> より前
それ以前は インサートバッファ (INSERTにしかセカンダ
リーインデックスの非同期反映が有効でない)
DELETEはクラスターインデックスと全てのセカンダリーインデック
スを同期で反映させるのに対し
-
super̲hidden̲flagのUPDATEはクラスターインデックスはまず触ら
ないし、super̲hidden̲flagを含むセカンダリーインデックスだけ反
映させればいい
-
UPDATEにせよDELETEにせよ、MySQL 5.5とそれ以降にすることで
チェンジバッファの恩恵を受けられる
-
ということは別に前提ではないかも
28/33
じゃあ実際どれくらい違うかっていうと
次のページのグラフ
横軸はtpcc-mysqlのdeliveryトランザクションの経過時間
基本的に、最初がバッファプールあったまってなくてだんだんあった
まっていくので右に⾏くほどレイテンシーが下る
-
縦軸はレイテンシー、単位はミリ秒
⼩さければ⼩さいほどいい-
DELETE, UPDATEでただカラムを⾜しただけ, UPDATEでカ
ラムとインデックスを追加、の3パターン
トランザクション内のステートメントは7つ、対象をSELECTして
DELETEする2ステートメントが影響を受ける
-
29/33
微妙
super̲hidden̲flagを含めたインデックスを作らないとDELETE
より遅い(やっぱりSELECTが⾜を引っ張ってる)
30/33
UPDATEで済ませないといけない理由
“削除”されたデータにアクセス
“削除”された理由を解析しなければならない-
利⽤規約なのか法律なのかよく知らないけど、”いつ”削除されたのか
を保持しておく必要がある
-
非同期でS3のファイルを消したりするから、そっちの削除が終わるま
で取っておかないといけない
-
ユーザートラフィックなのに”削除”対象が数万レコード
Kazuho@Cybozu Labs: フレンド・タイムライン処理の原理と実践-
31/33
という訳で
サロゲートキーの毒を⾷らわば論理削除の皿まで
サロゲートキーじゃないといいことない-
インデックスがちゃんと切ってあればシステム的には⾔うほ
ど変わらない
DELETEされたレコードの⽐率が⼗分少ない場合に限る-
論理削除じゃなくて非表⽰状態へのUPDATEなんだっ⼿⽻先
32/33
Questions and/or Suggestions?
Q. インデックスの先頭にカーディナリティーの⼩さい
super̲hidden̲flagを置くのは悪⼿じゃね︖
A. ちゃんと正しい位置に置いてくれるならどこでもいいんだけど、「先頭」
って⾔っておかないとだいたいひどいことになる
Q. DELETEトリガーでアーカイブテーブルに追い出すののデメ
リットはなんかある︖
A. トリガーはトラブルシュート⾯倒。ADD COLUMNしたらトリガーが腐っ
て全部のDELETEが転けるとか。
33/33

More Related Content

MySQLで論理削除と正しく付き合う方法