窥探系列之多数据源切换

这篇具有很好参考价值的文章主要介绍了窥探系列之多数据源切换。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

资料

mysql中set autocommit=0与start transaction区别

事务管理

Spring通过设置autocommit 为 0来实现事务:
org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin

事务中@DS失效

绑定连接
org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin

  1. 开启事务 con.setAutoCommit(false)
  2. 绑定Connection TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
  3. 报错释放Connect DataSourceUtils.releaseConnection(con, obtainDataSource());
	protected void doBegin(Object transaction, TransactionDefinition definition) {
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
		Connection con = null;

		try {
			if (!txObject.hasConnectionHolder() ||
					txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
				Connection newCon = obtainDataSource().getConnection();
				if (logger.isDebugEnabled()) {
					logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
				}
				txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
			}

			txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
			con = txObject.getConnectionHolder().getConnection();

			Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
			txObject.setPreviousIsolationLevel(previousIsolationLevel);
			txObject.setReadOnly(definition.isReadOnly());

			// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
			// so we don't want to do it unnecessarily (for example if we've explicitly
			// configured the connection pool to set it already).
			if (con.getAutoCommit()) {
				txObject.setMustRestoreAutoCommit(true);
				if (logger.isDebugEnabled()) {
					logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
				}
				con.setAutoCommit(false);
			}

			prepareTransactionalConnection(con, definition);
			txObject.getConnectionHolder().setTransactionActive(true);

			int timeout = determineTimeout(definition);
			if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
				txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
			}

			// Bind the connection holder to the thread.
			if (txObject.isNewConnectionHolder()) {
				TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
			}
		}

		catch (Throwable ex) {
			if (txObject.isNewConnectionHolder()) {
				DataSourceUtils.releaseConnection(con, obtainDataSource());
				txObject.setConnectionHolder(null, false);
			}
			throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
		}
	}

org.springframework.transaction.support.TransactionSynchronizationManager#bindResource

	private static final ThreadLocal<Map<Object, Object>> resources =
			new NamedThreadLocal<>("Transactional resources");

	public static void bindResource(Object key, Object value) throws IllegalStateException {
		Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
		Assert.notNull(value, "Value must not be null");
		Map<Object, Object> map = resources.get();
		// set ThreadLocal Map if none found
		if (map == null) {
			map = new HashMap<>();
			resources.set(map);
		}
		Object oldValue = map.put(actualKey, value);
		// Transparently suppress a ResourceHolder that was marked as void...
		if (oldValue instanceof ResourceHolder && ((ResourceHolder) oldValue).isVoid()) {
			oldValue = null;
		}
		if (oldValue != null) {
			throw new IllegalStateException(
					"Already value [" + oldValue + "] for key [" + actualKey + "] bound to thread");
		}
	}

获取连接
org.springframework.jdbc.datasource.DataSourceUtils#doGetConnection

  1. 优先取线程绑定的连接;
  2. 否则,从DataSource里面获取(@DS的切换数据源的逻辑在这里)
	public static Connection doGetConnection(DataSource dataSource) throws SQLException {
		Assert.notNull(dataSource, "No DataSource specified");

		ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
		if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
			conHolder.requested();
			if (!conHolder.hasConnection()) {
				logger.debug("Fetching resumed JDBC Connection from DataSource");
				conHolder.setConnection(fetchConnection(dataSource));
			}
			return conHolder.getConnection();
		}
		// Else we either got no holder or an empty thread-bound holder here.

		logger.debug("Fetching JDBC Connection from DataSource");
		Connection con = fetchConnection(dataSource);
		......
		}

对话GPT

1. 事务中@DS失效

@DS注解是基于AOP实现的,它通过拦截器来切换数据源。在Spring中,AOP是通过代理模式实现的。当一个Bean被Spring容器代理后,调用该Bean的方法时,实际上是调用代理对象的方法,而不是原始对象的方法。因此,如果在事务中调用带有@DS注解的方法,实际上是调用的代理对象的方法,而不是原始对象的方法。


默认情况下,Spring的事务管理器和数据源切换器是分开的。事务管理器在事务开始时获取连接,然后将该连接绑定到当前线程上,直到事务结束后才释放连接。而数据源切换器是通过拦截器来切换数据源,它在方法执行前切换数据源,在方法执行后再切换回原始数据源。因此,在事务中使用@DS注解时,可能会出现数据源切换失效的情况,即在事务中切换数据源后,该数据源可能会被后续的操作覆盖掉,导致数据源切换失效。


为了解决这个问题,可以在事务管理器和数据源切换器之间添加一个动态数据源切换器。动态数据源切换器可以在事务开始时获取当前线程绑定的连接,然后根据连接的元数据来切换数据源。这样,即使在事务中切换数据源,动态数据源切换器也可以根据连接的元数据来切换回原始数据源,保证数据源切换的正确性。


