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

文章目录

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

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

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

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

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

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

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

💡 典型业务场景

业务需求
分库分表
读写分离
多租户隔离
用户库
订单库
日志库
主库写
从库读
租户A库
租户B库

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

💡 主流方案分析

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

🔧 方案选型建议

简单场景
原生注解
动态切换
AbstractRoutingDataSource
企业级需求
dynamic-datasource

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

💡 核心架构

ClientAOPThreadLocalRoutingDSDB调用@DS注解方法设置数据源key执行方法获取当前key返回key路由到目标数据源返回结果清除keyClientAOPThreadLocalRoutingDSDB

⚙️ 核心代码实现

// 1. 继承AbstractRoutingDataSource
public class DynamicDataSource extends AbstractRoutingDataSource {@Overrideprotected 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
@Component
public 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);}}
}

🚀 使用示例

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

🧩 四、多数据源事务管理

💣 单事务管理器问题

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

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

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

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

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

⚠️ 事务管理最佳实践

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

🚀 五、主从读写分离实战

💡 架构设计

同步
同步
同步
应用
主库
从库1
从库2
从库3

⚙️ 配置示例

spring:datasource:master:url: jdbc:mysql://master:3306/dbusername: rootpassword: master_pwddriver-class-name: com.mysql.cj.jdbc.Driverslave1:url: jdbc:mysql://slave1:3306/dbusername: read_userpassword: read_pwddriver-class-name: com.mysql.cj.jdbc.Driverslave2:url: jdbc:mysql://slave2:3306/dbusername: read_userpassword: read_pwddriver-class-name: com.mysql.cj.jdbc.Driver

🔧 动态路由配置

@Configuration
public 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();}@Beanpublic 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
@Component
public 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: masterstrict: truedatasource:master:url: jdbc:mysql://master:3306/dbusername: rootpassword: master_pwdslave:url: jdbc:mysql://slave:3306/dbusername: read_userpassword: read_pwd

🚀 注解使用

@Service
public 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.熔断机制​​:从库故障自动切换

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/90914.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/90914.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/90914.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端ApplePay支付-H5全流程实战指南

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档前言近期公司开展关于苹果支付的相关业务&#xff0c;与之前不同的是&#xff0c;以前后台直接获取第三方Wallet封装好的接口获取支付地址&#xff0c;H5页面直接跳转使用Appl…

Flink窗口:解锁流计算的秘密武器

Flink 窗口初识在大数据的世界里&#xff0c;数据源源不断地产生&#xff0c;形成了所谓的 “无限数据流”。想象一下&#xff0c;网络流量监控中&#xff0c;每一秒都有海量的数据包在网络中穿梭&#xff0c;这些数据构成了一个无始无终的流。对于这样的无限数据流&#xff0c…

Java排序算法之<希尔排序>

目录 1、希尔排序介绍 1.1、定义 1.2、核心思想 2、希尔排序的流程 第 1 轮&#xff1a;gap 4 第 2 轮&#xff1a;gap 2 第 3 轮&#xff1a;gap 1 3、希尔排序的实现 4、时间复杂度分析 5、希尔排序的优缺点 6、适用场景 前言 希尔排序&#xff08;Shell Sort&…

c++加载qml文件

这里展示了c加载qml文件的三种方式以及qml文件中根节点的访问准备在创建工程的初期&#xff0c;遇到了一个问题&#xff0c;cmake文件以前都是系统自动生成的&#xff0c;不需要我做过多的操作修改&#xff0c;但是&#xff0c;加载qml的程序主函数是需要用到QGuiApplication&a…

007TG洞察:GPT-5前瞻与AI时代竞争力构建:技术挑战与落地路径

最近&#xff0c;GPT-5 即将发布的消息刷爆了科技圈&#xff0c;更让人期待的是&#xff0c;GPT-6 已经悄悄启动训练了&#xff0c;OpenAI 的奥特曼表示对未来1-2年的模型充满信心&#xff0c;预测AI将进化为能够发现新知识的“AI科学家”。面对日益强大的通用AI&#xff0c;企…

Windows下编译OpenVDB

本文记录在Windows下编译OpenVDB的流程。 零、环境 操作系统Windows 11VS Code1.92.1Git2.34.1MSYS2msys2-x86_64-20240507Visual StudioVisual Studio Community 2022CMake3.22.1 一、编译 1.1 下载 git clone https://github.com/AcademySoftwareFoundation/openvdb.git …

react 内置hooks 详细使用场景,使用案例

useState场景&#xff1a;组件中管理局部状态&#xff0c;如表单值、开关、计数器等。const [count, setCount] useState(0); return <button onClick{() > setCount(count 1)}>Click {count}</button>;useEffect 场景&#xff1a;组件挂载时执行副作用&#…

从0到1学Pandas(九):Pandas 高级数据结构与操作

