连接池、事务、数据库迁移与数据访问技术决策
稳定的数据访问层依赖连接预算、事务语义、模式迁移和可观测性,而不仅是选择 MyBatis 或 JPA。
1. 本文覆盖范围
- HikariCP 连接池预算
- Spring 事务传播与隔离
- Flyway/Liquibase 迁移
- MyBatis、JPA、jOOQ/JDBC 决策
2. 核心知识详解
1. 连接池是受限资源
连接池复用昂贵的数据库连接,并限制应用对数据库的并发压力。池大小由数据库容量、实例数、查询延迟与并发模型共同决定。
- 总连接预算 = 每实例最大连接数 × 最大实例数,再为运维和迁移预留。
- 监控 active、idle、pending、acquire timeout 和数据库端会话。
- 连接泄漏检测用于诊断,不作为日常关闭连接的替代。
正确性边界: 池越大吞吐并非越高;超过数据库并行能力会增加排队、上下文切换和尾延迟。
2. Spring 声明式事务
@Transactional 通常由代理拦截公开方法,事务资源绑定到当前线程;传播行为定义嵌套调用如何加入、挂起或新建事务。
- 默认回滚规则、readOnly、timeout、isolation 均显式理解。
- 同类自调用绕过代理;异步/新线程不会自动继承事务。
- 数据库写入与消息发送使用 outbox 等方案连接,而非幻想单机注解覆盖两个系统。
正确性边界: REQUIRES_NEW 会占用额外连接并可能扩大连接池需求,不是“确保提交”的通用按钮。
3. 版本化模式迁移
迁移脚本与应用版本一起评审、测试和发布。生产使用 expand/migrate/contract 等兼容步骤,避免新旧实例并存时破坏读写。
- 历史已执行迁移保持不可变,修复用新版本脚本。
- 大表 DDL 评估锁、日志、复制延迟和回滚路径。
- 启动迁移与应用副本扩容解耦,防止多实例竞争。
正确性边界: 回滚应用不一定能回滚数据结构;破坏性迁移需要前向修复与兼容窗口。
4. 技术选择矩阵
MyBatis 适合 SQL 可控和复杂查询;JPA 适合聚合持久化和领域模型;JDBC/jOOQ 适合显式 SQL 与类型安全组合。一个系统可按模块选择。
- 按查询复杂度、对象图、团队技能、性能可预测性选择。
- 统一事务、异常翻译、连接池和观测,不重复造基础设施。
- 用基准和执行计划判断性能,不按框架名推测。
正确性边界: 数据访问框架不改变数据库的锁、隔离、索引与事务规律。
3. 工程链路
4. 最小可运行示例
下面的示例只保留关键路径。把它放入对应版本的最小工程,先运行测试或命令确认行为,再逐步加入重试、超时、监控和异常分支。
@Service
class TransferService {
@Transactional
public void transfer(long from, long to, BigDecimal amount) {
accounts.debit(from, amount);
accounts.credit(to, amount);
}
}
// 事务方法应从代理外部调用;同类自调用不会经过代理拦截。
5. 实践与验证
- 计算 3 个应用实例的连接预算并模拟池耗尽。
- 实现可跨两个版本滚动发布的列重命名迁移。
- 为同一查询分别写 MyBatis/JPA 投影并记录 SQL 与耗时。
6. 掌握检查
- 能解释代理和线程边界。
- 能制定连接预算。
- 能设计兼容迁移。
- 能用证据选择数据访问技术。