在Spring Boot中,可以使用@Transactional注解来开启事务,并使用@DS注解来切换数据源。如果在事务中使用@DS注解时出现失效问题,可以考虑使用DynamicDataSourceTransactionManager来作为事务管理器,从而解决数据源切换失效的问题。同时,需要确保动态数据源切换器和数据源切换拦截器的顺序正确,以保证数据源切换的正确性。

2. 连接绑定到当前线程 在哪个方法中

在Spring框架中,将连接绑定到当前线程是通过TransactionSynchronizationManager类实现的。具体来说,当一个事务开启时,事务管理器会获取一个数据库连接,并将该连接和当前线程进行绑定,以便在事务管理器操作数据库时使用该连接。当事务结束后,事务管理器会释放该连接,并将该连接和当前线程的绑定解除。
在TransactionSynchronizationManager类中,与连接绑定和解绑的方法包括:

  1. bindResource(Object key, Object value)方法
    该方法用于将一个资源对象和当前线程进行绑定。在事务管理器操作数据库时,可以通过TransactionSynchronizationManager.getResource(Objectkey)方法获取该资源对象,并使用该对象进行数据库操作。在Spring中,数据库连接就是一种资源对象,可以通过DataSourceUtils.getConnection(DataSource dataSource)方法获取连接,并将连接和当前线程进行绑定。

  2. unbindResource(Object key)方法
    该方法用于将一个资源对象和当前线程的绑定解除。在事务结束后,事务管理器会调用该方法将连接和当前线程的绑定解除。

  3. getResource(Object key)方法
    该方法用于获取一个资源对象,该对象已经和当前线程进行了绑定。在Spring中,可以通过该方法获取当前线程绑定的数据库连接。
    /

需要注意的是,为了确保连接和当前线程的绑定在整个事务过程中都有效,需要使用线程安全的方式来绑定和解绑连接。在Spring中,可以使用TransactionSynchronizationManager类来实现线程安全的连接绑定和解绑。同时,还需要确保在事务结束后,将连接和当前线程的绑定解除,以避免连接泄漏和资源浪费。在Spring中,可以通过TransactionSynchronizationManager类的事务同步器机制来实现连接的自动解绑。

3. spring获取Connection会优先读取线程绑定的Connection吗

在Spring中,当从数据源中获取连接时,首先会检查当前线程中是否已经有一个绑定的连接。如果有,就会直接使用该连接,而不会重新获取一个新的连接。这个机制是通过TransactionSynchronizationManager类实现的。
/
TransactionSynchronizationManager类是Spring事务管理器的核心类,它提供了一系列静态方法,用于将对象与当前线程进行绑定,并在事务结束后自动解绑。在Spring中,获取数据库连接时,可以使用DataSourceUtils类的getConnection方法,该方法内部会优先尝试从当前线程绑定的连接中获取连接。只有在当前线程没有绑定连接时,才会从数据源中获取一个新的连接。
/
需要注意的是,获取当前线程绑定的连接,需要使用DataSourceUtils类的getConnection方法,而不是直接使用Connection对象。因为在Spring中,使用Connection对象获取数据源时,并不会检查当前线程中是否已经有一个绑定的连接,而是直接从数据源中获取一个新的连接。因此,如果在事务中直接使用Connection对象获取数据源,可能会导致连接泄漏和资源浪费。
/
综上所述,当使用Spring获取数据库连接时,会优先尝试从当前线程绑定的连接中获取连接。这个机制可以避免重复获取连接和连接泄漏,提高程序性能和资源利用率。

4. 如果线程没有绑定Connection呢

如果当前线程没有绑定连接,Spring会从数据源中获取一个新的连接。这个新的连接会在事务结束后自动关闭和释放。
/
在Spring中,获取新的连接的方式取决于使用的是哪种事务管理器。如果使用的是DataSourceTransactionManager事务管理器,那么在事务开始时,该事务管理器会获取一个新的连接,并将该连接绑定到当前线程上。在事务提交或回滚时,该连接会被自动关闭和释放,同时解绑当前线程。如果在事务过程中发生异常,该连接也会被自动关闭和释放。
/
如果使用的是JtaTransactionManager事务管理器,那么在事务开始时,该事务管理器会协调多个数据源中的事务,从而保证多个数据源之间的一致性。在这种情况下,每个数据源都会创建一个连接,并将连接绑定到当前线程上。在事务结束时,该事务管理器会将所有连接都关闭和释放,并解绑当前线程。
/
需要注意的是,在使用事务管理器时,不要手动关闭和释放连接,否则会导致事务管理器出现异常。如果需要在事务中执行一些特殊的操作(如设置事务隔离级别、设置连接属性等),可以使用TransactionCallback接口的doInTransaction方法,在事务中执行这些操作。在这种情况下,Spring会为每个事务创建一个新的连接,并在事务结束后自动关闭和释放该连接。文章来源地址https://www.toymoban.com/news/detail-487994.html

到了这里,关于窥探系列之多数据源切换的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • springboot整合多数据源的配置以及动态切换数据源,注解切换数据源

    在许多应用程序中,可能需要使用多个数据库或数据源来处理不同的业务需求。Spring Boot提供了简便的方式来配置和使用多数据源,使开发人员能够轻松处理多个数据库连接。如果你的项目中可能需要随时切换数据源的话,那我这篇文章可能能帮助到你 ℹ️:这里对于pom文件

    2024年02月10日
    浏览(54)
  • SpringBoot——动态数据源(多数据源自动切换)

    日常的业务开发项目中只会配置一套数据源,如果需要获取其他系统的数据往往是通过调用接口, 或者是通过第三方工具比如kettle将数据同步到自己的数据库中进行访问。 但是也会有需要在项目中引用多数据源的场景。比如如下场景: 自研数据迁移系统,至少需要新、老两

    2024年02月16日
    浏览(40)
  • SpringBoot从数据库读取数据数据源配置信息,动态切换数据源

            首先准备多个数据库,主库smiling-datasource,其它库test1、test2、test3         接下来,我们在主库smiling-datasource中,创建表databasesource,用于存储多数据源相关信息。表结构设计如下         创建好表之后,向表databasesource中存储test1、test2、test3三个数据库的相关配置

    2024年01月16日
    浏览(68)
  • SpringBoot动态切换数据源

      Spring提供一个DataSource实现类用于动态切换数据源—— AbstractRoutingDataSource pom.xml 大概的项目结构 注意:这两个事务管理器,并不能处理分布式事务 链接:https://pan.baidu.com/s/1ymxeKYkI-cx7b5nTQX0KWQ  提取码:6bii  --来自百度网盘超级会员V4的分享                

    2024年02月06日
    浏览(51)
  • springboot,多数据源切换

    需求介绍:         要求做一个平台,有其他第三方系统接入;每个系统有自己的数据源配置,通过调用平台接口,实现将数据保存到第三方自己的数据库中; 实现过程:         1.在平台项目运行时,通过接口获取每个第三方系统的数据源;以key-value的形式保存到全局

    2024年02月16日
    浏览(39)
  • 多数据源切换、读写分离-02

    使用dynamic进行数据源切换、读写分离 特性 1、支持数据源分组 ,适用于多种场景纯粹多库读写分离一主多从混合模式。 2、支持数据库敏感配置信息 加密 ENC()。 3、支持每个数据库独立初始化表结构schema和数据库database。 4、支持无数据源启动,支持懒加载数据源(需要的时

    2024年02月16日
    浏览(36)
  • mybatisplus快速实现动态数据源切换

    1.背景   通常一个系统只需要连接一个数据库就可以了。但是在企业应用的开发中往往会和其他子系统交互,特别是对于一些数据实时性要求比较高的数据,我们就需要做实时连接查询,而不是做同步。这个时候就需要用到多数据源。   举个简单的例子某企业要做订单网上订

    2024年02月06日
    浏览(47)
  • springboot使用DynamicDataSource来动态切换数据源

    DynamicDataSource是一个数据源路由器,可以根据上下文动态选择数据源。可以在每个请求或线程中将数据源设置为当前需要使用的数据. 创建一个 DynamicDataSource 类,它继承自 AbstractRoutingDataSource 。在该类中重写**determineCurrentLookupKey()**方法,该方法返回一个字符串,用于指示当前

    2024年02月05日
    浏览(45)
  • 实例讲解Spring boot动态切换数据源

    摘要: 本文模拟一下在主库查询订单信息查询不到的时候,切换数据源去历史库里面查询。 本文分享自华为云社区《springboot动态切换数据源》,作者:小陈没烦恼 。 在公司的系统里,由于数据量较大,所以配置了多个数据源,它会根据用户所在的地区去查询那一个数据库

    2024年02月06日
    浏览(44)
  • 一种实现Spring动态数据源切换的方法

    不在现有查询代码逻辑上做任何改动,实现dao维度的数据源切换(即表维度) 节约bdp的集群资源。接入新的宽表时,通常uat验证后就会停止集群释放资源,在对应的查询服务器uat环境时需要查询的是生产库的表数据(uat库表因为bdp实时任务停止,没有数据落入),只进行服务

    2024年02月09日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包