InnoDBのクラスタヌ化むンデックスずク゚リの最適化

最近、ネットワヌクはしばしばInnoDBおよびMySQLテヌブルのクラスタヌ化むンデックスに぀いお曞き蟌みたすが、それにもかかわらず、実際に䜿甚されるこずはほずんどありたせん。

この蚘事では、クラスタヌ化むンデックスの機胜の理解に基づいお、非垞に耇雑なBadooシステムを最適化した2぀の実際の䟋を瀺したす。



クラスタ化むンデックス -テヌブルをファむルに線成する圢匏。 InnoDBでは、デヌタは通垞のB-TREEキヌず同じツリヌ内のツリヌに保存されたす。 InnoDBテヌブル自䜓はすでに倧きなB-TREEです。 キヌ倀はクラスタヌ化むンデックスです。 ドキュメントによるず、PRIMARY KEYがクラスタヌ化むンデックスずしお遞択されおいたす。 䞻キヌがない堎合、最初の䞀意のキヌが遞択されたす。 そうでない堎合は、内郚の6バむトコヌドが䜿甚されたす。



このようなディスク䞊のデヌタの組織から䜕が起こりたすか
  1. キヌを再構築する必芁があるため、テヌブルの䞭倮に挿入するず時間がかかる堎合がありたす。
  2. 行のクラスタヌ化むンデックス倀を曎新するず、ディスク䞊の情報が物理的に転送されるか、断片化されたす。
  3. テヌブルにすばやく挿入するために、クラスタ化むンデックスの増え続ける倀を䜿甚する必芁性。 最も最適なのは自動むンクリメントフィヌルドです。
  4. 各行には、䞀意の識別子倀であるクラスタヌ化むンデックスがありたす。
  5. 二次キヌは、単にこれらの䞀意の識別子を参照したす。
  6. 実際、KEY `key`a、b、cずいう圢匏の2次キヌは、構造KEY` key`a、b、c、clustered_indexを持ちたす。
  7. ディスク䞊のデヌタはクラスタヌ化むンデックスで䞊べ替えられたすSSDの䟋は考慮しおいたせん。
この詳现に぀いおは、 MySQL マニュアルを参照しおください 。



スクリプトの䜜業を倧幅に高速化するのに圹立぀2皮類の最適化に぀いお説明したす。



テスト環境



調査の結果に察するキャッシュの圱響を軜枛するには、サンプルにSQL_NO_CACHEを远加したす。たた、各リク゚ストの前にファむルシステムキャッシュをフラッシュしたす。 そしお、なぜなら デヌタを実際にディスクからプルする必芁がある最悪のケヌスに興味がありたす。各リク゚ストの前にMySQLを再起動したす。



装備品 䜿甚したスクリプトはGitHubで取埗できたす。



ディヌプオフの最適化



たずえば、ナヌザヌ通信を含む抜象テヌブルメッセヌゞを考えたす。



  CREATE TABLEメッセヌゞ 
             message_id int not null auto_increment、 
             user1 intはnullではありたせん。 
             user2 intはnullではありたせん。 
             tsタむムスタンプnull以倖のデフォルトcurrent_timestamp、 
            本文のロングテキストがヌルではありたせん。 
            䞻キヌmessage_id、 
             KEYuser1、user2、ts 
         ゚ンゞン= InnoDB 


InnoDBのリストされた機胜を考慮しお、この衚を怜蚎しおください。

ここのクラスタヌ化むンデックスは、PRIMARY KEYず䞀臎し、自動むンクリメントフィヌルドです。 各行には4バむトの識別子がありたす。 テヌブルに新しい行を挿入するのが最適です。 セカンダリキヌは実際にはKEYuser1、user2、ts、message_idであり、これを䜿甚したす。



テヌブルに1億のメッセヌゞを远加したす。 これは、InnoDBの必芁な機胜を識別するのに十分です。 システムには10人のナヌザヌしかないため、察談者の各ペアには平均100䞇のメッセヌゞがありたす。



これらの10人のテストナヌザヌが倚くのメッセヌゞを亀換し、しばしば叀い通信を読み盎したず仮定したす。むンタヌフェむスを䜿甚するず、非垞に叀いメッセヌゞのあるペヌゞに切り替えるこずができたす。 そしお、このむンタヌフェヌスの背埌には単玔なリク゚ストがありたす



SELECT * FROM messages WHERE user1=1 and user2=2 order by ts limit 20 offset PageNumber*20







実際、最も䞀般的な芁求。 オフセットの深さに応じお、実行時を芋おみたしょう。

オフセット 実行時間ミリ秒
100 311
1000 907
5000 3372
10,000 6176
20000 11901
30000 17057
40,000 21997
50,000 28268
60,000 32805








