MySQL讀寫分離

1. 複製安裝好的3380文件夾到3381java

 

 

2. 進入3381\logs目錄下將全部文件刪除mysql

3. 進入3381\data目錄,將全部的log文件刪除(注意不是全部文件哦!)spring

4. 打開 my.ini 文件,將全部的 3380 替換爲 3381sql

5. 修改serverid爲81數據庫

6. 打開cmd,進入C:\mysql-5.6\3381\bin目錄,執行以下命令:express

7. .\mysqld.exe install MySQL-3381 --defaults-file="C:\mysql-5.6\3381\data\my.ini" 注意改成本身的配置文件路徑apache

8. 進入系統服務查看windows

9. 修改啓動方式爲手動,安裝完成。 如需刪除服務執行以下命名便可: .\mysqld.exe remove MySQL-3381安全

1. MySQL主從複製mybatis

1.1. 原理

mysql主(稱master)從(稱slave)複製的原理:

  1. master將數據改變記錄到二進制日誌(binary log)中,也便是配置文件log-bin指定的文件(這些記錄叫作二進制日誌事件,binary log events)

  2. slave將master的binary log events拷貝到它的中繼日誌(relay log)

  3. slave重作中繼日誌中的事件,將改變反映它本身的數據(數據重演)

1.2. 主從配置須要注意的地方

  1. 主DB server和從DB server數據庫的版本一致

  2. 主DB server和從DB server數據庫數據一致[ 這裏就會能夠把主的備份在從上還原,也能夠直接將主的數據目錄拷貝到從的相應數據目錄]

  3. 主DB server開啓二進制日誌,主DB server和從DB server的server_id都必須惟一

1.3. 主庫配置(windows,Linux下也相似)

在my.ini修改:

  #開啓主從複製,主庫的配置

  log-bin = mysql3306-bin

  #指定主庫

  serverid server-id=101

  #指定同步的數據庫,若是不指定則同步所有數據庫

   binlog-do-db=mybatis_1128

  執行SQL語句查詢狀態:

  SHOW MASTER STATUS

    

   須要記錄下Position值,須要在從庫中設置同步起始值。

1.4. 在主庫建立同步用戶

  #受權用戶slave01使用123456密碼登陸mysql

  grant replication slave on *.* to 'slave01'@'127.0.0.1' identified by '123456';

   flush privileges;

1.5. 從庫配置 在my.ini修改:

   #指定serverid,只要不重複便可,從庫也只有這一個配置,其餘都在SQL語句中操做

  server-id=102

  如下執行SQL:

  CHANGE MASTER TO

  master_host='127.0.0.1',

  master_user='slave01',

   master_password='123456',

  master_port=3306,

  master_log_file='mysql3306-bin.000006',

  master_log_pos=1120; #啓動slave同步 START SLAVE;

  #查看同步狀態

  SHOW SLAVE STATUS;

不同查看從庫錯誤日誌(E:\mysql\3381\logs) uuid設置:E:\mysql\3381\data\data)下的auto-cnf

最後附上mysql安裝包地址:https://pan.baidu.com/s/17XPg4hX09b-dzShC7aqxgA

 

代碼實現:

 

1.1. DynamicDataSource
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
 
/**
 * 定義動態數據源,實現經過集成Spring提供的AbstractRoutingDataSource,只須要實現determineCurrentLookupKey方法便可
 *
 * 因爲DynamicDataSource是單例的,線程不安全的,因此採用ThreadLocal保證線程安全,由DynamicDataSourceHolder完成。
 *
 * @author zhijun
 *
 */
public class DynamicDataSource extends AbstractRoutingDataSource{
 
    @Override
    protected Object determineCurrentLookupKey() {
        // 使用DynamicDataSourceHolder保證線程安全,而且獲得當前線程中的數據源key
        return DynamicDataSourceHolder.getDataSourceKey();
    }
 
}
1.2. DynamicDataSourceHolder
/**
 *
 * 使用ThreadLocal技術來記錄當前線程中的數據源的key
 *
 * @author zhijun
 *
 */
