設置數據庫主從切換的原因:數據庫中經常發生的是“讀多寫少”,這樣讀操作對數據庫壓力比較大,通過采用數據庫集群方案,
一個數據庫是主庫,負責寫;其他為從庫,負責讀,從而實現讀寫分離增大數據庫的容錯率。
那么,對數據庫的要求是:
1. 讀庫和寫庫的數據一致;
2. 寫數據必須寫到寫庫;
3. 讀數據必須到讀庫;
Spring AOP實現Mysql數據庫主從切換的過程:在進入Service之前,使用AOP來做出判斷,是使用寫庫還是讀庫,判斷依據可以根據方法名判斷,比如說以"update", "insert", "delete", "save"開頭的就走寫庫,其他的走讀庫。
實現主從(一主多從)分離:
首先配置主從數據庫
主程序啟動過程中,通過配置文件將主從數據庫的URL以及用戶名密碼加載到內存中:
AOP切入點的實現:
保證每個線程用到的是自己的數據源,使用ThreadLocal來防止並發帶來的問題:
對數據庫的主從切換進行調試,最好是分別使用SELECT,UPDATE來調試:
下面的代碼是一主多從配置文件的詳細解釋,而與一主一從的區別在於,一主一從的配置都在數據源配置中完成:
參考鏈接:https://blog.csdn.net/zbw18297786698/article/details/54343188
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--聲明:各DataSource的name 及 MapperScannerConfigurer的name,不要隨意更改,否則會影響AOP的讀寫分離正常使用--> <bean id="parentDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="driverClassName" value="${jdbc-driver}"/> <property name="url" value="${jdbc-url-restaurant}"/> <property name="username" value="${jdbc-user-restaurant}"/> <property name="password" value="${jdbc-password-restaurant}"/> <property name="filters" value="stat"/> <!-- 連接池最大數量 --> <property name="maxActive" value="20"/> <!-- 初始化連接大小 --> <property name="initialSize" value="1"/> <!-- 獲取連接最大等待時間 --> <property name="maxWait" value="5000"/> <!-- 連接池最小空閑 --> <property name="minIdle" value="1"/> <property name="timeBetweenEvictionRunsMillis" value="3000"/> <property name="minEvictableIdleTimeMillis" value="180000"/> <property name="validationQuery" value="SELECT 'x' FROM DUAL"/> <property name="testWhileIdle" value="true"/> <property name="testOnBorrow" value="false"/> <property name="testOnReturn" value="false"/> <property name="poolPreparedStatements" value="false"/> <property name="maxPoolPreparedStatementPerConnectionSize" value="20"/> <!-- 超過時間限制是否回收 --> <property name="removeAbandoned" value="true"/> <!-- 超時時間;單位為秒。300秒=5分鍾 --> <property name="removeAbandonedTimeout" value="300"/> <!-- 關閉abanded連接時輸出錯誤日志 --> <property name="logAbandoned" value="true"/> <!--<property name="connectionInitSqls" value="set names utf8mb4;"/>--> </bean> <!--動態獲取數據庫--> <bean id="dsRestaurant_master" parent="parentDataSource"> <property name="url" value="${jdbc-url-restaurant}"/> <property name="username" value="${jdbc-user-restaurant}"/> <property name="password" value="${jdbc-password-restaurant}"/> </bean> <!--restaurant數據源--> <bean id="dsRestaurant" class="cn.mwee.service.shop.lookup.CustomRoutingDataSource"> <property name="targetDataSources"> <map key-type="java.lang.String"> <!--寫庫--> <entry key="master" value-ref="dsRestaurant_master"/> </map> </property> <property name="defaultTargetDataSource" ref="dsRestaurant_master"/> </bean> <!--restaurant庫--><!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 --> <bean id="sqlSessionFactoryRestaurant" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="configLocation" value="classpath:mybatis-config.xml"></property> <property name="dataSource" ref="dsRestaurant"/> <!-- 自動掃描mapping.xml文件 --> <property name="mapperLocations"> <array> <value>classpath:mybatis/restaurant/*.xml</value> </array> </property> </bean> <!-- Mapper接口所在包名,Spring會自動查找其下的類 --> <bean id="restaurantScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="cn.mwee.service.shop.mapper.restaurant"/> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryRestaurant"/> </bean> <!--事務管理--> <bean id="restaurantTxManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dsRestaurant"/> </bean> <tx:annotation-driven transaction-manager="restaurantTxManager"/> <!--AOP切面設置 --> <bean id="masterSlaveAspect" class="cn.mwee.service.shop.util.MasterSlaveAspect"/> <aop:config> <aop:aspect ref="masterSlaveAspect" order="1"> <aop:pointcut id="masterSlave" expression="this(tk.mybatis.mapper.common.Mapper)"/> <aop:before pointcut-ref="masterSlave" method="doBefore"/> </aop:aspect> </aop:config> </beans>