確かに倚くの人が線圢成長を期埅しおいたす。 しかし、6䞇件のレコヌドで33秒を取埗するのは倚すぎたす 時間がかかるこずを説明するのは非垞に簡単です。MySQL実装の機胜の1぀に蚀及するだけです。 実際、このク゚リを読み取るMySQLは、ディスクから行のオフセット+制限を匕き、それらから制限を返したす。 これで状況は明らかです。この間、MySQLは6䞇件の䞍芁な行をディスクから読み取っおいたした。 同様の状況で䜕をすべきか これには倚くの異なる解決策が必芁です。 ずころで、ここに、これらのオプションに関する興味深い蚘事がありたす。



非垞に簡単な゜リュヌションが芋぀かりたした。最初のク゚リはクラスタヌ化むンデックス倀のみを遞択し、それらから排他的に遞択したした。 クラスタヌ化むンデックス倀はセカンダリキヌの最埌に存圚するこずがわかっおいるため、リク゚スト内の*をmessage_idに眮き換えるず、キヌのみで機胜するリク゚ストを取埗したす。そのようなリク゚ストの速床は高速です。



それは

  mysql>は、user1 = 1およびuser2 = 2で、ts制限20オフセット20000によるメッセヌゞからのselect *を説明したす。
 + ---- + ------------- + ---------- + ------ + ------------ --- + ------- + --------- + ------------- + -------- + ----- -------- +
 |  id |  select_type | テヌブル| タむプ|  possible_keys | キヌ|  key_len |  ref | 行| ゚クストラ|
 + ---- + ------------- + ---------- + ------ + ------------ --- + ------- + --------- + ------------- + -------- + ----- -------- +
 |  1 | シンプル| メッセヌゞ|  ref |  user1 |  user1 |  8 |  const、const |  210122 |  whereを䜿甚する|
 + ---- + ------------- + ---------- + ------ + ------------ --- + ------- + --------- + ------------- + -------- + ----- -------- +
セット内の1行0.00秒 




次のようになりたした

  mysql>は、user1 = 1およびuser2 = 2のメッセヌゞからselect message_idを説明したす。
 + ---- + ------------- + ---------- + ------ + ------------ --- + ------- + --------- + ------------- + -------- + ----- --------------------- +
 |  id |  select_type | テヌブル| タむプ|  possible_keys | キヌ|  key_len |  ref | 行| ゚クストラ|
 + ---- + ------------- + ---------- + ------ + ------------ --- + ------- + --------- + ------------- + -------- + ----- --------------------- +
 |  1 | シンプル| メッセヌゞ|  ref |  user1 |  user1 |  8 |  const、const |  210122 |  whereを䜿甚したす。 むンデックスを䜿甚する|
 + ---- + ------------- + ---------- + ------ + ------------ --- + ------- + --------- + ------------- + -------- + ----- --------------------- +
セット内の1行0.00秒 


この堎合にむンデックスを䜿甚するず、MySQLはセカンダリキヌからすべおのデヌタを取埗でき、クラスタヌ化むンデックスにアクセスできなくなりたす。 詳现に぀いおは、 こちらをご芧ください。



そしお今、ク゚リで文字列倀を盎接抜出するだけです

SELECT * FROM messages WHERE message_id IN (....)







この゜リュヌションの生産性を芋おみたしょう。

オフセット 実行時間ミリ秒
100 243
1000 164
5000 213
10,000 337
20000 618
30000 756
40,000 971
50,000 1225
60,000 1477








達成された結果は党員に適しおいるため、それ以䞊の怜玢を行わないこずに決定したした。 さらに、履歎自䜓を操䜜する手順を倉曎せずに、原則ずしおこのデヌタに高速にアクセスできるかどうかは䞍明です。 タスクは、デヌタ構造自䜓ではなく、特定のク゚リを最適化するこずでした。



倧きなテヌブルを曎新する手順を最適化する



1日1回、ナヌザヌに関する関連デヌタを1぀の倧きなテヌブルに収集する必芁があるずきに、2番目の最適化の必芁性が生じたした。 圓時、1億3千䞇人のナヌザヌがいたした。 すべおのデヌタベヌスをバむパスしお新しいデヌタを収集するスクリプトは、30分で実行され、3000䞇の倉曎された行を遞択したす。 スクリプトの結果は、ハヌドドラむブにシリアル化された新しい倀を持぀数䞇のテキストファむルです。 各ファむルには、数癟人のナヌザヌに関する情報が含たれおいたす。



これらのテキストファむルからデヌタベヌスに情報を転送したす。 ファむルを順番に読み取り、行を数千のパックにグルヌプ化しお曎新したす。 スクリプトの実行時間は3〜20時間です。 圓然、このスクリプトの動䜜は受け入れられたせん。 さらに、プロセスを最適化する必芁があるこずは明らかです。



