Sharding-JDBC基礎概念及API操作


  在分布式服務中,要實現數據源得選擇有如下相關方案

  • DAO:繼承  AbstractRoutingDataSource 類,實現對應的切換數據源的方法,結合自定義注解 + 切面實現動態數據源切換。
  • ORM:MyBatis 插件進行數據源切換
  • JDBC:Sharding-JDBC 基於客戶端的分庫分表方案
  • Proxy:Mycat、Sharding-Proxy 基於代理的分庫分表方案
  • Server:特定數據庫或者版本
  • .........

基本概念及架構:

  Sharding JDBC 是從當當網的內部架構 ddframe 里面的一個分庫分表的模塊脫胎出來的,用來解決當當的分庫分表的問題,把跟業務相關的敏感的代碼剝離后,就得到了 Sharding-JDBC。它是一個工作在客戶端的分庫分表的解決方案。

  DubboX,Elastic-job 也是當當開源出來的產品。

  2018 年 5 月,因為增加了 Proxy 的版本和 Sharding-Sidecar(尚未發布),Sharding-JDBC 更名為 Sharding Sphere,從一個客戶端的組件變成了一個套件。

  2018 年 11 月,Sharding-Sphere 正式進入 Apache 基金會孵化器,這也是對Sharding-Sphere 的質量和影響力的認可。

  官網 :https://shardingsphere.apache.org/index_zh.html

  一般我們用的還是 io.shardingsphere 的包。因為更名后和捐獻給 Apache 之后的 groupId 都不一樣,在引入依賴的時候千萬要注意。主體功能是相同的,但是在某些類的用法上有些差異,如果要升級的話 import 要全部修改,有些類和方法也要修改。

  定位為輕量級Java框架,在Java的JDBC層提供的額外服務。 它使用客戶端直連數據庫,以jar包形式提供服務,無需額外部署和依賴,可理解為增強版的JDBC驅動,完全兼容JDBC和各種ORM框架。

  • 適用於任何基於JDBC的ORM框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC。
  • 支持任何第三方的數據庫連接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP等。
  • 支持任意實現JDBC規范的數據庫。目前支持MySQL,Oracle,SQLServer,PostgreSQL以及任何遵循SQL92標准的數據庫。

  在 maven 的工程里面,我們使用它的方式是引入依賴,然后進行配置就可以了,不用像 Mycat 一樣獨立運行一個服務,客戶端不需要修改任何一行代碼,原來是 SSM 連接數據庫,還是 SSM,因為它是支持 MyBatis 的。

  我們在項目內引入 Sharding-JDBC 的依賴,我們的業務代碼在操作數據庫的時候,就會通過 Sharding-JDBC 的代碼連接到數據庫。分庫分表的一些核心動作,比如 SQL 解析,路由,執行,結果處理,都是由它來完成的。它工作在客戶端。

  在 Sharding-Sphere 里面同樣提供了代理 Proxy 的版本,跟 Mycat 的作用是一樣的。Sharding-Sidecar 是一個 Kubernetes 的雲原生數據庫代理,正在開發中。

核心功能 :

  分庫分表后的幾大問題:跨庫關聯查詢、分布式事務、排序翻頁計算、全局主鍵。

  數據分片

  1. 分庫 & 分表
  2. 讀寫分離:https://shardingsphere.apache.org/document/current/cn/features/read-write-split/
  3. 分片策略定制化
  4. 無中心化分布式主鍵(包括 UUID、雪花、LEAF)

  分布式事務

  1. 標准化事務接口
  2. XA 強一致事務
  3. 柔性事務

核心概念:

  • 邏輯表:水平拆分的數據庫(表)的相同邏輯和數據結構表的總稱。例:訂單數據根據主鍵尾數拆分為 10 張表,分別是 t_order_0 到 t_order_9,他們的邏輯表名為 t_order
  • 真實表:在分片的數據庫中真實存在的物理表。即上個示例中的 t_order_0 到 t_order_9
  • 數據節點:數據分片的最小單元。由數據源名稱和數據表組成,例:ds_0.t_order_0
  • 綁定表:指分片規則一致的主表和子表。例如:t_order 表和 t_order_item 表,均按照 order_id 分片,則此兩張表互為綁定表關系。綁定表之間的多表關聯查詢不會出現笛卡爾積關聯,關聯查詢效率將大大提升。
  • 廣播表:指所有的分片數據源中都存在的表,表結構和表中的數據在每個數據庫中均完全一致。適用於數據量不大且需要與海量數據的表進行關聯查詢的場景,例如:字典表。
  • 分片鍵:根據指定的分片鍵進行路由。分片鍵不一定是主鍵,也不一定有業務含義。

