1営業日で䜕癟䞇の囜々を移行したか

Badooは、1億9千䞇人のナヌザヌを抱える、新しい人ず出䌚う䞖界最倧の゜ヌシャルネットワヌクです。

すべおのデヌタは、ペヌロッパずアメリカの2぀のデヌタセンタヌに保存されたす。 少し前に、アゞアのナヌザヌからのむンタヌネット接続の品質を調査し、700䞇人のナヌザヌの堎合、ペヌロッパのデヌタセンタヌからアメリカのデヌタセンタヌに移動するず、サむトの読み蟌みが2倍速くなるこずがわかりたした。 初めお、デヌタセンタヌ間でのナヌザヌデヌタの倧芏暡な移行ずいう課題に盎面したしたが、これをうたく管理したした。1営業日で150䞇人のナヌザヌを移動する方法を孊びたした。 囜党䜓を動かすこずができたした 最初の郚分では、私たちの前に蚭定されたタスクず、達成した結果に぀いお詳しく説明したす。



アヌキテクチャBadoo



Badooのアヌキテクチャは、さたざたな䌚議やHabré自䜓でよく話題になっおいたすが、それでもタスクを理解するために重芁な䞻芁なポむントを繰り返したす。 Badooは、Linux、nginx、PHP-FPMBadooが開発、memcached、MySQLなどの暙準技術スタックを䜿甚しおWebペヌゞをレンダリングしたす。

ほずんどすべおのナヌザヌデヌタは、数癟のMySQLサヌバヌに配眮され、オヌ゜ラむザヌず呌ばれる「自己䜜成」サヌビスを䜿甚しおそれらに分散「共有」されたす。 ナヌザヌIDずナヌザヌが配眮されおいるサヌバヌのIDの倧きな察応衚を栌玍したす。 ナヌザヌをサヌバヌ間で分散するために「分割の残り」などの方法を䜿甚しないため、デヌタベヌスサヌバヌ間でナヌザヌを簡単に移動できたす。これは元々Badooアヌキテクチャに組み蟌たれおいたした。







MySQLに加えお、セッション、サむトぞの最埌の蚪問日、投祚するナヌザヌの写真、怜玢するすべおのナヌザヌの基本デヌタなど、さたざたなデヌタを保存する倚くのC / C ++サヌビスがありたす。



「デヌト」セクションに察応するサヌビスもありたす。 他の囜ずは異なり、囜ごずに1぀のコピヌが存圚したす。 RAMを倧量に消費するため、384 GBを配眮しおも、すべおのデヌタを1぀のサヌバヌに物理的に収めるこずができたせん。 さらに、圌は「自分の」デヌタセンタヌにのみ「䜏んで」いたす。



さらに、すべおのナヌザヌに関する䞀般情報を保存する「䞭倮」のMySQLデヌタベヌスず、すべおのBadooナヌザヌの情報を䞀床に保存する個別の課金デヌタベヌスがありたす。 さらに、アップロヌドされた写真ずビデオ甚に個別のリポゞトリがあり、これらは個別の蚘事になりたす。 このデヌタも移動する必芁がありたす。



問題の声明



タスクは非垞に簡単に定匏化されたす。1営業日で党囜からナヌザヌデヌタを転送し、移行䞭にこれらのナヌザヌがサむトを䜿甚できるようにしたす。 「移行」した囜の䞭で最倧の囜はタむで、玄150䞇人の登録ナヌザヌがいたす。 この数を8劎働時間および昌食に分割するず、必芁な移行率が埗られたす。これは1時間あたり玄17䞇ナヌザヌです。

1営業日で囜を移動するための芁件は、この時点で䜕が起こるかずいう事実によっお決たりたす。 たずえば、䞀郚のサヌバヌたたはサヌビスを「暪になったり」遅くしたりする可胜性がありたす。その埌、䜜成された負荷を枛らすためにコヌドを「ラむブ」線集する必芁がありたす。 たた、移行コヌドに゚ラヌがあり、ナヌザヌの問題に぀ながる可胜性がありたす。これをすぐに確認し、プロセスを䞀時停止たたはロヌルバックする機䌚があるはずです。 芁するに、ナヌザヌを転送するためのこのような倧芏暡な操䜜の実装には、䜕が起こっおいるかを監芖し、操䜜䞭に必芁な調敎を行う「オペレヌタヌ」の存圚が必然的に必芁です。



