RDBの拡張:レプリケーション 第07回 17年06月 / 最終更新:2017.06.08

みなさん、こんにちは。エンジニアの古庄道明です。
今回は「RDBのまま、重たい状況をどう打開するか」「大きくなってきたシステムで、RDBのまま、どうやってRDBをより拡張していくか」について、言及をしていきたいと思います。

RDBは、基本的には「一貫性と可用性を選択した結果として、分断耐性がない」設計になります。
そのため、本質的には「スケールアップ」が唯一の規模拡大の方向なのですが、スケールアップは比較的容易に限界がきてしまう、という問題があります。
しかし、運用の仕方によっては、RDBも「ある程度の」分断耐性を持たせることができます。

さて。
ゲーム業務問わず、Webアプリケーションを解析してみるとわかるのですが。
サイトの作りと構成にもよりますが、DBに対するwrite系動作(insert/update/delete)と比較して、read系動作(select)のほうが「多い」ケースが圧倒的に多数である、という事実があります。
仮に、writeとreadの比率を「1:4」である、と仮定します(これよりもreadに傾くサイト、も、少なからずありますが、一端仮の数値です)。

この場合「レプリケーション」という方法を使うことで、ある程度「1台のサーバでは難しい分量の処理を、複数台サーバを使ってこなす」事ができるようになります。
レプリケーションは「複製」と和訳される事があり、本来的な意味としては「同じ計算機を複数台横に並べることで性能の向上を目指す」といったところになります。
DBの場合、細かく言います「データレプリケーション」と呼ばれるもののひとつ、になります。

簡単に仕組みを書いてみましょう。
前提として、1台の「書き込み用サーバ」、複数台の「読み込み用サーバ」を用意します。

書き込み系として、例えばinsertが走った場合、を考えてみましょう。
Webアプリケーションは、「書き込み用サーバ」に対して書き込みを行います。
書き込み用サーバは、書き込まれた瞬間に「複数台の読み込み用サーバ」に同じデータを送出します。また、各読み込み用サーバは、送出されたデータを受け取って、自分のストレージに書き込みます。この一連の動作を「データの同期」あるいはもうちょっと単純に「同期」と呼称します。
この仕組みによって、「書き込み用サーバ」に書き込まれた内容は「読み込み用サーバ」に同期され、「書き込み用サーバ」と「読み込み用サーバ」は、同じデータを保持している事になります。

次にselectを考えてみます。
Webアプリケーションは、「複数台ある読み込み用サーバ」から任意の1台をチョイスし、selectのSQL文を発行し、処理をします。
理論上「書き込み用サーバ」と「読み込み用サーバ」は、同期によって「同じデータを持っている」はずなので、どのサーバをチョイスしても「同じデータ」が帰ってくる事が期待されます。

上述で「なぜ「ある程度の」分断耐性を持たせることが出来るか」を、簡単に解説していきましょう。
話をしやすいように、一端、1台のサーバのパワーを「100」と仮定します。
また、ユーザの1アクセスあたり平均的に「1回の書き込みと4回の読み込み」が発生する、と仮定します。そうして「1回の読み込み/書き込みには、サーバのパワーを1、消費する」と仮定します。

DBサーバが1台の場合を考えてみます。
20人がアクセスをすると「20回の書き込みと80回の読み込み」が発生し、1台のマシンの場合「パワーが100」なので、能力いっぱいいっぱい、になります。
ここから、21人目がアクセスしてきた時点でサーバには「待ち行列が発生」し、遅延します。

DBサーバがレプリケーションされている場合を考えてみます。
なお、計算しやすいように「読み込み用サーバ」は4台ある、とします。
20人がアクセスをすると「20回の書き込みと80回の読み込み」が発生します。
この時点で、まず「書き込みサーバ」が20回の書き込みでパワーを20消費、「読み込みサーバ」も書き込まれるので、同じく20パワーを消費し、残パワーが80となります。
読み込みサーバ4台にアクセスが20人なので、均等に振り分けられたとして、1サーバあたり5人分のアクセスが予測され、1人分の読み込みは4回なので、20読み込みが発生する、と予測されます。
上述から、読み込み用サーバはそれぞれ、「残パワー80」のマシンに「20読み込み(=20パワー)」が使われるので、残パワーは60。
21人目が来ても、まだまだ余裕をもって対応できる状況が、見て取れるかと思います。

このように、レプリケーションは「サーバ台数を増やすことで性能を増やす事ができる」ので、RDBの負荷が高くなってきたところでは重宝される事も多いです。
「サーバの割り振りはどうするか?」という部分については、プログラミングで解決をしてもよいのですが、ロードバランサも一般的に使われます。特にこだわりがなければ、MuSQLであれば例えば「MySQL Proxy」といったものがあります。

上述のように便利なレプリケーションではあるのですが、注意点がいくつかありますので、そのあたりを合わせて記述できれば、と思います。

まず「トランザクション」系のSQLには注意が必要です。
MySQL Proxyなどの製品を使っている時は(おそらくは)大丈夫なのですが。
データベースハンドルを「自力で切り替える」ようなコードを組んだ場合は、「トランザクション内の、for update月のselect」は「書き込み用サーバに対して発行する必要がある」事を意識しておきましょう。

また、レプリケーションも「仕組み」である以上、トラブルが発生する可能性があります。
レプリケーションの復旧は比較的あちこちで技術的な考察や提案などが出ているかと思うので、(できれば事前に)手順などを想定/訓練しておくとよいと思われます。

次に。
「読み込み用サーバのパワーが書き込み用サーバと比較して極端に弱い」と、「書き込みの同期」のタイミングで、同期自体で手一杯になる可能性があります。
なので、読み込み用サーバは、基本的には「書き込み用サーバと同一程度」にしておいたほうがよいかと思います。

幾分余談ですが、MySQLを使っている場合、用途は選びますが「BLACKHOLE ストレージエンジン」というものが、非常にユニークな機能をもっています。
ミッションクリティカルなデータには向きませんが、「書き込み用のサーバに結構な負荷がかかっている」かつ「書き込み等の量と頻度が比較的多く、かつ、そこまで致命的なデータでなく、トランザクションの対象にもならないデータ(テーブル)がある」場合は、一考の余地があるか、と思います。

最後に。
レプリケーションは、最終的に「書き込み用サーバが、書き込み切れない量の書き込みSQLを流した」時点でパンクします。
そこまでいくアクセスというのはそれ自体が「かなりサービスがにぎわった頃」だと思うのですが、「にぎわった時にいきなりサーバ能力が限界にくる」と厳しいものがあるので、定期的な監視、および場合によっては「次の一手」は、常に考えておきたいものだ、と思われます。

次回は「レプリケーション以外の"どうやってRDBをより拡張していくか"の手段」について書いていきたいと思います。