5.4 KiB
5.4 KiB
数据库无外键设计说明
📋 设计原则
🚫 不使用外键约束
本项目采用无外键约束的数据库设计,通过应用层代码维护数据关联关系。
🎯 设计理由
1. 性能优化
- 减少约束检查: 数据库不需要在每次插入/更新时检查外键约束
- 提高并发性: 避免外键锁定导致的并发问题
- 加快批量操作: 大批量数据导入时无需考虑外键顺序
2. 开发灵活性
- 表结构调整: 修改表结构时不需要先删除外键约束
- 数据迁移: 数据迁移和同步更加简单
- 测试便利: 测试数据准备更加灵活
3. 分布式友好
- 微服务架构: 不同微服务可以独立管理自己的数据表
- 数据分片: 便于后期进行数据库分片和分布式部署
- 跨库关联: 支持跨数据库的数据关联
4. 维护简化
- 避免级联问题: 不会因为外键级联操作导致意外的数据删除
- 减少死锁: 降低因外键约束导致的数据库死锁概率
- 简化备份恢复: 数据备份和恢复时无需考虑外键依赖顺序
🔗 关联关系维护
代码层面维护
通过业务代码确保数据一致性:
// 示例:创建对话时关联用户
@Service
public class ConversationService {
@Autowired
private UserService userService;
@Autowired
private ConversationMapper conversationMapper;
public Conversation createConversation(String userId, String title) {
// 1. 验证用户是否存在
User user = userService.getById(userId);
if (user == null) {
throw new BusinessException("用户不存在");
}
// 2. 创建对话
Conversation conversation = new Conversation();
conversation.setUserId(userId); // 通过ID关联,不使用外键
conversation.setTitle(title);
conversationMapper.insert(conversation);
return conversation;
}
}
数据一致性保证
- 业务层验证: 在业务逻辑中验证关联数据的存在性
- 事务管理: 使用数据库事务确保操作的原子性
- 定期检查: 定期运行数据一致性检查脚本
📊 表关联关系
主要关联关系
user (用户表)
├── conversation.user_id → user.id
├── emotion_record.user_id → user.id
├── community_post.user_id → user.id
└── user_stats.user_id → user.id
conversation (对话表)
├── message.conversation_id → conversation.id
└── coze_api_call.conversation_id → conversation.id
message (消息表)
├── emotion_analysis.message_id → message.id
└── coze_api_call.message_id → message.id
community_post (社区帖子表)
└── comment.post_id → community_post.id
growth_topic (成长课题表)
├── topic_interaction.topic_id → growth_topic.id
└── reward.topic_id → growth_topic.id
achievement (成就表)
└── reward.achievement_id → achievement.id
关联字段命名规范
- 外部ID字段: 统一使用
{table_name}_id格式 - 主键字段: 统一使用
id - 数据类型: 统一使用
VARCHAR(36)雪花算法ID
🛡️ 数据完整性保证
1. 应用层验证
// 删除用户前检查关联数据
public void deleteUser(String userId) {
// 检查是否有关联的对话
if (conversationService.countByUserId(userId) > 0) {
throw new BusinessException("用户存在关联对话,无法删除");
}
// 检查是否有关联的情绪记录
if (emotionRecordService.countByUserId(userId) > 0) {
throw new BusinessException("用户存在情绪记录,无法删除");
}
userService.deleteById(userId);
}
2. 定期数据检查
-- 检查孤立的对话记录(用户不存在)
SELECT c.id, c.user_id
FROM conversation c
LEFT JOIN user u ON c.user_id = u.id
WHERE u.id IS NULL AND c.is_deleted = 0;
-- 检查孤立的消息记录(对话不存在)
SELECT m.id, m.conversation_id
FROM message m
LEFT JOIN conversation c ON m.conversation_id = c.id
WHERE c.id IS NULL AND m.is_deleted = 0;
3. 软删除策略
- 使用
is_deleted字段标记删除状态 - 保留历史数据,避免硬删除导致的关联数据问题
- 定期清理真正需要删除的数据
🔧 最佳实践
1. 服务层设计
- 单一职责: 每个服务只管理自己的数据表
- 接口调用: 跨表查询通过服务接口调用
- 缓存策略: 合理使用缓存减少跨表查询
2. 查询优化
- 索引设计: 为关联字段创建合适的索引
- 批量查询: 使用 IN 查询减少数据库访问次数
- 分页处理: 大数据量查询时合理分页
3. 数据迁移
- 脚本化: 数据迁移操作脚本化,可重复执行
- 验证机制: 迁移后验证数据完整性
- 回滚方案: 准备数据回滚方案
⚠️ 注意事项
1. 开发规范
- 严格按照关联关系进行数据操作
- 删除数据前必须检查关联关系
- 使用事务确保数据一致性
2. 监控告警
- 监控孤立数据的产生
- 定期检查数据一致性
- 异常情况及时告警
3. 文档维护
- 及时更新关联关系文档
- 记录数据操作规范
- 维护数据字典
设计原则: 简单、高效、可维护
实施策略: 代码约束 + 定期检查
适用场景: 微服务架构 + 高并发系统