分庫分表中間件-sharding-jdbc學習筆記-基於SpringBoot+TKMybatis搭建

1、前言

經常使用分庫分表的框架或中間件有MyCat和Sharding-JDBC。MyCat是基於中間件的形式,shrrding-jdbc是基於本地jar包的類庫。sharding-jdbc屬於ShardingSphere體系的一個組件,ShardingSphere體系也有Sharding-Proxy,和MyCat同樣,是獨立部署的中間件。但sharding-jdbc與其不衝突,今天主要是學習了Sharding-JDBC。現將學習心得記錄在這裏,方便本身學習和他人蔘考(估計也不會有人看TAT)。java

2、先動手簡單寫個小demo

(一)數據準備

這裏用兩個庫,一張邏輯表,一個庫兩張真實表來演示。mysql

sql腳本(基於mysql):算法

CREATE TABLE `t_order_1` (
  `id` bigint(20) NOT NULL,
  `user_id` bigint(20) NOT NULL,
  `order_id` bigint(20) NOT NULL,
  `order_no` varchar(30) NOT NULL,
  `isactive` tinyint(4) NOT NULL DEFAULT '1',
  `inserttime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updatetime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

每一個庫t_order_1和t_order_2各建一張。sql

(一)引入jar包

maven引入當前最新的jar包:apache

<dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>sharding-jdbc-core</artifactId>
            <version>4.0.0-RC1</version>
</dependency>

(三)編寫真實數據源的方法

採起java配置方式,官方支持javaConfig、yml等配置方式,我這裏用的是JavaConfig。session

先將數據源信息寫入yml:mybatis

sharding:
  datasources:
      test1:
        driver-class-name: com.mysql.jdbc.Driver
        password: 123456
        url: jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false
        username: root
      test2:
        driver-class-name: com.mysql.jdbc.Driver
        password: 123456
        url: jdbc:mysql://localhost:3306/test2?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false
        username: root

而後建個配置信息類,保存全部的數據源信息:app

@Component
@ConfigurationProperties(prefix = "sharding")
@Data
public class DataSourceInfo {
    private Map<String, Map<String,String>> datasources;
}

對應的,建立真實數據源的方法,等會sharding-jdbc會用這些數據源封裝爲一個統一數據源入口。框架

public class DataSourceUtil {
    public static DataSource createDataSource(String data,DataSourceInfo dataSourceInfo){
        DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
        dataSourceBuilder.url(dataSourceInfo.getDatasources().get(data).get("url"));
        dataSourceBuilder.driverClassName(dataSourceInfo.getDatasources().get(data).get("driver-class-name"));
        dataSourceBuilder.username(dataSourceInfo.getDatasources().get(data).get("username"));
        dataSourceBuilder.password(dataSourceInfo.getDatasources().get(data).get("password"));
        DataSource dataSource = dataSourceBuilder.build();
        return dataSource;
    }
}

(四)配置sharding-JDBC的數據源

/**
 * @author chenzhicong
 * @time 2019/8/21 16:40
 * @description
 */
@Configuration
public class ShardingJdbcConfig {
    @Autowired
    private DataSourceInfo dataSourceInfo;
    @Bean(name = "shardingDataSource")
    @Primary
    DataSource getShardingDataSource() throws SQLException {
        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
        //添加t_order表邏輯表的分片規則配置對象
        shardingRuleConfig.getTableRuleConfigs().add(getOrderTableRuleConfiguration());
        //shardingRuleConfig.getTableRuleConfigs().add(getOrderItemTableRuleConfiguration());
        //添加綁定表
        //shardingRuleConfig.getBindingTableGroups().add("t_order, t_order_item");
        //添加廣播表
        //shardingRuleConfig.getBroadcastTables().add("t_config");
        //設置分庫策略
        //參數:第一個爲分片列名稱,第二個分片算法行表達式,需符合groovy語法
        shardingRuleConfig.setDefaultDatabaseShardingStrategyConfig(new InlineShardingStrategyConfiguration("user_id", "test${user_id % 2 + 1}"));
        //設置分表策略
        //這裏通常不用行表達式分片策略,由於涉及的邏輯表名有多個,通常本身實現一個標準分片策略
        shardingRuleConfig.setDefaultTableShardingStrategyConfig(new InlineShardingStrategyConfiguration("order_id", "t_order_${order_id % 2}"));
        return ShardingDataSourceFactory.createDataSource(createDataSourceMap(), shardingRuleConfig, new Properties());
    }

    private static KeyGeneratorConfiguration getKeyGeneratorConfiguration() {
        KeyGeneratorConfiguration result = new KeyGeneratorConfiguration("SNOWFLAKE", "id");
        return result;
    }
    /**
      * 獲取訂單表分片規則配置對象
      */
    TableRuleConfiguration getOrderTableRuleConfiguration() {
        //第一個參數是邏輯表名稱(logicTable),第二個參數是actualDataNodes(真實數據節點),由數據源名 + 表名組成,以小數點分隔(inline表達式)缺省表示使用已知數據源與邏輯表名稱生成數據節點
        TableRuleConfiguration result = new TableRuleConfiguration("t_order", "test${1..2}.t_order${0..1}");

        result.setKeyGeneratorConfig(getKeyGeneratorConfiguration());
        return result;
    }
    /**
     * 獲取訂單商品表分片規則配置對象
     */
    TableRuleConfiguration getOrderItemTableRuleConfiguration() {
        TableRuleConfiguration result = new TableRuleConfiguration("t_order_item", "test${1..2}.t_order_item${0..1}");
        return result;
    }

    Map<String, DataSource> createDataSourceMap() {
        Map<String, DataSource> result = new HashMap<>();
        result.put("test1", DataSourceUtil.createDataSource("test1",dataSourceInfo));
        result.put("test2", DataSourceUtil.createDataSource("test2",dataSourceInfo));
        return result;
    }
}

(五)將統一的sharding-jdbc數據源引入mybatis

@Configuration
@MapperScan(basePackages = "com.czc.study.mybatis.dao", sqlSessionFactoryRef = "sessionFactory")
public class SessionFactoryConfig {
    @Bean
    public SqlSessionFactory sessionFactory(DataSource shardingDataSource) throws Exception {
        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(shardingDataSource);
        return sessionFactory.getObject();
    }
}

注意了,這裏的@MapperScan是tkMybatis的MapperScan註解(不要踩坑了)。maven

3、sharding-jdbc概念介紹

(一)名詞解釋

  • 數據節點 表示一張真實表。
  • 邏輯表 表示邏輯意義上的一張表,對於寫業務代碼的人來講全部sql都是操做的邏輯表
  • 綁定表 分片規則一致的主表和子表。配置時能夠指定綁定表,sharding-jdbc將基於綁定表優化查詢

(二)分片

分片有庫分片和表分片,指的是咱們數據在庫和表間分佈的策略。

sharding-jdbc提供多種分片策略,在配置時用了策略模式,須要傳入特定算法來初始化,算法由咱們本身實現,提供最大自由度。,包括:

  • 標準分片策略

對應的Java類對象是StandardShardingStrategy,可傳入的算法對象是PreciseShardingAlgorithm和RangeShardingAlgorithm兩個分片算法,前者是精確分片算法,用於處理分片鍵in或=的查詢關係,後增是範圍分片算法,用於處理between and 的查詢關係。在配置時,咱們須要本身實現。好比精確分片算法的方法是:

String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<T> shardingValue);

其中availableTargetNames表明的是可用的庫或表,shardingValue封裝的是分片鍵(攜帶邏輯表名,字段名、值),該方法實現須要返回庫名或者表名,表明數據分佈到哪一個庫或哪一個表。後面介紹的算法對象都是相似這樣的接口,就很少介紹了。

  • 複合分片策略

對應策略類對象ComplexShardingStrategy,提供對SQL語句中的=, IN和BETWEEN AND的分片操做支持。對應算法對象ComplexKeysShardingAlgorithm,用於處理使用多鍵做爲分片鍵進行分片的場景,包含多個分片鍵的邏輯較複雜。這個也沒有實際操做過,就很少介紹了。

  • 行表達式分片策略

對應InlineShardingStrategy策略類對象,該策略類初始化有傳入分片鍵和Groovy的表達式,提供對SQL語句中的=和IN的分片操做支持,只支持單分片鍵。 咱們demo中用的就是這個。

  • Hint分片策略

對應HintShardingStrategy。經過Hint而非SQL解析的方式分片的策略。對應算法對象HintShardingAlgorithm,主要用於分片字段非SQL決定,而由其餘外置條件決定的場景。

(三)再來看看路由策略

路由策略指的是根據分片策略shrad-jdbc將把原始sql路由到哪些庫執行。具體方式有:

  • 直接路由,經過Hint(使用HintAPI直接指定路由至庫表)方式分片,而且是隻分庫不分表的前提下將進行直接路由
  • 標準路由,不包含關聯查詢或僅包含綁定表之間關聯查詢的SQL將進行標準路由,業務需求不變的話,效率是最優的。
  • 笛卡爾路由,沒有綁定表關係,又有一些級聯操做,會觸發笛卡爾路由。

好比原始SQL是:

SELECT * FROM t_order o JOIN t_order_item i ON o.order_id=i.order_id  WHERE order_id IN (1, 2);

若是有綁定鍵

SELECT * FROM t_order_0 o JOIN t_order_item_0 i ON o.order_id=i.order_id  WHERE order_id IN (1, 2);
SELECT * FROM t_order_1 o JOIN t_order_item_1 i ON o.order_id=i.order_id  WHERE order_id IN (1, 2);

但沒有綁定鍵的話,不能肯定order_id相等表的序號也相等,因而:

SELECT * FROM t_order_0 o JOIN t_order_item_0 i ON o.order_id=i.order_id  WHERE order_id IN (1, 2);
SELECT * FROM t_order_0 o JOIN t_order_item_1 i ON o.order_id=i.order_id  WHERE order_id IN (1, 2);
SELECT * FROM t_order_1 o JOIN t_order_item_0 i ON o.order_id=i.order_id  WHERE order_id IN (1, 2);
SELECT * FROM t_order_1 o JOIN t_order_item_1 i ON o.order_id=i.order_id  WHERE order_id IN (1, 2);
  • 廣播路由,不攜帶分片鍵的SQL,將進行廣播路由,廣播路由分爲全庫表路由、全實例路由、單播路由、阻斷路由,咱們只須要了解全庫表路由便可,其餘的SQL都不屬於crud,全庫表路由值得是針對一個SQL,sharding-jdbc會在全部庫都執行一次。

  • 圖示:

(四)SQL改寫相關功能

針對,邏輯SQL(表示業務代碼中寫的sql),某些狀況sharding-jdbc將會對其進行改寫,主要狀況有:

  • 標識符改寫,指的是將邏輯sql中的表名改成真實表名。
  • 補列,主要是針對order by和GROUP BY,歸併結果以前必須獲取分組字段或排序字段,因此改寫後的sql查詢結果會有分組或排序字段
  • 分頁修正,對於排序分頁,會改寫爲Limit老是從0開始,獲取正確的排序結果,再歸併篩選。
  • 批量拆分,批量插入時拆不一樣的SQL到不一樣的庫執行,或In查詢時,根據分片策略針對每一個庫的分片鍵範圍,縮小IN查詢條件的範圍。

須要注意的是,對於單節點路由,則不會進行改寫。

(五)關於歸併

對於排序、分組排序的歸併不必定將查詢結果搞到內存,而後在內存中進行歸併,sharding-jdbc有流式分組歸併的概念,即每一次從結果集中獲取到的數據,都可以經過逐條獲取的方式返回正確的單條數據。但對於分組排序的要求必須是SQL的排序項與分組項的字段以及排序類型(ASC或DESC)。

4、小結

關於sharding-jdbc的核心概念和配置demo就總結到這裏,大概對其有了必定了解,再總結一下配置的方式首先是先獲取到各個真實數據源的datasource對象,而後建立分片規則配置對象ShardingRuleConfiguration,經過分片規則配置對象添加各個邏輯表的分片規則配置對象TableRuleConfiguration(這裏能夠配置各個TableRuleConfiguration的主鍵生成策略,能夠選擇使用用雪花算法),而後添加各個綁定表組,經過傳入本身實現的算法配置分片策略(這裏須要注意,最好每一個表都有相同的字段做爲分片鍵,這樣比較好處理-我的見解,可能有可能不對,由於也沒有經歷過生產實踐),而後再經過ShardingDataSourceFactory結合真實數據源建立統一的ShardingDataSource,最後注入到咱們本身的持久層框架使用的數據源中就好了。

相關文章
相關標籤/搜索