SpringBoot結合ShardingSphere實現主從讀寫分離


ShardingSphere是一套開源的分布式數據庫中間件解決方案組成的生態圈,它由ShardingSphere-JDBC、ShardingSphere-Proxy和ShardingSphere-Sidecar(計划中)這3款相互獨立的產品組成。 他們均提供標准化的數據分片、分布式事務和數據庫治理功能,可適用於如Java同構、異構語言、雲原生等各種多樣化的應用場景。

ShardingSphere定位為關系型數據庫中間件,旨在充分合理地在分布式的場景下利用關系型數據庫的計算和存儲能力,而並非實現一個全新的關系型數據庫。 它與NoSQL和NewSQL是並存而非互斥的關系。

ShardingSphere-JDBC采用無中心化架構,適用於Java開發的高性能的輕量級OLTP應用;ShardingSphere-Proxy提供靜態入口以及異構語言的支持,適用於OLAP應用以及對分片數據庫進行管理和運維的場景。

分片概念講解

邏輯表

水平拆分的數據庫(表)的相同邏輯和數據結構表的總稱。例:訂單數據根據主鍵尾數拆分為10張表,分別是t_order_0t_order_9,他們的邏輯表名為t_order

真實表

在分片的數據庫中真實存在的物理表。即上個示例中的t_order_0t_order_9

數據節點

數據分片的最小單元。由數據源名稱和數據表組成,例:ds_0.t_order_0

綁定表

指分片規則一致的主表和子表。例如:t_order表和t_order_item表,均按照order_id分片,則此兩張表互為綁定表關系。綁定表之間的多表關聯查詢不會出現笛卡爾積關聯,關聯查詢效率將大大提升。舉例說明,如果SQL為:

SELECT i.* FROM t_order o JOIN t_order_item i ON o.order_id=i.order_id WHERE o.order_id in (10, 11); 

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

SELECT i.* FROM t_order_0 o JOIN t_order_item_0 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11); SELECT i.* FROM t_order_0 o JOIN t_order_item_1 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11); SELECT i.* FROM t_order_1 o JOIN t_order_item_0 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11); SELECT i.* FROM t_order_1 o JOIN t_order_item_1 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11); 

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

SELECT i.* FROM t_order_0 o JOIN t_order_item_0 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11); SELECT i.* FROM t_order_1 o JOIN t_order_item_1 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11); 

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

分片算法

通過分片算法將數據分片,支持通過=>=<=><BETWEENIN分片。分片算法需要應用方開發者自行實現,可實現的靈活度非常高。

目前提供4種分片算法。由於分片算法和業務實現緊密相關,因此並未提供內置分片算法,而是通過分片策略將各種場景提煉出來,提供更高層級的抽象,並提供接口讓應用開發者自行實現分片算法。

  • 精確分片算法

對應PreciseShardingAlgorithm,用於處理使用單一鍵作為分片鍵的=與IN進行分片的場景。需要配合StandardShardingStrategy使用。

  • 范圍分片算法

對應RangeShardingAlgorithm,用於處理使用單一鍵作為分片鍵的BETWEEN AND、>、<、>=、<=進行分片的場景。需要配合StandardShardingStrategy使用。

  • 復合分片算法

對應ComplexKeysShardingAlgorithm,用於處理使用多鍵作為分片鍵進行分片的場景,包含多個分片鍵的邏輯較復雜,需要應用開發者自行處理其中的復雜度。需要配合ComplexShardingStrategy使用。

  • Hint分片算法

對應HintShardingAlgorithm,用於處理使用Hint行分片的場景。需要配合HintShardingStrategy使用。

分片策略

包含分片鍵和分片算法,由於分片算法的獨立性,將其獨立抽離。真正可用於分片操作的是分片鍵 + 分片算法,也就是分片策略。目前提供5種分片策略。

  • 標准分片策略

對應StandardShardingStrategy。提供對SQL語句中的=, >, <, >=, <=, IN和BETWEEN AND的分片操作支持。StandardShardingStrategy只支持單分片鍵,提供PreciseShardingAlgorithm和RangeShardingAlgorithm兩個分片算法。PreciseShardingAlgorithm是必選的,用於處理=和IN的分片。RangeShardingAlgorithm是可選的,用於處理BETWEEN AND, >, <, >=, <=分片,如果不配置RangeShardingAlgorithm,SQL中的BETWEEN AND將按照全庫路由處理。

  • 復合分片策略

對應ComplexShardingStrategy。復合分片策略。提供對SQL語句中的=, >, <, >=, <=, IN和BETWEEN AND的分片操作支持。ComplexShardingStrategy支持多分片鍵,由於多分片鍵之間的關系復雜,因此並未進行過多的封裝,而是直接將分片鍵值組合以及分片操作符透傳至分片算法,完全由應用開發者實現,提供最大的靈活度。

  • 行表達式分片策略

