> 技术文档 > 《 Spring Boot整合多数据源:分库业务的标准做法》

《 Spring Boot整合多数据源:分库业务的标准做法》


🚀 Spring Boot整合多数据源:分库业务的标准做法

文章目录

  • 🚀 Spring Boot整合多数据源:分库业务的标准做法
  • 🔍 一、为什么需要多数据源支持?
    • 💡 典型业务场景
  • ⚙️ 二、多数据源集成方案对比
    • 💡 主流方案分析
    • 🔧 方案选型建议
  • 🔄 三、动态数据源切换原理
    • 💡 核心架构
    • ⚙️ 核心代码实现
    • 🚀 使用示例
  • 🧩 四、多数据源事务管理
    • 💣 单事务管理器问题
    • 💡 解决方案一:独立事务管理器
    • 💡 解决方案二:分布式事务
    • ⚠️ 事务管理最佳实践
  • 🚀 五、主从读写分离实战
    • 💡 架构设计
    • ⚙️ 配置示例
    • 🔧 动态路由配置
    • ⚡️ 读写分离策略
  • 🧪 六、整合MyBatis-Plus多数据源
    • 💡 官方推荐方案
    • ⚙️ 配置示例
    • 🚀 注解使用
  • 💎 七、最佳实践总结
    • 🏆 核心实施步骤
    • ⚠️ 避坑指南
    • 🛠 推荐工具栈
    • 🌟 建议

🔍 一、为什么需要多数据源支持?

在实际业务开发中,随着系统规模的扩大,一个单一的数据源很难满足以下典型场景:

  • 分库分表:将用户表、订单表、日志表拆分到不同数据库,降低单库压力。
  • 读写分离:主库用于写操作,从库用于读操作,提高查询性能。
  • 多租户架构:为不同客户接入独立的数据源,保障数据隔离。

如果仍使用单数据源架构,将面临以下问题:

问题 表现 单点瓶颈 数据库连接数受限,性能下降 数据隔离困难 各业务之间相互影响,风险扩大化 扩展困难 无法灵活配置租户或业务线的数据库策略

​​案例分享​​:在电商平台中,我们将用户、订单、日志分离到不同数据库集群,使QPS提升300%,故障恢复时间缩短70%

💡 典型业务场景

#mermaid-svg-gtuIfYzOWSsxnpvG {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-gtuIfYzOWSsxnpvG .error-icon{fill:#552222;}#mermaid-svg-gtuIfYzOWSsxnpvG .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-gtuIfYzOWSsxnpvG .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-gtuIfYzOWSsxnpvG .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-gtuIfYzOWSsxnpvG .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-gtuIfYzOWSsxnpvG .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-gtuIfYzOWSsxnpvG .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-gtuIfYzOWSsxnpvG .marker{fill:#333333;stroke:#333333;}#mermaid-svg-gtuIfYzOWSsxnpvG .marker.cross{stroke:#333333;}#mermaid-svg-gtuIfYzOWSsxnpvG svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-gtuIfYzOWSsxnpvG .label{font-family:\"trebuchet ms\",verdana,arial,sans-serif;color:#333;}#mermaid-svg-gtuIfYzOWSsxnpvG .cluster-label text{fill:#333;}#mermaid-svg-gtuIfYzOWSsxnpvG .cluster-label span{color:#333;}#mermaid-svg-gtuIfYzOWSsxnpvG .label text,#mermaid-svg-gtuIfYzOWSsxnpvG span{fill:#333;color:#333;}#mermaid-svg-gtuIfYzOWSsxnpvG .node rect,#mermaid-svg-gtuIfYzOWSsxnpvG .node circle,#mermaid-svg-gtuIfYzOWSsxnpvG .node ellipse,#mermaid-svg-gtuIfYzOWSsxnpvG .node polygon,#mermaid-svg-gtuIfYzOWSsxnpvG .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-gtuIfYzOWSsxnpvG .node .label{text-align:center;}#mermaid-svg-gtuIfYzOWSsxnpvG .node.clickable{cursor:pointer;}#mermaid-svg-gtuIfYzOWSsxnpvG .arrowheadPath{fill:#333333;}#mermaid-svg-gtuIfYzOWSsxnpvG .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-gtuIfYzOWSsxnpvG .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-gtuIfYzOWSsxnpvG .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-gtuIfYzOWSsxnpvG .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-gtuIfYzOWSsxnpvG .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-gtuIfYzOWSsxnpvG .cluster text{fill:#333;}#mermaid-svg-gtuIfYzOWSsxnpvG .cluster span{color:#333;}#mermaid-svg-gtuIfYzOWSsxnpvG div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-gtuIfYzOWSsxnpvG :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;} 业务需求 分库分表 读写分离 多租户隔离 用户库 订单库 日志库 主库写 从库读 租户A库 租户B库

