雑なMySQLパフォーマンスチューニング | PPT
SlideShare a Scribd company logo
MySQLのパフォーマンスチューニン
グについて少々
2016/01/20
yoku0825の中の⼈
GMOアドパートナーズグループ勉強会
\こんにちは/
yoku0825@とある企業のDBA の中の⼈
オラクれない-
ポスグれない-
マイエスキューエる-
Twitter: @yoku0825
Blog: ⽇々の覚書
Oracle ACE Details
MySQL 5.7 Community Contributor Award
1/141
このスライド書いた中の⼈です
2/141
ちなみに
3/141
というわけで安⼼
して 地雷友達 5.7
使っていいです
よ :)
4/141
クエリーのライフサイクル
client
connection_handling parser optimizer executor
handler storage_engine
application
mysqld
5/141
クエリーのライフサイクル
Connection Handling
Parser
Optimizer
Executor
Handler
Storage Engine
6/141
クエリーのライフサイクル
Connection Handling
Parser
Optimizer
Executor
Handler
Storage Engine
7/141
Connection Handling
ソケット, ポートからの接続を待ち受ける
接続があったらclone(またはスレッドキャッシュから取り
出してディスパッチ)
⼀次認証
8/141
クエリーのライフサイクル
Connection Handling
Parser
Optimizer
Executor
Handler
Storage Engine
9/141
Parser
MySQLプロトコルのパース
SQL構⽂のパース
⼆次認証(データベース, テーブル単位のアクセス権限チ
ェック)
クエリーキャッシュ処理
ジェネラルログ
10/141
クエリーのライフサイクル
Connection Handling
Parser
Optimizer
Executor
Handler
Storage Engine
11/141
Optimizer
統計情報の取得(Storage Engine APIを叩いてるっぽい)
クエリーの書き換えを含めた実⾏計画の決定
12/141
クエリーのライフサイクル
Connection Handling
Parser
Optimizer
Executor
Handler
Storage Engine
13/141
Executor
オプティマイザーから渡された実⾏計画の通りにHandlerを
叩く
Handlerから戻ってきた結果を使って実⾏計画の残りを実⾏
Using where; Using filesort; Using temporary; はコ
イツが頑張ってる証拠
-
スローログ, バイナリーログ
14/141
クエリーのライフサイクル
Connection Handling
Parser
Optimizer
Executor
Handler
Storage Engine
15/141
Handler
ストレージエンジンの抽象化レイヤー
あんまり意識することはない
16/141
クエリーのライフサイクル
Connection Handling
Parser
Optimizer
Executor
Handler
Storage Engine
17/141
Storage Engine
実際にデータを格納するレイヤー
プラガブルアーキテクチャーなので、ほとんどの機能はこの
レイヤーで実装されている
ストレージエンジンごとにトランザクション対応が違う
とか
-
ストレージエンジンごとにロック粒度が違うとか-
ストレージエンジンごとにバッファが違うとか-
18/141
クエリーのライフサイクル
client
connection_handling parser optimizer executor
handler storage_engine
application
mysqld
19/141
どこが 遅
い︖
20/141
どこが遅い︖
connection_handling parser optimizer executor
handler storage_engine
mysqld
だいたいExecutor
たまにStorage Engine
21/141
Executorが遅い理由
MySQLの不得意なことをやろうとしている
⾊々パターンはあるけど、⼀⾔で⾔うならこれにあたる-
Executorに仕事をさせたら負け
22/141
MySQLの不得意なこと
複数のインデックスを使いこなす
相関サブクエリー
23/141
複数のインデックスに弱い理由
MySQLは原則1クエリー内で1つのテーブルあたり1つのイ
ンデックスしか使わない
インデックスマージという例外が⼀応あるにはあるが、
後付け
-
そもそもこういう設計思想なんだと思う-
テーブルスキャンに強い/弱いはストレージエンジンのレイ
ヤー
MyISAMはスキャンに強く、InnoDBは弱い-
関数演算、(インデックスで解決できない)フィルター、
(インデックスで解決できない)ソートはExecutorのお仕
事(= 遅い)
24/141
EXPLAIN
mysql> EXPLAIN SELECT * FROM table_1 a JOIN `table_2` s ON a.user_id=s.`user_id` AND s.site_i
d=120 WHERE app_id=8250G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: a
type: ref
possible_keys: PRIMARY,ix_table_1,ix2_table_2,ix3_table_1,idx_table_1_06,idx_table_1_07,idx_t
able_1_09
key: idx_table_1_06
key_len: 4
ref: const
rows: 13496
Extra:
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: s
type: ref
possible_keys: idx_table_2_02,idx_table_2_03
key: idx_table_2_02
key_len: 4
ref: xxx.a.user_id
rows: 1
Extra: Using where
2 rows in set (0.00 sec)
25/141
いくつかのルール
Extra列はExecutorの都合
それ以外はオプティマイザー、ハンドラー(= ストレージエ
ンジン)の都合
key_len, rowsがヒントになる
26/141
type
ALLがダメなんて良く⾔う
ALL .. テーブルスキャン-
ref .. 非ユニークキーによる選択-
range .. レンジスキャン-
27/141
type
どの⽅法でアクセスさせるのが最速だと オプティマイザー
が ⾒積もった結果
特にInnoDBはクラスターインデックスなので、下⼿にセ
カンダリーインデックスを使うよりも本当に速いケース
もある
-
MyISAMもスキャンに強いので、本当に速いケースがあ
る
-
ダメなのはALLじゃなく、本当の最適なアクセスと違う時
rows, key_lenが判断の助けになる-
28/141
オプティマイザーが⾒誤る理由
InnoDBの統計情報がサンプリング1.
オプティマイザーはLIMIT句を正しくハンドリングできない2.
29/141
InnoDBの統計情報
InnoDBの統計情報は テーブル全体から⼀部の情報をランダ
ムに選び出し、全体を推測する サンプリング統計
5.5とそれ以前では1インデックスあたり8ページ(=
16kB * 8ページで128kB)でハードコード
-
5.6とそれ以降では1インデックスあたりデフォルト20ペ
ージ(innodb_stats_persistent_sample_pages)
CREATE TABLE, ALTER TABLEでテーブル単位でページ数
を指定することも可能
-
30/141
InnoDBの統計情報
サンプリングなので
統計情報の再計算はそこまで(MyISAMと⽐べれば)⼤
きくない
-
統計情報が間違っている(著しく実際のデータの分布と
乖離している)可能性が⾼い
-
31/141
LIMIT句のハンドリング
LIMIT句はORDER BY句の処理が終わってから実⾏される
ORDER BY句の処理は最終的にExecutorで終端するので-
つまりLIMIT句もExecutorの処理の範疇-
オプティマイザーは⾃分より後のExecutorの動作を正しく
⾒積もれない
なので、ORDER BY .. LIMIT ..で効率的にORDER BY狙い
のキーが使える場合でも、選んでくれないケースがある
-
5.7でLIMITありの場合のオプティマイズが効くようにな
っている
-
32/141
Extra: Using where; Using filesort;
ExecutorがWHEREをフィルターしている、ExecutorがORDER
BYのソートをしている
インデックスを使って⾼速化できるのは WHERE, ORDER BY,
GROUP BY, select_list
どうすればいいかと⾔われると、「インデックス使う」とし
か⾔いようがない
33/141
トランプの
話をします
34/141
Query As A Card
100枚のトランプが裏返して積まれた⼭があります
何組かのトランプを混ぜたもので何が何枚⼊ってるかはわか
りません
35/141
この⼭の中から
スペードのAを
探してください
36/141
どうするかというと
⼭の⼀番上のトランプをめくる1.
スートと番号を⾒る2.
スペードのAだったら⼿元に残す3.
スペードのAじゃなかったらどっかに置いておく4.
⼭の全てのカードに対して2, 3, 4を繰り返す
⼭にスペードのAが何枚あるかに関わらず、⼭の全てのカー
ドに対して繰り返す
5.
37/141
これがtype: ALLかつExtra: Using where
トランプをめくる .. ⾏をフェッチする
スートと番号を⾒る .. Executorでのフィルター処理
38/141
この⼭の中からス
ペードを全て探し
出して、K〜Aの順
で並べてください
39/141
どうするかというと
右⼿で ⼭の⼀番上のトランプをめくる1.
スートと番号を⾒る2.
スペードだったらそのカードを 左⼿に 残しておく3.
スペードじゃなかったらどっかに置いておく4.
⼭の全てのカードに対して2, 3, 4を繰り返す5.
⼭から全てのスペードが出揃ったら、⼿元のカードをK〜A
まで順番に並べる
6.
40/141
これがtype: ALLかつExtra: Using where; Using
filesort
トランプをめくる .. ⾏をフェッチする
トランプをめくった 右⼿ .. リードバッファー
スートを⾒る .. Executorでのフィルター処理
スートが⼀致した時にカードを⼊れた 左⼿ .. ソートバッフ
ァー
⼿元のカードを並べ替える .. Executorによるクイックソー
ト
41/141
KEY(suit)
suit 上から
club 3,5,13,14,16,18,19,20,37,44,45,55,57,
63,66,81,86,87,91,92,98,99,100
diamond 1,4,15,17,21,22,25,27,31,34,39,50,56,
58,60,65,68,70,72,73,78,97
heart 6,7,10,12,24,26,32,36,38,40,41,43,48,
51,52,61,62,67,74,75,84,85,88,89,90,9
3,95
spade 2,8,9,11,23,28,29,30,33,35,42,46,47,4
9,53,54,59,64,69,71,76,77,79,80,82,8
3,94,96
42/141
ここからスペードのAを探す
suit 上から
club 3,5,13,14,16,18,19,20,37,44,45,55,57,
63,66,81,86,87,91,92,98,99,100
diamond 1,4,15,17,21,22,25,27,31,34,39,50,56,
58,60,65,68,70,72,73,78,97
heart 6,7,10,12,24,26,32,36,38,40,41,43,48,
51,52,61,62,67,74,75,84,85,88,89,90,9
3,95
spade 2,8,9,11,23,28,29,30,33,35,42,46,47,4
9,53,54,59,64,69,71,76,77,79,80,82,8
3,94,96
43/141
スペードのAを探す
トランプの上から2, 8, 9, .. 枚目を順番にめくり1.
番号を⾒る(スペードのものだけめくってるんだから、スー
トは⾒る必要がない)
2.
Aだったらそのカードを残しておく3.
Aじゃなかったらどっかに置いておく4.
44/141
これがtype: ref, ref: const, Extra: Using where
インデックス上からスペードを探して「上から2番目」の情
報を得る
1.
上から2番目のカードをめくる .. ここまでha̲index̲read2.
Aかどうかを判定する .. Executorによるフィルター3.
インデックス上の全てのリーフをさらうまで繰り返す ..
ha̲index̲nextがHA̲ERR̲END̲OF̲FILEを返すまでルー
プ
4.
45/141
ここからスペードを全て探し出して、K〜Aの順で並べる
suit 上から
club 3,5,13,14,16,18,19,20,37,44,45,55,57,
63,66,81,86,87,91,92,98,99,100
diamond 1,4,15,17,21,22,25,27,31,34,39,50,56,
58,60,65,68,70,72,73,78,97
heart 6,7,10,12,24,26,32,36,38,40,41,43,48,
51,52,61,62,67,74,75,84,85,88,89,90,9
3,95
spade 2,8,9,11,23,28,29,30,33,35,42,46,47,4
9,53,54,59,64,69,71,76,77,79,80,82,8
3,94,96
46/141
スペードを全て探し出して、K〜Aの順で並べる
トランプの上から2, 8, 9, .. 枚目を順番にめくり 左⼿ に取
る
1.
番号を⾒て、並べ替える2.
47/141
type: ref, ref: const, Extra: Using filesort
インデックス上からスペードを探して「上から2番目」の情
報を得る
1.
上から2番目のカードを 左⼿ に .. ha̲index̲readしてソー
トバッファーに詰める
2.
番号を⾒て並べ替える .. Executorによるクイックソート3.
48/141
KEY(suit, number)
suit number 上から
club 2 13,20
club 3 18,45
.. .. ..
heart 13 85
spade 1 2,23,47,76
spade 2 77
spade 3 83
.. .. ..
spade 11 11,33,35,96
spade 12 49,59,80
49/141
ここからスペードのAを探す
suit number 上から
club 2 13,20
club 3 18,45
.. .. ..
heart 13 85
spade 1 2,23,47,76
spade 2 77
spade 3 83
.. .. ..
spade 11 11,33,35,96
spade 12 49,59,80
50/141
スペードのAを探す
トランプの上から2, 23, 47, 76枚目を順番にめくる(おし
まい︕)
インデックスの情報だけで完結するため、⼭を全て⼿繰
る(不要なものをフェッチする)必要がない
1.
51/141
type: ref, ref: const, const
インデックス上から(スペード, 1)を探して「上から2番目」
の情報を得る
1.
上から2番目のカードを取る .. ha̲index̲read2.
インデックス上の全てのリーフをさらうまで繰り返す ..
ha̲index̲nextがHA̲ERR̲END̲OF̲FILEを返すまでルー
プ
3.
52/141
スペードを全て探し出して、K〜Aの順で並べる
suit number 上から
club 2 13,20
club 3 18,45
.. .. ..
heart 13 85
spade 1 2,23,47,76
spade 2 77
spade 3 83
.. .. ..
spade 11 11,33,35,96
spade 12 49,59,80
Kなかった。。
53/141
スペードを全て探し出して、K〜Aの順で並べる
トランプの上から49, 59, 80枚目を⼿に取る1.
トランプの上から11, 33, 35, 96枚目を⼿に取る2.
全てのスペードのカードぶん1, 2を繰り返す3.
54/141
type: ref, ref: const, const, Extra: Using index
インデックス上から(スペード, 最後のノード)を探して「上
から49番目」の情報を得る
1.
上から49番目のカードを取る .. ha̲index̲read2.
インデックス上の全てのリーフをさらうまで繰り返す ..
ha̲index̲prevがHA̲ERR̲END̲OF̲FILEを返すまでルー
プ
3.
55/141
:(;゙゚ʼω゚ʼ): Extra: Using whereが消えなかった
たまに消えない
たぶんオプティマイザーのバグ(ORDER BYと混じると
Executorが仕事してなくてもUsing whereが消えない)
56/141
KEY(number)
number 上から
1 2,22,23,26,47,76,93
2 4,13,20,25,75,77
.. ..
12 16,31,41,49,59,68,73,80,91
13 19,58,85
57/141
ここからスペードのAを探す
インデックスからAを探して、「2, 22, 23, ..枚目」の情報
を得て
1.
ひっくり返してスペードかどうかを判定する2.
Aのリーフが全部終わったら終わり3.
type: ref, ref: const, Extra: Using where
58/141
スペードを全て探し出して、K〜Aの順で並べる
インデックスからKを探して19, 58, 85枚目を⼿に取る1.
スペードなら⼿元に、そうでなければよける2.
カードの⼭全てに対して1, 2を繰り返す3.
59/141
type: index, Extra: Using where
ただしこれはLIMITがない場合はテーブルスキャンを選んで
た
5.6の場合、LIMITがあってもテーブルスキャンを選ぶ-
そりゃ、結局全部のカードを裏返してるから、頭から全部ひ
っくり返した⽅が速い
LIMITがある= 「枚数が揃ったらそこでそれ以降の結果セ
ットは考慮する必要がない」
-
この条件にマッチした場合、フェッチする⾏数を⼤幅に
減らせる
-
60/141
インデックスのイメージ
インデックスは ソート済みのデータの部分複製 (異論は認め
る)
root club
spade
2
3
2
13
20
18
45
77
B+Treeじゃないけど取り敢えずそこまで気にしない
61/141
インデックスのリーフ
root club
spade
2
3
2
13
20
18
45
77
MyISAMの場合はMYDファイルの先頭からのオフセットバイト
数
InnoDBの場合はクラスターインデックスの値 62/141
InnoDBのクラスターインデックスイメージ
root club
spade
3
5
13
14
2
23
root p3
p5
p13
p14
p2
p23
Club-A
Club-4
Club-2
Club-5
Spade-A
Spade-A
Secondary Index Clustered Index
63/141
InnoDBでWHERE狙いのキー
SELECT * FROM card USING(idx_suit) WHERE suit= 'club'
ORDER BY number LIMIT 1;
root club
spade
3
5
13
14
2
23
root p3
p5
p13
p14
p2
p23
Club-A
Club-4
Club-2
Club-5
Spade-A
Spade-A
Secondary Index Clustered Index
64/141
InnoDBでWHERE狙いのキー
SELECT * FROM card USING(idx_suit) WHERE suit= 'club'
ORDER BY number LIMIT 1;
root club
spade
3
5
13
14
2
23
root p3
p5
p13
p14
p2
p23
Club-A
Club-4
Club-2
Club-5
Spade-A
Spade-A
Secondary Index Clustered Index
65/141
InnoDBでWHERE狙いのキー
SELECT * FROM card USING(idx_suit) WHERE suit= 'club'
ORDER BY number LIMIT 1;
root club
spade
3
5
13
14
2
23
root p3
p5
p13
p14
p2
p23
Club-A
1
Club-4
Club-2
Club-5
Spade-A
Spade-A
Secondary Index Clustered Index
66/141
InnoDBでWHERE狙いのキー
SELECT * FROM card USING(idx_suit) WHERE suit= 'club'
ORDER BY number LIMIT 1;
root club
spade
3
5
13
14
2
23
root p3
p5
p13
p14
p2
p23
Club-A
1
Club-4
Club-2
Club-5
Spade-A
Spade-A
Secondary Index Clustered Index
67/141
InnoDBでWHERE狙いのキー
SELECT * FROM card USING(idx_suit) WHERE suit= 'club'
ORDER BY number LIMIT 1;
root club
spade
3
5
13
14
2
23
root p3
p5
p13
p14
p2
p23
Club-A
1
Club-4
2
Club-2
Club-5
Spade-A
Spade-A
Secondary Index Clustered Index
68/141
InnoDBでWHERE狙いのキー
SELECT * FROM card USING(idx_suit) WHERE suit= 'club'
ORDER BY number LIMIT 1;
root club
spade
3
5
13
14
2
23
root p3
p5
p2
p23
p13
p14
Club-A
1
Club-4
2
Club-2
3
Club-5
4
Spade-A
Spade-A
Secondary Index Clustered Index
フェッチした⾏をクイックソートしてからLIMITを評価
69/141
InnoDBでORDER BY狙いのキー
SELECT * FROM card USING(idx_number) WHERE suit= 'club'
ORDER BY number LIMIT 1;
root A
2
2
22
23
26
4
13
root p2
p22
p23
p26
p4
p13
Spade-A
Diamond-A
Spade-A
Heart-A
Diamond-2
Club-2
Secondary Index Clustered Index
70/141
InnoDBでORDER BY狙いのキー
SELECT * FROM card USING(idx_number) WHERE suit= 'club'
ORDER BY number LIMIT 1;
root A
2
2
22
23
26
4
13
root p2
p22
p23
p26
p4
p13
Spade-A
1
Diamond-A
2
Spade-A
3
Heart-A
4
Diamond-2
5
Club-2
6
Secondary Index Clustered Index
LIMIT件数が早めに⾒つかれば勝ち、そうでなければ負け。
ところでこれ、 何かに似てると思いません︖
71/141
InnoDBでcovering index
SELECT * FROM card USING(idx_number) WHERE suit= 'club'
ORDER BY number LIMIT 1;
root club
spade
2
3
A
13
20
18
45
2
23
root p13
p20
p18
p45
p2
p23
Club-2
1
Club-2
Club-3
Club-3
Spade-A
Spade-A
Secondary Index Clustered Index
72/141
WHERE狙いのキー, ORDER BY狙いのキー
WHEREとORDER BY .. LIMITを同時にカバーできるインデック
スが作成できない時に
WHEREをストレージエンジンに任せてフェッチする⾏を減
らしてファイルソートをExecutorにやらせるか
-
ORDER BY .. LIMITをストレージエンジンに任せて
ExecutorにフィルターさせつつLIMITでループを抜けるの
を期待するか
-
73/141
WHERE狙いのキー, ORDER BY狙いのキー
「JOINが遅い」はJOINが遅いんじゃなくてJOINの参照表で
ソートしようとしてExecutorがソートしちゃってるケース
あとはオプティマイザーにバグがあって、駆動表を
Executorがフィルターして参照表の参照回数が減る可能
性を考慮していない。
-
See also, http://www.slideshare.net/yoku0825/
whereorder-by
74/141
Executorに仕
事をさせたら
負け
75/141
Executorが必要以上の仕事をしていないか
GROUP BYのケースを除いて、Rows_sent= Rows_examinedが⼀
番良い
MySQLにはヒストグラム統計がないので、WHERE狙いと
ORDER BY狙いのどちらが本当に速いのかMySQLは知らない
(憶えてない)
76/141
Executorが必要以上の仕事をしていないか
⼈間は得てして 正しい 統計情報を知っているケースがある
サービスの特性(⼥性は少ないからWHERE gender= 'F'
ORDER BY birthdayはWHEREで狙った⽅がいいだろうし、
WHERE gender= 'M' ORDER BY birthdayはORDER BY狙いの
⽅が良いだろう、とか)
-
サンプリングでない実際の分布(InnoDBの統計情報は飽
くまでサンプリングなのでたまに踏み外す)
-
LIMITの考慮(5.6とそれ以前のMySQLはORDER BY ..
LIMITの時でも積極的にWHEREを狙う)
-
77/141
オプティマイザーの強化
MySQL 5.6
Multi-Range Read, Batched Key Access(デフォルトOFF),
Semi-JOIN, Materialized
MySQL 5.7
ORDER BY .. LIMIT Optimization, Configurable cost
Model, JOIN optimization
78/141
迷ったら最新(オ
プティマイザーに
関しては)
79/141
クエリーの最適化(5.5)
mysql55> EXPLAIN SELECT * FROM Country WHERE Continent = 'Asia' AND Code IN
io
n / 2);
+----+--------------------+---------+----------------+---------------+------
-------+---------+------+------+-------------+
e
y | key_len | ref | rows | Extra |
+----+--------------------+---------+----------------+---------------+------
-------+---------+------+------+-------------+
| 1 | PRIMARY | Country | ALL | NULL | NUL
L | NULL | NULL | 226 | Using where |
| 2 | DEPENDENT SUBQUERY | City | index_subquery | CountryCode | Count
ryCode | 3 | func | 7 | Using where |
+----+--------------------+---------+----------------+---------------+------
-------+---------+------+------+-------------+
2 rows in set (0.02 sec)
80/141
クエリーの最適化(5.7)
mysql57> EXPLAIN SELECT * FROM Country WHERE Continent = 'Asia' AND Code IN
-> (SELECT CountryCode FROM City WHERE Population > Country.Population / 2);
+----+-------------+---------+------------+------+---------------+-------------+
---------+-----------------+------+----------+----------------------------------
+
| id | select_type | table | partitions | type | possible_keys | key
| key_len | ref | rows | filtered | Extr
a |
+----+-------------+---------+------------+------+---------------+-------------+
---------+-----------------+------+----------+----------------------------------
+
| 1 | SIMPLE | Country | NULL | ALL | PRIMARY | NULL
| NULL | NULL | 239 | 14.29 | Using wher
e |
| 1 | SIMPLE | City | NULL | ref | CountryCode | CountryCode
| 3 | d1.Country.Code | 18 | 33.33 | Using where; FirstMatch(Country
) |
+----+-------------+---------+------------+------+---------------+-------------+
---------+-----------------+------+----------+----------------------------------
+
2 rows in set, 2 warnings (0.00 sec)
81/141
古いMySQLに慣れていると、⼾惑うことも
「MySQLはこう 間違うだろう」という予測と 違う間違え⽅
をする
EXPLAIN EXTENDED, SHOW WARNINGSでオプティマイザーがクエ
リーをどう書き換えたか表⽰できる
mysql57> show warningsG
*************************** 2. row ***************************
Level: Note
Code: 1003
Message: /* select#1 */ select `d1`.`Country`.`Code` AS `Code`,`d1`.`Country`.`Name` AS `Name
`,`d1`.`Country`.`Continent` AS `Continent`,`d1`.`Country`.`Region` AS `Region`,`d1`.`Country
`.`SurfaceArea` AS `SurfaceArea`,`d1`.`Country`.`IndepYear` AS `IndepYear`,`d1`.`Country`.`Po
pulation` AS `Population`,`d1`.`Country`.`LifeExpectancy` AS `LifeExpectancy`,`d1`.`Country`.
`GNP` AS `GNP`,`d1`.`Country`.`GNPOld` AS `GNPOld`,`d1`.`Country`.`LocalName` AS `LocalName`,
`d1`.`Country`.`GovernmentForm` AS `GovernmentForm`,`d1`.`Country`.`HeadOfState` AS `HeadOfSt
ate`,`d1`.`Country`.`Capital` AS `Capital`,`d1`.`Country`.`Code2` AS `Code2` from `d1`.`Count
ry` semi join (`d1`.`City`) where ((`d1`.`City`.`CountryCode` = `d1`.`Country`.`Code`) and (`
d1`.`Country`.`Continent` = 'Asia') and (`d1`.`City`.`Population` > (`d1`.`Country`.`Populati
on` / 2)))
2 rows in set (0.00 sec)
82/141
sysスキーマ
performance̲schema, information̲schemaのビューと、
performance̲schema関連の設定をゴニョるプロシージャ
の詰め合わせ
なのでperformance_schema= ONが⼤前提-
合わせて使いたいinnodb_monitor_enable= all-
5.6の場合はgitリポジトリーからcloneしてsqlスクリプト
実⾏
-
83/141
sys.statement̲analysis
query
full̲scanしてれば*
exec̲count
err̲count, warn̲count
*̲latency
rows̲sent̲avg, rows̲examined̲avg
tmp̲tables, sort̲merge̲passes
84/141
Percona Server + log_slow_verbosity = query_plan
# Time: 130601 8:01:06.058915
# User@Host: root[root] @ localhost [] Id: 42
# Schema: imdb Last_errno: 0 Killed: 0
# Query_time: 7.725616 Lock_time: 0.000328 Rows_sent: 4 Rows_e
xamined: 1543720 Rows_affected: 0
# Bytes_sent: 272 Tmp_tables: 0 Tmp_disk_tables: 0 Tmp_table_s
izes: 0
# QC_Hit: No Full_scan: Yes Full_join: No Tmp_table: No Tmp_t
able_on_disk: No
# Filesort: No Filesort_on_disk: No Merge_passes: 0
85/141
Anemometer
86/141
相関サブクエリー
外側のクエリーが内側のサブクエリーの条件になっているケ
ース
内側のサブクエリーが外側の条件になるケースはMySQL
⽤語では単にサブクエリーと呼ぶ(個⼈的には明確に区
別するために「キャッシャブルなサブクエリー」と呼ん
でる)
-
しかしWHERE INはキャッシャブルなサブクエリーのはず
でも相関サブクエリーになるというダメ仕様が5.5とそれ
以前にあってだな。。
-
87/141
シンプルな話、外側のクエリーに1⾏マッチするたびに内
側のクエリーを実⾏してしまう
⾏のフェッチのみならず、クエリーまるごと実⾏する。
外側テーブルのサイズが⼤きくなると加速度的に遅くな
るのはこのせい。
-
mysql> SHOW PROFILE;
+--------------------------------+----------+
| Status | Duration |
+--------------------------------+----------+
| starting | 0.000023 |
| .. | .. |
| executing | 0.000006 |
| Sending data | 0.000006 |
| .. | .. |
| executing | 0.000005 |
| Sending data | 0.000014 |
| executing | 0.000005 |
| Sending data | 0.000047 |
| end | 0.000010 |
| .. | .. |
+--------------------------------+----------+
2000025 rows in set (4.78 sec)
88/141
相関サブクエリーの書き換え⽅
7割⽅JOINに書き換えられる
JOINに落とし込んでUSE INDEXまでつければ5.5とそれ以
前でも戦える
-
どうしてもロジックとしてJOINに書き換えられないケース
もある
CREATE TEMPORARY TABLEを使って落とし込むのがいい
テンポラリーテーブルはクラッシュアンセーフ( 更新
ステートメントには使わない︕ )
そもそもクラッシュアンセーフだから迷わずMyISAM
を選んでいい
⼤した量でなくてもインデックス張る。劇的に違う
-
のがいい、というか、SQLだけでやるにはそれしかない- 89/141
書き換える時に必要なもの
再帰テスト
無いと死ぬ-
ポジション上、返す結果が変わらないことの担保をしな
いといけない
パラメーター渡して、新旧クエリーの2パターン投げて
結果を⽐較するだけでも取り敢えずいいんだ
最近はコマンドラインクライアントで
REPEATABLE-READでトランザクション内でやればオ
ブジェクト⽐較するだけで済む
分布に極端な偏りがあるパラメーターはいくつかバリ
エーションを⽤意した⽅がいい(同じ形をしていても
パラメーターで実⾏計画は変わる)
-
90/141
ひとやす
み
91/141
Storage Engineが遅い理由
リソースが⾜りていない1.
パラメーターが適切でない2.
キャッシュミスヒット3.
ロックやmutexの競合(並列度が⾼すぎる)4.
バルク操作5.
92/141
リソースが⾜りていない
ホットデータが100GBあるのにInnoDBバッファプールが
16GBしかなかったら︖
物理メモリーが64GBしかないのに
innodb_buffer_pool_size= 128Gにしたら︖
50並列でクエリーを実⾏するのに8コアしかなかったら︖
秒間1000コミットあるのにIOPSが200しかなかったら︖
93/141
パラメーターが適切でない
innodb_buffer_pool_size
innodb_log_file_size * innodb_log_files_in_group
innodb_flush_method
innodb_log_buffer_size
innodb_flush_log_at_trx_commit
innodb_io_capacity, innodb_io_capacity_max
94/141
innodb̲buffer̲pool̲size
InnoDBの動作の要
個⼈的にはキャッシュじゃなくて データの本体-
⾜りてないとSELECTだけじゃなく全ての動作が遅い
ただしDROP TABLEとTRUNCATE TABLEはバッファプールが⼤き
ければ⼤きいほど遅くなる
95/141
InnoDBの動作の要
SELECTのとき
バッファプールを⾒る
あればそれを使う
なければテーブルスペースファイルを読んでバッファ
プールに載せる
バッファプールがいっぱいだったら⼀番古いのを追い
出してバッファプールに載せる
追い出そうとしたページがダーティーページだったら
フラッシュしてから追い出す
-
96/141
InnoDBの動作の要
INSERT, UPDATE, DELETEのとき
UNDOとログはちょっと省略
-
UPDATE, DELETEの時にPRIMARY KEY以外の属性で絞り込
む場合は、SELECTと同様にバッファプールを使って検索
する
-
97/141
InnoDBの動作の要
INSERT, UPDATE, DELETEのとき
データそのものの追加, 更新, 削除は最終的にクラスター
インデックスの値から、書き込むページ番号を特定する
バッファプールはCOMMIT前に更新される(COMMIT
されてるかどうかのフラグがある)
READ-UNCOMMITTEDはバッファプールから読んで
COMMITされてるかどうかのフラグを⾒ない
COMMITされてるかどうかに関わらず、古いファイル
はUNDOセグメントに移されて、そのページ番号がペ
ージに書き込まれる
-
98/141
対象のページがバッファプールに載っていれば
バッファプール上のデータを書き換える
InnoDBのDELETEは論理削除-
バッファプールのページに「delete mark」を付ける-
まだテーブルスペースファイルに反映されてないページ
のことを「ダーティーページ」と呼ぶ
ダーティーページはバッファプールから押し出される
時にテーブルスペースファイルにフラッシュされなけ
ればいけない
-
99/141
対象のページがバッファプールに載っていなければ
テーブルスペースファイルを読んでバッファプールに載せて
から書く
バッファプールに載せてからINSERT-
バッファプールに載せてからUPDATE-
バッファプールに載せてからDELETE-
Replication Booster for MySQLやinnodb̲fake̲changes
はこの仕組みを利⽤したもの
100/141
DROP TABLE, TRUNCATEのとき
TRUNCATEは内部的にDROP TABLE, CREATE TABLE
ディクショナリーから参照を切る
バッファプール上の全てのページをチェックし、そのテーブ
ルのページがあれば破棄する
「バッファプールに載せてdelete mark」はしない-
101/141
ROW̲FORMAT= Compressed
InnoDB圧縮
圧縮はページ単位、圧縮したままでは読むことも書くことも
できない
バッファプールに 圧縮状態のまま 読み込むa.
バッファプール上で 解凍してコピーするb.
解凍済みのページに対する操作をするc.
解凍済みのページがフラッシュされる時に再圧縮されて
圧縮状態のページ が更新される(再圧縮)
d.
圧縮状態のページはバッファプールにもう少し残るe.
ただでさえ重いディスクアクセスのタイミングで圧縮/解凍
処理が加わる。
102/141
InnoDB圧縮のワーストケース
SELECTクエリーがやってきて1.
バッファプールミスヒット2.
バッファプールの空きが無い3.
⼀番古いページを押し出す4.
⼀番古いページがダーティーページだったら︖5.
再圧縮してフラッシュ6.
空けたページに圧縮状態のページを持ってきて7.
解凍してコピー8.
103/141
InnoDB圧縮は
バッファプール効率とCPU効率を犠牲にして容量を稼いでい
る
IO回数が減らせるという側⾯もあるけれど、余程の場合でな
い限り⽀払う代償は⼤きい
バッファプールにミスヒットした時だけやたら重い
(20ms => 400msオーバーとか)
-
しかも実際にSELECTするとバッファプールに載るから、
スローログ⾒てSELECTした時は全然遅くないとか
-
104/141
innodb̲buffer̲pool̲sizeだいじ
⼩さすぎると(ほぼ)全てのクエリーが遅くなる
特にInnoDB圧縮を使うならデータサイズの1.5倍以上あっ
てもいい
105/141
innodb̲log̲file̲size *
innodb̲log̲files̲in̲group
バッファプールが更新されてからテーブルスペースファイル
に反映されるまでの中間状態がInnoDBログ
テーブルスペースに即時反映されなくともログファイル
に即時反映を保証することでデータの永続性を担保する
-
ログファイルに即時反映を保証できなくなると、データ
の保護のためInnoDBはトランザクション処理を中断する
(シャープチェックポイント)
-
106/141
innodb̲log̲file̲size *
innodb̲log̲files̲in̲group
⾜りているか , ⾜りていないか の⼆軸なので、⾜りていれ
ば⼤きすぎる必要もないし、⾜りていなければどんどん⼤き
くしないといけない
⼤きすぎるとクラッシュリカバリー終わらないって話が
あるけど、MySQL 5.5以降でログファイルサイズによる
クラッシュリカバリーが終わらないって話はそうそうな
い(⻑くて数分程度)
-
innodb_flush_method= O_DIRECTでもページキャッシュさ
れる(O̲DIRECTで開くのはテーブルスペースファイル
だけ)ので、ページキャッシュと相談
-
107/141
innodb̲log̲file̲size *
innodb̲log̲files̲in̲group
InnoDBログの性能は innodb_log_file_size と
innodb_log_files_in_group の積にほぼ⽐例
64M * 3 と96M * 2 はほぼ同等の性能-
⾜りてるか⾜りてないかだから-
5.5とそれ以前はinnodb_log_files_in_groupのみを増やす場
合のみ再作成不要
5.6とそれ以降は増やす時でも減らす時でもmy.cnfを書
き換えて再起動するだけでいい。楽ちん。
-
108/141
InnoDBのデータ永続化
InnoDBはテーブルスペースファイル + ログファイルで初め
て完全なデータ
バッファプールは (テーブルスペースファイル + ログフ
ァイル) の部分集合
-
ある意味「フルバックアップ」と「バイナリーログ」か
らポイントインタイムリカバリーをするのに似ている
-
バッファプール上にあってテーブルスペースファイルに
ないデータ(ダーティページ)が⼀定割合を超えると強制
チェックポイント
-
109/141
innodb̲flush̲method
テーブルスペースファイル(ibdata1, .ibd)のopenフラグを
変更できる
暗黙のデフォルトは実質fsync
ファイルシステムキャッシュを併⽤しつつfsyncする
-
使うとしてもO̲DIRECTくらいしかぱっと思いつかない
ファイルシステムキャッシュを通さずにfsyncする
ページキャッシュで節約したぶんをバッファプールに
回せる
ブロックデバイスが対応している場合は、DMA転送が
有効になるらしい(ioDriveがちょっぱやなのはこれが
効いているらしい)
-
nosync, O_DIRECT_NO_FSYNCとか…わかるな︖- 110/141
innodb̲log̲buffer̲size
InnoDBログのバッファ
ログファイルがwriteされるまでの間、ログに書き込まれる
べきデータはここにいる
1トランザクションが⼤きすぎてログバッファに⼊りきら
ないと途中でフラッシュ
-
innodb_flush_log_at_trx_commit = 0のときはここにあ
りったけ詰め込まれる
-
⼼当たりがあれば⼤きくすれば良いけどせいぜいMB単位
なので⼤きくしてもいい
-
111/141
innodb̲flush̲log̲at̲trx̲commit
InnoDBログをsyncする頻度
デフォルトは1。RDBMSとしては1であるべき。
innodb̲flush̲log̲at̲trx̲co
mmit
write fsync
0 InnoDBまかせ InnoDBまかせ
1 COMMITごと COMMITごと
2 COMMITごと 1秒に1回
112/141
InnoDBログとクラッシュ
problem commit write sync detail
mysqldクラッ
シュ
OK OK OK データの⽋損な
し
OSクラッシュ OK OK OK データの⽋損な
し
mysqldクラッ
シュ
OK OK Not yet データの⽋損な
し
OSクラッシュ OK OK Not yet データ⽋損
mysqldクラッ
シュ
OK Not yet Not yet データ⽋損
OSクラッシュ OK Not yet Not yet データ⽋損
mysqldクラッ
シュ
Not yet Not yet Not yet データの⽋損な
し
OSクラッシュ Not yet Not yet Not yet データの⽋損な
し
113/141
innodb̲flush̲log̲at̲trx̲commit= 1
problem commit write sync detail
mysqldクラッ
シュ
OK OK OK データの⽋損な
し
OSクラッシュ OK OK OK データの⽋損な
し
mysqldクラッ
シュ
OK OK Not yet データの⽋損な
し
OSクラッシュ OK OK Not yet データ⽋損
mysqldクラッ
シュ
OK Not yet Not yet データ⽋損
OSクラッシュ OK Not yet Not yet データ⽋損
mysqldクラッ
シュ
Not yet Not yet Not yet データの⽋損な
し
OSクラッシュ Not yet Not yet Not yet データの⽋損な
し
114/141
innodb̲flush̲log̲at̲trx̲commit= 2
problem commit write sync detail
mysqldクラッ
シュ
OK OK OK データの⽋損な
し
OSクラッシュ OK OK OK データの⽋損な
し
mysqldクラッ
シュ
OK OK Not yet データの⽋損な
し
OSクラッシュ OK OK Not yet データ⽋損
mysqldクラッ
シュ
OK Not yet Not yet データ⽋損
OSクラッシュ OK Not yet Not yet データ⽋損
mysqldクラッ
シュ
Not yet Not yet Not yet データの⽋損な
し
OSクラッシュ Not yet Not yet Not yet データの⽋損な
し
115/141
innodb̲flush̲log̲at̲trx̲commit= 0
problem commit write sync detail
mysqldクラッ
シュ
OK OK OK データの⽋損な
し
OSクラッシュ OK OK OK データの⽋損な
し
mysqldクラッ
シュ
OK OK Not yet データの⽋損な
し
OSクラッシュ OK OK Not yet データ⽋損
mysqldクラッ
シュ
OK Not yet Not yet データ⽋損
OSクラッシュ OK Not yet Not yet データ⽋損
mysqldクラッ
シュ
Not yet Not yet Not yet データの⽋損な
し
OSクラッシュ Not yet Not yet Not yet データの⽋損な
し
116/141
独断と偏⾒
MySQLが単体でダウンするよりはH/W障害で軒並みごっそ
りいかれる⽅が回数が多い気がする
ということはinnodb_flush_log_at_trx_commitは1か0( ク
ラッシュしたら必ず毎回作り直す )が効率がいいと思う
毎回作り直す ことを前提に設計すれば、
innodb_log_group_home_dir= /dev/shm も可能
/dev/shmだとページキャッシュ⾷わないし
更にはskip_innodb_doublewriteも使える
-
117/141
innodb̲io̲capacity, innodb̲io̲capacity̲max
ダーティーページのアダプティブフラッシュに使うIO回数を
制限する
5.6で⼤幅に動作が変わっていて、5.5とそれ以前より⼤幅に
下げないとバックグラウンドスレッドがIOを⾷い切る
innodb_adaptive_flushing_lwmを上げるのもいいかも知れな
い
118/141
キャッシュミスヒット
InnoDBの圧縮はミスヒットのコストがヒット時に⽐べて異
様に⾼い
テーブルキャッシュミスヒットは統計情報を再計算するので
インデックスの数が多いと重い
APサーバーを再起動したタイミングで⼀⻫にSHOW FIELDS
を取りに来るようなORMは「⼀⻫に」と相まって⼀瞬で
テーブルキャッシュを⾷い尽くすことがある
-
クエリーキャッシュはヒット/ミスヒットに関わらずジャイ
アントロックがでかすぎ
119/141
ロックの競合(並列度が⾼いだけとは限らない)
あるいは単にInnoDBが⾏ロックだと 思い込んで ロック粒
度が適切でない
InnoDBのロックはネクストキーロック-
つまり インデックスロック-
ネクストキーロックは インデックス間のギャップもロッ
ク
ギャップロックが避けたい場合はREAD-COMMITTED
にするとまあまあ良くなる
-
sys.innodb̲lock̲waitsがロック解析を助けてくれる
InnoDBは基本的に参照ロックフリー
120/141
Too many connectionsは⼤概並列度⾼すぎではない
単にクエリーが遅い
単にロックが競合してる
単に性能限界を超えたmax_connectionsを設定してしまって
る
Connection Handlerをcloneして認証までしてから”Too
many connections”の判定をするから、cloneの負荷はか
かる
-
稼げる時間は⼗数分、それもエラーを減らすために他の
クエリーの速度を犠牲にする
-
ただしホントにmutexが変に刺さってもこれは出る(けど、
頻度は少ない)
mutexが刺さったならmysqldの再起動⼀択- 121/141
mutexの競合(並列度が⾼すぎる)
ガチでmutexが詰まってる場合は%sysが跳ねあがることが
多い
InnoDBのmutexの詰まりはSHOW ENGINE INNODB STATUSで
SEMAPHORESセクションにいっぱい吐く
こうなると正直⼿の打ちようがほぼない-
多少IO処理を軽くしたりスレッドの数減らして再起動し
ても焼け⽯に⽔
-
スケールアウト検討どき-
⾼負荷状況下で新機能を使うと刺さって残ることがたまにあ
る
というか先週InnoDB Online DDLで変に刺さって再起動
した
-
122/141
バルク操作
最初の戦略
トランザクションの粒度をできるだけ(アトミック性を担保
できるだけ)⼩さくする
トランザクションが⼤きいと
ロックを取る時間が⻑くなる
5.5とそれ以前のスレーブSQLスレッドは同時に1つの
クエリーしかさばけないためレプリケーションが遅延
する
5.6でもスキーマが違わないとパラレルに動かない
5.7でテーブル単位もイケるらしいが未検証
-
123/141
バルク操作
トランザクションが⼩さいと
InnoDBログにフラッシュする回数が多くなる-
ダブルライトの回数が多くなる(ライトコンバインドが
効きにくくなる)
-
バイナリーログエントリーが増えるのでI/Oと帯域に注意-
124/141
バルク操作
新しいインスタンスをスレーブで⽴てて、そっち側でバルク
更新
skip_innodb_doublewrite,
innodb_flush_log_at_trx_commit= 0, skip_log_bin,
skip_slow_query_log
-
バルク操作を分割して並列化-
切り替え時に ショートメンテが必要 (テーブルごとデー
タの転送)
他のスレーブがあった場合、そっちにも転送しないと
いけない
5.6とそれ以降ならFLUSH TABLE FOR EXPORTを使ってテ
ーブル単位でファイルコピーできる
-
125/141
バルク操作
pt-online-schema-changeを流⽤してしのぐ
pt-online-schema-changeは5.5とそれ以前のバージョン
でもオンラインでALTER TABLEを実現できるように作られ
たハックの粋が詰まったスクリプト
-
要はトリガーとREAD-COMMITTEDを使って上⼿いこと
新しいテーブルにデータを移す
-
126/141
pt-oscの仕組み
オリジナルテーブルの定義をコピーしてクローンテーブルを
作る
クローンに対して定義の操作をする
オリジナルにトリガーをかけ、オリジナルに対する更新をク
ローンに伝播させる
READ-COMMITTEDでオリジナルからクローンに少しずつ
データをコピー
データがコピーし終わったらRENAME TABLEでオリジナルとク
ローンを⼊れ替える
127/141
pt-oscの流⽤でバルク操作
トリガーを⼿で(か、pt-oscを中途半端に終わらせてトリガ
ーを残す)張ってpt-archiverとか使うとレプリケーション
遅延とかもチェックしてくれて便利
スレーブを使う⽅法に⽐べてショートメンテは要らない
pt-oscと同じデメリットがある
最⼤でテーブルサイズの2倍以上の空き容量が必要
デッドロック発⽣の可能性
トリガー処理分のレスポンスタイムのオーバーヘッド
数回のメタデータロック
-
128/141
バルクデリートが遅い
InnoDBのDELETEは論理削除、だが
クラスターインデックスだけは再編成する-
5.5とそれ以降ならセカンダリーインデックスはチェンジ
バッファが効いて非同期にマージ
それ以前の場合、⾏を消すということは必ず全てのセ
カンダリーインデックスに更新が⾛るので超重い
-
DELETEの場合、クラスターインデックスのリーフを削除す
るので場合によっては再編成が⾛る
B+Treeはツリーの再編成が重い-
129/141
容量との戦い
DELETEは⼀度拡張したテーブルスペースを縮⼩させない
空き領域が再利⽤されるので、テーブルスペースのサイ
ズがそこで頭打ちになる、程度。
-
テーブルスペースを⼩さくするにはOPTIMIZE TABLEが必
要。
ただしInnoDBでinnodb_file_per_table= 0の場合は
OPTIMIZE TABLEでも⼩さくならない。
この設定だとDROP TABLEやTRUNCATEでも決して⼩さく
ならないので結構⾯倒。
-
130/141
容量との戦い
オンラインでOPTIMIZE TABLE
pt-oscなら5.5や5.6.16とそれ以前でもオンラインででき
る
ただしオリジナルテーブルを残したままクローンテー
ブルに⾏をコピーするので、(容量的な意味での)最
⼤瞬間風速に注意。
-
InnoDBネイティブのオンラインALTER TABLEができる
ようになるのは5.6.17とそれ以降
-
更新がそこまで多くないテーブルなら(秒間数千叩かれ
続けるようなのは⾟かった)5.6.17以降を使うのが簡単
でいい
-
131/141
容量との戦い
タイトになるのがわかってる時はパーティショニングしてる
削除条件が設計段階できれいに決まっているなら、それ
に合わせてパーティショニングすることでALTER TABLE
TRUNCATE PARTITIONが使える。
-
12か⽉保存のためにPARTITION BY LIST ((YEAR
(register_date) MOD 2)* 100 + MONTH(register_date))
で24パーティション確保して13か⽉目にTRUNCATE
PARTITIONするとか
-
132/141
容量との戦い
int型かsmallint型か、timestamp型かdatetime型かは多く
の場合どうでもいい
2バイトの差= 10億⾏でやっと2GB-
インデックスの数に⽐例するけど、それでも⽂字列型に
⽐べれば全然⼤したことない
-
テーブルのサイズは基本的にvarcharに⽀配される
EUC, SJISは2バイト、UTF-8は3バイト(utf8mb4の場
合は最⼤で4バイト⽂字も⼊る)
-
とはいえユーザーコンテンツはどうしようもないんだよ
なぁ。。
垂直シャードするのがギリギリのライン。。
-
133/141
論理削除
個⼈的には好きじゃない
やるなら、これは守っておいた⽅がいい
整数型かENUM型(でないとc.が守りにくい)a.
全てのセカンダリーインデックス の先頭に削除フラグの
カラムが存在しなければならない
b.
決して”=”以外の演算⼦を使わない(NOTとかダメ、絶
対)
c.
”=”演算⼦で等価⽐較するものを全てのWHERE句の先頭に
つける
d.
これ以外で迂闊に使うと、あっという間にExecutorさんが
頑張る世界線に突⼊する
134/141
Questions
and/or
Suggestions?
135/141
Question
MySQL 5.5からそれ以降にアップグレードするにあたって注意
事項はある︖
5.6ではInnoDBのバックグラウンドスレッドが活発になってい
るのでinnodb̲io̲capacityを減らす、メモリーを⼤⾷いにな
ったので「物理メモリーの75%」よりも減らしておくと良い。
SQL的な非互換はほぼない。
5.7はsql̲modeのデフォルト値に変更があったので明⽰的に
sql_mode= ''をコンフィグに記載しないとSQLが非互換。
sql̲modeが同じでも多少の非互換があり、古いRailsだと
migrationに失敗する。いくつか古いオプションが削除になっ
ている。
136/141
Question
オプティマイザーだけアップグレードとかできない︖
できません><
137/141
Question
Using whereとUsing filesort、どっちが悪いっていう指標はあ
るの︖
フェッチするのが2⾏だったらUsing whereでもUsing filesort
でも構わないけどフェッチするのが1000万⾏だったらダメだ
よね、という感じなので、Rows_sentとRows_examinedの⽐率で
⽐べるのがいいんじゃ。
138/141
Question
InnoDBの統計情報をサンプリングではなく全ページをスキャン
にはできないの︖
グローバル変数またはテーブル単位でページ数を指定できるの
で、それを⼗分⼤きくすれば全ページをスキャンした統計情報
は可能。ただ、5.5で知⼈が試した限り遅かったらしい。
139/141
Question
MyISAMならALTER TABLE .. ORDER BY ..で降順のインデック
スを作れる︖
インデックス上は必ず昇順。ALTER TABLE .. ORDER BY ..で並
べ替えられるのはデータファイル上の並び順。
MySQL :: MySQL 5.6 リファレンスマニュアル :: 13.1.7
ALTER TABLE 構⽂
140/141
Question
MySQLからMariaDBに移⾏したら性能があがった。考えられる
要因は︖
MariaDBのオプティマイザーは性能が良い。MariaDBの
InnoDBはPerconaが開発しているXtraDBなのでちょっとだけ
いい。バイナリーログのグループコミットが性能が良く、スレ
ーブの遅延が起きにくい。
メディアではMariaDBは使っている︖
スレッドプールプラグインが使いたくて導⼊したことがあった
が、ワークロード的にMySQLより遅くなったので、より
MySQLに近いPercona Serverに移⾏した。
141/141

More Related Content

雑なMySQLパフォーマンスチューニング