中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么在springboot中使用shardingjdbc實現分庫分表

發布時間:2021-06-07 18:10:16 來源:億速云 閱讀:582 作者:Leah 欄目:開發技術

這篇文章給大家介紹怎么在springboot中使用shardingjdbc實現分庫分表,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。

一、概覽

1.1 簡介

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

  1. 適用于任何基于 JDBC 的 ORM 框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template 或直接使用 JDBC。

  2. 支持任何第三方的數據庫連接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP 等。

  3. 支持任意實現 JDBC 規范的數據庫,目前支持 MySQL,Oracle,SQLServer,PostgreSQL 以及任何遵循 SQL92 標準的數據庫。

怎么在springboot中使用shardingjdbc實現分庫分表

1.2 對比

怎么在springboot中使用shardingjdbc實現分庫分表

名稱ShardingSphere-JDBCShardingSphere-ProxyShardingSphere-Sidecar
數據庫任意MySQL/PostgreSQLMySQL/PostgreSQL
連接消耗數
異構語言僅 Java任意任意
性能損耗低損耗略高損耗低
無中心化
靜態入口

1.3 分庫分表場景

隨著時間和業務的發展,造成表里面的數據越來越多,如果再去對數據庫表curd操作,很容易造成性能問題。為了解決由于數據量過大而造成數據庫性能降低的問題,常見的解決方案如下:

  • 從硬件上增加數據庫服務器的存儲,

  • 分庫分表處理

分庫分表又可以分為水平分表、水平分庫、垂直分表、垂直分庫

怎么在springboot中使用shardingjdbc實現分庫分表

水平分表

特點:
每個表的結構都一樣;
每個表的數據都不一樣,沒有交集;
所有表的并集是該表的全量數據;場景:單表的數據量過大或增長速度很快,已經影響或即將會影響SQL查詢效率,加重了CPU負擔,提前到達瓶頸。

水平分庫

特點:
每個庫的結構都一樣;
每個庫的數據都不一樣,沒有交集;
所有庫的并集是全量數據;場景:系統絕對并發量上來了,CPU內存壓力大。分表難以根本上解決量的問題,并且還沒有明顯的業務歸屬來垂直分庫,主庫磁盤接近飽和。

垂直分表

特點:
每個表的結構都不一樣;
每個表的數據也不一樣,
有一個關聯字段,一般是主鍵或外鍵,用于關聯兄弟表數據;
所有兄弟表的并集是該表的全量數據;場景:
有幾個字段屬于熱點字段,更新頻率很高,要把這些字段單獨切到一張表里,不然innodb行鎖很惡心的
有大字段,如text,存儲壓力很大,畢竟innodb數據和索引是同一個文件;同時,我又喜歡用SELECT *,你懂得,這磁盤IO消耗的,跟玩兒似的,誰都扛不住的。

垂直分庫
縱向切庫基于表進行切分,類似多數據源,通常是把新的業務模塊或集成公共模塊拆分出去,比如我們最熟悉的單點登錄、鑒權模塊。

1.4 非分片表處理方法

我們知道分庫分表是針對某些數據量持續大幅增長的表,比如用戶表、訂單表等,而不是一刀切將全部表都做分片。那么不分片的表和分片的表如何劃分,一般有兩種解決方案。

  • 嚴格劃分功能庫,分片的庫與不分片的庫剝離開,業務代碼中按需切換數據源訪問

  • 默認數據源,以 Sharding-JDBC 為例,不給未分片表設置分片規則,它們就不會執行,因為找不到路由規則,如果我們設置一個默認數據源,在找不到規則時一律訪問默認庫。

# 配置數據源 m1
spring.shardingsphere.datasource.name=m1
spring.shardingsphere.datasource.m1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m1.driverClassName=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m1.url=jdbc:mysql://xxxx:3306/sharding_db-1?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT
spring.shardingsphere.datasource.m1.username=root
spring.shardingsphere.datasource.m1.password=xxxx
# 默認數據源,未分片的表默認執行庫
spring.shardingsphere.sharding.default-data-source-name=m1

1.5 技術棧

  • SpringBoot2.3.8.RELEASE

  • MyBatis-Plus3.4.0

  • Sharding-JDBC

  • Druid連接池

二、 項目整合

2.1 pom.xml

<!-- shardingjdbc依賴包 -->
		<dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
            <version>4.1.1</version>
        </dependency>
       	<!-- 連接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.5</version>
        </dependency>

        <!-- 分布式事務所需包 -->
        <!-- 使用 XA 事務時,需要引入此模塊 -->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>sharding-transaction-xa-core</artifactId>
            <version>4.1.1</version>
        </dependency>

<!--        &lt;!&ndash; 使用 BASE 事務時,需要引入此模塊 &ndash;&gt;-->
<!--        <dependency>-->
<!--            <groupId>org.apache.shardingsphere</groupId>-->
<!--            <artifactId>sharding-transaction-base-seata-at</artifactId>-->
<!--            <version>4.1.1</version>-->
<!--        </dependency>-->
<!--        &lt;!&ndash; https://mvnrepository.com/artifact/io.seata/seata-core &ndash;&gt;-->
<!--        <dependency>-->
<!--            <groupId>io.seata</groupId>-->
<!--            <artifactId>seata-core</artifactId>-->
<!--            <version>1.4.2</version>-->
<!--        </dependency>-->

注意:如果原有項目引入了 druid包 以及多數據源包dynamic-datasource-spring-boot-starter,需要注釋掉相關引用

  <!-- https://mvnrepository.com/artifact/com.baomidou/dynamic-datasource-spring-boot-starter -->
<!--        <dependency>-->
<!--            <groupId>com.baomidou</groupId>-->
<!--            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>-->
<!--            <version>3.1.0</version>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>com.alibaba</groupId>-->
<!--            <artifactId>druid-spring-boot-starter</artifactId>-->
<!--        </dependency>-->

2.2 jpa/mybatis項目其他調整 springboot啟動類增加如下配置

@SpringBootApplication(exclude = {DruidDataSourceAutoConfigure.class})

分片表ORM映射實體類注釋表名映射

怎么在springboot中使用shardingjdbc實現分庫分表

數據庫鏈接賬號賦予分布式事務XA權限

GRANT XA_RECOVER_ADMIN ON *.* TO root@'%'

配置springboot數據源健康檢查sql(可選)

