☰
SpringBoot动态数据源切换实战:Nacos配置中心与AbstractRoutingDataSource
2026/10/24 6:08:12 网站建设 项目流程

SpringBoot 动态数据源切换(Nacos做配置中心)

先说结论:动态数据源这块,是我在实际项目里被逼出来的经验。当时的情况是,某个系统上线后要接三个不同业务库的数据查询,但代码又不能各自维护三套连接池配置,更糟糕的是,运营还时不时要根据活动需求调整数据源的目标地址。用静态配置和硬编码切换逻辑,这几轮改下来就是噩梦。后来选定用Nacos做配置中心,配合SpringBoot的AbstractRoutingDataSource实现动态切换,才把这些问题理顺。

这篇文章适合正在做多租户、读写分离、多库路由这类需求的开发者。如果你只是单库单表,那用不上这套方案,但如果你也遇到“一个服务根据业务标识走不同库”的情况,这篇文章可以帮你少踩几个坑。

1. 整体思路拆解:为什么选动态数据源 + 配置中心

1.1 最关键的抽象:AbstractRoutingDataSource

动态数据源的实现,核心思路其实很简单。Spring的javax.sql.DataSource接口定义了一套获取连接的规范,而Spring本身就提供了一个抽象模板类AbstractRoutingDataSource,它允许你注册一个目标数据源Map,再通过determineCurrentLookupKey()方法决定当前线程该走哪个数据源。

这就好比你去综合医院挂号,前台会根据你的医保类型把你分流到不同窗口,而AbstractRoutingDataSource就是那个分流员。你只需要告诉它“当前应该用哪个库”,它就会从预先注册好的数据源列表里挑出对应的那一个。

我这里真正要解决的是三个层面的问题:

  • 数据源的注册和管理:多个库的连接参数、连接池配置不能写在死代码里,得能热更新。
  • 数据源的选择逻辑:可能根据租户ID、项目ID、接口参数里的某个字段来动态决定用哪个库。
  • 配置的一致性和下发:如果换了库的地址或密码,不可能去改代码再重启,必须走配置中心实时推送。

所以方案自然分成两块:SpringBoot侧做路由抽象,Nacos侧做配置存储和动态刷新。两边互相配合,才是一个可落地的完整方案。

1.2 为什么配置中心必须选Nacos

市面上做配置中心的有好几个选择,比如Apollo、Consul、Spring Cloud Config。这里选Nacos有几个实际考量:

第一,Nacos天然支持命名空间和数据ID的分组,你可以把数据源配置按环境、按业务线拆开,灵活性高。第二,Nacos与Spring Cloud Alibaba的整合非常顺滑,@RefreshScope和@NacosValue用起来几乎零成本。第三,部署运维成本低,Nacos自身带控制台,公司内部已经用了它来做服务发现,顺手就把配置中心也接管了。

更重要的是,Nacos支持长轮询机制,当配置修改后,客户端能在秒级感知变化并触发刷新回调。这对动态数据源来说特别关键,因为数据源里保存着连接池,你总不能让连接池里的老连接一直指向旧库地址。通过Nacos的监听事件,我们可以在配置变更时优雅地重建数据源。

1.3 切面与上下文:让切换逻辑不侵入业务代码

如果每次调用数据源之前都要手动set一个key,那业务代码会被污染得很严重。所以这里引入AOP切面,通过注解在Service或Mapper方法上声明使用的是哪个数据源。

这部分的架构如下:

  • 自定义一个@DataSource注解,里面可以指定数据源名称。
  • 用一个AOP切面拦截带有注解的方法,解析注解值,设置到DataSourceContextHolder中。
  • DataSourceContextHolder内部用ThreadLocal保存当前线程的数据源key。
  • 方法执行完后,在@After通知里清理ThreadLocal,防止内存泄漏和线程复用串号。

这样业务层不需要关心底层是什么数据源,只需要在方法上标注一个注解,维护成本就大大降低了。

2. 核心细节解析:动态数据源配置类与上下文设计

2.1 多数据源配置类的搭建

首选需要把多个数据源定义出来,没法写死,否则就不是动态了。在配置类里,我会通过Nacos的ConfigService去读取数据源JSON配置,解析后构建DruidDataSource实例。