public class DynamicDataSourceHolder {
   
    //寫庫對應的數據源key
    private static final String MASTER = "master";
 
    //讀庫對應的數據源key
    private static final String SLAVE = "slave";
   
    //使用ThreadLocal記錄當前線程的數據源key
    private static final ThreadLocal<String> holder = new ThreadLocal<String>();
 
    /**
     * 設置數據源key
     * @param key
     */
    public static void putDataSourceKey(String key) {
        holder.set(key);
    }
 
    /**
     * 獲取數據源key
     * @return
     */
    public static String getDataSourceKey() {
        return holder.get();
    }
   
    /**
     * 標記寫庫
     */
    public static void markMaster(){
        putDataSourceKey(MASTER);
    }
   
    /**
     * 標記讀庫
     */
    public static void markSlave(){
        putDataSourceKey(SLAVE);
    }
 
}
1.3. DataSourceAspect
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
 
/**
 * 定義數據源的AOP切面,經過該Service的方法名判斷是應該走讀庫仍是寫庫
 *
 * @author zhijun
 *
 */
public class DataSourceAspect {
 
    /**
     * 在進入Service方法以前執行
     *
     * @param point 切面對象
     */
    public void before(JoinPoint point) {
        // 獲取到當前執行的方法名
        String methodName = point.getSignature().getName();
        if (isSlave(methodName)) {
            // 標記爲讀庫
            DynamicDataSourceHolder.markSlave();
        } else {
            // 標記爲寫庫
            DynamicDataSourceHolder.markMaster();
        }
    }
 
    /**
     * 判斷是否爲讀庫
     *
     * @param methodName
     * @return
     */
    private Boolean isSlave(String methodName) {
        // 方法名以query、find、get開頭的方法名走從庫
        return StringUtils.startsWithAny(methodName, "query", "find", "get");
    }
 
}
 
1.4. 配置2個數據源
1.4.1.   jdbc.properties
jdbc.master.driver=com.mysql.jdbc.Driver
jdbc.master.url=jdbc:mysql://127.0.0.1:3306/mybatis_1128?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
jdbc.master.username=root
jdbc.master.password=123456
 
 
jdbc.slave01.driver=com.mysql.jdbc.Driver
jdbc.slave01.url=jdbc:mysql://127.0.0.1:3307/mybatis_1128?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
jdbc.slave01.username=root
jdbc.slave01.password=123456
1.4.2.   定義鏈接池
    <!-- 配置鏈接池 -->
    <bean id="masterDataSource" class="com.jolbox.bonecp.BoneCPDataSource"
       destroy-method="close">
       <!-- 數據庫驅動 -->
       <property name="driverClass" value="${jdbc.master.driver}" />
       <!-- 相應驅動的jdbcUrl -->
       <property name="jdbcUrl" value="${jdbc.master.url}" />
       <!-- 數據庫的用戶名 -->
       <property name="username" value="${jdbc.master.username}" />
       <!-- 數據庫的密碼 -->
       <property name="password" value="${jdbc.master.password}" />
       <!-- 檢查數據庫鏈接池中空閒鏈接的間隔時間,單位是分,默認值:240,若是要取消則設置爲0 -->
       <property name="idleConnectionTestPeriod" value="60" />
       <!-- 鏈接池中未使用的連接最大存活時間,單位是分,默認值:60,若是要永遠存活設置爲0 -->
       <property name="idleMaxAge" value="30" />
       <!-- 每一個分區最大的鏈接數 -->
       <property name="maxConnectionsPerPartition" value="150" />
       <!-- 每一個分區最小的鏈接數 -->
       <property name="minConnectionsPerPartition" value="5" />
    </bean>
   
    <!-- 配置鏈接池 -->
    <bean id="slave01DataSource" class="com.jolbox.bonecp.BoneCPDataSource"
       destroy-method="close">
       <!-- 數據庫驅動 -->
       <property name="driverClass" value="${jdbc.slave01.driver}" />
       <!-- 相應驅動的jdbcUrl -->
       <property name="jdbcUrl" value="${jdbc.slave01.url}" />
       <!-- 數據庫的用戶名 -->
       <property name="username" value="${jdbc.slave01.username}" />
       <!-- 數據庫的密碼 -->
       <property name="password" value="${jdbc.slave01.password}" />
       <!-- 檢查數據庫鏈接池中空閒鏈接的間隔時間,單位是分,默認值:240,若是要取消則設置爲0 -->
       <property name="idleConnectionTestPeriod" value="60" />
       <!-- 鏈接池中未使用的連接最大存活時間,單位是分,默認值:60,若是要永遠存活設置爲0 -->
       <property name="idleMaxAge" value="30" />
       <!-- 每一個分區最大的鏈接數 -->
       <property name="maxConnectionsPerPartition" value="150" />
       <!-- 每一個分區最小的鏈接數 -->
       <property name="minConnectionsPerPartition" value="5" />
    </bean>
