sharding-jdbc 相容 MybatisPlus的動態數據源

来源:https://www.cnblogs.com/lwjQAQ/p/18330274
-Advertisement-
Play Games

背景:之前的項目做讀寫分離的時候用的 MybatisPlus的動態數據做的,很多地方使用的@DS直接指定的讀庫或者寫庫實現的業務;隨著表數據量越來越大,現在打算把比較大的表進行水平拆分,準備使用 ShardingJDBC實現,但是發現兩者配合起來並不是那麼順利,網上大部分文章都是直接把整個Shard ...


背景:之前的項目做讀寫分離的時候用的 MybatisPlus的動態數據做的,很多地方使用的@DS直接指定的讀庫或者寫庫實現的業務;隨著表數據量越來越大,現在打算把比較大的表進行水平拆分,準備使用 ShardingJDBC實現,但是發現兩者配合起來並不是那麼順利,網上大部分文章都是直接把整個Sharding的數據源當成MybatisPlus的一個數據源,那麼在原本@DS上面指定的數據源就無法直接使用Sharding的分庫等邏輯,所以我研究了一下源碼,實現了這一邏輯,給後面有需要的朋友提供一個案例,避免浪費不必要的時間

一. 版本選擇

目前ShardingJDBC主要有兩個版本,一個是ShardingJDBC早期版本,一個是ShardingSphere項目中的ShardingSphere-JDBC

  • Sharding-JDBC:Sharding-JDBC 最初由當時的項目發起人在2016年發佈。它最早作為一個輕量級的 JDBC 層解決方案,旨在解決資料庫分片和讀寫分離的問題。
  • ShardingSphere:ShardingSphere 項目是由 Sharding-JDBC 項目發展而來的,併在2018年正式發佈。Apache ShardingSphere 致力於構建更為完整的分散式資料庫管理生態系統,包含了 Sharding-JDBC、Sharding-Proxy 和 Sharding-Sidecar等多個組件。

目前獨立的ShardingJDBC已經停更,使用到的最多的版本是 4.1.1

<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
    <version>4.1.1</version>
</dependency>

ShardingSphere項目目前一直處於更新迭代中,ShardingSphere-JDBC 是通過ShardingJDBC 更新迭代過來的,在原有代碼的基礎進行了一些優化和新功能加入,對於開發者而言,主要是參數的配置發生了一些調整。但是參數的作用和配置方式和以前一樣;

這裡我為了方便以後會使用到新特性,我直接使用的是 ShardingSphere-JDBC 5.2.1

官方幫助文檔https://www.bookstack.cn/read/shardingsphere-5.1.0-zh/ecf18b21ab3f559c.md

<dependency>
	<groupId>org.apache.shardingsphere</groupId>
	<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
	<version>5.2.1</version>
</dependency>

二. 項目依賴

案例全部的 Maven依賴如下:

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>com.zaxxer</groupId>
            <artifactId>HikariCP</artifactId>
            <version>3.4.5</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.0</version>
        </dependency>

        <!-- 讀寫分離 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
            <version>3.3.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.33</version>
        </dependency>

        <!--Shardingjdbc-->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
            <version>5.2.1</version>
            <exclusions>
                <exclusion>
                    <groupId>org.yaml</groupId>
                    <artifactId>snakeyaml</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- 添加正確版本的 SnakeYAML  shardingsphere-jdbc裡面的依賴版本有問題,會報錯-->
        <dependency>
            <groupId>org.yaml</groupId>
            <artifactId>snakeyaml</artifactId>
            <version>1.33</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

三. 參數配置

application.yml 配置

server:
  port: 8080