package com.yss.datamiddle.config;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.jdbc.DataSourceHealthContributorAutoConfiguration;
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator;
import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProvider;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.util.StringUtils;

import javax.sql.DataSource;
import java.util.Map;

/**
 * @description: 重寫健康檢查sql,解決項目啟動健康檢查異常
 * @author: Han LiDong
 * @create: 2021/5/28 14:40
 * @update: 2021/5/28 14:40
 */
@Configuration
public class DataSourceHealthConfig extends DataSourceHealthContributorAutoConfiguration {

    private static final String defaultQuery = "select 1";

    public DataSourceHealthConfig(Map<String, DataSource> dataSources, ObjectProvider<DataSourcePoolMetadataProvider> metadataProviders) {
        super(dataSources, metadataProviders);
    }

    @Override
    protected AbstractHealthIndicator createIndicator(DataSource source) {
        DataSourceHealthIndicator indicator = (DataSourceHealthIndicator) super.createIndicator(source);
        if (!StringUtils.hasText(indicator.getQuery())) {
            indicator.setQuery(defaultQuery);
        }
        return indicator;
    }
}

三、分庫分表實現

3.1 水平分表-單分片鍵(標準分片算法、自定義分布式主鍵生成算法)

創建表單course_1,course_2
約定規則:如果添加的主鍵ID是偶數把數據添加進course_1表,如果是奇數添加進course_2表

