HN 日本語サマリー

← 一覧へ戻る
インフラ・DevOps

768台のサーバーを1台のように見せる方法

Making 768 servers look like 1 (planetscale.com)

111 pointsby hisamafahri37 コメント

要約

この記事は、大規模アプリケーションのインフラストラクチャにおいて、多数のデータベースサーバーを単一の論理的なデータベースとして機能させるための「シャーディング」という技術について解説しています。特に、Postgresデータベースを768台のサーバーに分散させる場合の複雑さと、それを解決するためのプロキシレイヤーの重要性を説明しています。

全文翻訳

768台のサーバーを1台のように見せる方法Ben Dicken [@BenjDicken] | 2026年7月15日これは768台のサーバーです。一部の人には、それは多くのコンピューターに見えるでしょう。数百万人の顧客を持つアプリのインフラストラクチャを管理している人々にとっては、毎秒数百万クエリを実行しており、かなり普通のことです。この規模の製品は、しばしば何千ものサーバーが連携して動作することを必要とします。スケーリングする上で最も難しいインフラストラクチャコンポーネントは、ほぼ常にデータベースです。単一のデータベースサーバーはそのような需要を処理できないため、多くのサーバーにクエリとデータを分散させる必要があります。データベースシャーディングは、数テラバイトを超えるPostgresまたはMySQLデータベースをスケーリングするための最良の方法です。小さな単一ノードデータベースから、4つのシャードに分散された数テラバイトのデータベース、さらには768台のサーバーに分散され、ペタバイトのデータを格納するデータベースまで、どのように移行するかを見てみましょう。成長痛スケーリングにシャーディングが必要な理由を理解するには、スケーリング性の低いアプローチのボトルネックを理解する必要があります。まず、単純なアプリケーションアーキテクチャを考えてみましょう。あなたがこれまで使用したほとんどのアプリケーションは、このように機能するか、少なくともその初期には機能していました。クライアントデバイスで実行されているソフトウェアは、インターネット経由でアプリサーバーに接続します。このアプリサーバーはデータセンターにあり、認証、ページロード、およびアプリケーションの動作方法に関するすべてのサーバーサイドロジックを処理します。ユーザーアカウント、投稿、設定、メッセージなどのすべての永続データは、データベースサーバー(通常はPostgresまたはMySQLですが、この記事の焦点はPostgresです)に格納され、そこから取得されます。大規模なデータベースサーバー(数十CPUコア、数百ギガバイトのRAM)であっても、ボトルネックはかなり早く発生します。通常、クエリボリュームが高いことによるCPU制約、または大量の読み書きによるI/O制約(IOPS)のいずれかです。これは、Universal Scalability Lawによってうまく要約されています。要するに、USLは、リソースの競合により、リソースが増加してもスケーラビリティは線形未満で成長し、ある時点では、不整合によりパフォーマンスが低下すると述べています。これは、より大きなサーバー上の多くのスレッドまたはプロセスにスケーリングしようとする他のソフトウェアシステムと同様に、Postgresでも真実です。これを解決する1つの方法は、少なくとも短期的には、リードレプリカを活用することです。この構成では、元のサーバーをプライマリとして維持し、上記のように追加のレプリカを追加します。プライマリは、プライマリのデータ変更を確実に最新の状態に保つために、すべてのレプリカに継続的なメッセージストリームを送信します。書き込み(INSERT、UPDATE、DELETE)はプライマリにのみ送信できます。いずれかのサーバーに書き込みを許可した場合、競合するデータが発生する可能性があります。これを解決するには複雑で遅い合意アルゴリズムが必要ですが、ほとんどの場合、最適なパフォーマンスには理想的ではありません。しかし、アプリサーバーはリード(SELECT)クエリをレプリカに送信できます。ほとんどのアプリは書き込みと比較して読み取りの割合がはるかに高いため、これによりスケーラビリティが大幅に向上します。(レプリカは、クエリトラフィックで必要とされない場合でも、高可用性とデータ耐久性のために必要です)。レプリカを追加することで、データベースはより多くのトラフィックを処理できるようにスケーリングできます。この極端な例は、OpenAIが単一プライマリで50のレプリカを使用していることです。サーバーを垂直にスケーリングする(CPU/RAMを増やす)ことやレプリカを追加することは、ある程度までしか機能しないことがわかりました。この方法では解決できないいくつかのボトルネックがあります。1)書き込みは1台のサーバーに限定される書き込みボリュームが十分に高い場合、追加の読み取り専用レプリカは問題を緩和できません。Postgresがコミットされた書き込みを認識する前に、書き込み先頭ログ(WAL)に変更を記録し、そのログを耐久性のあるストレージにフラッシュする必要があります。WALは、プライマリ上のすべての接続の共有リソースです。これは、数十のレプリカがあっても、実質的にデータベース全体の単一の書き込みボトルネックです。2)レプリカはデータ容量を増やさないレプリカは、すべてのインデックスを含むプライマリデータの完全なコピーです。レプリカを追加すると、読み取りを実行する場所が増えますが、データは分散されません。3)バックアップバックアップは、データ耐久性とRPO/RTO保証の重要な部分です。大規模なモノリシックデータベースのバックアップをオブジェクトストレージに取得するには、ノードからストレージへの通信帯域幅の制限により、数時間または数日かかる場合があります。これは、頻繁で検証済みのバックアップに依存する多くの組織にとって許容できないほど長いです。これを処理する最も実績のある方法はシャーディングです。シャーディング("d"付き)シャーディングは、データとクエリを多くの個別のプライマリに分散することで、これらの3つのボトルネックを解決します。データについては、単一ノードが格納できる量には限界があり、書き込みスループットも制限されているため役立ちます。クエリについては、ネットワーク相互接続とCPUが一度に処理できるクエリの数には限りがあるため役立ちます。シャーディングは、数テラバイトを超えるデータであれば、あらゆる規模で役立ちます。たとえば、2テラバイトのデータの場合、4つのシャード(それぞれ500ギガバイトを格納し、合計クエリトラフィックの1/4を処理する)のセットアップを選択するかもしれません。1ペタバイト(100万ギガバイト)のデータを格納する必要がある場合は、さらに多くのシャードが必要になります。この場合、256のシャード(それぞれにプライマリ+2つのレプリカがあり、それぞれ約4テラバイトを格納する責任がある)を使用できます。これには256 * 3 = 768台のサーバーが必要です!適切なシステムがないと、これはアプリのバックエンドにかなりの複雑さを追加します。これほど多くのことが進行中であるのに、システムはどうやって…どのデータがどのサーバーに行くかを決定しますか?どのクエリがどのサーバーに行くかを決定しますか?同時に複数のシャードと通信する必要があるクエリを処理しますか?この分散されたデータベース全体でバックアップを取得しますか?システム全体の健全性を監視しますか?障害のあるサーバーに対応しますか?これらの懸念事項のそれぞれに対処するために、多くのことが言えます。しかし、この記事で対処する質問は次のとおりです。これらの768台のサーバーを、アプリにとって1つのまとまったデータベースのように見せるにはどうすればよいでしょうか?アプリサーバーが、次のような複雑なシステムとのやり取りから、単一の接続文字列を介したやり取りに移行できるようにしたいと考えています。あたかも、実際には、数十または数百のシャードを利用しながら、1つの大規模でスケーラブルなデータベースとインターフェースしているかのように見せます。Neki for PostgresとVitess for MySQLがこれを解決します。どのように機能するか見てみましょう。プロキシレイヤーいくつかの重要な部分の中で最も重要なのはプロキシレイヤーです。プロキシは、2つのサービスの間に配置されるミドルウェアサーバーです。この場合、これらの2つのサービスはアプリケーションサーバーとデータベースサーバーです。プロキシはPostgresデータベースで頻繁に使用されます。シャーディングがない場合でも、接続プーリングとリクエストキューイングに役立ちます。通常の(シャーディングされていない)Postgresの場合、PgBouncerは、多数のアプリ接続を、より少ない直接Postgres接続に多重化するために人々が使用する一般的なプロキシです。PgBouncerの目標は単純です。多数のクライアントからの多数の接続を受け入れ、それらを、Postgresとの間で継続的に維持するより少ない接続プールにルーティングするように構築されています。クエリキューイングは、トラフィックサージやデータベースフェイルオーバー中に役立ちます。新しいプライマリがオンラインになったときにリクエストを再開できます。詳細については、PgBouncerに関するブログ全体があります。Postgresのシャーディングには、さらに洗練されたプロキシが必要です。最大の​​違いは、多重化とバッファリングに加えて、プロキシはデータがサーバーにどのように分散されているかを理解し、SQLクエリを正しいシャードにルーティングする必要があるということです。このため、ルーターと呼びます。データを挿入するとき、ルーターはデータがどのように分散されるかを認識している必要があります。これはシャーディング戦略として知られています。一般的なアプローチは、ID列のハッシュに基づいて受信行をシャードすることです。次のような行をデータベースに挿入するとき:INSERT INTO users (id, username, email) VALUES (1, 'ada', 'ada@example.com'), (2, 'grace', 'grace@example.com'), (3, 'linus', 'linus@example.com'), (4, 'margaret', 'margaret@example.com'), (5, 'dennis', 'dennis@example.com'), (6, 'barbara', 'barbara@example.com'), (7