每个数据源连接池需要设置的参数有url、username、password、driverClassName、initialSize、minIdle、maxActive等。这些参数如果全放在一个配置类里写死,那动态也就无从谈起。所以我把数据源的定义设计成一个Map结构,key是数据源标识,value是连接参数。

配置类大致长这样:

@Configuration public class DynamicDataSourceConfig { @Bean @Primary public DataSource dataSource(Map<String, DataSource> dataSourceMap) { DynamicRoutingDataSource routingDataSource = new DynamicRoutingDataSource(); routingDataSource.setTargetDataSources(new HashMap<>(dataSourceMap)); routingDataSource.setDefaultTargetDataSource(dataSourceMap.get("primary")); return routingDataSource; } }

这是最基本的版本。但注意,dataSourceMap这里指的是被Spring容器管理的所有DataSource实例。为了真正实现动态新增数据源,要把所有被Nacos管理的数据源实例也注册到这里,并维护一份动态数据源的Map在内存中。

动态新增数据源这个操作是有风险的,因为Druid连接池一旦初始化,它的某些参数就不能再改了。所以实际中我不会直接修改一个已有连接池的对象,而是先构建一个新连接池,再把新的数据源对象放入路由表中。

2.2 DataSourceContextHolder线程隔离

线程隔离这块,我强烈建议大家养成规范习惯。直接new一个ThreadLocal来存数据源key很简单,但如果你把代码交给别人维护,很容易出问题。

我的实现思路是这样的:

public class DataSourceContextHolder { private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>(); public static void setDataSource(String dataSourceKey) { CONTEXT_HOLDER.set(dataSourceKey); } public static String getDataSource() { return CONTEXT_HOLDER.get(); } public static void clear() { CONTEXT_HOLDER.remove(); } }

这里最容易被忽略的点是clear()方法里的remove(),而不是set(null)。如果使用set(null),ThreadLocal里依然残留着Entry,在线程池复用场景下可能造成数据错乱。remove()是彻底清除线程变量,这是我在生产环境排查问题后改出来的习惯。

还有一个容易踩坑的地方是异步线程。如果Service方法里开了子线程,子线程是拿不到父线程的ThreadLocal值的。这时要么把数据源key作为参数显式传递给子线程,要么在使用动态数据源前先判断上下文是否存在。

2.3 切面拦截和数据源切换顺序

切面的拦截顺序很有讲究。有人会把切面定义在Controller层,有人在Service层。我建议切在Service层,因为Controller层主要负责参数接收和响应封装,它不是真正操作数据源的边界。Service层的方法往往是事务边界,也最能体现业务意图。

举个例子,某个订单查询接口中,需要根据请求参数中的商户号来决定走哪个分库。那我会在Service查询方法上加注解:

@DataSource("merchant_" + "#merchantCode")

这里其实是SpEL表达式,可以让切面动态解析参数值。当然,为了简单稳固起见,你也可以不用SpEL,而是手动在方法内根据业务逻辑调用DataSourceContextHolder.setDataSource()。但如果可以在注解里直接解析,代码会干净不少。

切面执行顺序需要注意:如果同一个方法上既有事务注解@Transactional又有数据源注解,那么要确保数据源注解的切面优先级高于事务切面。否则事务管理器可能已经提前拿到了连接,数据源切换就不生效了。

我这边实现时给自定义切面加了@Order(0),确保它优先执行。

3. 实操过程与核心环节实现:从零搭建这套动态数据源

3.1 环境准备与依赖引入

假设你现在有一个SpringBoot项目,版本选择2.7.x,JDK 8到17都行。Nacos版本建议用2.x,因为2.x的配置长轮询和gRPC通信比1.x更稳定。

引入依赖时注意版本兼容问题。我撸过一遍比较稳定的依赖组合:

<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> <version>2021.0.1.0</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.8</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency>

注意,如果你同时引入了spring-cloud-starter-alibaba-nacos-discovery也不用担心,它和config是可以共存的。Nacos config的配置会从bootstrap.yml里读取,所以要确保引入了spring-cloud-starter-bootstrap,否则2.4版本以上的SpringBoot不会主动加载bootstrap配置。

3.2 在Nacos中配置数据源参数