使用規范 :

  雖然 Apache ShardingSphere 希望能夠完全兼容所有的SQL以及單機數據庫,但分布式為數據庫帶來了更加復雜的場景。包括一些特殊的 sql 或者分頁都帶來了巨大的挑戰。對於這方面sharding-jdbc也做出了相關的說明

  與 Mycat 對比 :

  Sharding-JDBC  Mycat
工作 層面 JDBC 協議 MySQL 協議/JDBC 協議
運行方式 Jar 包,客戶端   獨立服務,服務端
開發 方式 代碼/配置改動 連接地址(數據源)
運維 方式  管理獨立服務,運維成本高
性能 多線程並發按操作,性能高 獨立服務+網絡開銷,存在性能損失風險
功能 范圍 協議層面  包括分布式事務、數據遷移等
適用 操作 OLTP  OLTP+OLAP
支持 數據庫 基於 JDBC 協議的數據庫 MySQL 和其他支持 JDBC 協議的數據庫
支持 語言 Java 項目中使用  支持 JDBC 協議的語言
維度 二維,支持分庫又分表,比如user表繼續拆分為user1、user2 一維,分了庫后表不可以繼續拆分,或者單庫分表

從易用性和功能完善的角度來說,Mycat 似乎比 Sharding-JDBC 要好,因為有現成的分片規則,也提供了 4 種 ID 生成方式,通過注解可以支持高級功能,比如跨庫關聯查詢。

建議:小型項目,分片規則簡單的項目可以用 Sharding-JDBC。大型項目,可以用Mycat。

Sharding-JDBC 案例 :

  Sharding-JDBC要實現分庫分表的方案主要分為以下幾個步驟:

  1. 配置數據源。
  2. 配置表規則 TableRuleConfiguration。
  3. 配置分庫+分表策略 DatabaseShardingStrategyConfig,TableShardingStrategyConfig。
  4. 獲取數據源對象。
  5. 執行數據庫操作。

1.首先我們創建一個標准的springboot工程。還需要引入相關依賴: 

<dependency>
  <groupId>org.apache.shardingsphere</groupId>
  <artifactId>shardingsphere-jdbc-core</artifactId>
  <version>5.0.0-alpha</version>
</dependency>
<dependency>
  <groupId>com.zaxxer</groupId>
  <artifactId>HikariCP</artifactId>
  <version>3.4.1</version>
</dependency>
<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
</dependency>
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
</dependency>

2.編寫類 :

public class ShardingJdbc5Test {