⚙️ 二、多数据源集成方案对比

💡 主流方案分析

方案 优点 缺点 场景适配 @Primary + @Qualifier 简单明了,配置直观 不支持动态切换 静态业务分离 AbstractRoutingDataSource 支持动态路由,结合 ThreadLocal 使用 配置复杂,事务处理较麻烦 动态切换读/写、多租户 Dynamic Datasource 中间件 快速集成、支持注解 + AOP + 多事务 引入依赖,需理解封装逻辑 推荐使用,兼容性强

🔧 方案选型建议

#mermaid-svg-GHZLW04pfMLgFX0p {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GHZLW04pfMLgFX0p .error-icon{fill:#552222;}#mermaid-svg-GHZLW04pfMLgFX0p .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-GHZLW04pfMLgFX0p .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-GHZLW04pfMLgFX0p .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-GHZLW04pfMLgFX0p .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-GHZLW04pfMLgFX0p .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-GHZLW04pfMLgFX0p .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-GHZLW04pfMLgFX0p .marker{fill:#333333;stroke:#333333;}#mermaid-svg-GHZLW04pfMLgFX0p .marker.cross{stroke:#333333;}#mermaid-svg-GHZLW04pfMLgFX0p svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-GHZLW04pfMLgFX0p .label{font-family:\"trebuchet ms\",verdana,arial,sans-serif;color:#333;}#mermaid-svg-GHZLW04pfMLgFX0p .cluster-label text{fill:#333;}#mermaid-svg-GHZLW04pfMLgFX0p .cluster-label span{color:#333;}#mermaid-svg-GHZLW04pfMLgFX0p .label text,#mermaid-svg-GHZLW04pfMLgFX0p span{fill:#333;color:#333;}#mermaid-svg-GHZLW04pfMLgFX0p .node rect,#mermaid-svg-GHZLW04pfMLgFX0p .node circle,#mermaid-svg-GHZLW04pfMLgFX0p .node ellipse,#mermaid-svg-GHZLW04pfMLgFX0p .node polygon,#mermaid-svg-GHZLW04pfMLgFX0p .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-GHZLW04pfMLgFX0p .node .label{text-align:center;}#mermaid-svg-GHZLW04pfMLgFX0p .node.clickable{cursor:pointer;}#mermaid-svg-GHZLW04pfMLgFX0p .arrowheadPath{fill:#333333;}#mermaid-svg-GHZLW04pfMLgFX0p .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-GHZLW04pfMLgFX0p .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-GHZLW04pfMLgFX0p .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-GHZLW04pfMLgFX0p .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-GHZLW04pfMLgFX0p .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-GHZLW04pfMLgFX0p .cluster text{fill:#333;}#mermaid-svg-GHZLW04pfMLgFX0p .cluster span{color:#333;}#mermaid-svg-GHZLW04pfMLgFX0p div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-GHZLW04pfMLgFX0p :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;} 简单场景 原生注解 动态切换 AbstractRoutingDataSource 企业级需求 dynamic-datasource