1.4.3.   定義DataSource
    <!-- 定義數據源,使用本身實現的數據源 -->
    <bean id="dataSource" class="cn.itcast.usermanage.spring.DynamicDataSource">
       <!-- 設置多個數據源 -->
       <property name="targetDataSources">
           <map key-type="java.lang.String">
              <!-- 這個key須要和程序中的key一致 -->
              <entry key="master" value-ref="masterDataSource"/>
              <entry key="slave" value-ref="slave01DataSource"/>
           </map>
       </property>
       <!-- 設置默認的數據源,這裏默認走寫庫 -->
       <property name="defaultTargetDataSource" ref="masterDataSource"/>
    </bean>
1.5. 配置事務管理以及動態切換數據源切面
1.5.1.   定義事務管理器
    <!-- 定義事務管理器 -->
    <bean id="transactionManager"
       class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
       <property name="dataSource" ref="dataSource" />
    </bean>
1.5.2.   定義事務策略
    <!-- 定義事務策略 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
       <tx:attributes>
           <!--定義查詢方法都是隻讀的 -->
           <tx:method name="query*" read-only="true" />
           <tx:method name="find*" read-only="true" />
           <tx:method name="get*" read-only="true" />
 
           <!-- 主庫執行操做,事務傳播行爲定義爲默認行爲 -->
           <tx:method name="save*" propagation="REQUIRED" />
           <tx:method name="update*" propagation="REQUIRED" />
           <tx:method name="delete*" propagation="REQUIRED" />
 
           <!--其餘方法使用默認事務策略 -->
           <tx:method name="*" />
       </tx:attributes>
    </tx:advice>
1.5.3.   定義切面
    <!-- 定義AOP切面處理器 -->
    <bean class="cn.itcast.usermanage.spring.DataSourceAspect" id="dataSourceAspect" />
 
    <aop:config>
       <!-- 定義切面,全部的service的全部方法 -->
       <aop:pointcut id="txPointcut" expression="execution(* xx.xxx.xxxxxxx.service.*.*(..))" />
       <!-- 應用事務策略到Service切面 -->
       <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
      
       <!-- 將切面應用到自定義的切面處理器上,-9999保證該切面優先級最高執行 -->
       <aop:aspect ref="dataSourceAspect" order="-9999">
           <aop:before method="before" pointcut-ref="txPointcut" />
       </aop:aspect>
    </aop:config>1.1. DynamicDataSource
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
 
/**
 * 定義動態數據源,實現經過集成Spring提供的AbstractRoutingDataSource,只須要實現determineCurrentLookupKey方法便可
 *
 * 因爲DynamicDataSource是單例的,線程不安全的,因此採用ThreadLocal保證線程安全,由DynamicDataSourceHolder完成。
 *
 * @author zhijun
 *
 */