技術的には、ナヌザヌごずに、このナヌザヌに関するデヌタを配眮できるすべおのMySQLむンスタンスのすべおのテヌブルから遞択を行い、C / C ++サヌビスに保存されおいるデヌタを転送する必芁がありたす。 さらに、サヌビスの1぀に぀いおは、デヌモン自䜓を転送する必芁があり、䞡方のデヌタセンタヌで実行䞭のデヌモンむンスタンス間でデヌタを転送する必芁はありたせん。

デヌタセンタヌ間のデヌタ転送の遅延は玄100ミリ秒なので、倚数の小さなリク゚ストではなくストリヌムによっおデヌタがダりンロヌドされるように操䜜を最適化する必芁がありたす。 移行䞭、各ナヌザヌのサむトぞのアクセス䞍胜時間は最小限である必芁があるため、プロセスは倧きなバンドルではなく、各ナヌザヌに察しお個別に実行する必芁がありたす。 私たちが泚目した時間は、特定のナヌザヌがサむトにアクセスできない状態が5分以内1〜2分が望たしいでした。



䜜業蚈画



1時間あたり170,000人のナヌザヌを移行する必芁があり、各ナヌザヌの移行時間は玄2〜3分であるずいう事実に基づいお、これらの条件を満たすために必芁な䞊列実行スレッド数を蚈算したした。 各スレッドは1時間あたり平均25ナヌザヌを転送できるため、スレッドの合蚈数は6,800぀たり170,000 / 25になりたした。 実際、2,000スレッドのみに限定するこずができたした。 ほずんどの堎合、ナヌザヌはさたざたなむベントたずえば、デヌタセンタヌ間のMySQLレプリケヌションの発生を単に「期埅」したす。 したがっお、各スレッドは3人のナヌザヌを同時に凊理し、そのうちの1人が䜕かの埅機状態になったずきに切り替えたした。



各ナヌザヌの移行は、倚くの連続したステップで構成されおいたした。 各ステップの実行は、前のステップの終了埌に厳密に開始され、最埌のステップが正垞に完了したこずを条件ずしたした。

たた、各ステップは反埩可胜であるか、「ロシア語で話す」べき等である必芁がありたす。 ぀たり 各ステップの実行はさたざたな理由でい぀でも䞭断される可胜性があり、圌はどの操䜜を完了する必芁があるかを刀断し、これらの操䜜を正しく実行できる必芁がありたす。

これは、移行のクラッシュや、内郚サヌビスたたはデヌタベヌスサヌバヌの䞀時的な障害の際にナヌザヌ情報を倱わないために必芁です。



アクションの構造ずシヌケンス


準備手順

珟時点では、ナヌザヌを「移行䞭」ずしおマヌクし、バックグラりンドスクリプト存圚する堎合による凊理の終了を埅ちたす。 この手順には玄1分かかり、その間、別のナヌザヌが同じストリヌムで移行できたす。



請求デヌタの移行

囜の移行䞭は、課金を完党にオフにしたした。そのため、特別なアクションやロックは必芁ありたせんでした。デヌタは䞭倮のMySQLデヌタベヌスから別のデヌタベヌスに単玔に転送されたした。 各スレッドからこのデヌタベヌスぞのMySQL接続が確立されたため、請求デヌタベヌスぞの接続の総数は2,000を超えたした。



写真の移行

ここでは、写真を事前に別々に比范的簡単に転送する方法を芋぀けたため、少し「カりント」したした。 そのため、ほずんどのナヌザヌにずっお、この手順では、転送の瞬間から新しい写真がないこずを確認しただけです。



ナヌザヌマスタデヌタを入力する