Nacos服务端启动后,我们需要在控制台创建配置文件。这里有个关键点:如何组织配置的Data ID和分组。

我给每个不同环境建了不同namespace,比如dev、test、prod。每个namespace下创建配置:

Data ID:dynamic-datasource.ymlGroup:DEFAULT_GROUP配置格式: YAML

核心配置内容如下:

spring: datasource: primary: url: jdbc:mysql://127.0.0.1:3306/primary_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 123456 driverClassName: com.mysql.cj.jdbc.Driver secondary: url: jdbc:mysql://127.0.0.1:3306/secondary_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 123456 driverClassName: com.mysql.cj.jdbc.Driver

这里用primary和secondary做示例,实际上你可以根据业务维度去命名,比如order库、user库、log库。

bootstrap.yml里的配置如下:

spring: application: name: dynamic-datasource-demo cloud: nacos: config: server-addr: 127.0.0.1:8848 file-extension: yml namespace: dev-namespace-id

启动应用后,Nacos config模块会自动把spring.application.name+.yml作为Data ID去拉取配置。如果上面配置了多个数据源属性,还需要在本地代码中定义对应的@ConfigurationProperties类或直接用@Value注入。

3.3 动态路由数据源的实现

这里是整个方案最核心的代码,先撸一个自己的动态数据源路由类:

public class DynamicRoutingDataSource extends AbstractRoutingDataSource { private Map<Object, Object> dynamicTargetDataSources = new HashMap<>(); @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDataSource(); } public void addDataSource(String key, DataSource dataSource) { dynamicTargetDataSources.put(key, dataSource); super.setTargetDataSources(dynamicTargetDataSources); super.afterPropertiesSet(); } public void removeDataSource(String key) { dynamicTargetDataSources.remove(key); super.setTargetDataSources(dynamicTargetDataSources); super.afterPropertiesSet(); } }

注意两个点:第一,super.setTargetDataSources()传的Map是引用还是拷贝。如果你直接传引用,之后的修改会导致路由表内部的resolvedDataSources缓存失效。稳妥做法是传入一个新Map副本。第二,每次addDataSource或removeDataSource之后,必须调用afterPropertiesSet(),因为这一步才会把配置的targetDataSources转换成分辨后的resolvedDataSources。这个坑很隐蔽,如果不调,执行时会一直用旧路由表。

DataSource初始化这部分,要加上连接池的参数。例如Druid数据源创建时,如果配置了maxActive=20,那么在Nacos修改了连接池大小后,已存在的数据源对象不会自动生效,必须有重建过程。这也是为什么动态数据源不只是“换一个key”这么简单。

再来看一个核心问题:把Nacos中的配置转化为数据源实例。这一步是在启动时执行的,我封装了一个DataSourceFactory:

@Component public class DataSourceFactory { public DataSource createDataSource(Map<String, Object> dsConfig) { DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl((String) dsConfig.get("url")); dataSource.setUsername((String) dsConfig.get("username")); dataSource.setPassword((String) dsConfig.get("password")); dataSource.setDriverClassName((String) dsConfig.get("driverClassName")); dataSource.setInitialSize(Integer.parseInt(dsConfig.getOrDefault("initialSize", 5).toString())); dataSource.setMinIdle(Integer.parseInt(dsConfig.getOrDefault("minIdle", 5).toString())); dataSource.setMaxActive(Integer.parseInt(dsConfig.getOrDefault("maxActive", 20).toString())); return dataSource; } }

这里要提醒一个事情:Druid的密码建议用加密方式。Nacos上的配置虽然是内网访问,但为了安全,可以使用ConfigFilter或者集成Druid的WallFilter。当然这个阶段先把核心功能跑通再说。

3.4 从Nacos拉取配置并动态更新数据源

Nacos配置动态更新这部分,有两条路径。一种是用@NacosValue或@RefreshScope,但它适合配置属性的简单刷新。对于数据源这种需要重建连接池的场景,更推荐使用NacosConfigService提供的监听器方式。

我这里设计了一个监听器:

@Component public class DynamicDataSourceListener { @Autowired private DynamicRoutingDataSource routingDataSource; @Autowired private DataSourceFactory dataSourceFactory; @NacosConfigListener(dataId = "dynamic-datasource.yml", groupId = "DEFAULT_GROUP", timeout = 5000) public void onConfigChange(String config) { Map<String, Map<String, Object>> dsMap = parseConfig(config); for (Map.Entry<String, Map<String, Object>> entry : dsMap.entrySet()) { String dsKey = entry.getKey(); DataSource newDataSource = dataSourceFactory.createDataSource(entry.getValue()); routingDataSource.addDataSource(dsKey, newDataSource); } } }

这个监听器的触发逻辑是:Nacos客户端在长轮询中感知配置变更,回调onConfigChange方法。我在回调里做两件事,第一是解析最新配置,第二是重新创建数据源并注册到路由表中。

这里有个细节:旧数据源的销毁。如果不主动关闭旧连接池,那么旧连接池中的线程和连接就一直占着资源,时间久了会出现连接泄露。所以addDataSource内部最好能记录旧数据源并执行close():

public void updateDataSource(String key, DataSource newDataSource) { DataSource oldDataSource = dynamicTargetDataSources.get(key); dynamicTargetDataSources.put(key, newDataSource); super.setTargetDataSources(dynamicTargetDataSources); super.afterPropertiesSet(); if (oldDataSource != null) { closeQuietly(oldDataSource); } }

关闭旧数据源时要小心:如果当前线程池中还有遗留连接正在执行任务,直接关闭会导致执行中的SQL中断。稳妥的做法是在业务低峰期做切换,或者添加延迟关闭机制。

3.5 业务方法中使用数据源

基础工作完成后,业务使用就很简洁了。

定义一个注解:

@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface DataSource { String value() default "primary"; }

然后在切面中解析:

@Aspect @Component @Order(0) public class DataSourceAspect { @Before("@annotation(dataSource)") public void switchDataSource(JoinPoint joinPoint, DataSource dataSource) { DataSourceContextHolder.setDataSource(dataSource.value()); } @After("@annotation(dataSource)") public void restoreDataSource(DataSource dataSource) { DataSourceContextHolder.clear(); } }

到这里,整个主链路已经通了。接下来是我们在项目中经常会遇到的问题汇总。

4. 常见问题与排查技巧实录

4.1 数据源切换失败,仍然走默认库

这是出现频率最高的问题。第一个排查点就是看DataSourceContextHolder里有没有正确设置值。在切面里加了日志后,如果发现切面已经执行但路由仍失败,大概率是切面执行顺序不对。

还有一个原因是AbstractRoutingDataSource类里有个defaultTargetDataSource,如果你没有配置它,并且当前ThreadLocal的key无法匹配到已路由的数据源,就会抛异常。如果没抛异常,反而是走了默认库,那可能是determineCurrentLookupKey()返回了null,此时Spring会走默认数据源。

所以生产环境建议把默认数据源指向一个测试库或主库,原因就在这里——宁可走默认库也不要接口直接500。

4.2 事务方法内切换数据源不生效

这个坑非常经典。当一个Service方法被@Transactional修饰后,Spring会在进入方法时通过DataSourceTransactionManager获取数据库连接。默认情况下,事务管理器会从DataSource接口拿到连接,然后在事务结束后释放。如果你的事务管理器配置的是动态数据源路由类,那么没问题;但如果你某处不小心把事务管理器直接绑定到了具体的某一个DataSource,那动态切换就失效了。

解决办法就是让DataSourceTransactionManager的dataSource属性指向动态路由数据源:

@Bean public DataSourceTransactionManager transactionManager(DataSource dataSource) { return new DataSourceTransactionManager(dataSource); }

要确保这里的dataSource是DynamicRoutingDataSource这个Bean,而不是某个具体的库连接池。

另外还要注意,事务一旦开启,连接是复用同一连接的,中间切换ThreadLocal里的key并不会改变当前事务已经在用的连接。因此,事务要尽量缩小范围。

4.3 修改Nacos配置后,数据源没有被重建

这种问题一般是监听器没有生效或者配置解析失败。排查时,先在监听器回调里打印原始config字符串,看是否真的有变化推送。如果原始config没问题,那就是解析逻辑有异常。

常见的原因是Nacos配置类型不匹配。比如配置中心里存的是JSON格式但监听回调中按JSON解析,结果控制台上写入的是YAML,中间没有配置好ConfigService。所以在解析之前,要约定好格式规范,这比代码实现更需要制度保证。

如果监听器确实触发了但路由表没更新,检查addDataSource方法里的afterPropertiesSet()是否执行成功。有时候在调用addDataSource时,会因为new的数据源参数不合法而抛出异常,前一个数据源可能还在,但路由表一直是旧的。

4.4 连接池数量过多,资源占用飙升

如果每次配置更新都重建所有数据源,那连接池数量会被大量堆积。假设有10个业务库,每次配置中心任意一个库的密码变更,监听器都会全量重建10个连接池,这样不仅浪费资源,还可能导致瞬间连接数超过数据库上限。

我这里用了一个优化:在onConfigChange方法中做差异对比。先比较新配置和旧配置中该数据源的url、username、password、maxActive等关键参数是否一致,如果一致就跳过重建,只把连接池的状态保持原样。如果变化了,再单独重建该数据源。

4.5 多环境配置互相干扰

当开发、测试、生产环境共用一套Nacos时,如果没有做好Namespace隔离,经常出现开发环境改了配置,生产环境被影响的情况。

所以Namespace的划分要严格绑定到环境。每个环境有独立的Namespace ID,且在bootstrap.yml里不要写死环境参数,而是通过启动参数或环境变量注入。比如:

java -jar app.jar --spring.cloud.nacos.config.namespace=dev-namespace-id

这样就在运行时解耦了环境。否则一到上线打包本地改配置,分分钟出事故。

再补充一个运维上的建议:线上修改配置前,先去Nacos控制台查看config的发布记录,确认上次变更的时间和操作人。如果配置异常,可以快速回滚。Nacos控制台本身有这个能力,但很多团队用得少。

5. 动态数据源在真实业务中的扩展思考

5.1 多租户场景的数据隔离方案

动态数据源最常见的落地场景就是多租户SaaS系统。我接触过的某项目中,每个租户对应一个独立的数据库,数据库表结构完全一致。这种情况下,根据当前登录租户ID动态路由到对应的库非常合适。

这个场景下不能简单用@DataSource("xxx")这种静态注解,而是要动态获取租户ID。比如在拦截器中从请求token里解析出租户ID,然后DataSourceContextHolder.setDataSource("tenant_" + tenantId)。

实现时需要检查租户ID是否存在、租户配置是否已经在路由表中。如果新租户注册后还没建库或配置未同步,则需要有自动建表和初始化数据源的机制。

多租户动态数据源还有一个性能考量:如果租户数量巨大(比如上万个),那内存里维护每个租户的连接池会非常恐怖。这时要考虑连接池共享方案,比如根据数据库实例和库名组合去复用连接池,或者引入分库分表中间件。

5.2 读写分离与主从延迟问题

动态数据源也很适合做读写分离。将读请求路由到从库,写请求路由到主库。这个场景下,难点不是切换本身,而是主从延迟。

用户写入主库后立刻读,有可能从库里查不到最新数据。假如你实现了自动读写分离,但没处理延迟,那就会出现非常诡异的bug。推荐的做法是把强制走主库的读操作加上@DataSource("master")注解,或者把写操作之后的读操作放在同一个方法中并开启事务强制走主库。

我在项目中是这样处理的:写操作后的读操作直接放在Service方法里,且方法加了@Transactional(readOnly = false),保证后续读连接继续绑定在主库上。这样虽然简单粗暴,但数据一致性有保障。

5.3 结合Nacos集群做高可用

最后说一下Nacos自身的稳定性。如果Nacos挂了,应用是不是就不能启动了?不是的。Nacos客户端在启动时会从远端拉取配置并缓存到本地快照文件,即使Nacos之后不可用,应用第二次启动也能从本地快照加载配置。

但如果是首次启动且Nacos不可用,那应用会启动失败。针对这个问题,可以设置spring.cloud.nacos.config.fail-fast=false,让它启动失败重试或者跳过错。另一种方式是在启动前先通过CI流程把配置生成到本地文件,作为降级方案。

对于核心生产环境,建议把Nacos做成集群模式,至少三节点,并且给客户端配置多个server-addr。确保一台机器宕机不影响配置拉取。

Nacos集群模式下,长轮询和gRPC通信会占用一定端口,部署时要提前规划好安全组和防火墙。如果公司内部对端口有管控,需要提前申请,这块很容易忽略。

6. 实战心得与调优记录

6.1 关于Druid连接池的几个参数调整建议

用Druid做连接池,在动态数据源切换过程中,最容易被忽略的是连接池内的监控统计。如果一个DataSource频繁被重建,Druid的监控面板会累积大量废弃连接池的监控数据,时间久了会占用大量内存。

建议在构造数据源时开启setRemoveAbandoned(true),并设置合理的removeAbandonedTimeout。这个参数能够及时回收长期不用的连接,避免动态更新数据源后留下残余连接。

另外一个参数是maxWait。如果数据源动态切换后,某个库连接不上,而maxWait又设得特别大,那这个请求会长时间阻塞,导致接口超时。我一般把maxWait控制在3000到5000毫秒之间。这样如果目标库出现问题,能快速失败返回错误,而不是卡住整个线程。

6.2 线程池与数据源上下文的配合

SpringBoot项目一般不会裸用线程池,但只要你用了@Async或者自己创建的线程池,就要注意线程上下文传递问题。

我在一个项目中遇到过这样的问题:主线程设置好数据源KEY之后,在Service方法内调了一个异步任务来查另一条库的数据。由于子线程无法继承ThreadLocal,异步任务获取的KEY是null,导致SQL走了默认库。

解决方式有两个方向:一是设计异步任务时,显式传入数据源KEY参数;二是自定义一个包装线程池任务类,在提交时复制父线程的上下文。第二种方案一劳永逸,但要注意清理子线程的ThreadLocal,防止子线程池内的线程被复用后上下文串掉。

我在实践中选择了包装任务的方式,自定义了DataSourceTaskDecorator,实现TaskDecorator接口,在sumbit时获取当前DataSourceContextHolder的值,然后在子线程中重新set。这比把KEY到处传参要干净很多。

6.3 常用监控指标,避免踩坑

当你上线动态数据源后,有几个指标一定要盯:

  • 当前应用持有的数据源连接数总和。如果出现持续增长,通常说明连接泄漏或旧连接池未关闭。
  • 各数据源活跃连接数。某个库的活跃数突然飙高,多半是业务流量暴涨或慢SQL拖住了连接。
  • 配置刷新失败的次数。如果Nacos监听回调经常报解析异常,配置格式问题就需要重视。

建议在代码中把每个数据源的连接数情况暴露到Metrics端点,比如集成Prometheus的Micrometer,这样就能在Grafana里看趋势。没有监控体系的动态数据源,等于裸奔。

个人体会是,动态数据源方案一旦上线,最大的受益其实是运维效率。以前改一个连接配置要发版重启,现在控制台点一下就能更新。但代价是,代码里处理边界情况的复杂度翻倍了。尤其是在事务、线程、连接池生命周期这几个交叉点上,必须每个细节都验证到位,不然线上出了诡异问题很难排查。

如果说要给你一个实用小建议,那必然是:在本地开发环境,模拟多个DataSource并用脚本频繁切换数据源配置,然后通过慢日志和日志观察连接切换的过程。我当初在一个模拟项目X上连续跑了三四天切换压测,才把动态数据源重建和旧连接池关闭的时序完全稳定下来。这个实验方式虽然笨,但效果远比直接写代码可靠。

6.4 未来这部分的扩展方向

如果你已经把这套动态数据源用稳了,接下来可以考虑两个方向:一是集成多数据源的事务协调。比如跨库操作需要分布式事务,可以尝试引入分布式事务框架,但这会增加复杂度,建议只在业务强一致需求下使用。二是把数据源映射表做成可视化页面,运维可以直接在后台界面维护租户库的地址和状态,不必直接改Nacos配置。这样就形成了完整的配置管理闭环。

我最后再提一句:动态数据源这块的方案没有什么玄学,关键是理解路由时机、连接生命周期和配置刷新这三个核心点。把这三点理顺了,剩下就是按部就班的编码。希望这篇整理能帮你少走些弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询