🔄 三、动态数据源切换原理

💡 核心架构

#mermaid-svg-lE3aBmXFpe7UF7pF {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-lE3aBmXFpe7UF7pF .error-icon{fill:#552222;}#mermaid-svg-lE3aBmXFpe7UF7pF .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-lE3aBmXFpe7UF7pF .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-lE3aBmXFpe7UF7pF .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-lE3aBmXFpe7UF7pF .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-lE3aBmXFpe7UF7pF .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-lE3aBmXFpe7UF7pF .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-lE3aBmXFpe7UF7pF .marker{fill:#333333;stroke:#333333;}#mermaid-svg-lE3aBmXFpe7UF7pF .marker.cross{stroke:#333333;}#mermaid-svg-lE3aBmXFpe7UF7pF svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-lE3aBmXFpe7UF7pF .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-lE3aBmXFpe7UF7pF text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-lE3aBmXFpe7UF7pF .actor-line{stroke:grey;}#mermaid-svg-lE3aBmXFpe7UF7pF .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-lE3aBmXFpe7UF7pF .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-lE3aBmXFpe7UF7pF #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-lE3aBmXFpe7UF7pF .sequenceNumber{fill:white;}#mermaid-svg-lE3aBmXFpe7UF7pF #sequencenumber{fill:#333;}#mermaid-svg-lE3aBmXFpe7UF7pF #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-lE3aBmXFpe7UF7pF .messageText{fill:#333;stroke:#333;}#mermaid-svg-lE3aBmXFpe7UF7pF .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-lE3aBmXFpe7UF7pF .labelText,#mermaid-svg-lE3aBmXFpe7UF7pF .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-lE3aBmXFpe7UF7pF .loopText,#mermaid-svg-lE3aBmXFpe7UF7pF .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-lE3aBmXFpe7UF7pF .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-lE3aBmXFpe7UF7pF .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-lE3aBmXFpe7UF7pF .noteText,#mermaid-svg-lE3aBmXFpe7UF7pF .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-lE3aBmXFpe7UF7pF .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-lE3aBmXFpe7UF7pF .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-lE3aBmXFpe7UF7pF .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-lE3aBmXFpe7UF7pF .actorPopupMenu{position:absolute;}#mermaid-svg-lE3aBmXFpe7UF7pF .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-lE3aBmXFpe7UF7pF .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-lE3aBmXFpe7UF7pF .actor-man circle,#mermaid-svg-lE3aBmXFpe7UF7pF line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-lE3aBmXFpe7UF7pF :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;} Client AOP ThreadLocal RoutingDS DB 调用@DS注解方法 设置数据源key 执行方法 获取当前key 返回key 路由到目标数据源 返回结果 清除key Client AOP ThreadLocal RoutingDS DB

⚙️ 核心代码实现

// 1. 继承AbstractRoutingDataSourcepublic class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceHolder.getDataSourceKey(); }}// 2. 数据源上下文持有器public class DataSourceHolder { private static final ThreadLocal<String> CONTEXT = new ThreadLocal<>(); public static void setDataSourceKey(String key) { CONTEXT.set(key); } public static String getDataSourceKey() { return CONTEXT.get(); } public static void clear() { CONTEXT.remove(); }}// 3. 自定义注解@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface DS { String value() default \"master\";}// 4. AOP切面@Aspect@Componentpublic class DSAspect { @Around(\"@annotation(ds)\") public Object around(ProceedingJoinPoint pjp, DS ds) throws Throwable { String oldKey = DataSourceHolder.getDataSourceKey(); DataSourceHolder.setDataSourceKey(ds.value()); try { return pjp.proceed(); } finally { DataSourceHolder.setDataSourceKey(oldKey); } }}

🚀 使用示例