mybatis-plus:
  mapper-locations: classpath*:mybatis/*.xml
  type-aliases-package: com.game.sharding.dto
  configuration:
    map-underscore-to-camel-case: false
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

spring:
  application:
    name: sharding-jdbc-test
  sharding-sphere:
    datasource:
      names: master,write,read,read2
      master:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://127.0.0.1:3306/game_dev?characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&autoReconnect=true&serverTimezone=Asia/Shanghai
        username: root
        password: 123456
      write:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://127.0.0.1:3306/game_dev?characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&autoReconnect=true&serverTimezone=Asia/Shanghai
        username: root
        password: 123456
      read:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://127.0.0.1:3306/game_dev_read?characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&autoReconnect=true&serverTimezone=Asia/Shanghai
        username: root
        password: 123456
      read2:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://127.0.0.1:3306/game_dev_read?characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&autoReconnect=true&serverTimezone=Asia/Shanghai
        username: root
        password: 123456
    rules:
      sharding:
        tables:
          team_msg:
		  ## 這裡的customer-ds是下麵配置的讀寫分離的數據源名稱
            actual-data-nodes: customer-ds.team_msg_${0..1}
            table-strategy:
              standard:
                sharding-column: id
                sharding-algorithm-name: msg-id # 對應下麵的sharding-algorithms
        sharding-algorithms:
          ## 註意這裡名稱(例如msg-id)不能用下劃線,會載入不了下麵的參數導致啟動報錯
          msg-id:
            type: INLINE
            props:
			## 使用id取模演算法
              algorithm-expression: team_msg_${id % 2}
	 ## 讀寫分離相關		  
      readwrite-splitting:
        data-sources:
          customer-ds:
            load-balancer-name: customer-lb
            static-strategy:
              write-data-source-name: master
              read-data-source-names: read,read2,write
        load-balancers:
            customer-lb:
			    ## 使用自定義的複雜均衡演算法
                type: CUSTOM
    props:
      # 顯示處理之後的真實sql
      sql-show: true

四. 代碼配置

最關鍵的配置就是需要把MybatisPlus的數據源註冊為使用 shardingsphere-jdbc 的數據源,並且保證數據源的名稱和原來MybatisPlus的數據源一致,shardingSphereDataSource裡面其實有一個Map保存了application.yml中所有配置的數據源,這裡主要是為了方便後續使用@DS做動態數據源切換,所以把同一個ShardingSphere的資料庫註冊為4個動態數據源,避免使用@DS找不到對應的數據源;

import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.provider.AbstractDataSourceProvider;
import com.baomidou.dynamic.datasource.provider.DynamicDataSourceProvider;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
import org.apache.commons.lang3.StringUtils;
import org.apache.shardingsphere.driver.jdbc.adapter.AbstractDataSourceAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

@Configuration
@AutoConfigureBefore({DynamicDataSourceAutoConfiguration.class, SpringBootConfiguration.class})
public class MyDataSourceConfiguration {

    /**
     * mybatisplus 動態數據源配置項
     */
    @Autowired
    private DynamicDataSourceProperties properties;

    /**
     * shardingjdbc的數據源
     */
    @Lazy
    @Resource(name = "shardingSphereDataSource")
    private AbstractDataSourceAdapter shardingSphereDataSource;

    @Value("${spring.sharding-sphere.datasource.names}")
    private String shardingDataSourceNames;

    /**
     * 註冊動態數據源  這裡非常關鍵,因為我們需要用到@DS註解配置動態選擇數據源,同上又要讓選擇的數據源使用shardingjdbc的數據源
     * 所以,這裡需要動態的把所有的數據源都註冊為  shardingjdbc的數據源
     */
    @Bean
    public DynamicDataSourceProvider dynamicDataSourceProvider() {
        if (StringUtils.isBlank(shardingDataSourceNames)) {
            throw new RuntimeException("配置 spring.sharding-sphere.datasource.names 不能為空");
        }
        String[] names = shardingDataSourceNames.split(",");
        return new AbstractDataSourceProvider() {
            @Override
            public Map<String, DataSource> loadDataSources() {
                Map<String, DataSource> dataSourceMap = new HashMap<>();
                Arrays.stream(names).forEach(name -> dataSourceMap.put(name, shardingSphereDataSource));
                return dataSourceMap;
            }
        };
    }

    /**
     * 將動態數據源設置為首選數據源
     */
    @Primary
    @Bean
    public DataSource dataSource(DynamicDataSourceProvider dynamicDataSourceProvider) {
        DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();
        dataSource.setPrimary(properties.getPrimary());
        dataSource.setStrict(properties.getStrict());
        dataSource.setStrategy(properties.getStrategy());
        dataSource.setProvider(dynamicDataSourceProvider);
        dataSource.setP6spy(properties.getP6spy());
        dataSource.setSeata(properties.getSeata());
        return dataSource;
    }
}

五. 自定義ShardingSphere中的複雜均衡演算法

shardingsphere中的負載均衡需要實現ReadQueryLoadBalanceAlgorithm介面併在getType方法中返回自定義的演算法名稱,官方自帶的又RoundRobinReadQueryLoadBalanceAlgorithm,RandomReadQueryLoadBalanceAlgorithm等,這裡我們必須自定義演算法才能相容@DS註解實現自由切換數據源;

ShardingSphere使用的是SPI機制載入的,對應的載入源碼部分如下:

image

所以如果我們要讓自定義的ReadQueryLoadBalanceAlgorithm類生效,需要在項目中的 META-INF的services文件夾中創建org.apache.shardingsphere.readwritesplitting.spi.ReadQueryLoadBalanceAlgorithm 文件,並且把自定義的類填入該文件中

源碼中的配置如下:
image

那麼我們按照源碼的配置直接在自己的項目中創建即可

image

最後自定義的CustomLoadBalanceAlgorithm 實現


