1. 程式人生 > >Spring AOP實現Mysql資料庫主從切換(一主多從)

Spring AOP實現Mysql資料庫主從切換(一主多從)

設定資料庫主從切換的原因:資料庫中經常發生的是“讀多寫少”,這樣讀操作對資料庫壓力比較大,通過採用資料庫叢集方案, 
一個數據庫是主庫,負責寫;其他為從庫,負責讀,從而實現讀寫分離增大資料庫的容錯率。 
那麼,對資料庫的要求是: 
1. 讀庫和寫庫的資料一致; 
2. 寫資料必須寫到寫庫; 
3. 讀資料必須到讀庫;

Spring AOP實現Mysql資料庫主從切換的過程:在進入Service之前,使用AOP來做出判斷,是使用寫庫還是讀庫,判斷依據可以根據方法名判斷,比如說以"update", "insert", "delete", "save"開頭的就走寫庫,其他的走讀庫。

image

image

 實現主從(一主多從)分離:

首先配置主從資料庫

主程式啟動過程中,通過配置檔案將主從資料庫的URL以及使用者名稱密碼載入到記憶體中:

AOP切入點的實現:

 

保證每個執行緒用到的是自己的資料來源,使用ThreadLocal來防止併發帶來的問題:

對資料庫的主從切換進行除錯,最好是分別使用SELECT,UPDATE來除錯:

下面的程式碼是一主多從配置檔案的詳細解釋,而與一主一從的區別在於,一主一從的配置都在資料來源配置中完成:

<?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>