5.1 KiB
5.1 KiB
修复 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 自动迁移(推荐)
-
停止当前服务
-
删除旧表并重建(谨慎:会丢失所有进度数据)
连接到 PostgreSQL 数据库:
psql -U your_username -d your_database执行:
DROP TABLE IF EXISTS practice_progress; -
重启服务,GORM 会自动创建正确的表结构
.\bin\server.exe
方案2:手动修复索引(保留数据)
-
连接到 PostgreSQL 数据库:
psql -U your_username -d your_database -
手动执行 SQL:
-- 删除旧索引 DROP INDEX IF EXISTS idx_user_question; -- 创建新索引 CREATE UNIQUE INDEX IF NOT EXISTS idx_user_type ON practice_progress(user_id, type); -
验证索引:
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) -
检查现有数据是否有冲突:
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. 测试插入不同题型
测试步骤:
- 登录系统
- 选择"单选题",答几道题
- 切换到"多选题",答几道题
- 切换到"判断题",答几道题
检查数据库:
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"
其他注意事项
-
JSONB vs JSONP:
- 修改了
UserAnswerRecords的类型从jsonp改为jsonb jsonb是正确的 PostgreSQL JSON 类型- 性能更好,支持索引
- 修改了
-
数据备份:
- 在修改表结构前,建议备份数据:
pg_dump -U your_username -d your_database -t practice_progress > backup_practice_progress.sql -
回滚方案:
- 如果需要回滚,可以恢复备份:
psql -U your_username -d your_database < backup_practice_progress.sql
相关文件
- 模型定义:
internal/models/practice_progress.go - 写入逻辑:
internal/handlers/practice_handler.go:356-387