MySQLのFEDERATEDストレージエンジンの設定:リモートテーブルの接続とクエリ実行

MySQL tutorial - IT technology blog
MySQL tutorial - IT technology blog

課題:データベース分割時におけるデータの結合

マイクロサービス化に伴いシステムを分割する際、データベースの分割には大きな課題がつきまといます。それは、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、DB auth_db および元の users テーブルを保持。
  • Local Server: IP 192.168.1.50、DB order_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未満)のサーバー間に限定すべきです。地理的に離れたデータセンター間など、パブリックインターネットを経由した接続は避けてください。ネットワークジッターによってコネクションがスタックし、アプリケーションがハングアップする原因となります。
Share: