spring多数据源动态切换的实现原理及读写分离的应用 | 京东云技术团队
简介
AbstractRoutingDataSource
是Spring框架中的一个抽象类,可以实现多数据源的动态切换和路由,以满足复杂的业务需求和提高系统的性能、可扩展性、灵活性。
应用场景
- 多租户支持:对于多租户的应用,根据当前租户来选择其对应的数据源,实现租户级别的隔离和数据存储。
- 分库分表:为了提高性能和扩展性,将数据分散到多个数据库或表中,根据分片规则来选择正确的数据源,实现分库分表。
- 读写分离:为了提高数据库的读写性能,可能会采用读写分离的方式,根据读写操作的类型来选择合适的数据源,实现读写分离。
- 数据源负载均衡:根据负载均衡策略来选择合适的数据源,将请求均匀地分配到不同的数据源上,提高系统的整体性能和可伸缩性。
- 多数据库支持:在一些场景下,可能需要同时连接多个不同类型的数据库,如关系型数据库、NoSQL数据库等。根据业务需求选择不同类型的数据源,实现对多数据库的支持。
实现原理
1.AbstractRoutingDataSource
实现了DataSource
接口,作为一个数据源的封装类,负责路由数据库请求到不同的目标数据源
2.该类中定义了一个determineTargetDataSource
方法,会获取当前的目标数据源标识符,进而返回真正的数据源;
值得注意的是:其中determineCurrentLookupKey
为抽象方法,明显是要让用户自定义实现获取数据源标识的业务逻辑。
3.当系统执行数据库操作之前,会先获取数据源链接,即调用getConnection
方法,该类重写的getConnection
方法,会获取到真正的目标数据源,进而将数据库操作委托给目标数据源进行处理。
读写分离实现V1版
- yml中配置主从数据库连接信息
spring: datasource: business-master: url: jdbc:mysql://ip1:3306/xxx username: c_username password: p1 business-slaver: url: jdbc:mysql://ip2:3306/xxx username: c_username password: p2
2.读取yml中的主从数据源配置
@Data @ConfigurationProperties(prefix = "spring.datasource") @Component public class DataSourcePropertiesConfig { /** * 主库配置 */ DruidDataSource businessMaster; /** * 从库配置 */ DruidDataSource businessSlaver; }
3.自定义动态数据源类DynamicRoutingDataSource
,继承AbstractRoutingDataSource
类,并重写determineCurrentLookupKey
方法,定义获取目标数据源标识的逻辑。
此处的逻辑为:定义一个DataSourceHolder
类,将数据源标识放到ThreadLocal
中,当需要时从ThreadLocal
中获取。
public class DynamicRoutingDataSource extends AbstractRoutingDataSource { /** * 获取目标数据源标识 */ @Override protected Object determineCurrentLookupKey() { return DataSourceHolder.getDbName(); } }
public class DataSourceHolder { /** * 当前线程使用的 数据源名称 */ private static final ThreadLocal<String> THREAD_LOCAL_DB_NAME = new ThreadLocal<>(); /** * 设置数据源名称 */ public static void setDbName(String dbName) { THREAD_LOCAL_DB_NAME.set(dbName); } /** * 获取数据源名称,为空的话默认切主库 */ public static String getDbName() { String dbName = THREAD_LOCAL_DB_NAME.get(); if (StringUtils.isBlank(dbName)) { dbName = DbNameConstant.MASTER; } return dbName; } /** * 清除当前数据源名称 */ public static void clearDb() { THREAD_LOCAL_DB_NAME.remove(); } }
4.创建动态数据源DynamicRoutingDataSource
对象,并注入到容器中。这里创建了主从两个数据源,并进行了初始化,分别为其设置了数据源标识并放到了DynamicRoutingDataSource
对象中,以便后面使用。
若为多个数据源,可参考此处进行批量定义。
@Configuration public class DataSourceConfig { @Autowired private DataSourcePropertiesConfig dataSourcePropertiesConfig; /** * 主库数据源 */ public DataSource masterDataSource() throws SQLException { DruidDataSource businessDataSource = dataSourcePropertiesConfig.getBusinessMaster(); businessDataSource.init(); return businessDataSource; } /** * 从库数据源 */ public DataSource slaverDataSource() throws SQLException { DruidDataSource businessDataSource = dataSourcePropertiesConfig.getBusinessSlaver(); businessDataSource.init(); return businessDataSource; } /** * 动态数据源 */ @Bean public DynamicRoutingDataSource dynamicRoutingDataSource() throws SQLException { DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource(); Map<Object, Object> targetDataSources = new HashMap<>(2); targetDataSources.put("master", masterDataSource()); targetDataSources.put("slaver", slaverDataSource()); dynamicRoutingDataSource.setDefaultTargetDataSource(masterDS); dynamicRoutingDataSource.setTargetDataSources(targetDataSources); dynamicRoutingDataSource.afterPropertiesSet(); return dynamicRoutingDataSource; } }
5.自定义一个注解,指定数据库。
可以将一些常用的查询接口自动路由到读库,以减轻主库压力。
@Documented @Inherited @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface DataSourceSwitch { /** * 数据源名称,默认主库 */ String dbName() default "master"; }
6.定义一个切面,拦截所有Controller
接口,使用DataSourceSwitchRead
注解的方法,将统一路由到读库查询
@Aspect @Component @Slf4j public class DataSourceAspect { /** * 切库,若为多个从库,可在这里添加负载均衡策略 */ @Before(value = "execution ( * com.jd.gyh.controller.*.*(..))") public void changeDb(JoinPoint joinPoint) { Method m = ((MethodSignature) joinPoint.getSignature()).getMethod(); DataSourceSwitch dataSourceSwitch = m.getAnnotation(DataSourceSwitch.class); if (dataSourceSwitch == null) { DataSourceHolder.setDbName(DbNameConstant.MASTER); log.info("switch db dbName = master"); } else { String dbName = dataSourceSwitch.dbName(); log.info("switch db dbName = {}", dbName); DataSourceHolder.setDbName(dbName); } } }
作者:京东科技 郭艳红
来源:京东云开发者社区
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
一文详述流媒体传输网络MediaUni
一张「多元融合」的网络。 黄海宇|演讲者 大家好,我是阿里云视频云的黄海宇,今天分享主题是MediaUni——面向未来的流媒体传输网络设计与实践。 下面我将会从应用对流媒体传输网络的要求、MediaUni定位与系统架构、MediaUni技术剖析、基于MediaUni的应用落地和流媒体传输网络的未来5个方面展开介绍。 01 应对流媒体传输网络的要求 如图所示,不同场景延时从左往右依次升高。最右侧,超过30秒的延时,通常被认为是一个可传播的场景,例如视频点播,行业内会通过一张CDN点播网,服务这样的传输需求。 3~10秒的延时区间多见于直播,会使用如RTMP、HTTPFLV和HLS等协议,进行一些流式或小分片的传输,可满足3~5秒的延时需求,通常适用于弹幕互动的场景。在行业内,通常使用CDN直播网络进行这样的传输服务。 最近兴起的800毫秒到1秒左右的延时,通常可以满足体育赛事直播中的传输延时需求,例如,在世界杯直播场景中,该延时的直播让用户不再会感受到进球时刻的明显差异。另外,通过淘宝等电商场景的实践发现,该延时级别的直播,相对于3~10秒的普通直播,可以有效提升GMV。 视频会议、直播...
- 下一篇
GitOps 与 DevOps:了解关键差异,为企业做出最佳选择
在软件开发领域,GitOps 和 DevOps 是加强协作和实现软件交付流程自动化的重要技术。虽然这两种模式都旨在提高软件开发生命周期的效率,但它们的核心原则和实施方式却各不相同。 本篇文章将帮助您了解 GitOps 和 DevOps 之间的差异、它们的工作流程,并了解哪种方法更适合您的企业,以及决定采用哪种方法时要考虑的因素。通过阅读本文,您将对 GitOps 和 DevOps 的不同方面有一个清晰的了解,从而为您所在的企业做出合适的选择。 什么是 GitOps? GitOps 是一种持续交付方法,用户可以使用 Git 存储库作为应用程序代码和基础设施即代码(IaC)的单一真实源(SSOT)。它专注于部署 IaC 以及应用 DevOps 实践,例如拉取请求、分支、合并、代码审查和基础设施自动化测试。GitOps 的目的是通过使用 Git 作为管理所有部署相关工件的主要工具,实现应用程序和基础架构变更的自动化部署。 GitOps 如何运行? GitOps 的目标是像对待应用程序代码一样对待 IaC。这意味着用户可以将基础架构配置存储在 Git 存储库中,并使用与应用程序代码相同的...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS8编译安装MySQL8.0.19
- CentOS6,CentOS7官方镜像安装Oracle11G
- 2048小游戏-低调大师作品
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- Docker快速安装Oracle11G,搭建oracle11g学习环境