ShardingSphere-ShardingJdbc 讀寫分離

2020-10-26 12:01:27

一、讀寫分離背景

分庫分表雖然可以優化資料庫操作,但是要實現高並行,主從架構就應運而生了。資料庫的主從複製架構,將資料庫的寫操作定位到主庫中進行,主庫和從庫之間通過非同步複製、半同步複製保持資料一致。所有的讀操作都在主庫的N個從庫上進行。通過負載均衡使得每一次查詢均勻的落在每一個從庫上。

一主n從,做讀寫分離(資料寫入主庫,通過mysql資料同步機制將主庫資料同步到從庫–>程式讀取從庫資料),多個從庫之間可以實現負載均衡。次外,ShardingSphere-ShardingJdbc可手動強制部分讀請求到主庫上。(因為主從同步有延遲,對實時性要求高的系統,可以將部分讀請求也走主庫)
MySQL設定主從同步可參考另一篇部落格:https://blog.csdn.net/u014553029/article/details/108832268

二、讀寫分離實現

2.1 環境準備

程式環境:SpringBoot+MyBatis-plus
資料庫環境:

資料庫ip資料庫作用
127.0.0.1ShardingSpheremaster
127.0.0.1ShardingSphere1slave1
127.0.0.1ShardingSphere2slave2

2.2 新增ShardingSphere依賴

<!--shardingsphere資料分片、脫敏工具-->
<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
    <version>4.1.0</version>
</dependency>

2.3 設定讀寫分離規則

#### spring  ####
spring:
  # 設定說明地址 https://shardingsphere.apache.org/document/legacy/4.x/document/cn/manual/sharding-jdbc/configuration/config-spring-boot/#%E6%95%B0%E6%8D%AE%E5%88%86%E7%89%87
  shardingsphere:
    # 資料庫
    datasource:
      # 資料庫的別名
      names: ds0,ds1,ds2
      # 主庫1 ,master資料庫
      ds0:
        ###  資料來源類別
        type: com.alibaba.druid.pool.DruidDataSource
        driverClassName: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://146.56.192.87:3306/shardingsphere?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8
        username: oyc
        password: oyc@123456
      # 從庫1 ,slave資料庫
      ds1:
        ###  資料來源類別
        type: com.alibaba.druid.pool.DruidDataSource
        driverClassName: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://146.56.192.87:3306/shardingsphere1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8
        username: oyc
        password: oyc@123456
      # 從庫2 ,slave資料庫
      ds2:
        ###  資料來源類別
        type: com.alibaba.druid.pool.DruidDataSource
        driverClassName: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://146.56.192.87:3306/shardingsphere2?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8
        username: oyc
        password: oyc@123456

    # *** 資料庫分庫分表設定 start
    masterslave:
      # 查詢時的負載均衡演演算法,目前有2種演演算法,round_robin(輪詢)和random(隨機),
      # 演演算法介面是io.shardingjdbc.core.api.algorithm.masterslave.MasterSlaveLoadBalanceAlgorithm。
      # 實現類有RandomMasterSlaveLoadBalanceAlgorithm 和 RoundRobinMasterSlaveLoadBalanceAlgorithm。
      load-balance-algorithm-type: round_robin
      name: dataSource
      # 主資料來源名稱
      master-data-source-name: ds0
      # 從資料來源名稱,多個用逗號隔開
      slave-data-source-names: ds1,ds2
    # *** 資料庫分庫分表設定 end

    props:
      # 列印SQL
      sql.show: true
      check:
        table:
          metadata: true
          # 是否在啟動時檢查分表後設資料一致性
          enabled: true
      query:
        with:
          cipher:
            column: true

2.4 測試讀寫分離效果

(1)寫入資料,都是寫主庫:

讀寫分離效果-寫入資料

(2)讀取資料,根據規則負載從從庫選擇讀取;

讀寫分離效果-讀取資料

2.5 強制讀取主庫資料

讀延遲問題解決方案:
  剛插入一條資料,然後馬上就要去讀取,這個時候有可能會讀取不到?歸根到底是因為主節點寫入完之後資料是要複製給從節點的,讀不到的原因是複製的時間比較長,也就是說資料還沒複製到從節點,你就已經去從節點讀取了,肯定讀不到。mysql5.7 的主從複製是多執行緒了,意味著速度會變快,但是不一定能保證百分百馬上讀取到,這個問題我們可以有兩種方式解決:
  (1)業務層面妥協,是否操作完之後馬上要進行讀取
  (2)對於操作完馬上要讀出來的,且業務上不能妥協的,我們可以對於這類的讀取直接走主庫,當然Sharding-JDBC也是考慮到這個問題的存在,所以給我們提供了一個功能,可以讓使用者在使用的時候指定要不要走主庫進行讀取。在讀取前使用下面的方式進行設定就可以了:

/**
 * 使用者列表,強制路由主庫
 */
@RequestMapping("ds0")
public List<User> userListDs0() {
    logger.info("********TestController userListDs0():強制路由主庫");

    HintManager hintManager = HintManager.getInstance();
    List<User> users = userMapper.selectList(null);

    //清除分片鍵值,分片鍵值儲存在ThreadLocal中,所以需要在操作結束時呼叫hintManager.close()來清除ThreadLocal中的內容。hintManager實現了AutoCloseable介面,可推薦使用try with resource自動關閉。
    hintManager.close();
    List<User> users1 = userMapper.selectList(null);
    users.addAll(users1);
    return users;
}

強制路由到主庫:
強制路由到主庫
強制讀取主庫結果:
強制讀取主庫內容
從上圖可以看出,我們設定的強制讀取主庫之後,每次查詢都是去獲取主庫的資料。

官網傳送門:https://shardingsphere.apache.org/document/legacy/4.x/document/cn/manual/sharding-jdbc/usage/read-write-splitting/
原始碼傳送門:https://github.com/oycyqr/springboot-learning-demo/tree/master/springboot-shardingsphere-load