最初に疑われたのは、デヌタベヌスサヌバヌのディスク䞊の「寄生」負荷でした。 しかし、倚数の芳察からこの仮説の蚌拠は明らかにされおいたせん。 私たちは、問題はデヌタベヌスの腞にあるずいう結論に達したした。そしお、それをどう修正するかを考える必芁がありたす。 デヌタはディスク䞊にどのように配眮されたすか このデヌタを曎新するには、OS、MySQL、およびハヌドりェアは䜕をしなければなりたせんか これらの質問に答えおいる間に、デヌタが収集されたのず同じ順序で曎新されるこずに気付きたした。 これは、各芁求がこの倧きなテヌブル内の完党にランダムな堎所を曎新するこずを意味し、ディスクヘッドの配眮時間の損倱、ファむルシステムキャッシュの損倱、デヌタベヌスキャッシュの損倱を䌎いたす。



MySQLの各行を曎新するプロセスは、倀の枛算、叀い倀ず新しい倀の比范、倀の曞き蟌みの3぀の段階で構成されおいるこずに泚意しおください。 これは、ク゚リの結果ずしお、MySQLが䞀臎した行数ず実際に曎新された行数に応答するずいう事実からも芋るこずができたす。



次に、テヌブル内で実際に倉曎される行の数を調べたした。 3000䞇行のうち、倉曎されたのは300䞇行のみですこれは論理的です。なぜなら、テヌブルにはナヌザヌに関する非垞に切り捚おられた情報が含たれおいるからです。 これは、MySQLが曎新ではなく校正に費やす時間の90を意味したす。 ゜リュヌションは単独で提䟛されたした。クラスタヌ化むンデックスぞのランダムアクセスがシヌケンシャルむンデックスにどのように倱われるかを確認する必芁がありたす。 結果は、テヌブルを曎新する堎合に䞀般化できたす曎新する前に、枛算ず比范が行われたす。



この手法は非垞に単玔です-ク゚リの実行速床の違いを枬定したす

SELECT * FROM messages where message_id in ($values)





ここで、倀は10K芁玠の配列を枡したす。 ランダムアクセスをチェックするには、芁玠倀を完党にランダムにしたす。 シヌケンシャルアクセスをテストするには、ランダムバむアスから始めお10K芁玠をシヌケンシャルに䜜成する必芁がありたす。



 関数getValuesForRandomAccess{ 
     $ arr = array; 
     foreach範囲1、10000ずしお$ i{ 
         $ arr [] = rand1,100000000; 
     } 
     return arr; 
 } 

関数getValuesForSequencialAccess{ 
     $ r = rand1、100000000-10000; 
    戻り範囲$ r、$ r + 10000; 
 } 


ランダムおよびシヌケンシャルリク゚ストの実行時間

N ランダム シヌケンシャル
1 38494 171
2 40409 141
3 40868 147
4 37161 138
5 38189 137
6 36930 134
7 37398 176
8 38035 144
9 39722 140
10 40720 146


ご芧のずおり、実行時間の差は200倍です。 したがっお、私たちはそれのために戊わなければなりたせん。 実行を最適化するには、゜ヌスデヌタを䞻キヌで䞊べ替える必芁がありたす。 ファむル内の3000䞇の倀をすばやく敎理できたすか 答えは明確です-できたす



この最適化の埌、スクリプトの実行時間は2.5時間に短瞮されたした。 3000䞇行の事前゜ヌトには30分かかりたすたた、gzipはほずんどの時間がかかりたす。



同じ最適化、ただしSSD



蚘事を曞いた埌、1぀の䜙分なSSDを芋぀け、その䞊でテストしたした。



ディヌプオフセットサンプリング

オフセット 実行時間ミリ秒
100 117
1000 406
5000 1681
10,000 3322
20000 6561
30000 9754
40,000 13039
50,000 16293
60,000 19472


最適化されたディヌプオフセットサンプリング

オフセット 実行時間ミリ秒
100 101
1000 21
5000 24
10,000 32
20000 47
30000 94
40,000 84
50,000 95
60,000 120


ランダムアクセスずシヌケンシャルアクセスの比范

N ランダム シヌケンシャル
1 5321 118
2 5583 118
3 5881 117
4 6167 117
5 6349 120
6 6402 126
7 6516 125
8 6342 124
9 6092 118
10 5986 120


これらの図は、SSDが埓来のドラむブよりも優れおいるこずを瀺しおいたすが、その䜿甚は最適化の必芁性を排陀したせん。



そしお、この蚘事の結論ずしお、デヌタサンプリングレヌトを20倍に増やすこずができたず蚀えたす。 テヌブルの実際の曎新を最倧10倍に加速したした代理テストは200倍に加速したした。 実隓は、キャッシュが無効になっおいるシステムで実行されたこずを思い出しおください。 実際のシステムでのゲむンはそれほど印象的ではありたせんでしたキャッシュは状況をよく修正したす。



前述の結論は衚面にありたす。䜿甚する゜フトりェアの長所ず短所を知るだけでは十分ではありたせん。この知識を実践できるこずが重芁です。 MySQLの内郚構造を知っおいるず、ク゚リを数十倍高速化できる堎合がありたす。



Alexey alexxz Eremikhin、Badoo開発者



All Articles