課題:データベース分割時におけるデータの結合
マイクロサービス化に伴いシステムを分割する際、データベースの分割には大きな課題がつきまといます。それは、users と orders が別々の物理サーバーに存在する場合、注文情報と一緒に表示するユーザー情報をどのように取得するかという問題です。
多くのチームが手軽な解決策として、Order DBから user_id のリストを取得して配列に詰め、User DBに対して WHERE user_id IN (...) を発行するという手法を選びがちです。数十レコード程度であれば問題なく動作しますが、IDの件数が2万件規模に達すると、バックエンドのメモリを圧迫し、ペイロードが肥大化して、ネットワークレイテンシが15msから1,200ms超へと跳ね上がることになります。
サーバー間データ連携における代表的な3つのアプローチ
要件やユースケースに応じて、データ連携には主に3つのアプローチが存在します。
- アプリケーション層での結合(Application-level Join): バックエンドが両方のDBに対してコネクションプールを張り、メモリ上にデータをロードしてからコード上で手動マッピングします。
- CDC / ETL による同期: Debezium、Kafka、またはMySQLレプリケーションを利用して、ソーステーブルからターゲットサーバーへデータをプッシュします。
- FEDERATED ストレージエンジンの利用: ローカルサーバー上にリモートサーバーの実テーブルを直接指し示すプロキシテーブルを作成します。ローカルのMySQL自体がクライアントとして動作し、ネットワーク経由でクエリを送信します。
各ソリューションの技術的トレードオフ比較
万能な解決策は存在せず、どのアプローチにもトレードオフが伴います。
1. Application-level Join
- メリット: 実装が迅速で、データベースの設定変更を一切必要としません。
- デメリット: バックエンドのメモリ消費が極めて大きく、
IN (...)内の配列サイズがプロセスのメモリ上限を超えると、Out-of-Memory(OOM)エラーが発生しやすくなります。
2. CDC / ETL による同期
- メリット: ローカルディスク上にデータが事前に保持されるため、読み取り速度が極めて高速です。
- デメリット: ディスク容量が実質2倍消費されます。また、常にデータの遅延(数百ミリ秒〜数秒のレプリケーションラグ)が発生します。リモートサーバー側でカラム名を1つ変更しただけでも、同期パイプラインが破損するリスクがあります。
3. FEDERATED Storage Engine
- メリット:
- 同期遅延がなく、常にリアルタイムで正確なデータを取得できます。
- ローカルサーバー側でデータやインデックス用のディスク容量を消費しません(テーブル構造のメタデータのみ保持)。
- クエリを直感的に記述可能で、ローカルのInnoDBテーブルとFEDERATEDテーブル間をシームレスに
JOINできます。
- デメリット: 処理速度がネットワークの往復遅延時間(RTT)に直結します。また、分散トランザクション(XA)や直接の
ALTER TABLE文はサポートされておらず、複雑なクエリの最適化には向いていません。
FEDERATED を採用すべきユースケース
FEDERATEDは、高トラフィックなコアOLTPシステムの負荷を処理するためのものではありません。主に以下のようなシーンでの利用を検討すべきです。
- 高コストなETLパイプラインを構築するほどではない、管理画面(Admin Dashboard)や社内ツールでのクイックなユーザー情報参照。
- マスターDBに集約された、更新頻度の低いマスタテーブル(都道府県、郵便番号、商品カテゴリなど)の参照。
- レガシーシステムと新システム間でのデータ移行(マイグレーション)スクリプトの実行やデータ照合。
FEDERATED の詳細な設定手順
検証環境として、以下の2台の内部サーバーを想定します。
- Remote Server: IP
192.168.1.100、ポート3306、DBauth_dbおよび元のusersテーブルを保持。 - Local Server: IP
192.168.1.50、DBorder_dbを保持し、リモートのusersテーブルを直接読み取る。
ステップ1:Local ServerでFEDERATEDエンジンを有効化する
ほとんどのMySQLインストール環境では、このエンジンはデフォルトで無効になっています。まず、ローカルサーバーで現在のステータスを確認します。
SHOW ENGINES;
Support列に NO または DISABLED と表示されている場合は、MySQL設定ファイル(通常は /etc/mysql/mysql.conf.d/mysqld.cnf)を開き、[mysqld] セクションに以下の行を追加します。
[mysqld]
federated
設定を反映させるために、MySQLデーモンを再起動します。
sudo systemctl restart mysql
再度 SHOW ENGINES; を実行し、値が YES に変わっていれば、エンジンの準備は完了です。
ステップ2:Remote Server上でアカウントとテーブルを準備する
Remote Server(192.168.1.100)へ切り替え、スキーマを作成した上で、Local ServerのIPに対して最小限の読み取り権限を付与します。
-- Remote Server上で実行
CREATE DATABASE IF NOT EXISTS auth_db;
USE auth_db;
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL,
email VARCHAR(100) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;
-- Local ServerのIPからのみSELECT権限を付与
CREATE USER 'fed_reader'@'192.168.1.50' IDENTIFIED BY 'StrongPass#2026';
GRANT SELECT ON auth_db.users TO 'fed_reader'@'192.168.1.50';
FLUSH PRIVILEGES;
ステップ3:Local Server上で連携テーブルを作成する
最も直接的な方法は、CONNECTION パラメータに接続文字列を直接記述することです。
-- Local Server上で実行
USE order_db;
CREATE TABLE federated_users (
id INT NOT NULL,
username VARCHAR(50) NOT NULL,
email VARCHAR(100) NOT NULL,
created_at TIMESTAMP
)
ENGINE=FEDERATED
CONNECTION='mysql://fed_reader:StrongPass#[email protected]:3306/auth_db/users';
URL構文は mysql://user:password@host:port/database/table の形式に従います。
ステップ4:`CREATE SERVER` による認証情報の安全な分離
CREATE TABLE 文に平文でパスワードを記述すると、SHOW CREATE TABLE を実行できるすべてのユーザーにパスワードが露出するため非常に危険です。CREATE SERVER を利用して接続情報を隠蔽することが推奨されます。
-- サーバー接続オブジェクトを作成
CREATE SERVER remote_auth_srv
FOREIGN DATA WRAPPER mysql
OPTIONS (
HOST '192.168.1.100',
DATABASE 'auth_db',
USER 'fed_reader',
PASSWORD 'StrongPass#2026',
PORT 3306
);
-- 定義したサーバー名を使用してテーブルを作成
CREATE TABLE federated_users_secure (
id INT NOT NULL,
username VARCHAR(50) NOT NULL,
email VARCHAR(100) NOT NULL,
created_at TIMESTAMP
)
ENGINE=FEDERATED
CONNECTION='remote_auth_srv/users';
ステップ5:JOIN クエリの動作とパフォーマンス検証
この時点で、federated_users_secure テーブルは order_db 内のローカルテーブルと全く同じように扱えます。
-- プライマリキーで1件のレコードを取得
SELECT id, username, email FROM federated_users_secure WHERE id = 10;
-- ローカルのInnoDBテーブルとリモートのFEDERATEDテーブルを結合
SELECT
o.id AS order_id,
o.total_amount,
u.username,
u.email
FROM orders o
JOIN federated_users_secure u ON o.user_id = u.id
WHERE o.status = 'PAID'
LIMIT 20;
パフォーマンス上の落とし穴と実践的なノウハウ
本番環境でFEDERATEDを利用する際は、以下の極めて重要な3つのポイントに注意してください。
- フルテーブルスキャン(Full Table Scan)の罠: FEDERATEDがリモートサーバーにプッシュダウンできるのは、インデックスが効く単純な
WHERE句に限られます。インデックスが使用されない条件(例えばLIKE '%keyword%'など)を指定した場合、ローカルサーバーはリモートから数十万行規模の全データを密かにフェッチしてからローカル側でフィルタリングします。これにより、1GbpsのLAN帯域であっても一瞬で飽和する恐れがあります。 - スキーマの不整合によるクエリ失敗: ローカルとリモートのカラム定義は完全に一致している必要があります。リモート側で
VARCHAR(50)をVARCHAR(150)に変更したりカラム名をリネームしたりした際、ローカル側が追従していなければ、関連するすべてのクエリが即座にエラーとなります。 - ネットワークレイテンシの制御: FEDERATEDテーブルの配置は、同一ローカルネットワーク内(LAN / VPC、pingが1〜2ms未満)のサーバー間に限定すべきです。地理的に離れたデータセンター間など、パブリックインターネットを経由した接続は避けてください。ネットワークジッターによってコネクションがスタックし、アプリケーションがハングアップする原因となります。