-- ----------------------------
-- Table structure for course_1
-- ----------------------------
DROP TABLE IF EXISTS `course_1`;
CREATE TABLE `course_1`  (
  `id` bigint(20) NOT NULL,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '課程名稱',
  `status` int(255) DEFAULT NULL COMMENT '狀態',
  `create_time` date DEFAULT NULL COMMENT '創建日期',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for course_2
-- ----------------------------
DROP TABLE IF EXISTS `course_2`;
CREATE TABLE `course_2`  (
  `id` bigint(20) NOT NULL,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '課程名稱',
  `status` int(255) DEFAULT NULL COMMENT '狀態',
  `create_time` date DEFAULT NULL COMMENT '創建日期',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

application-table-horizontal.yml配置分表規則

#水平分表配置
spring:
  main:
    #允許名稱相同的bean的覆蓋(一個實體類對應多張表)
    allow-bean-definition-overriding: true
  shardingsphere:
    props:
      sql:
        show: true
    datasource:
      # 數據源名稱,多數據源以逗號分隔(m1,m2)
      names: m1
      #names定義的數據源名稱作為key(key不能包含下劃線,否則無法識別配置)
      m1:
        url: jdbc:mysql://182.92.219.202:3306/sharding_db-1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: xxxx
        driver-class-name: com.mysql.cj.jdbc.Driver
        # 數據庫連接池類名稱  com.alibaba.druid.pool.DruidDataSource  com.zaxxer.hikari.HikariDataSource
        type: com.alibaba.druid.pool.DruidDataSource
#    rules:
    sharding:
      tables:
        course:
          key-generator:
            column: id
            #主鍵生成策略 可選內置的 SNOWFLAKE(雪花算法)/UUID
            # 也可以自定義(實現ShardingKeyGenerator,并配置META-INF/services/org.apache.shardingsphere.spi.keygen.ShardingKeyGenerator) SIMPLE
            type: SNOWFLAKE
          # 由數據源名 + 表名組成,以小數點分隔。多個表以逗號分隔,支持inline表達式。缺省表示使用已知數據源與邏輯表名稱生成數據節點,用于廣播表(即每個庫中都需要一個同樣的表用于關聯查詢,多為字典表)或只分庫不分表且所有庫的表結構完全一致的情況
          actual-data-nodes: m1.course_$->{1..2}
          #分庫策略:單分片鍵
          table-strategy:
            inline:
      		  #分片鍵
              sharding-column: id
              #數據分片規則(ID是偶數把數據添加入course_1,奇數入course_2)
              algorithm-expression: course_$->{id % 2 + 1}

測試-分表-新增

 /**
     * 測試分表-新增
     */
    @Test
    public void addCourse() {
        for (int i = 0; i < 10; i++) {
            Course course = new Course();
            course.setName("java" + i);
            course.setStatus(1);
            course.setCreateTime(new Date());
            courseMapper.insert(course);
        }
    }

怎么在springboot中使用shardingjdbc實現分庫分表

測試-分表-查詢

/**
     * 查詢分表數據
     */
    @Test
    public void findCourse() {
		//分區字段查詢數據:精準匹配分片表,不會去別的表中掃描數據
       Course course = courseMapper.selectById(Long.valueOf("607168187053637632"));
        log.info(course.toString());

      //非分區字段查詢:全表匹配,匯總結果
        QueryWrapper<Course> queryWrapper2 = new QueryWrapper<Course>();
        queryWrapper2.between("create_time",
                DateUtil.stringToDate("2021-01-26 11:39:05"),
                DateUtil.stringToDate("2021-07-26 11:39:05"));
        List<Course> list2 = courseMapper.selectList(queryWrapper2);
        log.info("數據量{}",list2.size());

    }

怎么在springboot中使用shardingjdbc實現分庫分表

由上可以看出分片字段作為查詢條件時,請準定位分片數據所在分片表。非分片字段查詢時,全表匹配,匯總結果

自定義分布式主鍵生成算法
實現ShardingKeyGenerator接口,自定義分布式主鍵生成算法

import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import org.apache.shardingsphere.spi.keygen.ShardingKeyGenerator;
import org.springframework.stereotype.Component;

import java.util.Properties;
import java.util.concurrent.atomic.AtomicLong;

/**
 * @description: 自定義sharding-jdbc主鍵生成算法
 * @author: Han LiDong
 * @create: 2021/5/25 09:36
 * @update: 2021/5/25 09:36
 */
@Component
public class SimpleShardingKeyGenerator implements ShardingKeyGenerator {

    private AtomicLong atomic = new AtomicLong(0);

    @Getter
    @Setter
    private Properties properties = new Properties();

	/**
	 * 分布式主鍵實現算法。	
	 */
    @Override
    public Comparable<?> generateKey() {
        return atomic.incrementAndGet();
    }

    @Override
    public String getType() {
        //聲明類型,需要在配置文件中配置此key
        return "SIMPLE";
    }
}

resources下配置META-INF/services/org.apache.shardingsphere.spi.keygen.ShardingKeyGenerator

怎么在springboot中使用shardingjdbc實現分庫分表

配置主鍵生成策略為自定義key

怎么在springboot中使用shardingjdbc實現分庫分表

3.2 水平分表-單分片鍵-按照月份分表(標準分片算法)

創建course_202101到course_202108表單
約定規則:按照創建時間對應的yyyyMM將數據分片到不同的表中

-- ----------------------------
-- 表名自己調整,創建202101-202112的表單
-- ----------------------------
DROP TABLE IF EXISTS `course_202101`;
CREATE TABLE `course_202101`  (
  `id` bigint(20) NOT NULL,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '課程名稱',
  `status` int(255) DEFAULT NULL COMMENT '狀態',
  `create_time` datetime(0) DEFAULT NULL COMMENT '創建日期',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

application-table-time-horizontal.yml配置月份分表規則

#按照月份自定義水平分表策略配置
spring:
  main:
    allow-bean-definition-overriding: true
  shardingsphere:
    props:
      sql:
        show: true
    datasource:
      # 數據源名稱,多數據源以逗號分隔
      names: m1
      m1:
        password: xxxx
        url: jdbc:mysql://182.92.219.202:3306/sharding_db-1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        driver-class-name: com.mysql.cj.jdbc.Driver
        # 數據庫連接池類名稱  com.alibaba.druid.pool.DruidDataSource  com.zaxxer.hikari.HikariDataSource
        type: com.alibaba.druid.pool.DruidDataSource
#    rules:
    sharding:
      tables:
        course:
          key-generator:
            column: id
            #主鍵生成策略 可選內置的 SNOWFLAKE(雪花算法)/UUID
            type: SNOWFLAKE
          ##配置 t_order 表規則  ->{a..b}  a必須存在,否則分布式主鍵無法獲取
          actual-data-nodes: m1.course_$->{2021..2200}0$->{1..9},m1.course_$->{2021..2200}$->{10..12}
          table-strategy:
            standard:
              #精確分片算法類名稱,用于 = 和 IN。該類需實現PreciseShardingAlgorithm 接口并提供無參數的構造器
              precise-algorithm-class-name: com.xlhj.sharding.config.CoursePreciseShardingAlgorithm
              # 范圍分片算法類名稱,用于 BETWEEN,可選。該類需實現RangeShardingAlgorithm 接口并提供無參數的構造器
              range-algorithm-class-name: com.xlhj.sharding.config.TableRangeShardAlgorithm
              # 分片字段
              sharding-column: create_time

精準分片算法實現

package com.xlhj.sharding.config;

import com.xlhj.sharding.util.DateUtil;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.Date;

/**
 * @description: 精準分片算法類
 * @author: Han LiDong
 * @create: 2021/5/25 10:32
 * @update: 2021/5/25 10:32
 */
@Component
public class CoursePreciseShardingAlgorithm implements PreciseShardingAlgorithm<Date> {

    /**
     * 按照 tablename_yyyyMM進行分表 用于 = in等
     * @param collection
     * @param preciseShardingValue
     * @return
     */
    @Override
    public String doSharding(Collection<String> collection, PreciseShardingValue<Date> preciseShardingValue) {
        StringBuffer tableName = new StringBuffer();
        tableName.append(preciseShardingValue.getLogicTableName())
                .append("_").append(DateUtil.dateToString(preciseShardingValue.getValue(),"yyyyMM");
        return tableName.toString();
    }
}

范圍分片算法實現

package com.xlhj.sharding.config;

import com.google.common.collect.Range;
import com.xlhj.sharding.util.DateUtil;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingValue;
import org.springframework.stereotype.Component;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @description: 范圍分片算法類 用于 BETWEEN等
 * @author: Han LiDong
 * @create: 2021/5/25 10:32
 * @update: 2021/5/25 10:32
 */
@Component
public class TableRangeShardAlgorithm implements RangeShardingAlgorithm<Date> {


    private static SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    /**
     * 分片鍵日期范圍包含分片表名稱集合
     * @param availableTargetNames
     * @param rangeShardingValue
     * @return
     */
    public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Date> rangeShardingValue) {
        System.out.println("范圍-*-*-*-*-*-*-*-*-*-*-*---------------" + availableTargetNames);
        System.out.println("范圍-*-*-*-*-*-*-*-*-*-*-*---------------" + rangeShardingValue);
        //物理表名集合
        //Collection<String> tables = new LinkedHashSet<>();
        //邏輯表名
        String logicTableName = rangeShardingValue.getLogicTableName();
        //分片鍵的值
        Range<Date> valueRange = rangeShardingValue.getValueRange();
        Date lowerEndpoint = valueRange.lowerEndpoint();

        Date upperEndpoint = valueRange.upperEndpoint();
        List<String> YMList = DateUtil.getYMBetweenDate(lowerEndpoint,upperEndpoint);
        List<String> tables = YMList.stream().map( ym ->{
            return logicTableName + "_" + ym;
        }).collect(Collectors.toList());
        return tables;
    }

}

測試-日期分表-新增

/**
     * 測試分表
     */
    @Test
    public void addCourse() {
        for (int i = 0; i < 10; i++) {
            Course course = new Course();
            course.setName("java" + i);
            course.setStatus(1);
            course.setCreateTime(new Date());
            courseMapper.insert(course);
        }
    }

怎么在springboot中使用shardingjdbc實現分庫分表

3.3 水平分表-多分片鍵(復合分片算法)

繼續使用course_1、course_2表單
約定規則:如果添加的主鍵ID是偶數把數據添加進course_1表,如果是奇數添加進course_2表

application-table-horizontal-columns.yml配置多分片鍵分表規則

#水平分表配置
spring:
  main:
    #允許名稱相同的bean的覆蓋(一個實體類對應多張表)
    allow-bean-definition-overriding: true
  shardingsphere:
    props:
      sql:
        show: true
    datasource:
      # 數據源名稱,多數據源以逗號分隔
      names: m1
      # names定義的數據源名稱作為key(key不能包含下劃線,否則無法識別配置)
      m1:
        url: jdbc:mysql://182.92.219.202:3306/sharding_db-1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: xxxx
        driver-class-name: com.mysql.cj.jdbc.Driver
        # 數據庫連接池類名稱  com.alibaba.druid.pool.DruidDataSource  com.zaxxer.hikari.HikariDataSource
        type: com.alibaba.druid.pool.DruidDataSource
    sharding:
      tables:
        # 表名
        course:
          key-generator:
            column: id
            #主鍵生成策略 可選內置的 SNOWFLAKE(雪花算法)/UUID
            # 也可以自定義(實現ShardingKeyGenerator,并配置META-INF/services/org.apache.shardingsphere.spi.keygen.ShardingKeyGenerator) SIMPLE
            type: SIMPLE
          #由數據源名 + 表名組成,以小數點分隔。多個表以逗號分隔,支持inline表達式。
          #缺省表示使用已知數據源與邏輯表名稱生成數據節點,用于廣播表(即每個庫中都需要一個同樣的表用于關聯查詢,多為字典表)或只分庫不分表且所有庫的表結構完全一致的情況
          actual-data-nodes: m1.course_$->{1..2}
          #分片策略:多分片鍵
          table-strategy:
            complex:
              # 分片鍵
              sharding-columns: id,status
              # 自定義分片算法
              algorithm-class-name: com.xlhj.sharding.config.CourseShardingAlgorithmColumns

自定義分片算法實現

package com.xlhj.sharding.config;

import org.apache.shardingsphere.api.sharding.complex.ComplexKeysShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.complex.ComplexKeysShardingValue;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * @description: 分表算法類-多分片鍵
 * @author: Han LiDong
 * @create: 2021/5/25 10:32
 * @update: 2021/5/25 10:32
 */
@Component
public class CourseShardingAlgorithmColumns implements ComplexKeysShardingAlgorithm  {


    /**
     *
     * @param collection        分片表名
     * @param shardingValues    分片字段值
     * @return
     */
    @Override
    public Collection<String> doSharding(Collection collection, ComplexKeysShardingValue shardingValues) {
        System.out.println("collection:" + collection + ",shardingValues:" + shardingValues);
        Map<String, Collection> map = shardingValues.getColumnNameAndShardingValuesMap();
        Collection<Long> idValues = map.get("id");
        Collection<Integer> statusValues = map.get("status");
        List<String> shardingSuffix = new ArrayList<>();
        //邏輯還是按照 id%2 + 1進行數據分片
        for (Long id : idValues) {
            Long suf = id % 2 + 1;
            for (Object s : collection) {
                String tableName = (String) s;
                // 分片表名后綴匹配
                if (tableName.endsWith(String.valueOf(suf))) {
                    shardingSuffix.add(tableName);
                }
            }
        }
        return shardingSuffix;
    }
}

測試-多分片鍵-新增

/**
     * 測試分表-新增
     */
    @Test
    public void addCourse() {
        for (int i = 0; i < 10; i++) {
            Course course = new Course();
            course.setName("java" + i);
            course.setStatus(1);
            course.setCreateTime(new Date());
            courseMapper.insert(course);
        }
    }

怎么在springboot中使用shardingjdbc實現分庫分表

3.4 水平分庫+分表-單分片鍵

另找一個數據庫創建表單course_1,course_2
約定規則:根據status=0數據到庫1,status=1數據到庫2. id為奇數到course_2表,偶數到course_1表

-- ----------------------------
--  在庫2中創建如下表單
-- ----------------------------
DROP TABLE IF EXISTS `course_1`;
CREATE TABLE `course_1`  (
  `id` bigint(20) NOT NULL,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '課程名稱',
  `status` int(255) DEFAULT NULL COMMENT '狀態 0:失效  1:有效',
  `create_time` date DEFAULT NULL COMMENT '創建日期',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;


DROP TABLE IF EXISTS `course_2`;
CREATE TABLE `course_2`  (
  `id` bigint(20) NOT NULL,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '課程名稱',
  `status` int(255) DEFAULT NULL COMMENT '狀態',
  `create_time` date DEFAULT NULL COMMENT '創建日期',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

application-database-horizontal.yml配置分庫分表規則

#水平分庫、分表配置
spring:
  main:
    #允許名稱相同的bean的覆蓋
    allow-bean-definition-overriding: true
  shardingsphere:
    props:
      sql:
        show: true
    datasource:
      # 數據源名稱,多數據源以逗號分隔
      names: m1,m2
      m1:
        url: jdbc:mysql://182.92.219.202:3306/sharding_db-1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: xxxx
        driver-class-name: com.mysql.cj.jdbc.Driver
        # 數據庫連接池類名稱  com.alibaba.druid.pool.DruidDataSource  com.zaxxer.hikari.HikariDataSource
        type: com.alibaba.druid.pool.DruidDataSource
      m2:
        url: jdbc:mysql://182.92.219.202:3306/sharding_db-2?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: xxxx
        driver-class-name: com.mysql.cj.jdbc.Driver
        # 數據庫連接池類名稱  com.alibaba.druid.pool.DruidDataSource  com.zaxxer.hikari.HikariDataSource
        type: com.alibaba.druid.pool.DruidDataSource
#    rules:
    sharding:
      tables:
        # 表名
        course:
          key-generator:
            column: id
            #主鍵生成策略 可選內置的 SNOWFLAKE(雪花算法)/UUID
            # 也可以自定義(實現ShardingKeyGenerator,并配置META-INF/services/org.apache.shardingsphere.spi.keygen.ShardingKeyGenerator) SIMPLE
            type: SNOWFLAKE
          #配置 course 表規則groovy語法  $->{a..b}
          actual-data-nodes: m$->{1..2}.course_$->{1..2}
          #分庫規則
          database-strategy:
            inline:
              #分庫字段
              sharding-column: status
              #數據分庫規則
              algorithm-expression: m$->{status + 1}
          #分表規則
          table-strategy:
            inline:
              #分表字段
              sharding-column: id
              #數據分表規則
              algorithm-expression: course_$->{id % 2 + 1}

測試-分庫分表-新增

 /**
     * 測試水平分庫+分表
     */
    @Test
    public void addCourseDB() {
        for (int i = 0; i < 10; i++) {
            Course course = new Course();
            course.setName("java");
            int rand = (int)(Math.random() * 10);
            course.setStatus(rand % 2);
            course.setCreateTime(new Date());
            courseMapper.insert(course);
        }
    }

怎么在springboot中使用shardingjdbc實現分庫分表

3.5 水平分庫+分表-Hint分片(強制分片路由)

庫2 創建course_yyyyMM相關表單
約定規則:查詢/新增數據的時候指定分片路由,強制路由到某張表

-- ----------------------------
-- 庫2創建表單,表名自己調整,創建202101-202112的表單
-- ----------------------------
DROP TABLE IF EXISTS `course_202101`;
CREATE TABLE `course_202101`  (
  `id` bigint(20) NOT NULL,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '課程名稱',
  `status` int(255) DEFAULT NULL COMMENT '狀態',
  `create_time` datetime(0) DEFAULT NULL COMMENT '創建日期',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

application-table-hint-horizontal.yml配置強制路由規則

#強制分片路由hint配置
spring:
  main:
    allow-bean-definition-overriding: true
  shardingsphere:
    props:
      sql:
        show: true
    datasource:
      # 數據源名稱,多數據源以逗號分隔
      names: m1,m2
      m1:
        url: jdbc:mysql://182.92.219.202:3306/sharding_db-1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: xxxx
        driver-class-name: com.mysql.cj.jdbc.Driver
        # 數據庫連接池類名稱  com.alibaba.druid.pool.DruidDataSource  com.zaxxer.hikari.HikariDataSource
        type: com.alibaba.druid.pool.DruidDataSource
      m2:
        password: xxxx
        url: jdbc:mysql://182.92.219.202:3306/sharding_db-2?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        driver-class-name: com.mysql.cj.jdbc.Driver
        # 數據庫連接池類名稱  com.alibaba.druid.pool.DruidDataSource  com.zaxxer.hikari.HikariDataSource
        type: com.alibaba.druid.pool.DruidDataSource
#    rules:
    sharding:
      tables:
        course:
          key-generator:
            column: id
            #主鍵生成策略 可選內置的 SNOWFLAKE(雪花算法)/UUID
            # 也可以自定義(實現ShardingKeyGenerator,并配置META-INF/services/org.apache.shardingsphere.spi.keygen.ShardingKeyGenerator) SIMPLE
            type: SNOWFLAKE
          #配置 t_order 表規則  ->{a..b}  a必須存在,否則分布式主鍵無法獲取
          actual-data-nodes: m1.course_$->{2021..2200}0$->{1..9},m1.course_$->{2021..2200}$->{10..12}
          database-strategy:
            hint:
              # 自定義分庫hit分片算法
              algorithm-class-name: com.xlhj.sharding.config.DatabaseHintShardingKeyAlgorithm
          table-strategy:
            hint:
              # 自定義分表hit分片算法
              algorithm-class-name: com.xlhj.sharding.config.TableHintShardingKeyAlgorithm

自定義強制分庫路由算法實現

package com.xlhj.sharding.config;

import com.alibaba.druid.util.StringUtils;
import org.apache.shardingsphere.api.sharding.ShardingValue;
import org.apache.shardingsphere.api.sharding.hint.HintShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.hint.HintShardingValue;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

/**
 * @description:
 * @author: Han LiDong
 * @create: 2021/5/27 09:53
 * @update: 2021/5/27 09:53
 */
@Component
public class DatabaseHintShardingKeyAlgorithm implements HintShardingAlgorithm {

    /**
     * 自定義Hint 實現算法
     * 能夠保證繞過Sharding-JDBC SQL解析過程
     * @param availableTargetNames
     * @param hintShardingValue 不再從SQL 解析中獲取值,而是直接通過hintManager.addTableShardingValue("t_order", 1)參數指定
     * @return
     */
    @Override
    public Collection<String> doSharding(Collection availableTargetNames, HintShardingValue hintShardingValue) {
        System.out.println("shardingValue=" + hintShardingValue);
        System.out.println("availableTargetNames=" + availableTargetNames);

        List<String> shardingResult = new ArrayList<>();

        Iterator i = availableTargetNames.iterator();
        while (i.hasNext()){
            String targetName = (String) i.next();
            String suffix = targetName.substring(targetName.length() - 1);
            if (StringUtils.isNumber(suffix)) {
                // hint分片算法的ShardingValue有兩種具體類型:
                // ListShardingValue和RangeShardingValue
                // 使用哪種取決于HintManager.addDatabaseShardingValue(String, String, ShardingOperator,...),ShardingOperator的類型
                Iterator j = hintShardingValue.getValues().iterator();
                while (j.hasNext()){
                    Integer value = (Integer) j.next();
                    if (value % 2 + 1 == Integer.parseInt(suffix)) {
                        shardingResult.add(targetName);
                    }
                }
            }
        }
        return shardingResult;
    }

}

自定義強制分表路由算法實現

package com.xlhj.sharding.config;

import com.alibaba.druid.util.StringUtils;
import org.apache.shardingsphere.api.sharding.hint.HintShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.hint.HintShardingValue;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

/**
 * @description:
 * @author: Han LiDong
 * @create: 2021/5/27 09:53
 * @update: 2021/5/27 09:53
 */
@Component
public class TableHintShardingKeyAlgorithm implements HintShardingAlgorithm {

    /**
     * 自定義Hint 實現算法
     * 能夠保證繞過Sharding-JDBC SQL解析過程
     * @param availableTargetNames
     * @param hintShardingValue 不再從SQL 解析中獲取值,而是直接通過hintManager.addTableShardingValue("t_order", 1)參數指定
     * @return
     */
    @Override
    public Collection<String> doSharding(Collection availableTargetNames, HintShardingValue hintShardingValue) {
        System.out.println("shardingValue=" + hintShardingValue);
        System.out.println("availableTargetNames=" + availableTargetNames);

        List<String> shardingResult = new ArrayList<>();

        Iterator i = availableTargetNames.iterator();
        while (i.hasNext()){
            String targetName = (String) i.next();
            String suffix = targetName.substring(targetName.length() - 1);
            if (StringUtils.isNumber(suffix)) {
                // hint分片算法的ShardingValue有兩種具體類型:
                // ListShardingValue和RangeShardingValue
                // 使用哪種取決于HintManager.addDatabaseShardingValue(String, String, ShardingOperator,...),ShardingOperator的類型
                Iterator j = hintShardingValue.getValues().iterator();
                while (j.hasNext()){
                    Integer value = (Integer) j.next();
                    // 匹配月份
                    Integer month = value % 12 == 0 ? 12 : value;
                    if (month == Integer.parseInt(suffix)) {
                        shardingResult.add(targetName);
                    }
                }
            }
        }

        return shardingResult;
    }

}

測試-強制路由

 /**
     * hint分片算法測試
     * @throws Exception
     */
    @Test
    public void shardingHintDB() throws Exception {
        HintManager.clear();
        HintManager hintManager = HintManager.getInstance();
        // 方式1:
        // 下面2句話的意思時: 向3號庫中的1號 course 表執行sql
        // 選擇具體的數據庫, 3 可以簡單理解為: 3號庫,如果只有2個庫, 那么可以根據2取模+1,落到 2號庫上面
        hintManager.addDatabaseShardingValue("course", 3);
        // 同理:一個數據庫中可以有多張courser表, 2 可以理解為: 2月份相關表.
        hintManager.addTableShardingValue("course", 2);
        // 方式2
        // 直接指定對應具體的數據庫,會想此庫里所有分片表添加數據
        //hintManager.setDatabaseShardingValue(0);
        Course course = new Course();
        course.setName("java");
        int rand = (int)(Math.random() * 10);
        course.setStatus(rand % 2);
        course.setCreateTime(new Date());
        courseMapper.insert(course);
        HintManager.clear();
    }

怎么在springboot中使用shardingjdbc實現分庫分表

3.6 垂直分表

單庫垂直分表相當于 同一個庫的多張表單 通過外鍵關聯。
分庫垂直分表相當于多數據源。

這幾介紹下單庫垂直分表配置:
主要規則:

#垂直分庫需要直接指定到庫和表
spring.shardingsphere.sharding.tables.sys_user.actual-data-nodes: m2.sys_user

詳細配置:

#垂直分表策略配置
spring:
  main:
    allow-bean-definition-overriding: true
  shardingsphere:
    props:
      sql:
        show: true
    datasource:
      # 數據源名稱,多數據源以逗號分隔
      names: m1,m2
      m1:
        password: xxxx
        url: jdbc:mysql://182.92.219.202:3306/sharding_db-1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        driver-class-name: com.mysql.cj.jdbc.Driver
        # 數據庫連接池類名稱  com.alibaba.druid.pool.DruidDataSource  com.zaxxer.hikari.HikariDataSource
        type: com.alibaba.druid.pool.DruidDataSource
      m2:
        password: xxxx
        url: jdbc:mysql://182.92.219.202:3306/sharding_db-2?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        driver-class-name: com.mysql.cj.jdbc.Driver
        # 數據庫連接池類名稱  com.alibaba.druid.pool.DruidDataSource  com.zaxxer.hikari.HikariDataSource
        type: com.alibaba.druid.pool.DruidDataSource
    sharding:
      tables:
        sys_user:
          key-generator:
            column: id
            #主鍵生成策略 可選內置的 SNOWFLAKE(雪花算法)/UUID
            # 也可以自定義(實現ShardingKeyGenerator,并配置META-INF/services/org.apache.shardingsphere.spi.keygen.ShardingKeyGenerator) SIMPLE
            type: SNOWFLAKE
          #垂直分庫 做到專庫專表  指定到具體庫.具體表
          actual-data-nodes: m2.sys_user

3.7 廣播表

指所有的分片數據源中都存在的表,表結構和表中的數據在每個數據庫中均完全一致。適用于數據量不大且需要與海量數據的表進行關聯查詢的場景,例如:字典表。

庫1和庫2創建字典表t_dict

-- ----------------------------
-- Table structure for t_dict
-- ----------------------------
DROP TABLE IF EXISTS `t_dict`;
CREATE TABLE `t_dict`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `dic_code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '編碼',
  `dic_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '字典名',
  `dic_value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '字典值',
  `pcode` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '父編碼',
  `status` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '0:失效 1:生效',
  `dic_sort` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '排序',
  `remarks` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '備注',
  `create_time` datetime(0) DEFAULT NULL COMMENT '創建時間',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 604343147190812673 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

配置廣播表規則
主要規則:

# 配置廣播表表名
spring.shardingsphere.sharding.broadcast-tables: t_dict

詳細配置:

#廣播表配置
spring:
  main:
    allow-bean-definition-overriding: true
  shardingsphere:
    props:
      sql:
        show: true
    datasource:
      # 數據源名稱,多數據源以逗號分隔
      names: m1,m2
      m1:
        url: jdbc:mysql://182.92.219.202:3306/sharding_db-1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: xxxx
        driver-class-name: com.mysql.cj.jdbc.Driver
        # 數據庫連接池類名稱  com.alibaba.druid.pool.DruidDataSource  com.zaxxer.hikari.HikariDataSource
        type: com.alibaba.druid.pool.DruidDataSource
      m2:
        url: jdbc:mysql://182.92.219.202:3306/sharding_db-2?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        driver-class-name: com.mysql.cj.jdbc.Driver
        password: xxxx
        # 數據庫連接池類名稱  com.alibaba.druid.pool.DruidDataSource  com.zaxxer.hikari.HikariDataSource
        type: com.alibaba.druid.pool.DruidDataSource
    sharding:
      tables:
        t_dict:
          key-generator:
            column: id
            #主鍵生成策略 可選內置的 SNOWFLAKE(雪花算法)/UUID
            # 也可以自定義(實現ShardingKeyGenerator,并配置META-INF/services/org.apache.shardingsphere.spi.keygen.ShardingKeyGenerator) SIMPLE
            type: SNOWFLAKE
      # 配置廣播表
      broadcast-tables: t_dict

測試-廣播表

 /**
     * 測試公共表
     */
    @Test
    public void addDict() {
        TDict dict = new TDict();
        dict.setCreateTime(new Date());
        dict.setDicCode("test");
        dict.setDicName("test");
        dict.setDicSort("1");
        dict.setDicValue("test");
        dict.setPcode("0");
        dict.setStatus("1");
        dictMapper.insert(dict);
    }

怎么在springboot中使用shardingjdbc實現分庫分表

3.8 綁定表

概念:指分片規則一致的主表和子表。例如:course表和 course_detail表,均按照 course_id分片,則此兩張表互為綁定表關系。綁定表之間的多表關聯查詢不會出現笛卡爾積關聯,關聯查詢效率將大大提升。

舉例說明,如果 SQL 為:

 select * from course c left join course_detail cd on c.id = cd.course_id where c.id in (10, 11);

在不配置綁定表關系時,假設分片鍵 course_id將數值 10 路由至第 0 片,將數值 11 路由至第 1 片,那么路由后的 SQL 應該為 4 條,它們呈現為笛卡爾積:

 select * from course_1 c left join course_detail_1 cd on c.id = cd.course_id where c.id in (10, 11);
 
 select * from course_1 c left join course_detail_2 cd on c.id = cd.course_id where c.id in (10, 11);

 select * from course_2 c left join course_detail_1 cd on c.id = cd.course_id where c.id in (10, 11);

 select * from course_2 c left join course_detail_2 cd on c.id = cd.course_id where c.id in (10, 11);

在配置綁定表關系后,路由的 SQL 應該為 2 條:

 select * from course_1 c left join course_detail_1 cd on c.id = cd.course_id where c.id in (10, 11);

 select * from course_2 c left join course_detail_2 cd on c.id = cd.course_id where c.id in (10, 11);

其中 course 在 FROM 的最左側,ShardingSphere 將會以它作為整個綁定表的主表。 所有路由計算將會只使用主表的策略,那么 course_detail表的分片計算將會使用 course 的條件。故綁定表之間的分區鍵要完全相同。

庫1創建course_detail_1、course_detail_2表單

-- ----------------------------
-- Table structure for course_detail_1
-- ----------------------------
DROP TABLE IF EXISTS `course_detail_1`;
CREATE TABLE `course_detail_1`  (
  `id` bigint(20) NOT NULL,
  `course_id` bigint(20) DEFAULT NULL COMMENT '課程id',
  `remark` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '備注',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for course_detail_2
-- ----------------------------
DROP TABLE IF EXISTS `course_detail_2`;
CREATE TABLE `course_detail_2`  (
  `id` bigint(20) NOT NULL,
  `course_id` bigint(20) DEFAULT NULL COMMENT '課程id',
  `remark` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '備注',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

配置廣播表規則

#水平分表配置
spring:
  main:
    #允許名稱相同的bean的覆蓋(一個實體類對應多張表)
    allow-bean-definition-overriding: true
  shardingsphere:
    props:
      sql:
        show: true
    datasource:
      # 數據源名稱,多數據源以逗號分隔
      names: m1
      # names定義的數據源名稱作為key(key不能包含下劃線,否則無法識別配置)
      m1:
        url: jdbc:mysql://182.92.219.202:3306/sharding_db-1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: xxxx
        driver-class-name: com.mysql.cj.jdbc.Driver
        # 數據庫連接池類名稱  com.alibaba.druid.pool.DruidDataSource  com.zaxxer.hikari.HikariDataSource
        type: com.alibaba.druid.pool.DruidDataSource
    sharding:
      tables:
        # 表名
        course:
          key-generator:
            column: id
            #主鍵生成策略 可選內置的 SNOWFLAKE(雪花算法)/UUID
            # 也可以自定義(實現ShardingKeyGenerator,并配置META-INF/services/org.apache.shardingsphere.spi.keygen.ShardingKeyGenerator) SIMPLE
            type: SNOWFLAKE
          #由數據源名 + 表名組成,以小數點分隔。多個表以逗號分隔,支持inline表達式。
          #缺省表示使用已知數據源與邏輯表名稱生成數據節點,用于廣播表(即每個庫中都需要一個同樣的表用于關聯查詢,多為字典表)或只分庫不分表且所有庫的表結構完全一致的情況
          actual-data-nodes: m1.course_$->{1..2}
          #分片策略:單分片鍵
          table-strategy:
            inline:
              #分片鍵
              sharding-column: id
              #數據分片規則
              algorithm-expression: course_$->{id % 2 + 1}
        course_detail:
          key-generator:
            column: id
            #主鍵生成策略 可選內置的 SNOWFLAKE(雪花算法)/UUID
            # 也可以自定義(實現ShardingKeyGenerator,并配置META-INF/services/org.apache.shardingsphere.spi.keygen.ShardingKeyGenerator) SIMPLE
            type: SNOWFLAKE
          #配置 t_order 表規則  ->{a..b}  a必須存在,否則分布式主鍵無法獲取
          actual-data-nodes: m1.course_detail_$->{1..2}
          table-strategy:
            inline:
              # 綁定表分片字段要一致(外鍵字段)
              sharding-column: course_id
              algorithm-expression: course_detail_$->{course_id % 2 + 1}
      # 綁定表規則列表(避免查詢笛卡爾積),多套規則使用binding-tables[0],binding-tables[1]...
      binding-tables: course_detail,course

注意:綁定表的分區鍵要一致

測試-綁定表查詢(笛卡爾積)

	/**
     * 綁定表測試(查詢笛卡爾積)
     */
    @Test
    public void bindingTest(){
        List<Long> ids = new ArrayList<>();
        for (int i = 0; i < 4; i++) {
            Course course = new Course();
            course.setName("java" + i);
            course.setStatus(1);
            course.setCreateTime(new Date());
            courseMapper.insert(course);
            CourseDetail courseDetail = new CourseDetail();
            courseDetail.setCourseId(course.getId());
            courseDetail.setRemark("備注" + i);
            courseDetailMapper.insert(courseDetail);
            ids.add(course.getId());
        }
        List<Course> res = courseMapper.binding(ids);
        log.info("查詢結果:{}",res.size());
    }

首先注釋掉綁定表配置,查看關聯查詢笛卡爾積

 # 綁定表規則列表(避免查詢笛卡爾積),多套規則使用binding-tables[0],binding-tables[1]...
      #binding-tables: course_detail,course

怎么在springboot中使用shardingjdbc實現分庫分表
怎么在springboot中使用shardingjdbc實現分庫分表

然后打開綁定表配置,查看關聯查詢是否還有笛卡爾積

# 綁定表規則列表(避免查詢笛卡爾積),多套規則使用binding-tables[0],binding-tables[1]...
      binding-tables: course_detail,course

怎么在springboot中使用shardingjdbc實現分庫分表

3.9 分布式事務XA

默認的 XA 事務管理器為 Atomikos
BASE事務管理器為Seata

配置事務管理器

package com.yss.datamiddle.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;

/**
 * @description:
 * @author: Han LiDong
 * @create: 2021/5/27 11:16
 * @update: 2021/5/27 11:16
 */
@Configuration
@EnableTransactionManagement
public class TransactionConfiguration {

    @Bean
    public PlatformTransactionManager txManager(final DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean
    public JdbcTemplate jdbcTemplate(final DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
}

使用分布式事務

   @Test
//    @Rollback(value = false)
    @Transactional
    @ShardingTransactionType(TransactionType.XA)  // 支持TransactionType.LOCAL, TransactionType.XA, TransactionType.BASE
    public void transactionTest() {
        Course course = new Course();
        course.setName("java");
        int rand = (int)(Math.random() * 10);
        course.setStatus(rand % 2);
        course.setCreateTime(new Date());
        courseMapper.insert(course);
        Course course1 = new Course();
        course1.setName("java");
        int rand1 = (int)(Math.random() * 10);
        course1.setStatus(rand1 % 2 + 1);
        course1.setCreateTime(new Date());
        courseMapper.insert(course1);
        int a = 1/0;
    }

四、踩坑指南

4.1 項目引入shardingjdbc相關包,啟動項目報錯required a bean named ‘entityManagerFactory' that could not be found

怎么在springboot中使用shardingjdbc實現分庫分表

解決方案:
注釋pom中durid、dynamic-datasource-spring-boot-starter引用

<!-- https://mvnrepository.com/artifact/com.baomidou/dynamic-datasource-spring-boot-starter -->
<!--        <dependency>-->
<!--            <groupId>com.baomidou</groupId>-->
<!--            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>-->
<!--            <version>3.1.0</version>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>com.alibaba</groupId>-->
<!--            <artifactId>druid-spring-boot-starter</artifactId>-->
<!--        </dependency>-->

4.2 項目啟動報錯

Failed to configure a DataSource: ‘url' attribute is not specified and no embedded datasource could be configured.

問題描述:項目引入shardingjdbc包、配置好分片規則之后啟動項目報錯找不到數據庫配置,但是配置文件中明明按照sharding數據源配置規則配置了數據庫鏈接信息。

問題原因:DruidDataSourceAutoConfigure在DynamciDataSourceAutoConfiguration之前,其會注入一個DataSourceWrapper,會在原生的spring.datasource下找url,username,password等。而我們動態數據源的配置路徑是變化的。

解決方案二選一

springboot啟動類增加如下配置
@SpringBootApplication(exclude = {DruidDataSourceAutoConfigure.class})項目配置文件新增:

# 使用多數據源時要有這個配置,要不然會啟動失敗。單數據源的時候不要加這個配置。
 spring.autoconfigure.exclude = com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure

4.3 項目啟動報錯

org.springframework.dao.InvalidDataAccessApiUsageException: ConnectionCallback; isValid; nested exception is java.sql.SQLFeatureNotSupportedException: isValid

怎么在springboot中使用shardingjdbc實現分庫分表

解決方案:
此問題是Spring Boot 2.3.8數據源健康檢查sql為null引起。
解決辦法是繼承 DataSourceHealthContributorAutoConfiguration 重寫 createIndicator 方法

package com.yss.datamiddle.config;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.jdbc.DataSourceHealthContributorAutoConfiguration;
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator;
import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProvider;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.util.StringUtils;

import javax.sql.DataSource;
import java.util.Map;

/**
 * @description: 重寫健康檢查sql,解決項目啟動健康檢查異常
 * @author: Han LiDong
 * @create: 2021/5/28 14:40
 * @update: 2021/5/28 14:40
 */
@Configuration
public class DataSourceHealthConfig extends DataSourceHealthContributorAutoConfiguration {

    private static final String defaultQuery = "select 1";

    public DataSourceHealthConfig(Map<String, DataSource> dataSources, ObjectProvider<DataSourcePoolMetadataProvider> metadataProviders) {
        super(dataSources, metadataProviders);
    }

    @Override
    protected AbstractHealthIndicator createIndicator(DataSource source) {
        DataSourceHealthIndicator indicator = (DataSourceHealthIndicator) super.createIndicator(source);
        if (!StringUtils.hasText(indicator.getQuery())) {
            indicator.setQuery(defaultQuery);
        }
        return indicator;
    }
}

4.4 分片表新增數據,但是分片鍵未賦值導致全表入庫數據。

解決方法:分片鍵必須為非空,否則會全表新增數據。

4.5 項目啟動報錯

Caused by: org.hibernate.AnnotationException: No identifier specified for entity: com.yss.datamiddle.po.PrometheusAlertRecordSummaryPo

解決方法:ORM實體類必須有主鍵注解 @Id

4.6 Table ‘xxx_sequences' doesn't exist

怎么在springboot中使用shardingjdbc實現分庫分表

解決方案:分表字段主鍵生成策略改為:

  @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

4.7 Table ‘tablename' doesn't exist

分片表對應ORM映射實體類 去掉表名映射:@TableName(“source”)
怎么在springboot中使用shardingjdbc實現分庫分表

4.8 org.springframework.boot.context.properties.source.InvalidConfigurationPropertyNameException: Configuration property name ‘spring.shardingsphere.datasource.monitor_1' is not valid

解決方案:yml配置key不能包含下劃線,調整monitor_1為monitor-1

4.9 報錯:

Caused by: java.lang.NullPointerException: please config application id within seata.conf file.

原因:使用XA分布式事務,但同時又引入了Base事務相關包
解決方案:注釋掉Base事務包

<!-- 使用 BASE 事務時,需要引入此模塊 -->
<!--        <dependency>-->
<!--            <groupId>org.apache.shardingsphere</groupId>-->
<!--            <artifactId>sharding-transaction-base-seata-at</artifactId>-->
<!--            <version>4.1.1</version>-->
<!--        </dependency>-->
<!--        &lt;!&ndash; https://mvnrepository.com/artifact/io.seata/seata-core &ndash;&gt;-->
<!--        <dependency>-->
<!--            <groupId>io.seata</groupId>-->
<!--            <artifactId>seata-core</artifactId>-->
<!--            <version>1.4.2</version>-->
<!--        </dependency>-->

關于怎么在springboot中使用shardingjdbc實現分庫分表就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

措勤县| 定远县| 婺源县| 永昌县| 保德县| 南投市| 保靖县| 临澧县| 富川| 兴仁县| 华蓥市| 和林格尔县| 沾益县| 临清市| 中宁县| 仙居县| 岳阳县| 尼玛县| 门头沟区| 石门县| 揭东县| 保定市| 绥芬河市| 隆子县| 运城市| 贡嘎县| 综艺| 肇州县| 天台县| 柳江县| 莒南县| 沙坪坝区| 宜昌市| 临朐县| 庄河市| 澳门| 西城区| 平罗县| 绥宁县| 漳平市| 澎湖县|