    public static void main(String[] args) throws SQLException {
        //代表真實的數據源
        Map<String, DataSource> dataSourceMap = new HashMap<>();
        //數據源1
        HikariDataSource source = new HikariDataSource();
        source.setDriverClassName("com.mysql.cj.jdbc.Driver");
        source.setJdbcUrl(String.format("jdbc:mysql://%s:%s/%s?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8", "192.168.1.101", 3306, "study"));
        source.setUsername("root");
        source.setPassword("123456");
        //數據源2
        HikariDataSource source2 = new HikariDataSource();
        source2.setDriverClassName("com.mysql.cj.jdbc.Driver");
        source2.setJdbcUrl(String.format("jdbc:mysql://%s:%s/%s?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8", "192.168.1.101", 3306, "study2"));
        source2.setUsername("root");
        source2.setPassword("123456");

        dataSourceMap.put("ds0", source);
        dataSourceMap.put("ds1", source2);

        //創建分片規則
        // * 針對數據庫
        // * 針對表
        //* 一定要配置分片鍵
        //* 一定要配置分片算法
        //* 完全唯一id的問題

        ShardingRuleConfiguration configuration = new ShardingRuleConfiguration();

        ShardingTableRuleConfiguration tableRuleConfiguration =
                new ShardingTableRuleConfiguration("t_order", "ds${0..1}.t_order_${0..1}");
        tableRuleConfiguration.setKeyGenerateStrategy(new KeyGenerateStrategyConfiguration("order_id", "snowflake"));

        //把邏輯表和真實表的對應關系添加到分片規則配置中
        configuration.getTables().add(tableRuleConfiguration);
        //設置數據庫分庫規則
        configuration.setDefaultDatabaseShardingStrategy(
                new StandardShardingStrategyConfiguration
                        ("user_id", "db-inline"));
        Properties properties = new Properties();
        properties.setProperty("algorithm-expression", "ds${user_id%2}");
        //設置分庫策略
        configuration.getShardingAlgorithms().
                put("db-inline", new ShardingSphereAlgorithmConfiguration("INLINE", properties));

        //設置表的分片規則(數據的水平拆分)
        configuration.setDefaultTableShardingStrategy(new StandardShardingStrategyConfiguration
                ("order_id", "order-inline"));
        //設置分表策略
        Properties props = new Properties();
        props.setProperty("algorithm-expression", "t_order_${order_id%2}");
        configuration.getShardingAlgorithms().put("order-inline",
                new ShardingSphereAlgorithmConfiguration("INLINE", props));
        //設置主鍵生成策略
        // * UUID
        // * 雪花算法
        Properties idProperties = new Properties();
        idProperties.setProperty("worker-id", "123");
        configuration.getKeyGenerators().put("snowflake", new ShardingSphereAlgorithmConfiguration(
                "SNOWFLAKE", idProperties));

        //被代理的 數據源
        DataSource dataSource = ShardingSphereDataSourceFactory
                .createDataSource(dataSourceMap, Collections.singleton(configuration), new Properties());
        //初始化數據庫表
        String sql = "CREATE TABLE IF NOT EXISTS t_order (order_id BIGINT NOT NULL AUTO_INCREMENT, user_id INT NOT NULL, address_id BIGINT NOT NULL, status VARCHAR(50), PRIMARY KEY (order_id))";
        try (Connection connection = dataSource.getConnection();
             Statement statement = connection.createStatement()) {
            statement.executeUpdate(sql);
        }

        System.out.println("-------------- Process Success Begin ---------------");
        Random random = new Random();
        System.out.println("---------------------------- Insert Data ----------------------------");
        List<Long> result = new ArrayList<>(10);
        for (int i = 1; i <= 10; i++) {
            Order order = new Order();
            order.setUserId(random.nextInt(10000));
            order.setAddressId(i);
            order.setStatus("INSERT_TEST");
            String insertSql = "INSERT INTO t_order (user_id, address_id, status) VALUES (?, ?, ?)";

            try (Connection connection = dataSource.getConnection();
                 PreparedStatement preparedStatement = connection.prepareStatement(insertSql, Statement.RETURN_GENERATED_KEYS)) {
                preparedStatement.setInt(1, order.getUserId());
                preparedStatement.setLong(2, order.getAddressId());
                preparedStatement.setString(3, order.getStatus());
                preparedStatement.executeUpdate();
                try (ResultSet resultSet = preparedStatement.getGeneratedKeys()) {
                    if (resultSet.next()) {
                        order.setOrderId(resultSet.getLong(1));
                    }
                }
            }
        }
        System.out.println("-------------- Process Success Finish --------------");
    }
}

