Files
happy-life-star/backend/数据库无外键设计说明.md
T

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;
    }
}

数据一致性保证

  1. 业务层验证: 在业务逻辑中验证关联数据的存在性
  2. 事务管理: 使用数据库事务确保操作的原子性
  3. 定期检查: 定期运行数据一致性检查脚本

📊 表关联关系

主要关联关系

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. 文档维护

  • 及时更新关联关系文档
  • 记录数据操作规范
  • 维护数据字典

设计原则: 简单、高效、可维护
实施策略: 代码约束 + 定期检查
适用场景: 微服务架构 + 高并发系统