目录一、探秘多级索引1.1 创建多级索引1.2 多级索引操作1.3 索引转换二、探索 Panel 与 xarray2.1 Panel 数据结构2.2 xarray 库2.3 高维数据操作三、时间序列高级应用3.1 时区处理3.2 时间序列重采样与频率转换3.3 时间序列分解与预测四、数据透视与重塑高级技巧4.1 复杂透视表…

C# 图像转换实战:Bitmap 转 BitmapSource 的 2 种方法

C# 图像转换实战:Bitmap 转 BitmapSource 的 2 种方法 引言 两种转换方法的完整实现 1. 基于GDI句柄的直接转换 (ToBitmapSourceFast) 2. 基于内存流的编码转换 (ToBitmapSourceSafe) 方法对比与选型指南 避坑指南 GDI句柄泄漏问题 图像显示不完整 多线程访问图像引发异常 不同…

Spring Boot 整合 Spring MVC:自动配置与扩展实践

Spring MVC 作为 Java Web 开发的核心框架&#xff0c;在传统 SSM 项目中需要大量 XML 配置&#xff08;如 DispatcherServlet、视图解析器等&#xff09;。而 Spring Boot 通过 "自动配置" 特性&#xff0c;简化了 Spring MVC 的整合过程&#xff0c;同时保留了灵活…

print(“\033[31m红\033[32m绿\033[34m蓝\033[0m默认色“)

可以让python的终端字体有着不一样的颜色。代码&#xff1a;print("\033[31m红\033[32m绿\033[34m蓝\033[0m默认色")效果&#xff1a;

LNMP-zblog分布式部署

一、准备3台主机&#xff08;rocky8&#xff09;下载相应服务[rootnginx ~]# yum install -y nginx nfs-utils[rootphp ~]# yum install -y nfs-utils php-mysqlnd php php-fpm[rootmysql ~]# yum install -y mysql-server二、挂载php端[rootphp ~]# vim /etc/exports [rootphp…

常见代码八股

1. 利用梯度下降法&#xff0c;计算二次函数yx^2x4的最小值 def target_function(x):return x ** 2 x 4def gradient(x):return 2*x 1x_init 10 x x_init steps 100 lr 0.1 for i in range(100):x x - lr*gradient(x)print(f"最小值 f(x) {target_function(x):.4f…

【深入底层】C++开发简历4+4技能描述6

简历书写 熟悉C的封装、继承、多态&#xff0c;STL常用容器&#xff0c;熟悉C11的Lambda表达式、智能指针等&#xff0c;熟悉C20协程语法&#xff0c;具有良好的编码习惯与文档能力。 回答思路 这里是基本上就是要全会&#xff0c;考察的问题也很固定&#xff0c;stl这块可以定…

forest远程调用注意事项

1、如果在项目中&#xff0c;同时依赖了其中多个框架&#xff0c;那么按 Fastjson2 > Fastjson > Jackson > Gson 这样的优先级来判断&#xff0c;Forest 会以优先级最高的框架作为 JSON 转换器。2、Forest 支持哪几种 JSON 框架&#xff1f;A: 支持 Jackson、Gson、F…

网络资源模板--基于Android Studio 实现的新闻App

目录 一、测试环境说明 二、项目简介 三、项目演示 四、部设计详情&#xff08;部分) 登录页 首页 五、项目源码 一、测试环境说明 电脑环境 Windows 11 编写语言 JAVA 开发软件 Android Studio (2020) 开发软件只要大于等于测试版本即可(近几年官网直接下载也可…

通过Location API精准获取位置信息并优化定位精度!

&#x1f44b; 你好&#xff0c;欢迎来到我的博客&#xff01;我是【菜鸟不学编程】    我是一个正在奋斗中的职场码农&#xff0c;步入职场多年&#xff0c;正在从“小码农”慢慢成长为有深度、有思考的技术人。在这条不断进阶的路上&#xff0c;我决定记录下自己的学习与成…

构建可扩展的状态系统:基于 ArkTS 的模块化状态管理设计与实现

摘要 在 HarmonyOS 的日常开发中&#xff0c;很多人都会遇到一个问题&#xff1a;多个页面之间的数据状态如何共享&#xff1f;尤其是在组件结构越来越复杂的场景下&#xff0c;如果还用传统方式来传值&#xff0c;不仅代码混乱&#xff0c;维护也很吃力。 为了解决这个问题&am…

重生之我在暑假学习微服务第二天《MybatisPlus-下篇》

本系列参考黑马程序员微服务课程&#xff0c;有兴趣的可以去查看相关视频&#xff0c;本系列内容采用渐进式方式讲解微服务核心概念与实践方法&#xff0c;每日更新确保知识点的连贯性。通过系统化学习路径帮助开发者掌握分布式系统构建的关键技术。读者可通过平台订阅功能获取…

系统整理Python的条件语句和常用方法

Python 的条件语句&#xff08;if 语句&#xff09;是控制程序流程的基础之一&#xff0c;结构清晰、语法简洁&#xff0c;非常适合初学者掌握。一、基本语法结构if 条件:执行代码块1 elif 条件2:执行代码块2 else:执行代码块3示例&#xff1a;score 85if score > 90:print…