サービスが動き始めると「どの機能が使われているか」を知りたくなります。
ただ、本番DBに分析クエリを直接投げるのは危険です。
重いクエリ1本でサービス全体が遅くなります。
最小構成
本番DB ──日次エクスポート──> オブジェクトストレージ ──> BigQuery
↓
クエリ / 可視化
リアルタイム性を捨てて 日次バッチにすると、一気に簡単になります。
個人開発〜小規模サービスなら、これで十分実用になります。
エクスポートは「必要な列だけ」
全テーブルを丸ごと出す必要はありません。
分析に使う列だけを選び、個人情報は最初から外します。
def export_orders(path):
rows = Order.objects.values(
"id", "created_at", "status", "total", "plan",
# email, name, address は出さない
)
with open(path, "w", newline="") as f:
w = csv.DictWriter(f, fieldnames=rows[0].keys())
w.writeheader()
w.writerows(rows)
分析基盤に個人情報を置かないと決めておくと、
権限設計とアクセス管理が一気に楽になります。
分析でよく使うクエリの型
1. 継続率(コホート)
SELECT
DATE_TRUNC(signup_date, MONTH) AS cohort,
DATE_DIFF(action_date, signup_date, MONTH) AS months_after,
COUNT(DISTINCT user_id) AS users
FROM user_actions
GROUP BY 1, 2
ORDER BY 1, 2
「登録から N ヶ月後に何人残っているか」が出ます。
新機能の効果は、この曲線が上に動いたかどうかで判断します。
2. 機能ごとの利用率
SELECT feature, COUNT(DISTINCT user_id) AS users
FROM feature_events
WHERE event_date >= DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)
GROUP BY feature
ORDER BY users DESC
ここで ほとんど使われていない機能が見つかります。
削除の判断材料になり、保守範囲を減らせます。
3. 離脱している段階
SELECT step, COUNT(DISTINCT user_id) AS users
FROM funnel_events
GROUP BY step
ORDER BY MIN(step_order)
登録フォームの3段目で半分落ちている、といったことが数字で見えます。
コストで事故らないために
BigQuery はスキャン量課金です。何も考えずに SELECT * を投げると、
1クエリで想定外の請求が発生します。
- 日付でパーティションを切る
- 必要な列だけ選ぶ(
SELECT *を禁止する) - 上限を設定しておく
-- パーティション列で必ず絞る
WHERE event_date BETWEEN '2021-07-01' AND '2021-07-31'
まとめ
- 本番DBに分析クエリを投げない。日次エクスポートで十分
- 個人情報は最初から持ち込まない
- 見るべきは「継続率」「機能別利用率」「離脱段階」の3つ
- スキャン量課金なので、パーティションと列指定を徹底する
分析基盤は大げさに作るほど使われなくなります。
日次CSV + クエリ3本から始めるのが、結局いちばん続きました。