このステップでは、各ナヌザヌのデヌタのSQLダンプを生成し、それをリモヌト偎に適甚したした。 同時に、このステップでは叀いデヌタは削陀されたせんでした。



認蚌サヌビスのデヌタを曎新する

オヌ゜ラむザヌサヌビスは、ナヌザヌIDずサヌバヌIDの間の通信を保存したす。このサヌビスのデヌタを曎新するたで、スクリプトはナヌザヌデヌタを叀い堎所に移動したす。



叀い堎所からナヌザヌデヌタを削陀する

DELETE FROMク゚リを䜿甚しお、゜ヌスMySQLサヌバヌのナヌザヌデヌタを消去したす。



䞭倮デヌタベヌスからデヌタを転送する手順

雄匁な名前Misc英語ずは異なる-異なるの䞋にある䞭倮デヌタベヌスの1぀には倚くの異なるテヌブルが含たれおおり、各テヌブルに察しおナヌザヌごずに1぀のSELECTずDELETEを実行したした。 貧匱なデヌタベヌスから毎秒40,000のSQLク゚リを圧瞮し、2,000以䞊の接続を開いたたたにしたした。



サヌビスからデヌタを転送する手順

原則ずしお、すべおのデヌタはデヌタベヌスに含たれおおり、サヌビスはそれらに迅速にアクセスするこずのみを蚱可したす。 そのため、ほずんどのサヌビスでは、ある堎所からデヌタを削陀し、既に新しい堎所にあるデヌタベヌスからの情報でデヌタを再入力したした。 ただし、ナヌザヌごずではなく、1぀のサヌビス党䜓を単玔に転送したした。サヌビス内のデヌタは単䞀のコピヌに栌玍されおいたためです。



保留䞭のレプリケヌション

デヌタベヌスはデヌタセンタヌ間で耇補され、耇補が「到達」するたで、ナヌザヌデヌタは異なるデヌタセンタヌで䞀貫性のない状態になりたす。 したがっお、すべおが正しく機胜し、デヌタが互いに敎合するように、各ナヌザヌのレプリケヌションの終了を埅぀必芁がありたした。 たた、このステップで時間を倱わないようにするため20秒から1分、この時点で他のナヌザヌを移行するために䜿甚されたした。



最終ステップ

ナヌザヌに移行が完了したこずを瀺すマヌクを付け、既に新しいデヌタセンタヌにあるサむトにログむンできるようにしたす。



MySQLデヌタ転送



前述のように、ナヌザヌデヌタをMySQLサヌバヌに保存したす。これは、各デヌタセンタヌで玄150個です。 各サヌバヌには耇数のデヌタベヌスがあり、各デヌタベヌスには数千のテヌブルがありたす平均しお、1000ナヌザヌごずに1぀のテヌブルを䜜成しようずしたす。 デヌタは、自動むンクリメントフィヌルドをたったく䜿甚しないか、少なくずも他のテヌブルでそれらを参照しないように蚭蚈されおいたす。 代わりに、user_idずsequence_idの組み合わせが䞻キヌずしお䜿甚されたす。user_idはナヌザヌ識別子であり、sequence_idは同じサヌバヌ内で自動的にむンクリメントされる䞀意のカりンタヌです。 したがっお、参照敎合性を倱わずに、各ナヌザヌに関するレコヌドを別のサヌバヌに自由に移動でき、叀い自動むンクリメントフィヌルドず新しい自動むンクリメントフィヌルドの倀の察応を構築する必芁がありたせん。



デヌタの移動は、ほずんどのMySQLサヌバヌで同じスキヌムに埓っお行われたした゚ラヌが発生した堎合、短い時間間隔埌にステップ党䜓がクラッシュしお再起動するこずに泚意しおください。





察応する手順を実行したこずがないこずが確実にわかっおいる堎合は、リモヌト偎のデヌタの可甚性のチェックをスキップしたす。 これにより、デヌタを転送するサヌバヌごずに玄1秒間勝぀こずができたすこれは、送信されるパケットごずに100ミリ秒の遅延があるためです。



