データ・分析

小さいサービスの利用データを BigQuery で分析する — 個人開発規模での始め方

サービスが動き始めると「どの機能が使われているか」を知りたくなります。
ただ、本番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本から始めるのが、結局いちばん続きました。