public class DynamicDataSource extends AbstractRoutingDataSource{
 
    @Override
    protected Object determineCurrentLookupKey() {
        // 使用DynamicDataSourceHolder保證線程安全,而且獲得當前線程中的數據源key
        return DynamicDataSourceHolder.getDataSourceKey();
    }
 
}
1.2. DynamicDataSourceHolder
/**
 *
 * 使用ThreadLocal技術來記錄當前線程中的數據源的key
 *
 * @author zhijun
 *
 */
public class DynamicDataSourceHolder {
   
    //寫庫對應的數據源key
    private static final String MASTER = "master";
 
    //讀庫對應的數據源key
    private static final String SLAVE = "slave";
   
    //使用ThreadLocal記錄當前線程的數據源key
    private static final ThreadLocal<String> holder = new ThreadLocal<String>();
 
    /**
     * 設置數據源key
     * @param key
     */
    public static void putDataSourceKey(String key) {
        holder.set(key);
    }
 
    /**
     * 獲取數據源key
     * @return
     */
    public static String getDataSourceKey() {
        return holder.get();
    }
   
    /**
     * 標記寫庫
     */
    public static void markMaster(){
        putDataSourceKey(MASTER);
    }
   
    /**
     * 標記讀庫
     */
    public static void markSlave(){
        putDataSourceKey(SLAVE);
    }
 
}
1.3. DataSourceAspect
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
 
/**
 * 定義數據源的AOP切面,經過該Service的方法名判斷是應該走讀庫仍是寫庫
 *
 * @author zhijun
 *
 */
public class DataSourceAspect {
 
    /**
     * 在進入Service方法以前執行
     *
     * @param point 切面對象
     */
    public void before(JoinPoint point) {
        // 獲取到當前執行的方法名
        String methodName = point.getSignature().getName();
        if (isSlave(methodName)) {
            // 標記爲讀庫
            DynamicDataSourceHolder.markSlave();
        } else {
            // 標記爲寫庫
            DynamicDataSourceHolder.markMaster();
        }
    }
 
    /**
     * 判斷是否爲讀庫
     *
     * @param methodName
     * @return
     */
    private Boolean isSlave(String methodName) {
        // 方法名以query、find、get開頭的方法名走從庫
        return StringUtils.startsWithAny(methodName, "query", "find", "get");
    }
 
}
 
1.4. 配置2個數據源
1.4.1.   jdbc.properties
jdbc.master.driver=com.mysql.jdbc.Driver
jdbc.master.url=jdbc:mysql://127.0.0.1:3306/mybatis_1128?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
jdbc.master.username=root
jdbc.master.password=123456
 
 
jdbc.slave01.driver=com.mysql.jdbc.Driver
jdbc.slave01.url=jdbc:mysql://127.0.0.1:3307/mybatis_1128?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
jdbc.slave01.username=root
jdbc.slave01.password=123456
1.4.2.   定義鏈接池
    <!-- 配置鏈接池 -->
    <bean id="masterDataSource" class="com.jolbox.bonecp.BoneCPDataSource"
       destroy-method="close">
       <!-- 數據庫驅動 -->
       <property name="driverClass" value="${jdbc.master.driver}" />
       <!-- 相應驅動的jdbcUrl -->
       <property name="jdbcUrl" value="${jdbc.master.url}" />
       <!-- 數據庫的用戶名 -->
       <property name="username" value="${jdbc.master.username}" />
       <!-- 數據庫的密碼 -->
       <property name="password" value="${jdbc.master.password}" />
       <!-- 檢查數據庫鏈接池中空閒鏈接的間隔時間,單位是分,默認值:240,若是要取消則設置爲0 -->
       <property name="idleConnectionTestPeriod" value="60" />
       <!-- 鏈接池中未使用的連接最大存活時間,單位是分,默認值:60,若是要永遠存活設置爲0 -->
       <property name="idleMaxAge" value="30" />
       <!-- 每一個分區最大的鏈接數 -->
       <property name="maxConnectionsPerPartition" value="150" />
       <!-- 每一個分區最小的鏈接數 -->
       <property name="minConnectionsPerPartition" value="5" />
    </bean>
   
    <!-- 配置鏈接池 -->
    <bean id="slave01DataSource" class="com.jolbox.bonecp.BoneCPDataSource"
       destroy-method="close">
       <!-- 數據庫驅動 -->
       <property name="driverClass" value="${jdbc.slave01.driver}" />
       <!-- 相應驅動的jdbcUrl -->
       <property name="jdbcUrl" value="${jdbc.slave01.url}" />
       <!-- 數據庫的用戶名 -->
       <property name="username" value="${jdbc.slave01.username}" />
       <!-- 數據庫的密碼 -->
       <property name="password" value="${jdbc.slave01.password}" />
       <!-- 檢查數據庫鏈接池中空閒鏈接的間隔時間,單位是分,默認值:240,若是要取消則設置爲0 -->
       <property name="idleConnectionTestPeriod" value="60" />
       <!-- 鏈接池中未使用的連接最大存活時間,單位是分,默認值:60,若是要永遠存活設置爲0 -->
       <property name="idleMaxAge" value="30" />
       <!-- 每一個分區最大的鏈接數 -->
       <property name="maxConnectionsPerPartition" value="150" />
       <!-- 每一個分區最小的鏈接數 -->
       <property name="minConnectionsPerPartition" value="5" />
    </bean>
