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.

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

37,411 views

Published on

2016/01/20 GMOアドパートナーズグループ勉強会

Published in: Technology
  • Be the first to comment

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

  1. 1. MySQLのパフォーマンスチューニン グについて少々 2016/01/20 yoku0825の中の⼈ GMOアドパートナーズグループ勉強会
  2. 2. \こんにちは/ yoku0825@とある企業のDBA の中の⼈ オラクれない- ポスグれない- マイエスキューエる- Twitter: @yoku0825 Blog: ⽇々の覚書 Oracle ACE Details MySQL 5.7 Community Contributor Award 1/141
  3. 3. このスライド書いた中の⼈です 2/141
  4. 4. ちなみに 3/141
  5. 5. というわけで安⼼ して 地雷友達 5.7 使っていいです よ :) 4/141
  6. 6. クエリーのライフサイクル client connection_handling parser optimizer executor handler storage_engine application mysqld 5/141
  7. 7. クエリーのライフサイクル Connection Handling Parser Optimizer Executor Handler Storage Engine 6/141
  8. 8. クエリーのライフサイクル Connection Handling Parser Optimizer Executor Handler Storage Engine 7/141
  9. 9. Connection Handling ソケット, ポートからの接続を待ち受ける 接続があったらclone(またはスレッドキャッシュから取り 出してディスパッチ) ⼀次認証 8/141
  10. 10. クエリーのライフサイクル Connection Handling Parser Optimizer Executor Handler Storage Engine 9/141
  11. 11. Parser MySQLプロトコルのパース SQL構⽂のパース ⼆次認証(データベース, テーブル単位のアクセス権限チ ェック) クエリーキャッシュ処理 ジェネラルログ 10/141
  12. 12. クエリーのライフサイクル Connection Handling Parser Optimizer Executor Handler Storage Engine 11/141
  13. 13. Optimizer 統計情報の取得(Storage Engine APIを叩いてるっぽい) クエリーの書き換えを含めた実⾏計画の決定 12/141
  14. 14. クエリーのライフサイクル Connection Handling Parser Optimizer Executor Handler Storage Engine 13/141
  15. 15. Executor オプティマイザーから渡された実⾏計画の通りにHandlerを 叩く Handlerから戻ってきた結果を使って実⾏計画の残りを実⾏ Using where; Using filesort; Using temporary; はコ イツが頑張ってる証拠 - スローログ, バイナリーログ 14/141
  16. 16. クエリーのライフサイクル Connection Handling Parser Optimizer Executor Handler Storage Engine 15/141
  17. 17. Handler ストレージエンジンの抽象化レイヤー あんまり意識することはない 16/141
  18. 18. クエリーのライフサイクル Connection Handling Parser Optimizer Executor Handler Storage Engine 17/141
  19. 19. Storage Engine 実際にデータを格納するレイヤー プラガブルアーキテクチャーなので、ほとんどの機能はこの レイヤーで実装されている ストレージエンジンごとにトランザクション対応が違う とか - ストレージエンジンごとにロック粒度が違うとか- ストレージエンジンごとにバッファが違うとか- 18/141
  20. 20. クエリーのライフサイクル client connection_handling parser optimizer executor handler storage_engine application mysqld 19/141
  21. 21. どこが 遅 い︖ 20/141
  22. 22. どこが遅い︖ connection_handling parser optimizer executor handler storage_engine mysqld だいたいExecutor たまにStorage Engine 21/141
  23. 23. Executorが遅い理由 MySQLの不得意なことをやろうとしている ⾊々パターンはあるけど、⼀⾔で⾔うならこれにあたる- Executorに仕事をさせたら負け 22/141
  24. 24. MySQLの不得意なこと 複数のインデックスを使いこなす 相関サブクエリー 23/141
  25. 25. 複数のインデックスに弱い理由 MySQLは原則1クエリー内で1つのテーブルあたり1つのイ ンデックスしか使わない インデックスマージという例外が⼀応あるにはあるが、 後付け - そもそもこういう設計思想なんだと思う- テーブルスキャンに強い/弱いはストレージエンジンのレイ ヤー MyISAMはスキャンに強く、InnoDBは弱い- 関数演算、(インデックスで解決できない)フィルター、 (インデックスで解決できない)ソートはExecutorのお仕 事(= 遅い) 24/141
  26. 26. 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
  27. 27. いくつかのルール Extra列はExecutorの都合 それ以外はオプティマイザー、ハンドラー(= ストレージエ ンジン)の都合 key_len, rowsがヒントになる 26/141
  28. 28. type ALLがダメなんて良く⾔う ALL .. テーブルスキャン- ref .. 非ユニークキーによる選択- range .. レンジスキャン- 27/141
  29. 29. type どの⽅法でアクセスさせるのが最速だと オプティマイザー が ⾒積もった結果 特にInnoDBはクラスターインデックスなので、下⼿にセ カンダリーインデックスを使うよりも本当に速いケース もある - MyISAMもスキャンに強いので、本当に速いケースがあ る - ダメなのはALLじゃなく、本当の最適なアクセスと違う時 rows, key_lenが判断の助けになる- 28/141
  30. 30. オプティマイザーが⾒誤る理由 InnoDBの統計情報がサンプリング1. オプティマイザーはLIMIT句を正しくハンドリングできない2. 29/141
  31. 31. InnoDBの統計情報 InnoDBの統計情報は テーブル全体から⼀部の情報をランダ ムに選び出し、全体を推測する サンプリング統計 5.5とそれ以前では1インデックスあたり8ページ(= 16kB * 8ページで128kB)でハードコード - 5.6とそれ以降では1インデックスあたりデフォルト20ペ ージ(innodb_stats_persistent_sample_pages) CREATE TABLE, ALTER TABLEでテーブル単位でページ数 を指定することも可能 - 30/141
  32. 32. InnoDBの統計情報 サンプリングなので 統計情報の再計算はそこまで(MyISAMと⽐べれば)⼤ きくない - 統計情報が間違っている(著しく実際のデータの分布と 乖離している)可能性が⾼い - 31/141
  33. 33. LIMIT句のハンドリング LIMIT句はORDER BY句の処理が終わってから実⾏される ORDER BY句の処理は最終的にExecutorで終端するので- つまりLIMIT句もExecutorの処理の範疇- オプティマイザーは⾃分より後のExecutorの動作を正しく ⾒積もれない なので、ORDER BY .. LIMIT ..で効率的にORDER BY狙い のキーが使える場合でも、選んでくれないケースがある - 5.7でLIMITありの場合のオプティマイズが効くようにな っている - 32/141
  34. 34. Extra: Using where; Using filesort; ExecutorがWHEREをフィルターしている、ExecutorがORDER BYのソートをしている インデックスを使って⾼速化できるのは WHERE, ORDER BY, GROUP BY, select_list どうすればいいかと⾔われると、「インデックス使う」とし か⾔いようがない 33/141
  35. 35. トランプの 話をします 34/141
  36. 36. Query As A Card 100枚のトランプが裏返して積まれた⼭があります 何組かのトランプを混ぜたもので何が何枚⼊ってるかはわか りません 35/141
  37. 37. この⼭の中から スペードのAを 探してください 36/141
  38. 38. どうするかというと ⼭の⼀番上のトランプをめくる1. スートと番号を⾒る2. スペードのAだったら⼿元に残す3. スペードのAじゃなかったらどっかに置いておく4. ⼭の全てのカードに対して2, 3, 4を繰り返す ⼭にスペードのAが何枚あるかに関わらず、⼭の全てのカー ドに対して繰り返す 5. 37/141
  39. 39. これがtype: ALLかつExtra: Using where トランプをめくる .. ⾏をフェッチする スートと番号を⾒る .. Executorでのフィルター処理 38/141
  40. 40. この⼭の中からス ペードを全て探し 出して、K〜Aの順 で並べてください 39/141
  41. 41. どうするかというと 右⼿で ⼭の⼀番上のトランプをめくる1. スートと番号を⾒る2. スペードだったらそのカードを 左⼿に 残しておく3. スペードじゃなかったらどっかに置いておく4. ⼭の全てのカードに対して2, 3, 4を繰り返す5. ⼭から全てのスペードが出揃ったら、⼿元のカードをK〜A まで順番に並べる 6. 40/141
  42. 42. これがtype: ALLかつExtra: Using where; Using filesort トランプをめくる .. ⾏をフェッチする トランプをめくった 右⼿ .. リードバッファー スートを⾒る .. Executorでのフィルター処理 スートが⼀致した時にカードを⼊れた 左⼿ .. ソートバッフ ァー ⼿元のカードを並べ替える .. Executorによるクイックソー ト 41/141
  43. 43. 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
  44. 44. ここからスペードの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
  45. 45. スペードのAを探す トランプの上から2, 8, 9, .. 枚目を順番にめくり1. 番号を⾒る(スペードのものだけめくってるんだから、スー トは⾒る必要がない) 2. Aだったらそのカードを残しておく3. Aじゃなかったらどっかに置いておく4. 44/141
  46. 46. これが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
  47. 47. ここからスペードを全て探し出して、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
  48. 48. スペードを全て探し出して、K〜Aの順で並べる トランプの上から2, 8, 9, .. 枚目を順番にめくり 左⼿ に取 る 1. 番号を⾒て、並べ替える2. 47/141
  49. 49. type: ref, ref: const, Extra: Using filesort インデックス上からスペードを探して「上から2番目」の情 報を得る 1. 上から2番目のカードを 左⼿ に .. ha̲index̲readしてソー トバッファーに詰める 2. 番号を⾒て並べ替える .. Executorによるクイックソート3. 48/141
  50. 50. 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
  51. 51. ここからスペードの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
  52. 52. スペードのAを探す トランプの上から2, 23, 47, 76枚目を順番にめくる(おし まい︕) インデックスの情報だけで完結するため、⼭を全て⼿繰 る(不要なものをフェッチする)必要がない 1. 51/141
  53. 53. type: ref, ref: const, const インデックス上から(スペード, 1)を探して「上から2番目」 の情報を得る 1. 上から2番目のカードを取る .. ha̲index̲read2. インデックス上の全てのリーフをさらうまで繰り返す .. ha̲index̲nextがHA̲ERR̲END̲OF̲FILEを返すまでルー プ 3. 52/141
  54. 54. スペードを全て探し出して、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
  55. 55. スペードを全て探し出して、K〜Aの順で並べる トランプの上から49, 59, 80枚目を⼿に取る1. トランプの上から11, 33, 35, 96枚目を⼿に取る2. 全てのスペードのカードぶん1, 2を繰り返す3. 54/141
  56. 56. 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
  57. 57. :(;゙゚ʼω゚ʼ): Extra: Using whereが消えなかった たまに消えない たぶんオプティマイザーのバグ(ORDER BYと混じると Executorが仕事してなくてもUsing whereが消えない) 56/141
  58. 58. 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
  59. 59. ここからスペードのAを探す インデックスからAを探して、「2, 22, 23, ..枚目」の情報 を得て 1. ひっくり返してスペードかどうかを判定する2. Aのリーフが全部終わったら終わり3. type: ref, ref: const, Extra: Using where 58/141
  60. 60. スペードを全て探し出して、K〜Aの順で並べる インデックスからKを探して19, 58, 85枚目を⼿に取る1. スペードなら⼿元に、そうでなければよける2. カードの⼭全てに対して1, 2を繰り返す3. 59/141
  61. 61. type: index, Extra: Using where ただしこれはLIMITがない場合はテーブルスキャンを選んで た 5.6の場合、LIMITがあってもテーブルスキャンを選ぶ- そりゃ、結局全部のカードを裏返してるから、頭から全部ひ っくり返した⽅が速い LIMITがある= 「枚数が揃ったらそこでそれ以降の結果セ ットは考慮する必要がない」 - この条件にマッチした場合、フェッチする⾏数を⼤幅に 減らせる - 60/141
  62. 62. インデックスのイメージ インデックスは ソート済みのデータの部分複製 (異論は認め る) root club spade 2 3 2 13 20 18 45 77 B+Treeじゃないけど取り敢えずそこまで気にしない 61/141
  63. 63. インデックスのリーフ root club spade 2 3 2 13 20 18 45 77 MyISAMの場合はMYDファイルの先頭からのオフセットバイト 数 InnoDBの場合はクラスターインデックスの値 62/141
  64. 64. 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
  65. 65. 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
  66. 66. 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
  67. 67. 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
  68. 68. 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
  69. 69. 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
  70. 70. 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
  71. 71. 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
  72. 72. 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
  73. 73. 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
  74. 74. WHERE狙いのキー, ORDER BY狙いのキー WHEREとORDER BY .. LIMITを同時にカバーできるインデック スが作成できない時に WHEREをストレージエンジンに任せてフェッチする⾏を減 らしてファイルソートをExecutorにやらせるか - ORDER BY .. LIMITをストレージエンジンに任せて ExecutorにフィルターさせつつLIMITでループを抜けるの を期待するか - 73/141
  75. 75. WHERE狙いのキー, ORDER BY狙いのキー 「JOINが遅い」はJOINが遅いんじゃなくてJOINの参照表で ソートしようとしてExecutorがソートしちゃってるケース あとはオプティマイザーにバグがあって、駆動表を Executorがフィルターして参照表の参照回数が減る可能 性を考慮していない。 - See also, http://www.slideshare.net/yoku0825/ whereorder-by 74/141
  76. 76. Executorに仕 事をさせたら 負け 75/141
  77. 77. Executorが必要以上の仕事をしていないか GROUP BYのケースを除いて、Rows_sent= Rows_examinedが⼀ 番良い MySQLにはヒストグラム統計がないので、WHERE狙いと ORDER BY狙いのどちらが本当に速いのかMySQLは知らない (憶えてない) 76/141
  78. 78. 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
  79. 79. オプティマイザーの強化 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
  80. 80. 迷ったら最新(オ プティマイザーに 関しては) 79/141
  81. 81. クエリーの最適化(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
  82. 82. クエリーの最適化(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
  83. 83. 古い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
  84. 84. sysスキーマ performance̲schema, information̲schemaのビューと、 performance̲schema関連の設定をゴニョるプロシージャ の詰め合わせ なのでperformance_schema= ONが⼤前提- 合わせて使いたいinnodb_monitor_enable= all- 5.6の場合はgitリポジトリーからcloneしてsqlスクリプト 実⾏ - 83/141
  85. 85. 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
  86. 86. 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
  87. 87. Anemometer 86/141
  88. 88. 相関サブクエリー 外側のクエリーが内側のサブクエリーの条件になっているケ ース 内側のサブクエリーが外側の条件になるケースはMySQL ⽤語では単にサブクエリーと呼ぶ(個⼈的には明確に区 別するために「キャッシャブルなサブクエリー」と呼ん でる) - しかしWHERE INはキャッシャブルなサブクエリーのはず でも相関サブクエリーになるというダメ仕様が5.5とそれ 以前にあってだな。。 - 87/141
  89. 89. シンプルな話、外側のクエリーに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
  90. 90. 相関サブクエリーの書き換え⽅ 7割⽅JOINに書き換えられる JOINに落とし込んでUSE INDEXまでつければ5.5とそれ以 前でも戦える - どうしてもロジックとしてJOINに書き換えられないケース もある CREATE TEMPORARY TABLEを使って落とし込むのがいい テンポラリーテーブルはクラッシュアンセーフ( 更新 ステートメントには使わない︕ ) そもそもクラッシュアンセーフだから迷わずMyISAM を選んでいい ⼤した量でなくてもインデックス張る。劇的に違う - のがいい、というか、SQLだけでやるにはそれしかない- 89/141
  91. 91. 書き換える時に必要なもの 再帰テスト 無いと死ぬ- ポジション上、返す結果が変わらないことの担保をしな いといけない パラメーター渡して、新旧クエリーの2パターン投げて 結果を⽐較するだけでも取り敢えずいいんだ 最近はコマンドラインクライアントで REPEATABLE-READでトランザクション内でやればオ ブジェクト⽐較するだけで済む 分布に極端な偏りがあるパラメーターはいくつかバリ エーションを⽤意した⽅がいい(同じ形をしていても パラメーターで実⾏計画は変わる) - 90/141
  92. 92. ひとやす み 91/141
  93. 93. Storage Engineが遅い理由 リソースが⾜りていない1. パラメーターが適切でない2. キャッシュミスヒット3. ロックやmutexの競合(並列度が⾼すぎる)4. バルク操作5. 92/141
  94. 94. リソースが⾜りていない ホットデータが100GBあるのにInnoDBバッファプールが 16GBしかなかったら︖ 物理メモリーが64GBしかないのに innodb_buffer_pool_size= 128Gにしたら︖ 50並列でクエリーを実⾏するのに8コアしかなかったら︖ 秒間1000コミットあるのにIOPSが200しかなかったら︖ 93/141
  95. 95. パラメーターが適切でない 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
  96. 96. innodb̲buffer̲pool̲size InnoDBの動作の要 個⼈的にはキャッシュじゃなくて データの本体- ⾜りてないとSELECTだけじゃなく全ての動作が遅い ただしDROP TABLEとTRUNCATE TABLEはバッファプールが⼤き ければ⼤きいほど遅くなる 95/141
  97. 97. InnoDBの動作の要 SELECTのとき バッファプールを⾒る あればそれを使う なければテーブルスペースファイルを読んでバッファ プールに載せる バッファプールがいっぱいだったら⼀番古いのを追い 出してバッファプールに載せる 追い出そうとしたページがダーティーページだったら フラッシュしてから追い出す - 96/141
  98. 98. InnoDBの動作の要 INSERT, UPDATE, DELETEのとき UNDOとログはちょっと省略 - UPDATE, DELETEの時にPRIMARY KEY以外の属性で絞り込 む場合は、SELECTと同様にバッファプールを使って検索 する - 97/141
  99. 99. InnoDBの動作の要 INSERT, UPDATE, DELETEのとき データそのものの追加, 更新, 削除は最終的にクラスター インデックスの値から、書き込むページ番号を特定する バッファプールはCOMMIT前に更新される(COMMIT されてるかどうかのフラグがある) READ-UNCOMMITTEDはバッファプールから読んで COMMITされてるかどうかのフラグを⾒ない COMMITされてるかどうかに関わらず、古いファイル はUNDOセグメントに移されて、そのページ番号がペ ージに書き込まれる - 98/141
  100. 100. 対象のページがバッファプールに載っていれば バッファプール上のデータを書き換える InnoDBのDELETEは論理削除- バッファプールのページに「delete mark」を付ける- まだテーブルスペースファイルに反映されてないページ のことを「ダーティーページ」と呼ぶ ダーティーページはバッファプールから押し出される 時にテーブルスペースファイルにフラッシュされなけ ればいけない - 99/141
  101. 101. 対象のページがバッファプールに載っていなければ テーブルスペースファイルを読んでバッファプールに載せて から書く バッファプールに載せてからINSERT- バッファプールに載せてからUPDATE- バッファプールに載せてからDELETE- Replication Booster for MySQLやinnodb̲fake̲changes はこの仕組みを利⽤したもの 100/141
  102. 102. DROP TABLE, TRUNCATEのとき TRUNCATEは内部的にDROP TABLE, CREATE TABLE ディクショナリーから参照を切る バッファプール上の全てのページをチェックし、そのテーブ ルのページがあれば破棄する 「バッファプールに載せてdelete mark」はしない- 101/141
  103. 103. ROW̲FORMAT= Compressed InnoDB圧縮 圧縮はページ単位、圧縮したままでは読むことも書くことも できない バッファプールに 圧縮状態のまま 読み込むa. バッファプール上で 解凍してコピーするb. 解凍済みのページに対する操作をするc. 解凍済みのページがフラッシュされる時に再圧縮されて 圧縮状態のページ が更新される(再圧縮) d. 圧縮状態のページはバッファプールにもう少し残るe. ただでさえ重いディスクアクセスのタイミングで圧縮/解凍 処理が加わる。 102/141
  104. 104. InnoDB圧縮のワーストケース SELECTクエリーがやってきて1. バッファプールミスヒット2. バッファプールの空きが無い3. ⼀番古いページを押し出す4. ⼀番古いページがダーティーページだったら︖5. 再圧縮してフラッシュ6. 空けたページに圧縮状態のページを持ってきて7. 解凍してコピー8. 103/141
  105. 105. InnoDB圧縮は バッファプール効率とCPU効率を犠牲にして容量を稼いでい る IO回数が減らせるという側⾯もあるけれど、余程の場合でな い限り⽀払う代償は⼤きい バッファプールにミスヒットした時だけやたら重い (20ms => 400msオーバーとか) - しかも実際にSELECTするとバッファプールに載るから、 スローログ⾒てSELECTした時は全然遅くないとか - 104/141
  106. 106. innodb̲buffer̲pool̲sizeだいじ ⼩さすぎると(ほぼ)全てのクエリーが遅くなる 特にInnoDB圧縮を使うならデータサイズの1.5倍以上あっ てもいい 105/141
  107. 107. innodb̲log̲file̲size * innodb̲log̲files̲in̲group バッファプールが更新されてからテーブルスペースファイル に反映されるまでの中間状態がInnoDBログ テーブルスペースに即時反映されなくともログファイル に即時反映を保証することでデータの永続性を担保する - ログファイルに即時反映を保証できなくなると、データ の保護のためInnoDBはトランザクション処理を中断する (シャープチェックポイント) - 106/141
  108. 108. innodb̲log̲file̲size * innodb̲log̲files̲in̲group ⾜りているか , ⾜りていないか の⼆軸なので、⾜りていれ ば⼤きすぎる必要もないし、⾜りていなければどんどん⼤き くしないといけない ⼤きすぎるとクラッシュリカバリー終わらないって話が あるけど、MySQL 5.5以降でログファイルサイズによる クラッシュリカバリーが終わらないって話はそうそうな い(⻑くて数分程度) - innodb_flush_method= O_DIRECTでもページキャッシュさ れる(O̲DIRECTで開くのはテーブルスペースファイル だけ)ので、ページキャッシュと相談 - 107/141
  109. 109. 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
  110. 110. InnoDBのデータ永続化 InnoDBはテーブルスペースファイル + ログファイルで初め て完全なデータ バッファプールは (テーブルスペースファイル + ログフ ァイル) の部分集合 - ある意味「フルバックアップ」と「バイナリーログ」か らポイントインタイムリカバリーをするのに似ている - バッファプール上にあってテーブルスペースファイルに ないデータ(ダーティページ)が⼀定割合を超えると強制 チェックポイント - 109/141
  111. 111. innodb̲flush̲method テーブルスペースファイル(ibdata1, .ibd)のopenフラグを 変更できる 暗黙のデフォルトは実質fsync ファイルシステムキャッシュを併⽤しつつfsyncする - 使うとしてもO̲DIRECTくらいしかぱっと思いつかない ファイルシステムキャッシュを通さずにfsyncする ページキャッシュで節約したぶんをバッファプールに 回せる ブロックデバイスが対応している場合は、DMA転送が 有効になるらしい(ioDriveがちょっぱやなのはこれが 効いているらしい) - nosync, O_DIRECT_NO_FSYNCとか…わかるな︖- 110/141
  112. 112. innodb̲log̲buffer̲size InnoDBログのバッファ ログファイルがwriteされるまでの間、ログに書き込まれる べきデータはここにいる 1トランザクションが⼤きすぎてログバッファに⼊りきら ないと途中でフラッシュ - innodb_flush_log_at_trx_commit = 0のときはここにあ りったけ詰め込まれる - ⼼当たりがあれば⼤きくすれば良いけどせいぜいMB単位 なので⼤きくしてもいい - 111/141
  113. 113. 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
  114. 114. 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
  115. 115. 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
  116. 116. 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
  117. 117. 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
  118. 118. 独断と偏⾒ 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
  119. 119. innodb̲io̲capacity, innodb̲io̲capacity̲max ダーティーページのアダプティブフラッシュに使うIO回数を 制限する 5.6で⼤幅に動作が変わっていて、5.5とそれ以前より⼤幅に 下げないとバックグラウンドスレッドがIOを⾷い切る innodb_adaptive_flushing_lwmを上げるのもいいかも知れな い 118/141
  120. 120. キャッシュミスヒット InnoDBの圧縮はミスヒットのコストがヒット時に⽐べて異 様に⾼い テーブルキャッシュミスヒットは統計情報を再計算するので インデックスの数が多いと重い APサーバーを再起動したタイミングで⼀⻫にSHOW FIELDS を取りに来るようなORMは「⼀⻫に」と相まって⼀瞬で テーブルキャッシュを⾷い尽くすことがある - クエリーキャッシュはヒット/ミスヒットに関わらずジャイ アントロックがでかすぎ 119/141
  121. 121. ロックの競合(並列度が⾼いだけとは限らない) あるいは単にInnoDBが⾏ロックだと 思い込んで ロック粒 度が適切でない InnoDBのロックはネクストキーロック- つまり インデックスロック- ネクストキーロックは インデックス間のギャップもロッ ク ギャップロックが避けたい場合はREAD-COMMITTED にするとまあまあ良くなる - sys.innodb̲lock̲waitsがロック解析を助けてくれる InnoDBは基本的に参照ロックフリー 120/141
  122. 122. Too many connectionsは⼤概並列度⾼すぎではない 単にクエリーが遅い 単にロックが競合してる 単に性能限界を超えたmax_connectionsを設定してしまって る Connection Handlerをcloneして認証までしてから”Too many connections”の判定をするから、cloneの負荷はか かる - 稼げる時間は⼗数分、それもエラーを減らすために他の クエリーの速度を犠牲にする - ただしホントにmutexが変に刺さってもこれは出る(けど、 頻度は少ない) mutexが刺さったならmysqldの再起動⼀択- 121/141
  123. 123. mutexの競合(並列度が⾼すぎる) ガチでmutexが詰まってる場合は%sysが跳ねあがることが 多い InnoDBのmutexの詰まりはSHOW ENGINE INNODB STATUSで SEMAPHORESセクションにいっぱい吐く こうなると正直⼿の打ちようがほぼない- 多少IO処理を軽くしたりスレッドの数減らして再起動し ても焼け⽯に⽔ - スケールアウト検討どき- ⾼負荷状況下で新機能を使うと刺さって残ることがたまにあ る というか先週InnoDB Online DDLで変に刺さって再起動 した - 122/141
  124. 124. バルク操作 最初の戦略 トランザクションの粒度をできるだけ(アトミック性を担保 できるだけ)⼩さくする トランザクションが⼤きいと ロックを取る時間が⻑くなる 5.5とそれ以前のスレーブSQLスレッドは同時に1つの クエリーしかさばけないためレプリケーションが遅延 する 5.6でもスキーマが違わないとパラレルに動かない 5.7でテーブル単位もイケるらしいが未検証 - 123/141
  125. 125. バルク操作 トランザクションが⼩さいと InnoDBログにフラッシュする回数が多くなる- ダブルライトの回数が多くなる(ライトコンバインドが 効きにくくなる) - バイナリーログエントリーが増えるのでI/Oと帯域に注意- 124/141
  126. 126. バルク操作 新しいインスタンスをスレーブで⽴てて、そっち側でバルク 更新 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
  127. 127. バルク操作 pt-online-schema-changeを流⽤してしのぐ pt-online-schema-changeは5.5とそれ以前のバージョン でもオンラインでALTER TABLEを実現できるように作られ たハックの粋が詰まったスクリプト - 要はトリガーとREAD-COMMITTEDを使って上⼿いこと 新しいテーブルにデータを移す - 126/141
  128. 128. pt-oscの仕組み オリジナルテーブルの定義をコピーしてクローンテーブルを 作る クローンに対して定義の操作をする オリジナルにトリガーをかけ、オリジナルに対する更新をク ローンに伝播させる READ-COMMITTEDでオリジナルからクローンに少しずつ データをコピー データがコピーし終わったらRENAME TABLEでオリジナルとク ローンを⼊れ替える 127/141
  129. 129. pt-oscの流⽤でバルク操作 トリガーを⼿で(か、pt-oscを中途半端に終わらせてトリガ ーを残す)張ってpt-archiverとか使うとレプリケーション 遅延とかもチェックしてくれて便利 スレーブを使う⽅法に⽐べてショートメンテは要らない pt-oscと同じデメリットがある 最⼤でテーブルサイズの2倍以上の空き容量が必要 デッドロック発⽣の可能性 トリガー処理分のレスポンスタイムのオーバーヘッド 数回のメタデータロック - 128/141
  130. 130. バルクデリートが遅い InnoDBのDELETEは論理削除、だが クラスターインデックスだけは再編成する- 5.5とそれ以降ならセカンダリーインデックスはチェンジ バッファが効いて非同期にマージ それ以前の場合、⾏を消すということは必ず全てのセ カンダリーインデックスに更新が⾛るので超重い - DELETEの場合、クラスターインデックスのリーフを削除す るので場合によっては再編成が⾛る B+Treeはツリーの再編成が重い- 129/141
  131. 131. 容量との戦い DELETEは⼀度拡張したテーブルスペースを縮⼩させない 空き領域が再利⽤されるので、テーブルスペースのサイ ズがそこで頭打ちになる、程度。 - テーブルスペースを⼩さくするにはOPTIMIZE TABLEが必 要。 ただしInnoDBでinnodb_file_per_table= 0の場合は OPTIMIZE TABLEでも⼩さくならない。 この設定だとDROP TABLEやTRUNCATEでも決して⼩さく ならないので結構⾯倒。 - 130/141
  132. 132. 容量との戦い オンラインでOPTIMIZE TABLE pt-oscなら5.5や5.6.16とそれ以前でもオンラインででき る ただしオリジナルテーブルを残したままクローンテー ブルに⾏をコピーするので、(容量的な意味での)最 ⼤瞬間風速に注意。 - InnoDBネイティブのオンラインALTER TABLEができる ようになるのは5.6.17とそれ以降 - 更新がそこまで多くないテーブルなら(秒間数千叩かれ 続けるようなのは⾟かった)5.6.17以降を使うのが簡単 でいい - 131/141
  133. 133. 容量との戦い タイトになるのがわかってる時はパーティショニングしてる 削除条件が設計段階できれいに決まっているなら、それ に合わせてパーティショニングすることでALTER TABLE TRUNCATE PARTITIONが使える。 - 12か⽉保存のためにPARTITION BY LIST ((YEAR (register_date) MOD 2)* 100 + MONTH(register_date)) で24パーティション確保して13か⽉目にTRUNCATE PARTITIONするとか - 132/141
  134. 134. 容量との戦い int型かsmallint型か、timestamp型かdatetime型かは多く の場合どうでもいい 2バイトの差= 10億⾏でやっと2GB- インデックスの数に⽐例するけど、それでも⽂字列型に ⽐べれば全然⼤したことない - テーブルのサイズは基本的にvarcharに⽀配される EUC, SJISは2バイト、UTF-8は3バイト(utf8mb4の場 合は最⼤で4バイト⽂字も⼊る) - とはいえユーザーコンテンツはどうしようもないんだよ なぁ。。 垂直シャードするのがギリギリのライン。。 - 133/141
  135. 135. 論理削除 個⼈的には好きじゃない やるなら、これは守っておいた⽅がいい 整数型かENUM型(でないとc.が守りにくい)a. 全てのセカンダリーインデックス の先頭に削除フラグの カラムが存在しなければならない b. 決して”=”以外の演算⼦を使わない(NOTとかダメ、絶 対) c. ”=”演算⼦で等価⽐較するものを全てのWHERE句の先頭に つける d. これ以外で迂闊に使うと、あっという間にExecutorさんが 頑張る世界線に突⼊する 134/141
  136. 136. Questions and/or Suggestions? 135/141
  137. 137. 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
  138. 138. Question オプティマイザーだけアップグレードとかできない︖ できません>< 137/141
  139. 139. Question Using whereとUsing filesort、どっちが悪いっていう指標はあ るの︖ フェッチするのが2⾏だったらUsing whereでもUsing filesort でも構わないけどフェッチするのが1000万⾏だったらダメだ よね、という感じなので、Rows_sentとRows_examinedの⽐率で ⽐べるのがいいんじゃ。 138/141
  140. 140. Question InnoDBの統計情報をサンプリングではなく全ページをスキャン にはできないの︖ グローバル変数またはテーブル単位でページ数を指定できるの で、それを⼗分⼤きくすれば全ページをスキャンした統計情報 は可能。ただ、5.5で知⼈が試した限り遅かったらしい。 139/141
  141. 141. Question MyISAMならALTER TABLE .. ORDER BY ..で降順のインデック スを作れる︖ インデックス上は必ず昇順。ALTER TABLE .. ORDER BY ..で並 べ替えられるのはデータファイル上の並び順。 MySQL :: MySQL 5.6 リファレンスマニュアル :: 13.1.7 ALTER TABLE 構⽂ 140/141
  142. 142. Question MySQLからMariaDBに移⾏したら性能があがった。考えられる 要因は︖ MariaDBのオプティマイザーは性能が良い。MariaDBの InnoDBはPerconaが開発しているXtraDBなのでちょっとだけ いい。バイナリーログのグループコミットが性能が良く、スレ ーブの遅延が起きにくい。 メディアではMariaDBは使っている︖ スレッドプールプラグインが使いたくて導⼊したことがあった が、ワークロード的にMySQLより遅くなったので、より MySQLに近いPercona Serverに移⾏した。 141/141

×