@Servicepublic class UserService { // 使用主库 @DS(\"master\") public void createUser(User user) { userMapper.insert(user); } // 使用从库 @DS(\"slave\") public User getUser(Long id) { return userMapper.selectById(id); }}

🧩 四、多数据源事务管理

💣 单事务管理器问题

@Servicepublic class OrderService { // 跨数据源操作将失效! @Transactional public void createOrder(Order order) { orderMapper.insert(order); // 订单库 userMapper.updatePoints(order.getUserId()); // 用户库 }}

💡 解决方案一:独立事务管理器

// 配置主库事务管理器@Beanpublic PlatformTransactionManager masterTxManager(DataSource masterDataSource) { return new DataSourceTransactionManager(masterDataSource);}// 配置从库事务管理器@Beanpublic PlatformTransactionManager slaveTxManager(DataSource slaveDataSource) { return new DataSourceTransactionManager(slaveDataSource);}// 使用指定事务管理器@Servicepublic class UserService { @Transactional(transactionManager = \"masterTxManager\") public void updateUser(User user) { // ... }}

💡 解决方案二:分布式事务

// 使用Seata分布式事务@GlobalTransactionalpublic void crossDbOperation() { serviceA.update(); serviceB.update();}

⚠️ 事务管理最佳实践

1.​​避免跨库事务​​​​:尽量在单个数据源内完成事务
2.​​​​补偿机制​​​​:无法避免时实现最终一致性
3.​​​​超时控制​​​​:设置合理的事务超时时间
4.​​​​监控告警​​​​:实现事务失败实时告警

🚀 五、主从读写分离实战

💡 架构设计

#mermaid-svg-upCo4iVKcy9xMH70 {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-upCo4iVKcy9xMH70 .error-icon{fill:#552222;}#mermaid-svg-upCo4iVKcy9xMH70 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-upCo4iVKcy9xMH70 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-upCo4iVKcy9xMH70 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-upCo4iVKcy9xMH70 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-upCo4iVKcy9xMH70 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-upCo4iVKcy9xMH70 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-upCo4iVKcy9xMH70 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-upCo4iVKcy9xMH70 .marker.cross{stroke:#333333;}#mermaid-svg-upCo4iVKcy9xMH70 svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-upCo4iVKcy9xMH70 .label{font-family:\"trebuchet ms\",verdana,arial,sans-serif;color:#333;}#mermaid-svg-upCo4iVKcy9xMH70 .cluster-label text{fill:#333;}#mermaid-svg-upCo4iVKcy9xMH70 .cluster-label span{color:#333;}#mermaid-svg-upCo4iVKcy9xMH70 .label text,#mermaid-svg-upCo4iVKcy9xMH70 span{fill:#333;color:#333;}#mermaid-svg-upCo4iVKcy9xMH70 .node rect,#mermaid-svg-upCo4iVKcy9xMH70 .node circle,#mermaid-svg-upCo4iVKcy9xMH70 .node ellipse,#mermaid-svg-upCo4iVKcy9xMH70 .node polygon,#mermaid-svg-upCo4iVKcy9xMH70 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-upCo4iVKcy9xMH70 .node .label{text-align:center;}#mermaid-svg-upCo4iVKcy9xMH70 .node.clickable{cursor:pointer;}#mermaid-svg-upCo4iVKcy9xMH70 .arrowheadPath{fill:#333333;}#mermaid-svg-upCo4iVKcy9xMH70 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-upCo4iVKcy9xMH70 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-upCo4iVKcy9xMH70 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-upCo4iVKcy9xMH70 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-upCo4iVKcy9xMH70 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-upCo4iVKcy9xMH70 .cluster text{fill:#333;}#mermaid-svg-upCo4iVKcy9xMH70 .cluster span{color:#333;}#mermaid-svg-upCo4iVKcy9xMH70 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-upCo4iVKcy9xMH70 :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;} 同步 同步 同步 应用 主库 从库1 从库2 从库3

⚙️ 配置示例

spring: datasource: master: url: jdbc:mysql://master:3306/db username: root password: master_pwd driver-class-name: com.mysql.cj.jdbc.Driver slave1: url: jdbc:mysql://slave1:3306/db username: read_user password: read_pwd driver-class-name: com.mysql.cj.jdbc.Driver slave2: url: jdbc:mysql://slave2:3306/db username: read_user password: read_pwd driver-class-name: com.mysql.cj.jdbc.Driver

🔧 动态路由配置

@Configurationpublic class DataSourceConfig { @Bean @ConfigurationProperties(\"spring.datasource.master\") public DataSource masterDataSource() { return DruidDataSourceBuilder.create().build(); } @Bean @ConfigurationProperties(\"spring.datasource.slave1\") public DataSource slave1DataSource() { return DruidDataSourceBuilder.create().build(); } @Bean public DataSource routingDataSource( @Qualifier(\"masterDataSource\") DataSource master, @Qualifier(\"slave1DataSource\") DataSource slave1) { Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put(\"master\", master); targetDataSources.put(\"slave1\", slave1); DynamicDataSource ds = new DynamicDataSource(); ds.setDefaultTargetDataSource(master); ds.setTargetDataSources(targetDataSources); return ds; }}

⚡️ 读写分离策略

// 读操作切面@Aspect@Componentpublic class ReadOnlyAspect { @Around(\"@annotation(org.springframework.transaction.annotation.Transactional)\") public Object around(ProceedingJoinPoint pjp) throws Throwable { TransactionDefinition td = ((MethodInvocationProceedingJoinPoint) pjp).getTransactionAttribute(); if (td != null && td.isReadOnly()) { DataSourceHolder.setDataSourceKey(\"slave\"); } try { return pjp.proceed(); } finally { DataSourceHolder.clear(); } }}

🧪 六、整合MyBatis-Plus多数据源

💡 官方推荐方案

<dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>3.5.1</version></dependency>

⚙️ 配置示例

spring: datasource: dynamic: primary: master strict: true datasource: master: url: jdbc:mysql://master:3306/db username: root password: master_pwd slave: url: jdbc:mysql://slave:3306/db username: read_user password: read_pwd

🚀 注解使用

@Servicepublic class UserServiceImpl extends ServiceImpl<UserMapper, User> { @DS(\"slave\") // 从库查询 public User getBySlave(Long id) { return getById(id); } @DS(\"master\") // 主库写入 public void saveToMaster(User user) { save(user); }}

💎 七、最佳实践总结

🏆 核心实施步骤

​​1.数据源规划​​:按业务划分数据源边界
2.​​路由策略​​:设计合理的数据源切换规则
​​3.事务管理​​:明确事务边界与处理方案
4.​​性能优化​​:连接池配置与监控
5.​​故障隔离​​:避免跨数据源故障扩散

⚠️ 避坑指南

问题 解决方案 主从延迟​​ 关键业务强制读主库 跨库事务​​ 使用Seata分布式事务 连接泄露​​ 严格使用try-with-resources ​​配置错误​​ 多环境配置分离 ​​监控缺失 ​​ 集成Druid监控

🛠 推荐工具栈

​​1.数据源管理​​:dynamic-datasource-spring-boot-starter
2.​​分布式事务​​:Seata
3.​​连接池:Druid
4.​​监控工具​​:Prometheus + Grafana
​​5.分库分表​​:ShardingSphere

🌟 建议

在系统中,我们采用以下策略保障数据安全:
​​1.写操作​​:强制主库+分布式事务
​​2.读操作​​:从库负载均衡+失败降级主库
​​3.数据校验​​:夜间对账任务
​​4.熔断机制​​:从库故障自动切换

​​最后结语​​:多数据源架构是应对业务增长的必经之路。合理的设计能显著提升系统性能与可用性,但需警惕过度设计带来的复杂度。记住:​​技术服务于业务,而非相反​​!