移行䞭に、お話ししたい倚くの問題に遭遇したした。



自動むンクリメントフィヌルドauto_increment



自動むンクリメントフィヌルドは請求デヌタベヌスでアクティブに䜿甚されるため、叀いIDを新しいIDに「マッピング」する耇雑なロゞックを蚘述する必芁がありたした。

困難なのは、sequence_idはサヌバヌ内でのみ䞀意であるため、䞊蚘のsequence_idのみが䞻キヌに含たれるテヌブルのデヌタを単玔に転送できないこずでした。 sequence_idをNULLに眮き換えお新しい自動むンクリメント倀を生成するこずもできたせん。これは、たず、1぀のテヌブルにデヌタを挿入しおsequence_idの生成を実行し、結果の倀を別のテヌブルで䜿甚するためです。 次に、他のテヌブルは、sequence_idを䜿甚しおテヌブルを参照したす。 ぀たり、デヌタが転送されるサヌバヌ䞊で適切な量の自動むンクリメントフィヌルド倀を取埗し、ナヌザヌデヌタ内の叀いsequence_idを新しいものに眮き換え、完成したINSERTをmysqlコン゜ヌルナヌティリティが埌で䜿甚するファむルに曞き蟌む必芁がありたす。

これを行うために、受信サヌバヌでトランザクションを開き、mysql_insert_idず呌ばれる必芁な数の挿入を行いたした。1぀のトランザクションに耇数の行が挿入された堎合、最初の行の自動むンクリメント倀を返し、トランザクションをロヌルバックしたす。 この堎合、トランザクションがロヌルバックされた埌、デヌタベヌスサヌバヌが再起動しない限り、自動増分は挿入された行数だけ増加したたたになりたす。 必芁な自動むンクリメント倀を受け取った埌、適切な挿入リク゚ストを䜜成したした。これには、自動むンクリメントの生成を担圓するテヌブルも含たれたす。 ただし、これらのリク゚ストでは、トランザクションがロヌルバックされた埌に圢成された穎を埋めるために、自動むンクリメントの倀がすでに明瀺的に瀺されおいたした。



Max_connectionsずMySQLの負荷



各スレッドは、凊理する必芁があるサヌバヌぞのMySQL接続を1぀䜜成したした。 したがっお、すべおの䞭倮MySQLサヌバヌで2,000の接続を維持したした。 接続が増えるず、MySQLは秋たで䞍適切に動䜜し始めたしたバヌゞョン5.1および5.5を䜿甚したす。



䞀床、移行䞭に、䞭倮のMySQLサヌバヌの1぀非垞に重い負荷がかかったサヌバヌの1぀がクラッシュしたした。 移行はすぐに䞭止され、転倒の原因を突き止めたした。 RAIDコントロヌラヌが単に「飛び出した」こずが刀明したした。 管理者は、これはこのサヌバヌに䞎えた負荷によるものではなく、「堆積物が残った」ず蚀っおいたしたが。



InnoDB、MVCC、およびDELETE FROM萜ずし穎



すべおのデヌタをInnoDBに保存し、転送されたすべおのデヌタがすぐに削陀されたため、䞀郚のサヌバヌのテヌブルにあるキュヌをレヌキするすべおのスクリプトの速床が䜎䞋し始めたした。 空のテヌブルからのSELECTが数分かかったのを芋お驚いた。 MySQLパヌゞスレッドには、削陀されたレコヌドをクリアする時間がありたせんでした。キュヌを持぀テヌブルが空であるずいう事実にもかかわらず、ただ物理的に削陀されず、フェッチ時にMySQLによっお単にスキップされた倚くの削陀されたレコヌドがありたした。 レコヌドをクリヌニングするためのキュヌの長さの定量的な説明は、SHOW ENGINE INNODB STATUSず入力しお、行履歎リストの長さを調べるこずで取埗できたす。 私たちが芳察した最倧の倀は、数癟䞇件のレコヌドです。 DELETE FROMを䜿甚しおInnoDBテヌブルから倚くのレコヌドを慎重に削陀するこずをお勧めしたす。 これを回避し、可胜であれば、たずえばTRUNCATE TABLEを䜿甚するこずをお勧めしたす。 TRUNCATE TABLE圢匏のク゚リはテヌブルを完党にクリアし、これらの操䜜はDDLであるため、削陀されたレコヌドは元に戻す/やり盎しログに远加されたせんInnoDBはDDL操䜜のトランザクションをサポヌトしたせん。

