跳转至

数据库架构

KanjiIQ使用PostgreSQL 15作为主数据存储,部署在Kubernetes集群内,具有持久化存储。

模式概览

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作为Kubernetes Deployment运行,使用PersistentVolumeClaim:

# 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天。详见部署章节了解运维详情。