メインコンテンツまでスキップ

データベース概要

subseek のデータストアは 2 系統 に分かれている。リレーショナル制約とトランザクションが必要なデータは Turso(SQLite) に、全文検索・ファセット・ソートが必要な字幕本文は Hetzner セルフホストの Meilisearch に格納する。

この章ではまず 2 系統の役割分担を俯瞰してから、各テーブル / インデックスの詳細ページへ進むことを想定している。

使い分け早見表

項目Turso (libSQL / SQLite)Meilisearch
役割メタデータ・課金・履歴の真実の源(source of truth)字幕本文の保存先 兼 検索インデックス
格納データチャンネル / 動画のメタデータ、ユーザー・認証・Stripe サブスクリプション、クレジット残高・消費履歴、お気に入り、検索履歴。字幕本文は保持していない字幕セグメント本文(text / startTime / endTime / language)+検索で使うフィルタ・ソート用の冗長フィールド(videos / channels から複製)
主な用途FK 制約・複合 PK・トランザクション・集計(課金や履歴)全文検索、ハイブリッド検索、ファセット集計、範囲フィルタ、ソート
書き込み契機API リクエスト / Stripe Webhook / 月次バッチ(詳細は Turso 更新タイミング字幕取得ジョブ完了時に投入、チャンネルメタ変動時に partial update(詳細は Meilisearch 更新タイミング
整合性モデル単一インスタンス上のトランザクション(Turso primary + libSQL replicas)単一インデックス。非同期タスクキューで反映
クエリ特性SQL / Drizzle ORMREST / SDK、BM25 + embedder によるハイブリッド検索
スケール単位マネージド(Turso Cloud)Docker コンテナ単位の縦方向スケール
バックアップTurso のスナップショット(マネージド)critical:字幕本文の唯一の保存先。VM の Docker ボリューム(/opt/meilisearch/data/{env})のスナップショット必須。損失時の復旧は YouTube Data API からの字幕再取得となるがクォータ制約が大きい
ホスティングTurso Cloud(マネージド)Hetzner Cloud CPX11(Ashburn, Ubuntu 24.04 + Docker)。Cloudflare Tunnel 経由で Workers から到達。詳細は Meilisearch セットアップ

データフロー

使い分け原則

  • リレーショナル制約や整合性が本質的に必要なら Turso
    • FK 制約(例: user_video_access.video_id → videos.id
    • 複合主キー(例: (user_id, video_id)
    • トランザクション内での複数テーブル更新(例: クレジット消費は users.credit_balance 減算と user_video_access UPSERT を同一 Tx で行う)
  • 全文検索・ファセット・ソートが必要なら Meilisearch
    • 字幕本文の検索(そもそも字幕本文の保存先として Meilisearch を使う)
    • 言語 / カテゴリ / 投稿元の国 / 再生時間などでの絞り込み・ソート
  • 両方に同じ値を持たせる場合の真実の源は Turso(ただし字幕本文は例外)
    • 例: channels.country は Turso が真実の源で、Meilisearch の channelCountry はそれを冗長保存した派生値
    • 値が変動した際は Meilisearch 側を partial update で追従させる(#305 以降)
    • 例外: 字幕本文(text)は Meilisearch のみが保持 する。Turso には存在しないため、Meilisearch のデータが失われた場合の復旧は YouTube からの再取得に依存する

詳細ドキュメント

Turso(SQLite)

Meilisearch

全体設計