ShardingSphere-JDBC分庫分表實現


1. 前言

ShardingSphere-JDBC 是 Apache ShardingSphere 的第一個產品,也是 Apache ShardingSphere 的前身。 定位為輕量級 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 標准的數據庫。

下面基於ShardingSphere-JDBC實現分庫分表。框架基於SpringBoot2.1.2.RELEASE,結合 shardingsphere和 mybatis-plus實現。

2. 分庫分表實現

2.1 數據庫搭建

user_db_1(ds0)    
  ├── user_0     
  └── user_1     
user_db_2(ds1)    
  ├── user_0     
  └── user_1 

 數據庫user_db_1(別名:ds0)

CREATE DATABASE /*!32312 IF NOT EXISTS*/`user_db_1` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;

USE `user_db_1`;

/*Table structure for table `user_0` */

DROP TABLE IF EXISTS `user_0`;

CREATE TABLE `user_0` (
  `id` bigint(20) NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

/*Table structure for table `user_1` */

DROP TABLE IF EXISTS `user_1`;

CREATE TABLE `user_1` (
  `id` bigint(20) NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

 數據庫user_db_2(別名:ds1)

CREATE DATABASE /*!32312 IF NOT EXISTS*/`user_db_2` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;

USE `user_db_2`;

/*Table structure for table `user_0` */

DROP TABLE IF EXISTS `user_0`;

CREATE TABLE `user_0` (
  `id` bigint(20) NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

/*Table structure for table `user_1` */

DROP TABLE IF EXISTS `user_1`;

CREATE TABLE `user_1` (
  `id` bigint(20) NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

數據庫結構和表結構需要保持一致:

2.2 搭建工程

2.2.1 依賴

 pom.xml

<?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.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.zang</groupId>
    <artifactId>shadingjdbc</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>shadingjdbc</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

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

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

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.20</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
            <version>4.0.0-RC1</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.1</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.1.4.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

2.2.2 實體類

import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;

@Data
@TableName("user")
public class User extends Model<User> {
    private Long id;
    private String name;
    private Integer age;
}

2.2.3 dao層

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zang.shadingjdbc.model.User;
import org.springframework.stereotype.Repository;

@Repository
public interface UserMapper extends BaseMapper<User> {
}

2.2.4 service層

import com.baomidou.mybatisplus.extension.service.IService;
import com.zang.shadingjdbc.model.User;
import java.util.List;

public interface UserService extends IService<User> {
    void insert(User user);
    User findById(Long id);
    List<User> findAll();
}
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zang.shadingjdbc.mapper.UserMapper;
import com.zang.shadingjdbc.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService{

    @Autowired
    private UserMapper userMapper;

    @Override
    public void insert(User entity) {
        userMapper.insert(entity);
    }

    @Override
    public User findById(Long id) {
        return userMapper.selectById(id);
    }

    @Override
    public List<User> findAll() {
        return userMapper.selectList(Wrappers.<User>lambdaQuery());
    }

}

2.3 分庫分表配置

ShardingSphere-JDBC在工程中的核心就是其配置。這里使用配置文件方式實現分庫以及分表。

# 數據源 ds0,ds1
spring.shardingsphere.datasource.names=ds0,ds1

# 第一個數據庫
spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds0.jdbc-url=jdbc:mysql://localhost:3306/user_db_1?characterEncoding=utf-8&&serverTimezone=GMT%2B8
spring.shardingsphere.datasource.ds0.username=root
spring.shardingsphere.datasource.ds0.password=123

# 第二個數據庫
spring.shardingsphere.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds1.jdbc-url=jdbc:mysql://localhost:3306/user_db_2?characterEncoding=utf-8&&serverTimezone=GMT%2B8
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=123

# 指定course表里面主鍵cid 生成策略  SNOWFLAKE
spring.shardingsphere.sharding.tables.user.key-generator.column=id
spring.shardingsphere.sharding.tables.user.key-generator.type=SNOWFLAKE

# 水平拆分的數據庫(表) 配置分庫 + 分表策略 行表達式分片策略
# 分庫策略 id為偶數添加到ds0,奇數添加到ds1
spring.shardingsphere.sharding.default-database-strategy.inline.sharding-column=id
spring.shardingsphere.sharding.default-database-strategy.inline.algorithm-expression=ds$->{id % 2}

# 分表策略 其中user為邏輯表 分表主要取決於age行
spring.shardingsphere.sharding.tables.user.actual-data-nodes=ds$->{0..1}.user_$->{0..1}
spring.shardingsphere.sharding.tables.user.table-strategy.inline.sharding-column=age
# 分片算法表達式 age為偶數時分配到user_0,奇數時分配到user_1
spring.shardingsphere.sharding.tables.user.table-strategy.inline.algorithm-expression=user_$->{age % 2}

# 打開SQL輸出日志
spring.shardingsphere.props.sql.show=true
# 一個實體類對應兩張表,覆蓋
spring.main.allow-bean-definition-overriding=true

部分說明:

邏輯表 user

水平拆分的數據庫(表)的相同邏輯和數據結構表的總稱。例:用戶數據根據主鍵尾數拆分為2張表,分別是user0到user1,他們的邏輯表名為user。

真實表

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

分片算法:

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

分片策略:

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

自增主鍵生成策略

    通過在客戶端生成自增主鍵替換以數據庫原生自增主鍵的方式,做到分布式主鍵無重復。 采用UUID.randomUUID()的方式產生分布式主鍵。或者 SNOWFLAKE

2.4 單元測試

這里使用單元測試來模擬業務調用。

import com.zang.shadingjdbc.model.User;
import com.zang.shadingjdbc.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest
public class ShadingjdbcApplicationTests {

    @Autowired
    private UserService userService;
    @Test
    public void addUserDb() {
        for (int i = 21; i < 40; i++) {
            User user = new User();
            user.setName("zhangsan"+i);
            user.setAge(i);
            userService.insert(user);
        }
    }

    @Test
    public void findAllUser() {
      List<User> userList = userService.findAll();
        System.out.println(userList.size());
    }

    @Test
    public void findUser() {
        User user = userService.findById(1346047719665295361L);
        System.out.println(user.getName());
    }
}

2.4.1 數據存儲

執行單元測試的 addUserDb方法,查看數據是按照配置的策略進行存儲。

 

 2.4.2 單個數據查詢

執行單元測試的 findUser方法,可以看到其根據分庫策略匹配到ds1庫,對庫中的表進行查詢;因為分表字段age沒有傳入,所以沒有定位到ds1中的表,查詢執行了兩次。

 2.4.3 查詢所有數據

執行單元測試的 findAllUser方法,可以看到其將所有庫表都查詢一遍。

 以上,即實現了簡單的分庫分表。


免責聲明!

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



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