對應InlineShardingStrategy。使用Groovy的表達式,提供對SQL語句中的=和IN的分片操作支持,只支持單分片鍵。對於簡單的分片算法,可以通過簡單的配置使用,從而避免繁瑣的Java代碼開發,如: t_user_$->{u_id % 8} 表示t_user表根據u_id模8,而分成8張表,表名稱為t_user_0t_user_7

  • Hint分片策略

對應HintShardingStrategy。通過Hint指定分片值而非從SQL中提取分片值的方式進行分片的策略。

 

實戰

引入依賴

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.xin</groupId>
    <artifactId>shardingsphere-jdbc-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>shardingsphere-jdbc-demo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <shardingsphere.version>4.0.0</shardingsphere.version>
        <shardingsphere.spi.impl.version>4.0.0</shardingsphere.spi.impl.version>
    </properties>

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

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

<!--        <dependency>-->
<!--            <groupId>org.apache.shardingsphere</groupId>-->
<!--            <artifactId>sharding-core-api</artifactId>-->
<!--            <version>${shardingsphere.version}</version>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>org.apache.shardingsphere</groupId>-->
<!--            <artifactId>sharding-jdbc-core</artifactId>-->
<!--            <version>${shardingsphere.version}</version>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>org.apache.shardingsphere</groupId>-->
<!--            <artifactId>sharding-jdbc-orchestration</artifactId>-->
<!--            <version>${shardingsphere.version}</version>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>org.apache.shardingsphere</groupId>-->
<!--            <artifactId>sharding-transaction-core</artifactId>-->
<!--            <version>${shardingsphere.version}</version>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>org.apache.shardingsphere</groupId>-->
<!--            <artifactId>sharding-transaction-xa-core</artifactId>-->
<!--            <version>${shardingsphere.version}</version>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>org.apache.shardingsphere</groupId>-->
<!--            <artifactId>sharding-transaction-base-seata-at</artifactId>-->
<!--            <version>${shardingsphere.version}</version>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>org.apache.shardingsphere</groupId>-->
<!--            <artifactId>sharding-jdbc-spring-boot-starter</artifactId>-->
<!--            <version>${shardingsphere.version}</version>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>org.apache.shardingsphere</groupId>-->
<!--            <artifactId>sharding-jdbc-orchestration-spring-boot-starter</artifactId>-->
<!--            <version>${shardingsphere.version}</version>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>org.apache.shardingsphere</groupId>-->
<!--            <artifactId>sharding-jdbc-spring-namespace</artifactId>-->
<!--            <version>${shardingsphere.version}</version>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>org.apache.shardingsphere</groupId>-->
<!--            <artifactId>sharding-jdbc-orchestration-spring-namespace</artifactId>-->
<!--            <version>${shardingsphere.version}</version>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>org.apache.shardingsphere</groupId>-->
<!--            <artifactId>sharding-orchestration-reg-zookeeper-curator</artifactId>-->
<!--            <version>${shardingsphere.version}</version>-->
<!--        </dependency>-->

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


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
    <repositories>
        <repository>
            <id>aliyun-repos</id>
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

建數據庫

建庫db0、db1,db0只寫,db1只讀,作為讀寫分離

兩個庫均建下表:

-- ----------------------------
-- Table structure for `user01`
-- ----------------------------
DROP TABLE IF EXISTS `user01`;
CREATE TABLE `user01` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(10) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- Records of user01
-- ----------------------------
INSERT INTO `user01` VALUES ('1', 'wangxin', '99');

 配置

server.port=8086

#指定mybatis信息
mybatis.config-location=classpath:mybatis-config.xml

spring.shardingsphere.datasource.names=master,slave0

# 數據源 主庫
spring.shardingsphere.datasource.master.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.master.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.master.url=jdbc:mysql://localhost:3307/db0?characterEncoding=utf-8
spring.shardingsphere.datasource.master.username=root
spring.shardingsphere.datasource.master.password=mysql

# 數據源 從庫
spring.shardingsphere.datasource.slave0.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.slave0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.slave0.url=jdbc:mysql://local:3307/db1?characterEncoding=utf-8
spring.shardingsphere.datasource.slave0.username=root
spring.shardingsphere.datasource.slave0.password=mysql

# 讀寫分離
spring.shardingsphere.masterslave.load-balance-algorithm-type=round_robin
spring.shardingsphere.masterslave.name=ms
spring.shardingsphere.masterslave.master-data-source-name=master
spring.shardingsphere.masterslave.slave-data-source-names=slave0

#打印sql
spring.shardingsphere.props.sql.show=true

 

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <typeAliases>
        <package name="com.xin.shardingspherejdbcdemo.entity"/>
    </typeAliases>
    <mappers>
        <mapper resource="mapper/UserMapper.xml"/>
    </mappers>
