こちらの記事は「Medley Summer Tech Blog Relay」の2日目の記事です。
はじめに
こんにちは、メドレー人材プラットフォームのSREを担当している本多です。
ジョブメドレーは日本最大級の医療介護求人サイトで、日々大量のデータが基幹データベースであるAurora MySQL(以下Aurora)に書き込まれています。そのデータをBigQuery(以下 BQ)に転送して分析・活用するパイプラインを長年Embulkで運用してきましたが、2025年11月にEmbulkがメンテナンスモードに移行したことを契機に、FivetranによるCDC(Change Data Capture)転送に切り替えを進めています。
As-Isの構成:Embulkによるバッチ転送
移行前のデータ転送フローは以下の構成でした。
EmbulkはJenkinsでスケジューリングし、AuroraのReaderインスタンスにSELECTをかけてデータを全件または差分取得し、BQのデータセットへ直接書き込んでいました。
なぜ移行が必要になったのか
Embulkのメンテナンスモード移行
Embulkでの運用は大きなトラブルもなく稼働していたのですが、2025年11月、Embulkプロジェクトがメンテナンスモードへの移行を発表しました。今後機能追加・バグ修正がされない可能性が極めて高く、継続利用は運用・セキュリティ両面でリスクを伴うと判断しました。
バッチ転送固有の課題
問題なく稼働していたとはいえ、バッチ転送固有の課題がいくつか存在しました。
データ鮮度
- サイズが大きいテーブルは高頻度に転送するとMySQLの負荷につながることと、時間もかかるため転送間隔を長くせざるを得ず、分析基盤側のデータ鮮度を保つことが難しい構造でした。
- 物理削除(DELETE)への追従には全件洗い替えが必要で、データ量が増えるほど転送時間・コストが増加していきました。
Embulk設定の運用コスト
- カラム追加等でDDLに変更があるたびにEmbulkの設定変更が必要で、設定漏れによる転送ミスが生じるリスクがありました。また、問題発生時のリカバリ作業は手動対応となり、都度工数が発生していました。
ネットワークコスト
- MySQLのデータは差分更新が難しいテーブルが多く、全件転送しているテーブルが殆どで月間約数TB程度のデータをBQに転送しており、アウトバウンド通信費が右肩上がりの傾向となっていました。
CDCという選択肢
他のバッチ転送ツールへの移行も選択肢としてありましたが、データ鮮度の向上・運用コストの削減を目的として、今回CDC(Change Data Capture)の採用を決めました。
CDCはソースDB(今回はAurora MySQL)のバイナリログを読み取り、INSERT/UPDATE/DELETEといった変更イベントをニアリアルタイムでターゲットへ伝播する仕組みです。Fivetranはマイクロバッチ形式で指定の間隔で更新ログを取得してターゲットDBに適用していく方式となっています。
バッチ転送とCDC転送の比較(ソリューションによって仕様に差があります)
| 観点 | バッチ転送 | CDC転送 |
|---|---|---|
| データ鮮度 | 分〜時間単位 | ニアリアルタイム〜分単位 |
| DBへの負荷 | SELECT負荷あり | バイナリログ読み取りのみ |
| 物理削除への追従 | 基本不可 | 削除フラグで追従可 |
| スキーマ変更追従 | 手動設定変更が必要 | 自動追従 |
| 転送データ量 | 全件(大量) | 差分のみ(少量) |
CDCを選ぶことで、データ鮮度の向上・ネットワークコスト削減・開発チームの実装コスト解消を一気に図れます。一方でトラブル時のリカバリコストはバッチより大きく、外部SaaSの導入によるデータガバナンスの検討も必要になります。
ソリューション選定
経緯は割愛しますがソリューションについてはいくつか検証し、最終的にはFivetranを採用しました。グローバルで広く採用されているCDCソリューションで、信頼性・機能面・サポート体制の充実が決め手となりました。
Fivetranを選定したポイント
初期同期の速さとソースDBへの負荷最小化
テーブルを同期する際に、必ず最初に一度全件をSELECTで取得する必要がありますが、Primary Keyがある場合はPKを元にしてクエリを分割して取得するので、1クエリのロック保持時間が短くなり、本番サービスへの影響を最小限に抑えられます。
- FivetranからソースDBに対して実行されるSELECTクエリサンプル
SELECT
*
FROM
{table}
WHERE
{primary key} > {前ページの最後のPK値}
ORDER BY {primary key}
LIMIT {page size}
Hybrid Deploymentによるセキュリティ確保
CDCを外部SaaSで実現する場合、通常はSaaS側のネットワークからDBポートへのインバウンド通信を許可する必要があります。しかしFivetranのHybrid Deploymentを利用すると、自社VPC内に構築したAgentからのアウトバウンド通信だけで済み、インバウンドの穴あけが不要になります。実データもSaaS側ネットワークを経由せず、AgentからBigQueryに直接転送されます。
個人情報を扱うサービスにとって、データを外部ネットワークに流さないことは重要な要件です。Hybrid Deploymentはその点でセキュリティ上の堅牢性が高く、選定の大きな決め手になりました(EnterpriseまたはBusiness Critical planで利用可能)。
サポートデータソースの多さ
ソース、ターゲット共に多くのソリューションに対応しています。ネイティブ対応していないデータソースでもSDKでカスタムコネクタを作成可能です。
サポートの応答速度
基本英語対応になりますが、24時間365日対応のサポートが利用できます。データパイプラインに障害が発生した場合、BQのデータが止まると分析・モニタリング業務に支障が出るため、夜間・休日でも問い合わせができる体制は重要です。実際に設定や動作についての問い合わせを行った際も、迅速かつ的確な回答が得られました。
To-Beの構成:FivetranによるCDC転送
EC2上に構築したFivetran AgentがAuroraのバイナリログを読み取り、変更データをBigQueryのRaw層データセットへ直接転送します。ユーザーが参照するデータセットはその上にViewを重ねて提供する構成としています。
工夫が必要なところ
この形に落ち着くまでにはいくつか工夫が必要なポイントがありました。
ターゲットテーブルのパーティショニング設定
ターゲットがBQの場合の対応になりますが、Fivetranは初期同期の際にターゲットテーブルのパーティションやクラスタリングを自動設定しません。そのため、何もしないで差分同期を行うとBQ側のMERGE文がフルスキャンになってしまい、スキャンコストが増大します。
ドキュメントによるとスキャンコストを最適化するには、ソースのPrimary Keyをパーティションに含める必要があると記載されていますが、クラスタリングでもスキャンコストの削減に一定の効果が見られたため、現状はPKでクラスタリング+日時カラム(レコード作成日など)でパーティショニングすることを基本運用としています(PKをパーティションキーにしても参照側でのユースケースがあまり無いため)。クラスタリング+パーティショニングの設定は一度初期同期を行った後に、いったん同期を止めてBQ側のテーブルを差し替える対応をスクリプトで行っています。
- ターゲット側で実行されているMERGE文のサンプル
MERGE INTO `<project>`.`<table_name>` AS existing
USING `fivetran_commentaries_captive_staging`.`<table_name>-staging-<uuid>` AS _fivetran_staging_
ON `existing`.`<pk>` = `_fivetran_staging_`.`<pk>`
AND <min_pk> <= `existing`.`<pk>` AND <max_pk> >= `existing`.`<pk>`
WHEN MATCHED AND `_fivetran_staging_`.`_fivetran_op_type` = 1
THEN UPDATE SET
`existing`.`<col1>` = `_fivetran_staging_`.`<col1>`,
`existing`.`<col2>` = `_fivetran_staging_`.`<col2>`,
-- ... (全カラム)
`existing`.`_fivetran_deleted` = `_fivetran_staging_`.`_fivetran_deleted`,
`existing`.`_fivetran_synced` = `_fivetran_staging_`.`_fivetran_synced`
WHEN NOT MATCHED AND `_fivetran_staging_`.`_fivetran_op_type` = 1
THEN INSERT (`<pk>`, `<col1>`, `<col2>`, ..., `_fivetran_deleted`, `_fivetran_synced`)
VALUES (`<pk>`, `<col1>`, `<col2>`, ..., `_fivetran_deleted`, `_fivetran_synced`);
このあたりの煩雑さはBQ側の仕様によるところが大きいため、いずれはFivetranからの一次転送先をIceberg TableなどOTF(Open Table Format)に移行したいと考えています。
転送先データセット名を指定できない
Fivetranの転送先BQデータセット名は {コネクション名}_{MySQLのデータベース名} の形式で自動生成され、転送先のデータセット名を任意に指定することができません(命名規則)。また転送時にテーブル名を変更することもできません。この制限により既存のデータセット名、テーブル名のままFivetranの転送に置き換えることは難しかったため、今回は転送先をRaw層として一般ユーザーが参照できないデータセットに転送し、ユーザーが実際に利用するデータセットにはViewでデータを参照させるような構成にしました。元々ユーザー利用のデータセットとソースデータの一次転送先は分ける想定だったため問題はありませんでしたが、既存システムをそのままリプレースしたい場合は少し工夫が必要になります。
型の自動変換への対処
ソーステーブルからターゲットテーブルへの型変換は自動で行われ、ユーザー側で指定することができないため、想定と型が異なる場合は、転送後にCASTするなど、何かしら変換をかける必要があります。例を挙げるとMySQLのTINYINT(1)型でデータの中身が0/1しか入っていない場合は、転送先ではBOOLEAN型に変換されます。これ以外にもデータソースによって意図せぬ型変換が行われるかもしれませんので、事前の入念なテストが必要です。
費用面
FivetranはMAR(Monthly Active Records)ベースの課金体系となっており、月内に1度でも更新されたレコードが1MARとカウントされますが、同じレコードが何度更新されても月内は1カウントとなります。
この仕様によりソース側の更新量が同じであれば、転送頻度を上げてもMARは変わらず、Fivetranのコストは増えません(ただしターゲット側のスキャンコストは上がる)。一方で、MARはコネクション単位で計測されるため、多数のインスタンスを運用している場合はコネクションも分割せざるを得ず、コスト面では不利になります。ターゲット側のスキャンコスト抑制のため、同じデータベースだがデータ鮮度をそれほど求めないテーブルは別コネクションにすることも検討しましたが、Fivetranのコスト計算ではコネクションをまとめたほうが有利になるため、今のところ1インスタンスに対して1コネクションとし、15分単位の更新で運用しています。
また1MARあたりの単価は更新量が増えるごとに下がっていき、レコード数の増加に応じてリニアに費用が上昇しないような仕組みにはなっていますが、更新行数によっては想定外の費用が発生する可能性がありますので、Pricing estimatorで事前に試算しておくことをお勧めします。
Hybrid Deployment Agentの制約
AWS上で利用の場合、Hybrid Deployment Agentにはいくつかの組み合わせ制約があります。できれば手軽なECSで動かしたいところです。
- Graviton(ARM)未対応:x86_64のみ対応のため、EKSでGravitonノードグループを使っている場合は専用のx86_64ノードが必要です
- Amazon ECS未対応:EC2またはEKS上での稼働が必要です(Fargateを含むECS上では動作しません)
まとめ
と、気になる点の方が長くなってしまいましたが、一度設定が固まればCDC転送は運用負荷も低くデータの鮮度も保たれ非常によいソリューションだと思います。現在はまだ移行中のフェーズのため、移行が完了したらまた使用感のアップデートやコストの変遷などをお伝えできればと思います。
明日の Medley Summer Tech Blog Relay 3日目は人材プラットフォームの森川さんです! お楽しみに!
We are Hiring!
メドレーでは一緒に働く仲間を募集しています!ご興味のある方、ぜひご連絡をお待ちしております。

