データベースアーキテクチャ¶
KanjiIQは、永続ストレージを備えたKubernetesクラスター内にデプロイされたPostgreSQL 15をプライマリデータストアとして使用しています。
スキーマ概要¶
erDiagram
users ||--o{ study_sessions : has
users ||--o{ quiz_results : has
users ||--o{ test_results : has
kanji ||--o{ quiz_results : referenced_in
vocabulary ||--o{ quiz_results : referenced_in
users {
uuid id PK
text email
text password_hash
jsonb preferences
jsonb stats
timestamp created_at
}
kanji {
uuid id PK
text character
int jlpt_level
jsonb meanings
jsonb readings
text example_sentences
}
vocabulary {
uuid id PK
text expression
text reading
int jlpt_level
jsonb meanings
text part_of_speech
}
locale_configs {
uuid id PK
text locale_code
text[] default_languages
text[] available_languages
}
regional_analytics {
uuid id PK
text country_code
text request_path
text user_agent
text device_type
boolean is_suspicious
int response_status
timestamp created_at
}
ip_blocklist {
uuid id PK
text ip_address
text reason
text blocked_by
timestamp expires_at
boolean is_active
}
多言語コンテンツストレージ¶
KanjiIQは、個別の翻訳テーブルの代わりにPostgreSQLのJSONBカラムを使用して翻訳を保存しています。これにより、柔軟でスキーマレスな多言語ストレージが実現されます:
// kanji.meanings column
{
"en": "mountain",
"es": "montaña",
"fr": "montagne",
"ja": "やま",
"pt": "montanha",
"ar": "جبل",
"zh-CN": "山"
}
なぜJSONBなのか?
- 新しい言語を追加する際にスキーマ移行が不要
- 単一のクエリで漢字のすべての翻訳を取得
- PostgreSQLのJSONB演算子が言語ごとの効率的な検索を実現
- 翻訳全体の全文検索にGINインデックスが利用可能
Kubernetesストレージ¶
PostgreSQLはPersistentVolumeClaimを持つKubernetes Deploymentとして動作します:
# k8s/02-postgres-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-pvc
namespace: jlpt-kanji
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
データベースはクラスター外に公開されていません — バックエンドPodのみがjlpt-postgres Serviceを通じてポート5432でアクセスできます。
主要な設計判断¶
UUID主キー¶
すべてのテーブルは自動インクリメント整数の代わりにUUID主キーを使用しています。これにより以下をサポートします:
- 分散ID生成(中央シーケンス不要)
- APIでの安全なID公開(推測不可能)
- IDの競合なしで将来のマルチリージョンレプリケーションに対応
カスケード削除¶
外部キーは参照整合性を維持するためにON DELETE CASCADEを使用しています。ユーザーを削除すると、そのユーザーの学習セッション、クイズ結果、テスト結果が自動的に削除されます。
JSONBによるユーザー設定¶
ユーザーの設定と統計は固定カラムではなくJSONBとして保存されます:
// users.preferences
{
"defaultLanguages": ["en", "pt", "es"],
"studyLevels": ["N5", "N4"],
"showAllLanguages": false
}
// users.stats
{
"totalKanjiStudied": 245,
"averageScore": 78.5,
"streakDays": 12
}
これにより、アプリケーションに新しい設定や統計が追加されるたびにスキーマ移行を行う必要がなくなります。
バックアップ戦略¶
データベースは3-2-1バックアップルールに従っています:
- データの3つのコピー(ライブ + 2つのバックアップ)
- 2種類の異なるメディア(PVC + オブジェクトストレージ)
- 1つのオフサイトコピー(Hetzner Object Storage)
毎日のpg_dumpエクスポートが30日間の保持期間で保存されます。運用の詳細はデプロイメントセクションを参照してください。