「データベースを複数台用意して、処理を分散させれば、たくさんの作業ができて、障害にも強くなるんじゃないか」
分散DBは、データや処理を複数のサーバー、あるいは複数の拠点に配置しながら、多くの仕組みでは、その複雑さをできるだけ隠し、利用者やアプリケーションからはひとつのデータベースのように扱えるようにします。
利用者側からは、ひとつのデータベースにアクセスしているように見えても、内部ではデータの種類や利用者の場所、障害の状態に応じて、適切なサーバーへ処理が振り分けられます。
こうした説明は、分散DBのコンセプトに初めて触れるときの入口としては入りやすいかもしれません。
ただ、実際の設計や活用の話を始めるには、もう少し解像度を上げていく必要があります。
この「分散DBは、複数のDBを地理的に別の場所に散りばめる」という考え方だけでは足りない理由は、図書館を比喩にして考えると分かりやすいかもしれません。
図書館の場合、利用者が読めるように建物に本を置きますよね。
もしあなたが図書館を運営するとしたら、次のようなことを考えるはずです。
-
本の置き場所はどうしますか?
複数の分館に同じ本を置くのか、分館ごとに扱う本を分担するのか、どちらにしましょう。 -
利用者が借りたい本が東京の図書館になかった場合、どうしましょう?
本を受け付けた窓口が、ほかの分館へ問い合わせるような仕組みにしますか?
複数の図書館に本を分けて置けば、全国の人が書籍を手軽に手に取れるようになります。この意味では、確かに「分散」されています。
しかし、同じ「分散」でも、それらをどう分散させて、どのように利用させるかという設計は、やり方次第でかなり変わります。
分散DBを理解するための3つのコンセプト
レプリケーション、シャーディング、分散SQL
レプリケーションは、同じデータのコピーを複数の場所に保つ方法です。
主な目的は、障害に備えること、読み取りを近い場所で処理すること、分析用に別系統へデータを連携することです。
更新を受け付ける中心の場所を決め、そこからほかのコピーへ変更を反映する構成がよく使われます。
「大切な原本とは別に、予備のコピーも保管しておく」という考え方です。
レプリケーションの場合、ほかのコピーが物理的に離れた場所にあると、更新内容を転送して反映するまでに時間がかかることがあります。
そのため、中心の場所で変更した直後に別のコピーを読み取ると、最新の値が見えない場面があります。
ここで注意したいのが、すべてのレプリケーションがこのような動きをするわけではないということです。更新を完了したとみなす前に、ほかのノードへの反映や合意を待つ方式もあります。その場合、データの整合性や障害時のデータ損失を抑えやすくなる一方で、拠点間の通信時間が更新処理の応答時間に影響しやすくなります。
シャーディングは、データそのものを分けて持つ方法です。
たとえば顧客IDや地域を手がかりに、顧客Aの注文はサーバー1、顧客Bの注文はサーバー2というように配置します。
1台で全件を処理する代わりに、複数台で仕事を分担できるため、データ量や書き込み量を横に広げやすくなります。
「重い仕事は複数人で分担したら、早いし楽だよね」という考え方です。
MongoDBの公式ドキュメントでも、シャーディングは複数のマシンへデータを分散し、大きなデータセットや高い処理量を扱う手法として説明されています。MongoDB: Sharding
ただし、データを複数台に分けただけで障害に強くなるわけではなく、あるシャードが停止すれば、そのシャードのデータを利用できなくなる構成もあります。可用性を高めるには、シャードごとのレプリケーションなど、別の設計も必要になります。
分散SQLは、データが複数のノードに分かれていても、アプリケーションからはひとつのSQLデータベースとして使えるようにする技術群です。
アプリケーションは「この条件に合う注文を取得して」とSQLで問い合わせるだけです。
内部ではデータベースが、データの置き場所を調べ、必要なノードへ処理を送り、結果を集めます。
複数のノードにまたがる更新でも、データの整合性を保つための調整を行います。
これは市役所の窓口に似ています。
窓口で「戸籍の住所を確認したい」と伝えると、利用者は書類の保管場所を意識しなくても、担当者が必要な台帳を探して結果を返してくれますよね。
分散SQLも同じように、データがどこに置かれているかという複雑さを、できるだけデータベース側で引き受けます。
ご想像の通り、これをソフトウェアで実装しようとすると、設計は非常に複雑になります。
Google Spannerの公開論文は、グローバルに分散したデータ管理、同期レプリケーション、外部一貫性を扱う例として、この領域の難しさと可能性を示しています。Google: Spanner
ここまでの内容をまとめるとこうなります。
| 言葉 | 主に扱うこと | 最初に確認したいこと |
|---|---|---|
| レプリケーション | 同じデータを複数の場所に持つ | どこで更新を受け付け、どのタイミングで複製し、障害時にどこまでデータを守るか |
| シャーディング | データの担当範囲を分ける | どのキーで分ければ、負荷やデータ量、問い合わせが偏らないか |
| 分散SQL | 分散したデータに対するSQL・トランザクションの協調 | 複数ノードにまたがる問い合わせや更新を、どの整合性と待ち時間で成立させるか |
ここで少し注意したいのは、レプリケーション、シャーディング、分散SQLは、完全に同じ軸で分類した言葉ではないということです。
レプリケーションとシャーディングが主に「データをどのように配置するか」を表すのに対し、分散SQLは「複数のノードにまたがるデータを、SQLやトランザクションとしてどのように扱わせるか」という観点を含みます。
そのため、分散SQLデータベースの内部でシャーディングやレプリケーションが使われることもあります。
つまり、分散DBを採用するという話に進む前に、
コピーを増やすのか、データの担当を分けるのか、複数地点をまたぐ更新をDBに調停させたいのかを問い直すほうが、話が具体的になりやすいということです。
分散すると、経路を決める仕事が生まれる
単一DBなら、アプリケーションは基本的にひとつの接続先へ要求を送ります。
しかし分散DBでは、その要求をどのノードへ送るか決める役割が必要です。
顧客IDを含む検索なら、担当するシャードだけへ送れます。一方で、条件に手がかりがなければ、複数のシャードを探し回り、結果を集める必要が出てくることがあります。この後者は「scatter/gather」と呼ばれます。問い合わせるシャードが増えるほど、各シャードへの処理の送信や結果の集約が必要になり、遅いノードや通信の影響も受けやすくなります。MongoDB: Sharding
ここで設計の中心になるのが、分割キーです。
分割キーは、どのデータをどの担当に置くかを決める目印です。
図書館のたとえでいえば、本の背表紙についた分類番号や、貸出カードに記載された利用者番号のようなものです。
その目印を見れば、「この本はどの分館にあるか」「この利用者の記録はどこで管理するか」を素早くたどれますよね。
たとえば顧客IDを分割キーにする場合、ある顧客の注文履歴を見たり、その顧客の注文を更新したりする処理は、担当するサーバーを絞り込みやすくなります。
つまり、毎回すべてのサーバーを探し回らなくてもよくなります。
一方で、特定の大口顧客だけにアクセスが集中すると、その顧客を担当するサーバーだけが忙しくなります。
分割キーは単なるDB設定ではなく、「アプリケーションが普段、どの単位でデータを読み、更新するか」という業務の形を、データの置き方に反映する大事な作業です。
この設計を誤ると、ユーザーから見た応答の遅さや、サーバーごとのリソースの偏りにも影響します。
ここでもうひとつ重要なのは、一緒に読み書きされるデータを、できるだけ同じ担当範囲に置けるかという点です。
ひとつの業務処理が毎回複数のシャードをまたぐようになると、ノード間の通信や調整が増えます。
そのため分割キーは、「データを均等に分けられるか」だけでなく、「普段の業務処理をできるだけひとつの担当範囲で完結させられるか」という視点でも考える必要があります。
増えるのは台数だけ……ではないんです
分散DBで増えるのは、サーバーの台数だけではありません。
通信経路、処理の振り分け、障害のパターン、監視対象、そして復旧時に判断しなければならないことも増えます。
なぜなら、複数の場所で処理するためには、それぞれの場所が必要に応じて連携しなければならないからです。
これはカスタマーセンターの電話をイメージすると分かりやすいかもしれません。
ひとつの0120で始まる電話番号にかけても、実際には音声ガイドによって、問い合わせ内容に応じた窓口へ振り分けられます。さらに、ひとつの窓口だけでは回答できなければ、別の担当者や部署へ確認することもありますよね。
分散DBでも似たように、要求を適切なノードへ送り、必要であればノードどうしで情報をやり取りします。
このように、分散すると「どこで処理するか」だけでなく、ノード間の通信が遅れたり途切れたりしたときに、どう振る舞うかも考える必要があります。
特に通信の遅延や一時的な切断が起きたときに難しいのが、「その状況でも、どこまで処理を続けてよいのか」という判断です。
- 更新内容が必要な場所へ反映されるまで待つのか。
- 一定の条件では、少し古い値の読み取りを許容するのか。
- それとも、必要なノードと通信できないときには、安全のために更新を受け付けないのか。
この答えは、システムやデータによって変わります。
たとえば在庫数を複数の場所から同時に減らすサービスなら、「同じ在庫を二重に売らない」ことが重要かもしれません。一方、地域ごとの閲覧履歴を集計するサービスなら、数秒前の値でも利用者体験への影響は小さいかもしれません。
だから大切なのは、「分散DBだからこう動く」と考えるのではなく、データや処理ごとに「何を守りたいのか」を決めることです。
似合う洋服も人それぞれですよね。カッコよく着るには、流行っている服をそのまま選ぶのではなく、その人に合った服を選び、必要に応じて採寸することが大切です。
分散DBも少し似ています。
「分散DB」という名前や流行から構成を決めるのではなく、システムの要件をきちんと“採寸”して、それに合ったデータの置き方や処理の仕方を選ぶ。
そう考えると、分散DBを設計するときに何を確認すべきかも見えてきます。
実際に使う前に考えたいこと
分散DBを検討するときは、以下のようなことを軸に考えるとよいかもしれません。
- なぜDB1台では足りないのか?
容量、読み取り、書き込み、利用者との距離など、何を改善したいのか。
- 常に最新でなければ困るデータは何か?
少し遅れた値でも問題のない読み取りはあるか。
- 1件の業務処理は、複数のデータの担当範囲をまたぐか?
できるだけひとつの担当範囲で完結させられないか。
- どこが壊れたとき、何を継続できなければならないか?
1台のサーバー、ひとつの拠点、地域全体、ネットワーク障害など、想定する障害は何か。
- 障害時に、どこまでのデータ損失と停止時間を許容できるか?
「止めない」と「データを失わない」は別の要件なので、それぞれ考える。
分散DBは「データベースをたくさん置く技術」ではなく、データをどう配置し、どう複製し、どこへ処理を送り、複数ノードの間でどんな整合性を守り、障害時にどう振る舞うかを設計する技術です。
まずは自分たちのデータが、どの単位で読まれ、更新され、どこが止まると困るのかを書き出してみる。
そこまで整理できれば、レプリケーション、シャーディング、分散SQLのどこを深掘りすべきかが見えてきます。