</configuration>

配置項說明

數據分片

spring.shardingsphere.datasource.names= #數據源名稱,多數據源以逗號分隔

spring.shardingsphere.datasource.<data-source-name>.type= #數據庫連接池類名稱
spring.shardingsphere.datasource.<data-source-name>.driver-class-name= #數據庫驅動類名
spring.shardingsphere.datasource.<data-source-name>.url= #數據庫url連接
spring.shardingsphere.datasource.<data-source-name>.username= #數據庫用戶名
spring.shardingsphere.datasource.<data-source-name>.password= #數據庫密碼
spring.shardingsphere.datasource.<data-source-name>.xxx= #數據庫連接池的其它屬性

spring.shardingsphere.sharding.tables.<logic-table-name>.actual-data-nodes= #由數據源名 + 表名組成,以小數點分隔。多個表以逗號分隔,支持inline表達式。缺省表示使用已知數據源與邏輯表名稱生成數據節點,用於廣播表(即每個庫中都需要一個同樣的表用於關聯查詢,多為字典表)或只分庫不分表且所有庫的表結構完全一致的情況

#分庫策略,缺省表示使用默認分庫策略,以下的分片策略只能選其一

#用於單分片鍵的標准分片場景
spring.shardingsphere.sharding.tables.<logic-table-name>.database-strategy.standard.sharding-column= #分片列名稱
spring.shardingsphere.sharding.tables.<logic-table-name>.database-strategy.standard.precise-algorithm-class-name= #精確分片算法類名稱,用於=和IN。該類需實現PreciseShardingAlgorithm接口並提供無參數的構造器
spring.shardingsphere.sharding.tables.<logic-table-name>.database-strategy.standard.range-algorithm-class-name= #范圍分片算法類名稱,用於BETWEEN,可選。該類需實現RangeShardingAlgorithm接口並提供無參數的構造器

#用於多分片鍵的復合分片場景
spring.shardingsphere.sharding.tables.<logic-table-name>.database-strategy.complex.sharding-columns= #分片列名稱,多個列以逗號分隔
spring.shardingsphere.sharding.tables.<logic-table-name>.database-strategy.complex.algorithm-class-name= #復合分片算法類名稱。該類需實現ComplexKeysShardingAlgorithm接口並提供無參數的構造器

#行表達式分片策略
spring.shardingsphere.sharding.tables.<logic-table-name>.database-strategy.inline.sharding-column= #分片列名稱
spring.shardingsphere.sharding.tables.<logic-table-name>.database-strategy.inline.algorithm-expression= #分片算法行表達式,需符合groovy語法

#Hint分片策略
spring.shardingsphere.sharding.tables.<logic-table-name>.database-strategy.hint.algorithm-class-name= #Hint分片算法類名稱。該類需實現HintShardingAlgorithm接口並提供無參數的構造器

#分表策略,同分庫策略
spring.shardingsphere.sharding.tables.<logic-table-name>.table-strategy.xxx= #省略

spring.shardingsphere.sharding.tables.<logic-table-name>.key-generator.column= #自增列名稱,缺省表示不使用自增主鍵生成器
spring.shardingsphere.sharding.tables.<logic-table-name>.key-generator.type= #自增列值生成器類型,缺省表示使用默認自增列值生成器。可使用用戶自定義的列值生成器或選擇內置類型:SNOWFLAKE/UUID
spring.shardingsphere.sharding.tables.<logic-table-name>.key-generator.props.<property-name>= #屬性配置, 注意:使用SNOWFLAKE算法,需要配置worker.id與max.tolerate.time.difference.milliseconds屬性。若使用此算法生成值作分片值,建議配置max.vibration.offset屬性

spring.shardingsphere.sharding.binding-tables[0]= #綁定表規則列表
spring.shardingsphere.sharding.binding-tables[1]= #綁定表規則列表
spring.shardingsphere.sharding.binding-tables[x]= #綁定表規則列表

spring.shardingsphere.sharding.broadcast-tables[0]= #廣播表規則列表
spring.shardingsphere.sharding.broadcast-tables[1]= #廣播表規則列表
spring.shardingsphere.sharding.broadcast-tables[x]= #廣播表規則列表

spring.shardingsphere.sharding.default-data-source-name= #未配置分片規則的表將通過默認數據源定位
spring.shardingsphere.sharding.default-database-strategy.xxx= #默認數據庫分片策略,同分庫策略
spring.shardingsphere.sharding.default-table-strategy.xxx= #默認表分片策略,同分表策略
spring.shardingsphere.sharding.default-key-generator.type= #默認自增列值生成器類型,缺省將使用org.apache.shardingsphere.core.keygen.generator.impl.SnowflakeKeyGenerator。可使用用戶自定義的列值生成器或選擇內置類型:SNOWFLAKE/UUID
spring.shardingsphere.sharding.default-key-generator.props.<property-name>= #自增列值生成器屬性配置, 比如SNOWFLAKE算法的worker.id與max.tolerate.time.difference.milliseconds