public class CustomLoadBalanceAlgorithm implements ReadQueryLoadBalanceAlgorithm {
    private Properties props;

    public CustomLoadBalanceAlgorithm() {

    }

    @Override
    public void init(Properties props) {
        this.props = props;
    }

    /**
     * 獲取數據源
     *
     * @param name  數據源名稱(ShardingJDBC使用的)
     * @param writeDataSourceName 寫數據源名稱
     * @param readDataSourceNames 所有配置的複雜均衡中讀數據源名稱
     * @param context 事務上下文對象,可以獲取context.isInTransaction() 判斷是否需要事務,可通過這個來判斷是否使用 寫數據源
     * @return java.lang.String
     */
    @Override
    public String getDataSource(String name, String writeDataSourceName, List<String> readDataSourceNames, TransactionConnectionContext context) {
        // 獲取當前MybatisPlus指定的數據源
        String dsKey = DynamicDataSourceContextHolder.peek();
        if (StringUtils.isNotBlank(dsKey)) {
            if (writeDataSourceName.equals(dsKey)) {
                return dsKey;
            }
            if (readDataSourceNames.contains(dsKey)) {
                return dsKey;
            }
            throw new RuntimeException("@DS 配置錯誤,當前數據源[" + dsKey + "]不在SharingJDBC數據源列表[" + readDataSourceNames + "]中");
        }
        return writeDataSourceName;
    }

    @Override
    public String getType() {
        return "CUSTOM";
    }

    @Override
    public boolean isDefault() {
        return true;
    }

    @Override
    @Generated
    public Properties getProps() {
        return this.props;
    }
}

那麼此時你的ShardingSphere就已經完全適配之前MybatisPlus動態數據源了

image

六. 源碼

Gitee: https://gitee.com/luowenjie98/sharing-sphere-mybatisplus-demo

如果覺得對你有幫助,請給我點一個star,非常感謝 !


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 拓展閱讀 Java Servlet 教程-20-自己手寫實現 spring mvc 整體思路 Java Servlet 教程-21-自己手寫 spring mvc 簡單實現 Spring Web MVC-00-重學 mvc mvc-01-Model-View-Controller 概覽 mvc-02 ...
  • 題目要求 給定兩個字元串 s 和 t ,編寫一個函數來判斷 t 是否是 s 的字母異位詞。 註意:若 s 和 t 中每個字元出現的次數都相同,則稱 s 和 t 互為字母異位詞。 由於字元在電腦記憶體中是以ASCII碼或Unicode編碼的形式存儲的,我們可以得出'a'在ASCII表中的值是97,'A ...
  • PART1: Java基礎知識概述與Java的下載安裝 1)Java語言概述: ① Java的發展史: 詹姆斯·高斯林(James Gosling) 1977年獲得了加拿大卡爾加里大學電腦科學學士學位,1983年獲得了美國卡內基梅隆大學電腦科學博士學位,畢業後到IBM工作,設計IBM第一代工作站 ...
  • 1、簡述C++中命名空間的作用。 答:避免重覆定義全局變數的問題。 2、定義兩個命名空間A 和 B 分別在A中和B中定義變數value。在main函數中將兩個空間的value列印出來。 #include "iostream" using namespace std; namespace A { in ...
  • 寫在前面 昨天說了一下Java中的數據類型、運算符、選擇語句、迴圈語句部分的基礎知識,今天寫的編程題就是來檢驗這部分的成果,來看看你能寫出來幾題。答案也是僅供參考,如果有更好的解法歡迎在下麵留言! 題目展示 1.輸入自己的名字,年齡和性別,分別用不同的變數接收,並將輸入的信息做輸出。 代碼: pub ...
  • 在氣象學和環境科學的研究中,理解和預測氣象數據的周期性變化至關重要。小波分析作為一種高效的數學工具,近年來在氣象數據的周期性分析中得到了廣泛應用。本文將詳細介紹如何通過Python進行小波分析,以探究氣象數據中的周期性變化。 ...
  • 不管你是做數據分析,還是網路爬蟲,Web 開發、亦或是機器學習,你都離不開要和資料庫打交道,而 MySQL 又是最流行的一種資料庫,這篇文章介紹 Python 操作 MySQL 的5種方式,你可以在實際開發過程中根據實際情況合理選擇。 1、MySQLdb MySQLdb又叫MySQL-python ...
  • Java 的多線程是一種允許在一個程式中同時運行多個線程的技術。每個線程是獨立的執行路徑,可以併發執行,從而提高程式的效率和響應能力。 1. 線程基礎 Java 中的線程可以通過繼承 Thread 類或實現 Runnable 介面來創建和管理。 1.1 繼承 Thread 類 class MyThr ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...