1.4.3.   定義DataSource
    <!-- 定義數據源,使用本身實現的數據源 -->
    <bean id="dataSource" class="cn.itcast.usermanage.spring.DynamicDataSource">
       <!-- 設置多個數據源 -->
       <property name="targetDataSources">
           <map key-type="java.lang.String">
              <!-- 這個key須要和程序中的key一致 -->
              <entry key="master" value-ref="masterDataSource"/>
              <entry key="slave" value-ref="slave01DataSource"/>
           </map>
       </property>
       <!-- 設置默認的數據源,這裏默認走寫庫 -->
       <property name="defaultTargetDataSource" ref="masterDataSource"/>
    </bean>
1.5. 配置事務管理以及動態切換數據源切面
1.5.1.   定義事務管理器
    <!-- 定義事務管理器 -->
    <bean id="transactionManager"
       class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
       <property name="dataSource" ref="dataSource" />
    </bean>
1.5.2.   定義事務策略
    <!-- 定義事務策略 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
       <tx:attributes>
           <!--定義查詢方法都是隻讀的 -->
           <tx:method name="query*" read-only="true" />
           <tx:method name="find*" read-only="true" />
           <tx:method name="get*" read-only="true" />
 
           <!-- 主庫執行操做,事務傳播行爲定義爲默認行爲 -->
           <tx:method name="save*" propagation="REQUIRED" />
           <tx:method name="update*" propagation="REQUIRED" />
           <tx:method name="delete*" propagation="REQUIRED" />
 
           <!--其餘方法使用默認事務策略 -->
           <tx:method name="*" />
       </tx:attributes>
    </tx:advice>
1.5.3.   定義切面
    <!-- 定義AOP切面處理器 -->
    <bean class="cn.itcast.usermanage.spring.DataSourceAspect" id="dataSourceAspect" />
 
    <aop:config>
       <!-- 定義切面,全部的service的全部方法 -->
       <aop:pointcut id="txPointcut" expression="execution(* xx.xxx.xxxxxxx.service.*.*(..))" />
       <!-- 應用事務策略到Service切面 -->
       <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
      
       <!-- 將切面應用到自定義的切面處理器上,-9999保證該切面優先級最高執行 -->
       <aop:aspect ref="dataSourceAspect" order="-9999">
           <aop:before method="before" pointcut-ref="txPointcut" />
       </aop:aspect>
    </aop:config>
相關文章
相關標籤/搜索