spring.shardingsphere.sharding.master-slave-rules.<master-slave-data-source-name>.master-data-source-name= #詳見讀寫分離部分
spring.shardingsphere.sharding.master-slave-rules.<master-slave-data-source-name>.slave-data-source-names[0]= #詳見讀寫分離部分
spring.shardingsphere.sharding.master-slave-rules.<master-slave-data-source-name>.slave-data-source-names[1]= #詳見讀寫分離部分
spring.shardingsphere.sharding.master-slave-rules.<master-slave-data-source-name>.slave-data-source-names[x]= #詳見讀寫分離部分
spring.shardingsphere.sharding.master-slave-rules.<master-slave-data-source-name>.load-balance-algorithm-class-name= #詳見讀寫分離部分
spring.shardingsphere.sharding.master-slave-rules.<master-slave-data-source-name>.load-balance-algorithm-type= #詳見讀寫分離部分

spring.shardingsphere.props.sql.show= #是否開啟SQL顯示,默認值: false
spring.shardingsphere.props.executor.size= #工作線程數量,默認值: CPU核數

讀寫分離

#省略數據源配置,與數據分片一致

spring.shardingsphere.sharding.master-slave-rules.<master-slave-data-source-name>.master-data-source-name= #主庫數據源名稱
spring.shardingsphere.sharding.master-slave-rules.<master-slave-data-source-name>.slave-data-source-names[0]= #從庫數據源名稱列表
spring.shardingsphere.sharding.master-slave-rules.<master-slave-data-source-name>.slave-data-source-names[1]= #從庫數據源名稱列表
spring.shardingsphere.sharding.master-slave-rules.<master-slave-data-source-name>.slave-data-source-names[x]= #從庫數據源名稱列表
spring.shardingsphere.sharding.master-slave-rules.<master-slave-data-source-name>.load-balance-algorithm-class-name= #從庫負載均衡算法類名稱。該類需實現MasterSlaveLoadBalanceAlgorithm接口且提供無參數構造器
spring.shardingsphere.sharding.master-slave-rules.<master-slave-data-source-name>.load-balance-algorithm-type= #從庫負載均衡算法類型,可選值:ROUND_ROBIN,RANDOM。若`load-balance-algorithm-class-name`存在則忽略該配置

spring.shardingsphere.props.sql.show= #是否開啟SQL顯示,默認值: false
spring.shardingsphere.props.executor.size= #工作線程數量,默認值: CPU核數
spring.shardingsphere.props.check.table.metadata.enabled= #是否在啟動時檢查分表元數據一致性,默認值: false

實體

package com.xin.shardingspherejdbcdemo.entity;

public class User {


    /**
     * 主鍵
     */
    private Integer id;
    private String name;
    private Integer age;

    public User() {

    }

    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }



}

 

map

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xin.shardingspherejdbcdemo.mapper.UserMapper">
  <resultMap id="BaseResultMap" type="com.xin.shardingspherejdbcdemo.entity.User">
    <id column="id" jdbcType="BIGINT" property="id" />
    <result column="name" jdbcType="VARCHAR" property="name" />
    <result column="age" jdbcType="INTEGER" property="age" />
  </resultMap>
  <sql id="Base_Column_List">
    id, name, age
  </sql>
  <select id="selectAll"  resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List" />
    from user01
  </select>

  <insert id="insert" parameterType="com.xin.shardingspherejdbcdemo.entity.User">
    insert into user01 (name, age)
    values ( #{name,jdbcType=VARCHAR},
      #{age,jdbcType=INTEGER}})
  </insert>
</mapper>

Service

控制器

package com.xin.shardingspherejdbcdemo.controller;

import com.xin.shardingspherejdbcdemo.entity.User;
import com.xin.shardingspherejdbcdemo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping("/saveUser")
    public Object saveUser() {
        return userService.save(new User("張三",  3));
    }

    @GetMapping("/listUser")
    public Object listUser() {
        return userService.list();
    }

}

 

嘗試查詢和添加,發現日志會有顯示:

2020-05-21 21:59:49.366  INFO 9476 --- [nio-8086-exec-2] ShardingSphere-SQL                       : Rule Type: master-slave
2020-05-21 21:59:49.367  INFO 9476 --- [nio-8086-exec-2] ShardingSphere-SQL                       : SQL: select
     
    id, name, age
   
    from user01 ::: DataSources: slave0

下載完整工程

shardingsphere-jdbc-demo 下載

 


免責聲明!

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



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