午前2時のスケーリングの壁
6ヶ月前、私たちのメインのPostgreSQLインスタンスが限界に達しました。AWSのr6g.2xlargeを使用しており、64GBのRAMを積んでいたにもかかわらず、ピーク時のCPU使用率は95%に張り付いていました。リードレプリカの追加やインデックスの最適化といった通常の対策を試みましたが、すぐにレプリケーション遅延が10秒に達し、ユーザーに深刻なデータ整合性の問題を引き起こしました。
私たちは苦渋の決断を迫られました。複雑なJOINクエリを書き直して数ヶ月かけて独自のシャーディング層を構築するか、水平スケーリングを前提に設計されたデータベースに移行するかです。いくつかの選択肢を検討した結果、コアサービスをYugabyteDBに移行することにしました。ここでは、私たちが学んだことと、導入方法を紹介します。
従来のSQLが成長に伴い苦戦する理由
PostgreSQLやMySQLは、1台の巨大なサーバーがデータセンターを支配していた時代に作られました。これらは非常に信頼性が高いものの、現代の高トラフィックなアプリにおいては、そのアーキテクチャに3つの根本的な欠陥があります。
- 書き込みのボトルネック: 標準的な構成では、書き込みを処理できるのは1つのノードだけです。データの取り込みレートが1つのCPUの処理能力を超えると、行き詰まってしまいます。
- シャーディングのコスト: 手動で複数のインスタンスにデータを分割するのは、運用の悪夢です。ACIDトランザクションが損なわれ、シャードをまたぐレポート作成はほぼ不可能になります。
- フェイルオーバーのタイムラグ: 高可用性ツールを使用しても、新しいレプリカを昇格させるには通常30〜60秒のダウンタイムが発生します。高負荷な環境において、それは永遠とも言える時間です。
代替案の検討
最初にAmazon Auroraを検討しました。素晴らしいサービスですが、依然として1つのクラスターにつき1つのライターノードに依存しています。単一のグローバルマスターによるレイテンシを避けつつ、マルチリージョンでの書き込み機能が必要な場合、Auroraはすぐに複雑で高価なものになります。
YugabyteDBは異なるアプローチをとりました。クエリ層には実際のPostgreSQLのCコードを再利用し、ストレージエンジンをDocDBと呼ばれる分散システムに入れ替えています。これにより、トリガーやストアドプロシージャなどの機能を維持したまま、データは自動的にノードのクラスター全体に分散されます。Postgresのような使い心地でありながら、NoSQLエンジンのようにスケーリングします。
初めてのYugabyteDBクラスターのセットアップ
本番環境は通常Kubernetes上で動作しますが、ローカルクラスターを構築するのがアーキテクチャを理解する最短の方法です。管理には yugabyted ユーティリティを使用します。
1. インストール
最新のバイナリを取得します。執筆時点では、バージョン2.19がほとんどのユーザーにとって安定したパスです。
# パッケージをダウンロード
wget https://downloads.yugabyte.com/releases/2.19.2.0/yugabyte-2.19.2.0-b121-linux-x86_64.tar.gz
# ファイルを展開
tar xvfz yugabyte-2.19.2.0-b121-linux-x86_64.tar.gz
cd yugabyte-2.19.2.0/
# 環境設定を実行
./bin/post_install.sh
2. クラスターの起動
1つのコマンドで完全に機能するデータベースを起動できます。これにより、YSQL(PostgreSQL互換)とYCQL(Cassandra着想)の両方のAPIが初期化されます。
./bin/yugabyted start --base_dir=$HOME/yugabyte-data
3. 3ノードクラスターのシミュレーション
単一ノードでは分散SQLの真価はわかりません。Raftコンセンサスが動作している様子を確認するために、異なるIPエイリアスとポートを使用して、ローカルマシン上に複数のノードをシミュレートできます。
# 2番目のノードを起動
./bin/yugabyted start \
--base_dir=$HOME/yugabyte-data-2 \
--listen=127.0.0.2 \
--join=127.0.0.1
# 3番目のノードを起動
./bin/yugabyted start \
--base_dir=$HOME/yugabyte-data-3 \
--listen=127.0.0.3 \
--join=127.0.0.1
これで回復力のあるクラスターが構築されました。1つのノードが故障しても、残りの2つが数秒以内に新しいリーダーを選出し、アプリをオンラインに保ちます。
データ操作
新しいツールは必要ありません。psql がインストールされていれば直接接続できますし、内蔵の ysqlsh シェルを使用することもできます。
./bin/ysqlsh -h 127.0.0.1
標準的なSQLコマンドが期待通りに動作します:
CREATE TABLE shipments (
shipment_id INT PRIMARY KEY,
customer_name TEXT NOT NULL,
status TEXT,
updated_at TIMESTAMP DEFAULT NOW()
);
INSERT INTO shipments (shipment_id, customer_name, status)
VALUES (1001, 'Global Logistics Corp', 'In Transit');
内部的には、YugabyteDBは shipment_id をハッシュ化し、データを「タブレット(tablet)」に配置します。このタブレットは3つのノードにレプリケートされます。シャーディングのロジックを一行も書くことなく、冗長性を確保できます。
大規模なデータのインポート
レガシーシステムからの移行では、数百万行のデータを移動することがよくあります。私たちの移行中、約80万件のレガシーレコードを乱雑なCSV形式からJSONBカラムに変換する必要がありました。
複雑なPythonスクリプトを書かずにこれを迅速に行うため、toolcraft.app/ja/tools/data/csv-to-json を使用しました。ブラウザ内でローカルに変換処理が行われるため、データセキュリティを損なうことなく、Postgresの COPY コマンド用にデータを素早くフォーマットできました。
回復力のテスト
分散SQLの真の価値は、ハードウェアの故障に耐えられることです。スクリプトでクラスターにデータを書き込んでいる最中に、実行中のプロセスの1つを停止させてみてください:
# ノードを特定して終了させる
ps aux | grep yugabyted
kill -9 <ノード2のPID>
アプリケーションのログを確認してください。クラスターが再バランスを行う間、通常3秒未満の短い一時停止が発生します。その後、書き込みは正常に続行されます。手動のフェイルオーバーや、接続文字列の更新は不要です。
YugabyteDBはあなたに適しているか?
すべてのプロジェクトにこれが適しているわけではありません。月額10ドルのVPSで十分に収まるような小規模なMVPを構築している場合は、標準のPostgreSQLを使い続けてください。その規模では、単一インスタンスの運用のシンプルさに勝るものはありません。
しかし、ロードマップにマルチリージョンでの可用性が含まれている場合や、半年ごとにインスタンスサイズをアップグレードすることに疲れているなら、YugabyteDBは論理的な次のステップです。PostgresのACID保証を維持しつつ、クラウドの水平成長パスを提供します。SQLのスキルを活かしたまま、スケーリングの不安を解消できます。

