Reverse ETLとモデル駆動オーディエンス ― マート定義をそのまま施策に
Hightouch/Censusに代表されるReverse ETLで、warehouseのマートや予測スコアを各ツールへ同期する仕組みを扱います。マートの定義をそのままオーディエンスにする「モデル駆動」の発想と、同期の鮮度・冪等性・失敗監視という運用の勘所を、SOLNAのChurnスコアで具体化します。
by Shin
warehouseに予測スコアもセグメントもあるのに、施策ツールからはそれが見えない——この断絶を埋める最後の配管がReverse ETLです。やることは単純で、warehouseのマートや予測スコアを、広告・CRM・MA・パーソナライズといった各ツールへ自動で同期します。この記事では、オーディエンスの定義そのものをマートに置く「モデル駆動」の発想と、同期を実務で回すための勘所(鮮度・冪等性・失敗監視)を、過去の受託案件をもとに設計した架空のD2C企業SOLNA(スキンケアの定期購入モデル)のChurnスコアを例に、SQLの最小構成つきで整理します。
新しい魔法の話ではありません。中身は「定義を一箇所に集約する」という、データ基盤では当たり前の原則を、活性化(施策側)にも適用するだけです。むしろ地味な配管だからこそ、運用設計を外すと静かに事故が積み上がります。
Reverse ETLとは何か
普段のETL/ELTは、広告媒体やECのローデータをwarehouseへ「集める」方向の流れです。Reverse ETLはその逆向きで、warehouseで整えた成果物——マート、予測スコア、セグメント——を、実際に施策が動くツールへ「戻す」流れを指します。warehouseを唯一の真実(single source of truth)に据えたうえで、その評価結果を各デスティネーションへ配るハブ、と捉えると位置づけが掴めます。
なぜ専用の層が必要なのか。各ツールへ個別にパイプを引くと、デスティネーションが増えるほど接続が掛け算で増えます。広告3媒体・CRM・MA・オンサイトへ別々にスクリプトを書けば、それぞれが別の定義・別の更新頻度・別の障害点を持ち、誰も全体を把握できなくなります。Reverse ETLはこのN×Mの配線を、warehouse側の「定義」と各ツールへの「マッピング」に分離します。HightouchやCensusが代表的なツールで、SQL(またはdbtモデル)でオーディエンスを定義し、各ツールのフィールドへ対応づけて同期します。
この連載の地図でいえば、Reverse ETLは「作る(Kaggle連載)・整える(dbt連載)」の出口にあたります。スコアやマートを作って止まるのではなく、それを施策に戻す最初の具体的な区間です。
モデル駆動オーディエンス(定義=マート)
従来のオーディエンス作成は、各ツールのGUIで「直近30日購入なし、かつLTV上位」のようにフィルタを手作りする方式でした。これには二つの弱点があります。定義がツールごとに散らばってズレること、そして定義が検証もバージョン管理もできないことです。広告側のセグメントとメール側のセグメントが「同じつもりで微妙に違う」のは、この方式の構造的な帰結です。
モデル駆動オーディエンスは、オーディエンスの定義をwarehouseのSQL(またはdbtモデル)に一本化します。定義は一箇所、各ツールへ同期されるのは「定義を評価した結果の名簿」だけです。定義がコードになると、レビューでき、テストでき、変更履歴が残ります。「このオーディエンスは何者か」がGUIの設定画面ではなくリポジトリのSQLで答えられる状態——これがモデル駆動の本質で、要するにsingle source of truthを活性化に降ろしただけです。
具体例として、SOLNA(本記事では共通スキーマの題材)で「解約防止のウィンバック配信」を打つためのオーディエンスを定義します。入力は、Kaggle連載で作ったChurn予測の出力(churn_scores)と、customers・subscriptionsです。
-- model-driven audience: churn_winback
-- 入力: churn_scores(Kaggle連載のChurn予測出力), customers, subscriptions
-- 出力: 各ツールへ同期する名簿(customer_id 単位)
with latest_scores as (
-- 同一顧客に複数スコアがある前提で、最新だけを残す(古いスコアの混入防止)
select
customer_id,
churn_proba,
model_version,
scored_at,
row_number() over (
partition by customer_id
order by scored_at desc
) as rn
from churn_scores
where model_version = 'churn_v3' -- バージョンを固定(再現性のため。後述の落とし穴)
),
active_subs as (
select customer_id
from subscriptions
where status = 'active' -- すでに解約済みは対象外
)
select
c.customer_id,
s.churn_proba,
c.ltv_to_date,
c.membership_tier
from latest_scores s
join customers c using (customer_id)
join active_subs a using (customer_id)
where s.rn = 1
and s.churn_proba between 0.55 and 0.85 -- 高すぎる層は実質離脱済み。中〜高を狙う
and c.ltv_to_date >= 30000 -- 投資が見合う下限(事業判断の閾値)ポイントは、業務判断(どの確率帯を狙うか、いくら以上のLTVを対象にするか)が、ツールの設定画面ではなくこのSQLのwhere句に明示的に書かれていることです。閾値を動かすときはこのコードを変更し、レビューを通します。複数ツールへ同じ名簿を配るなら、定義はこの一本だけを参照させます。
同期側の設定は、この定義を各ツールのフィールドへ対応づけるだけです。モデル駆動では「どの行を送るか」はSQLが決め、「どの列をどこへ入れるか」だけをマッピングします。
# 同期定義(Hightouch/Census 等のモデル駆動設定の最小構成)
model: churn_winback # 上のSQLが定義するオーディエンス
sync:
destination: crm_email
mode: upsert # 全件洗い替えではなく差分で揃える(冪等性のため)
identifier: customer_id # 突合キー(CRM内部ID)
mappings:
churn_proba: risk_score # 配信側で分岐に使えるよう属性も渡す
membership_tier: tier
removal: true # 対象から外れた人を送信先からも外す(古いセグメント対策)
schedule: "0 6 * * *" # 日次・朝6時(鮮度は施策の性質で決める)identifierに何を使うかはデスティネーションで変わります。自社CRM・MAならcustomer_idで足りますが、広告プラットフォームへ出すときはハッシュ化したメール等のマッチキーが必要になります。その識別子の用意(同意・ハッシュ化)は計測設計の連載の領分で、広告同期の実務は第3回に分けます。
同期の運用(鮮度・冪等性・失敗監視)
Reverse ETLは「一度繋いだら終わり」ではなく、毎日回り続ける運用対象です。設計で押さえるべき軸は三つです。
鮮度(freshness)。同期頻度は施策の性質から逆算します。解約防止のウィンバックは日次バッチで十分間に合いますが、カゴ落ちのリマインドのように分単位の鮮度が効く施策もあります。一方で、必要以上に頻繁な同期はコストとレート制限に跳ね返ります。Reverse ETLツールも送信先APIも処理行数や呼び出し回数で課金・制限されるため、「全部near-realtime」は運用負荷と費用の両面で割に合いません。施策ごとに必要な鮮度を決め、過剰同期を避けます。
冪等性(idempotency)。同期は「送信先を、あるべき状態に揃える」操作として設計します。同じ同期を二度流しても結果が変わらない、という性質です。これを満たすには、毎回の全件洗い替え(truncate→insert)ではなく、突合キーでの差分同期(追加・更新・削除を検知するupsert)を使います。冪等でない同期は、リトライのたびに重複を生んだり、途中失敗で中途半端な状態を残したりします。突合キーの設計(何で一意に揃えるか)が、ここの生命線です。
失敗監視。同期は必ず失敗します。送信先のAPI障害、warehouse側のスキーマ変更、レート制限、そして広告同期では後述のマッチ率低下。前提は「失敗する」で、設計すべきは失敗時の挙動(リトライ、部分適用の可否、ロールバック)と、それを検知する仕組みです。最低限、同期件数・成功率・遅延・拒否された行数(rejected rows)を毎回記録し、閾値を割ったらアラートを上げます。下は監視用の集計クエリの最小構成です。
-- 同期ヘルスの監視(直近の同期実行ログを集計する想定)
select
sync_name,
date_trunc('day', run_at) as day,
sum(rows_synced) as synced,
sum(rows_rejected) as rejected,
safe_divide(sum(rows_rejected), sum(rows_synced)) as reject_rate,
max(run_at) as last_run_at
from sync_runs
group by sync_name, day
having reject_rate > 0.02 -- 拒否率2%超は調査対象(閾値は運用で調整)
or max(run_at) < timestamp_sub(current_timestamp(), interval 26 hour) -- 日次なのに止まっている一次データの感触として、手作業のCSVアップロードで回している現場は更新が週次に落ち着きがちで、名簿が最新化されるまでのラグが数日から1週間規模になります。これを自動同期へ移すと、ラグは数時間から1日規模へ縮みます。重要なのは「速くなった」こと自体より、ラグが運用設計の中で予測可能になることです。
落とし穴(重複・古いセグメント・PII)
最後に、活性化で静かに事故が起きる三つの定番を、対処とセットで挙げます。
重複(過剰配信)。同じ人が複数のオーディエンスに入ると、配信が重なって過剰接触になります。対処は、オーディエンス間の優先順位と排他の設計、そして人単位の頻度上限(frequency cap)です。「ウィンバック対象」と「新商品案内対象」に同じ人が入ったとき、どちらを優先しどちらを抑制するかを、配信前に決めておきます。重複は「悪いセグメントを作った」のではなく「セグメント同士の関係を設計していない」ことから生まれます。
古いセグメント(stale audience)。一度同期した名簿が更新されないと、すでに解約した人・オプトアウトした人へ配信が続きます。これを防ぐには、オーディエンスへ「入れる」だけでなく「抜く」同期(removal sync)が必須です。上のYAMLでremoval: trueを立てたのはこのためで、定義から外れた人を送信先からも確実に外します。removalを実装しないモデル駆動同期は、追加専用の名簿が膨らみ続け、いずれ「誰に何を送っているか分からない」状態になります。
PII(個人データ)の越境。Reverse ETLはwarehouseから外部ツールへ個人データを「出す」操作です。だからこそ、同意・データ最小化・ハッシュ化(計測設計の連載)が前提になります。原則は三つ。同意のないオーディエンスは同期しない。送る属性は施策に必要な最小限に絞る。識別子をURLパラメータやクエリ文字列に載せない。送信先との関係ではデータ処理契約(DPA)の有無も確認対象です。活性化は「効かせる」前に「出してよいデータか」を通過させる必要があります。
これらに加えて、モデル駆動ならではの落とし穴がスコア定義の暗黙の変化です。上流のChurnモデルが再学習されてmodel_versionが変われば、同じSQLでもオーディエンスの中身が静かに入れ替わります。だからSQL側でmodel_versionを明示的に固定し、新バージョンへの切り替えは意図的な変更として扱います。再現性は、こうした小さな固定の積み重ねで守られます。
意思決定への接続(まとめ)
この回が動かすのは「配信対象の選定」という一点です。warehouseの予測やセグメントが、各ツールで実際に効くオーディエンスとして自動で更新され続ける状態——そこまで届かなければ、どれだけ良いスコアを作っても施策は変わりません。逆に言えば、Reverse ETLそのものは効果を生みません。効果を生むのは、そこへ載せる定義の質と、その施策が本当に効いたかの検証です。配管は配管として手堅く組み、勝負は定義と検証でする、という分業が健全です。
章末チェックリスト
- Reverse ETLの役割を「warehouse→各ツールへ定義を配るハブ」として説明できる
- オーディエンスの定義をマート(SQL/dbtモデル)に一本化できている
- スコアで選ぶのは「リスクが高い人」で「効く人」ではない、と区別できている
- 同期の鮮度を施策の性質から逆算して決めている
- 同期が冪等(差分upsert)で、二度流しても壊れない
- 同期件数・拒否率・遅延を監視し、閾値でアラートを上げている
- 「抜く」同期(removal)を実装し、古い名簿を作っていない
- 同期する個人データが同意・最小化・ハッシュ化の前提を満たしている
- スコアの
model_versionを固定し、定義の暗黙の変化を防いでいる
判断の土台となる「何を測り、どのリードを動かすか」は、対になる記述・予測編で扱っています。次回は、ここで定義したオーディエンスを広告プラットフォームのカスタム/類似として効かせる実務——同意・ハッシュ化・更新頻度・マッチ率——を、広告プラットフォームへのオーディエンス同期で掘ります。