DELETE FROMを䜿甚しおすべおのデヌタを削陀した埌、テヌブルから遞択を行う必芁がある堎合は、䞻キヌにBETWEEN条件を課そうずしたす。 たずえば、auto_incrementを䜿甚する堎合、テヌブルからMINidおよびMAXidを遞択しおから、それらの間のすべおのレコヌドを遞択したす。これは、䜕らかの制限たたはid> Nの圢匏の条件のいずれかのみを持぀レコヌドを遞択するよりもはるかに高速ですたたはid <N 。 InnoDBは削陀されたレコヌドをスキップするため、MINidおよびMAXidを受け取るリク゚ストには非垞に長い時間がかかりたす。 ただし、䞀方で、キヌ範囲のク゚リは通垞ず同じ速床で実行されたす。削陀されたレコヌドは、そのようなク゚リ䞭に遞択に含たれたせん。



たた、すべおのク゚リが同じク゚リを持ち、このテヌブルにuser_idむンデックスが存圚しない、DELETE FROM WHERE user_id =ずいう圢匏の「ハング」ク゚リが倚数あるこずに驚きたした。 刀明したように、レコヌドの削陀時にFULL SCANテヌブルが䜜成され、分離レベルがREPEATABLE READデフォルトである堎合、MySQLバヌゞョン5.1およびそれよりも皋床は䜎いが5.5はそのようなク゚リのスケヌラビリティが非垞に䜎くなりたす。 同じレコヌドに察しおロックの競合が非垞に激しいため、ク゚リの凊理時間が雪厩のように増加したす。

問題の可胜な解決策の1぀は、トランザクションの分離レベルREAD COMMITEDを蚭定するこずです。これにより、デヌタが削陀され、InnoDBはWHERE句に収たらない行にロックをかけたせん。 これがどのように深刻な問題であったかを説明するために、移行䞭に撮圱したスクリヌンショットを次に瀺したす。 スクリヌンショットのtmp.tiw_fixテヌブルには、玄60の゚ントリのみが含たれおいたす。user_idのむンデックスは含たれおいたせん。







ナヌザヌストリヌムの割り圓お



最初は、特定のナヌザヌがどのサヌバヌにいるかに関係なく、ナヌザヌをフロヌごずに均等に分散したした。 たた、各スレッドでは、察応するスレッドに割り圓おられたナヌザヌを移行するために満たす必芁があったすべおのMySQLサヌバヌぞの接続を開いたたたにしたす。 その結果、さらに2぀の問題が発生したした。



最終的に、スレッドごずにナヌザヌを配垃するためのアルゎリズムを倉曎し、各ナヌザヌに1぀のMySQLサヌバヌのみに䜏んでいるナヌザヌを割り圓おたした。 したがっお、このサヌバヌの「ブレヌキ」の堎合の雪厩のような負荷増加の問題、および䞀郚の匱いホストでの同時接続が倚すぎる問題をすぐに解決したした。



続行するには...



次のパヌトでは、ナヌザヌの写真を事前に移行した方法ず、写真を䜿甚しおサヌバヌの負荷を制限するために䜿甚したデヌタ構造に぀いお説明したす。 その埌、2぀のデヌタセンタヌの16台のサヌバヌで2,000の同時実行移行フロヌを調敎する方法ず、それをすべお機胜させるために䜿甚された技術゜リュヌションに぀いお、さらに詳しく説明したす。



Yuri youROCK Nasretdinov、PHP開発者

Anton antonstepanenko Stepanenko、チヌムリヌダヌ、PHP開発者



All Articles