3.在兩個庫上都建立對應的 order1、order2 表,表結構一致。字段自己調整就行

  運行上述main方法可以查看到相應的效果。

  總結:ShardingRuleConfiguration 可以包含多個 ShardingTableRuleConfiguration(多張表),也可以設置默認的分庫和分表策略。每個 ShardingTableRuleConfiguration 可以針對表設置 ShardingSphereAlgorithmConfiguration,包括分庫分表策略。

  ShardingSphereAlgorithmConfiguration有 5 種實現(標准、自動、復合、Hint、自定義)。ShardingDataSourceFactory 利用 ShardingRuleConfiguration 創建數據源。有了數據源,就可以走 JDBC 的流程了。

  • 自動分片算法:取模分片算法(MOD)、哈希取模分片算法(HASH_MOD)、基於分片容量的范圍分片算法(VOLUME_RANGE)、基於分片邊界的范圍分片算法(BOUNDARY_RANGE)、自動時間段分片算法(AUTO_INTERVAL)
  • 標准分片算法:行表達式分片算法(INLINE)、時間范圍分片算法(INTERVAL)
  • 復合分片算法:復合行表達式分片算法(COMPLEX_INLINE)多個分片鍵,其他都是單一的
  • Hint 分片算法:Hint 行表達式分片算法(HINT_INLINE)
  • 自定義類分片算法(CLASS_BASED)

  更多配置可以參考 https://shardingsphere.apache.org/document/current/cn/user-manual/shardingsphere-jdbc/configuration

整合SpringBoot :

  Sharding-JDBC 進行與 SpringBoot (2.3.0)的整合是方便的,主要是進行配置文件的配置。

1.創建標准的SpringBoot 工程,再加入以下依賴:

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.4.1</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.72</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.9</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
            <version>5.0.0-alpha</version>
        </dependency>
        <dependency>
            <groupId>com.zaxxer</groupId>
            <artifactId>HikariCP</artifactId>
            <version>3.4.2</version>
        </dependency>
    </dependencies>

2.進行 分庫分表規則配置,新建 application-sharding.properties文件 :

server.port=8080
spring.shardingsphere.datasource.names=ds-0,ds-1
spring.shardingsphere.datasource.common.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.common.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds-0.username=root
spring.shardingsphere.datasource.ds-0.password=123456
spring.shardingsphere.datasource.ds-0.jdbc-url=jdbc:mysql://192.168.1.101:3306/study?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8
spring.shardingsphere.datasource.ds-1.username=root
spring.shardingsphere.datasource.ds-1.password=123456
spring.shardingsphere.datasource.ds-1.jdbc-url=jdbc:mysql://192.168.1.101:3306/study2?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8
# 行表達式分庫分表策略
# 針對分庫的規則配置
spring.shardingsphere.rules.sharding.default-database-strategy.standard.sharding-column=user_id
spring.shardingsphere.rules.sharding.default-database-strategy.standard.sharding-algorithm-name=database-inline
spring.shardingsphere.rules.sharding.tables.t_order.actual-data-nodes=ds-$->{0..1}.t_order_$->{0..1}
spring.shardingsphere.rules.sharding.tables.t_order.table-strategy.standard.sharding-column=order_id
spring.shardingsphere.rules.sharding.tables.t_order.table-strategy.standard.sharding-algorithm-name=t-order-inline
# 單個 綁定表
#spring.shardingsphere.rules.sharding.binding-tables=t_order,t_order_detail
# 如果有多個
#spring.shardingsphere.rules.sharding.binding-tables[0]=t_order,t_order_detail
#spring.shardingsphere.rules.sharding.binding-tables[1]=t_order,t_order_detail
# 采用key-generate-strategy 的 字段
spring.shardingsphere.rules.sharding.tables.t_order.key-generate-strategy.column=order_id
spring.shardingsphere.rules.sharding.tables.t_order.key-generate-strategy.key-generator-name=snowflake
# 分庫策略--行表達式分片算法
spring.shardingsphere.rules.sharding.sharding-algorithms.database-inline.type=INLINE
spring.shardingsphere.rules.sharding.sharding-algorithms.database-inline.props.algorithm-expression=ds-$->{user_id % 2}
#  分表策略--行表達式分片算法
spring.shardingsphere.rules.sharding.sharding-algorithms.t-order-inline.type=INLINE
spring.shardingsphere.rules.sharding.sharding-algorithms.t-order-inline.props.algorithm-expression=t_order_$->{order_id % 2}
# 雪花算法
spring.shardingsphere.rules.sharding.key-generators.snowflake.type=SNOWFLAKE
spring.shardingsphere.rules.sharding.key-generators.snowflake.props.worker-id=123

3.其他關於 mybatis 的相關配置這里就不貼出來了。然后在數據庫中創建對應的表。編寫 dao、service 進行測試。關於事務、全局ID、自定義分片策略下篇博客中會詳細介紹。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM