AnCao/docs/fix_practice_progress_index.md

5.1 KiB
Raw Permalink Blame History

修复 practice_progress 表索引问题

问题描述

在测试中发现除了单选题multiple-choice能正常插入进度数据外其他类型的题目无法插入数据到 practice_progress 表。

问题原因

错误的索引定义

之前的模型定义(错误):

type PracticeProgress struct {
    ID                int64          `gorm:"primarykey" json:"id"`
    CurrentQuestionID int64          `gorm:"not null" json:"current_question_id"`
    UserID            int64          `gorm:"not null;uniqueIndex:idx_user_question" json:"user_id"`  // ❌ 只在 user_id 上建索引
    Type              string         `gorm:"type:varchar(255);not null" json:"type"`
    UserAnswerRecords datatypes.JSON `gorm:"type:jsonp" json:"answers"`
}

问题

  • 唯一索引 idx_user_question 只在 user_id 字段上
  • 同一用户只能有一条进度记录
  • 当用户答第二种题型时,因为 user_id 重复,插入失败
  • 日志中应该会看到类似错误:duplicate key value violates unique constraint "idx_user_question"

正确的索引定义

修复后的模型定义(正确):

type PracticeProgress struct {
    ID                int64          `gorm:"primarykey" json:"id"`
    CurrentQuestionID int64          `gorm:"not null" json:"current_question_id"`
    UserID            int64          `gorm:"not null;uniqueIndex:idx_user_type" json:"user_id"`      // ✅ 联合索引
    Type              string         `gorm:"type:varchar(255);not null;uniqueIndex:idx_user_type" json:"type"`  // ✅ 联合索引
    UserAnswerRecords datatypes.JSON `gorm:"type:jsonb" json:"answers"`
}

改进

  • 唯一索引 idx_user_type(user_id, type) 的联合索引
  • 同一用户可以有多条进度记录(每种题型一条)
  • 例如用户1 可以有 (1, "multiple-choice")(1, "true-false") 两条记录

解决方案

方案1使用 GORM 自动迁移(推荐)

  1. 停止当前服务

  2. 删除旧表并重建(谨慎:会丢失所有进度数据)

    连接到 PostgreSQL 数据库:

    psql -U your_username -d your_database
    

    执行:

    DROP TABLE IF EXISTS practice_progress;
    
  3. 重启服务GORM 会自动创建正确的表结构

    .\bin\server.exe
    

方案2手动修复索引保留数据

  1. 连接到 PostgreSQL 数据库

    psql -U your_username -d your_database
    
  2. 手动执行 SQL

    -- 删除旧索引
    DROP INDEX IF EXISTS idx_user_question;
    
    -- 创建新索引
    CREATE UNIQUE INDEX IF NOT EXISTS idx_user_type ON practice_progress(user_id, type);
    
  3. 验证索引

    SELECT indexname, indexdef
    FROM pg_indexes
    WHERE tablename = 'practice_progress';
    

    应该看到:

    indexname     | indexdef
    --------------|--------------------------------------------------
    idx_user_type | CREATE UNIQUE INDEX idx_user_type ON practice_progress USING btree (user_id, type)
    
  4. 检查现有数据是否有冲突

    SELECT user_id, type, COUNT(*)
    FROM practice_progress
    GROUP BY user_id, type
    HAVING COUNT(*) > 1;
    

    如果有重复数据,需要手动清理:

    -- 保留每组的最新记录,删除旧记录
    DELETE FROM practice_progress a
    WHERE id NOT IN (
      SELECT MAX(id)
      FROM practice_progress b
      WHERE a.user_id = b.user_id AND a.type = b.type
    );
    

验证修复

1. 检查表结构

\d practice_progress

应该看到:

Indexes:
    "practice_progress_pkey" PRIMARY KEY, btree (id)
    "idx_user_type" UNIQUE, btree (user_id, type)

2. 测试插入不同题型

测试步骤

  1. 登录系统
  2. 选择"单选题",答几道题
  3. 切换到"多选题",答几道题
  4. 切换到"判断题",答几道题

检查数据库

SELECT id, user_id, type, current_question_id
FROM practice_progress
WHERE user_id = 1;  -- 替换为你的用户ID

应该看到多条记录:

id | user_id | type                | current_question_id
---|---------|---------------------|--------------------
1  | 1       | multiple-choice     | 157
2  | 1       | multiple-selection  | 45
3  | 1       | true-false          | 10

3. 检查后端日志

如果之前有错误,应该不再看到类似日志:

保存练习进度失败: duplicate key value violates unique constraint "idx_user_question"

其他注意事项

  1. JSONB vs JSONP

    • 修改了 UserAnswerRecords 的类型从 jsonp 改为 jsonb
    • jsonb 是正确的 PostgreSQL JSON 类型
    • 性能更好,支持索引
  2. 数据备份

    • 在修改表结构前,建议备份数据:
    pg_dump -U your_username -d your_database -t practice_progress > backup_practice_progress.sql
    
  3. 回滚方案

    • 如果需要回滚,可以恢复备份:
    psql -U your_username -d your_database < backup_practice_progress.sql
    

相关文件

  • 模型定义:internal/models/practice_progress.go
  • 写入逻辑:internal/handlers/practice_handler.go:356-387