从0开始学习shardingjdbc之分库分表

462 阅读5分钟

从0开始学习shardingjdbc之分库分表

最近笔者在找java后台工作,经常在面试的过程中被问到分库分表的相关知识,因此自己本地跑了一个项目,完成了分库分表。 源代码地址:github.com/faxuexiaoxi… 该项目包含了其他的内容,例如只使用mybatis druid实现的增删改查的standardDemo。本项目的内容是simpleDemo

image.png

分库分表项目简单介绍:通过springboot + shardingjdbc+mybatis plus+ druid完成查询,插入数据能分别路由到指定的表中。如下图所示

image.png

image.png

下面简单介绍下用到的框架以及版本

框架版本
springboot3.2.2
mybatis3.0.3
mybatis plus3.5.5
druid1.2.20
shardingjdbc5.2.1

一般来说,mybatis和mybatis plus只要兼容springboot 3.0版本即可 sharding jdbc由于版本更新比较快,建议用稳定版本。

  1. 下面是maven的包的引入,可以一键复制。
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--    Mybatis    -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <!--     Mybatis Plus    -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- Druid 数据库连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.20</version>
        </dependency>

<!--        shardingsphere-->
                <dependency>
                    <groupId>org.apache.shardingsphere</groupId>
                    <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
                    <version>5.2.1</version>
                </dependency>

<!--    shardingsphere和2.2不兼容,需要使用1.33-->
                <dependency>
                    <groupId>org.yaml</groupId>
                    <artifactId>snakeyaml</artifactId>
                    <version>1.33</version>
                </dependency>

2. mysql数据库的搭建,建表语句如下。建表语句init.sql在resource目录下

DROP TABLE IF EXISTS `user_0`;
CREATE TABLE if not exists `user_0`
(
    `userId`        BIGINT         NOT NULL AUTO_INCREMENT,
    `userName`      VARCHAR(45) NOT NULL,
    `userPassword`  VARCHAR(45) NOT NULL,
    `userPassword_` VARCHAR(45) NOT NULL,
    PRIMARY KEY (`userId`)
);

DROP TABLE IF EXISTS `user_1`;
CREATE TABLE if not exists `user_1`
(
    `userId`        BIGINT         NOT NULL AUTO_INCREMENT,
    `userName`      VARCHAR(45) NOT NULL,
    `userPassword`  VARCHAR(45) NOT NULL,
    `userPassword_` VARCHAR(45) NOT NULL,
    PRIMARY KEY (`userId`)
)
  • 主要注意的地方如下:由于使用分库分表的shardingjdbc需要自己配置分布式主键,所以一般来说分布式主键的生成规则都是雪花算法,生成的数字都是需要8字节存储,因此需要BIGINT。由于是demo,userId的自增属性没有实际意义。
  • 上述建表语句没有指定数据库,可能需要玩家自行创建数据库。例如:create database m0;use m0;
  • 字段含义如下:userId表示玩家的id,username玩家名称,userpassword玩家密码,userpassword_表示加密后的密码,shardingjdbc加密功能会在下一篇文章中讲到,本文章只设计分库分表。

数据库格式如下所示即创建成功。需要在sharding和sharding_tmp中分别创建user_0,user_1

image.png

  1. 下面是编写项目代码,首先是实体类 User。
  • 主键的字段必须为LONG,不能long。否则sharding-jdbc不能生成主键
  • 主键字段不能有TableId注解,TableId注解会导致使用mybatis配置的主键生成策略(如果使用mybatis的主键生成策略也能满足需求的话,用@TableId似乎也无妨,如果要映射字段可以使用@TableField来指定)
  • 主键字段尽量全部小写,不要有特殊符号例如下划线。否则在sharding jdbc配置分表策略中,会提示找不到字段。例如:主键如果是UserId,报错提示 找不到数据库的userid字段....
package org.faxuexiaoxin.simpledemo.entity;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;

@TableName("user")
public class User {

    /**
     * 玩家id
     */
//    @TableId(value = "userId")

    @TableField("userId")
    private Long userid;

    /**
     * 玩家名称
     */
    @TableField("userName")
    private String userName;

    /**
     * 玩家密码
     */
    @TableField("userPassword")
    private String userPassword;

    /**
     * 玩家加密后的密码
     */
    @TableField("userPassword_")
    private String userPassword_;

    public Long getUserid() {
        return userid;
    }

    public void setUserid(Long userid) {
        this.userid = userid;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getUserPassword() {
        return userPassword;
    }

    public void setUserPassword(String userPassword) {
        this.userPassword = userPassword;
    }

    public String getUserPassword_() {
        return userPassword_;
    }

    public void setUserPassword_(String userPassword_) {
        this.userPassword_ = userPassword_;
    }
}

下面是mapper,使用mybatis plus提供的BaseMapper即可

package org.faxuexiaoxin.simpledemo.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.faxuexiaoxin.simpledemo.entity.User;

public interface UserMapper extends BaseMapper<User> {
}

最后是入口类,扫码mapper即可

package org.faxuexiaoxin.simpledemo;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan(basePackages = "org.faxuexiaoxin.simpledemo.mapper")
public class SimpleDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(SimpleDemoApplication.class, args);
    }

}

编写测试用例 UserMapperTest,批量插入10个数据:

package org.faxuexiaoxin.simpledemo.mapper;

import org.faxuexiaoxin.simpledemo.entity.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class UserMapperTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    void testInsert() {
        User user = new User();
        for (int i = 0; i < 10; i++) {
//            user.setUserid(i);
            user.setUserName("法学小心鑫");
            user.setUserPassword("test");
            user.setUserPassword_("fsdfsdfs");
            userMapper.insert(user);
        }
    }
}

4. 下面修改application.properties.

如果只使用durid mybatis mybatis plus。那么需要按照springboot data中的配置url,username,password,datasource即可。 但是目前使用了shardingjdbc需要配置的属性会复杂很多。需要考虑下面问题。

1. 分库分到sharding 和 sharding_tmp两个数据库中,因此需要填写两个库的连接信息。笔者为了简单,两个库用的同一个数据库下的不同schema。
2. 分库是怎么分的,通过哪个字段来分。用户的主键id %2分别映射到sharding 和 sharding_tmp中
3. 分表是怎么分的,每个数据库中有user_0和user_1
4. 分表是怎么分的,通过哪个字段来分。用户的主键id %2分别映射到user_0和user_1中。
5. 主键的id是怎么生成的。雪花算法
spring.application.name=simpleDemo

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

#配置sharding的数据源
spring.shardingsphere.datasource.names=m0,m1

spring.shardingsphere.datasource.m0.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m0.dirver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m0.url=jdbc:mysql://localhost:3306/sharding
spring.shardingsphere.datasource.m0.username=root
spring.shardingsphere.datasource.m0.password=root

spring.shardingsphere.datasource.m1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m1.dirver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m1.url=jdbc:mysql://localhost:3306/sharding_tmp
spring.shardingsphere.datasource.m1.username=root
spring.shardingsphere.datasource.m1.password=root

#配置sharding需要的表
spring.shardingsphere.rules.sharding.tables.user.actual-data-nodes=m$->{0..1}.user_$->{0..1}

#分库策略
spring.shardingsphere.rules.sharding.tables.user.database-strategy.standard.sharding-column=userid
spring.shardingsphere.rules.sharding.tables.user.database-strategy.standard.sharding-algorithm-name= user_alg
spring.shardingsphere.rules.sharding.sharding-algorithms.user_alg.type= MOD
spring.shardingsphere.rules.sharding.sharding-algorithms.user_alg.props.sharding-count=2

#分表策略
spring.shardingsphere.rules.sharding.tables.user.table-strategy.standard.sharding-column=userid
spring.shardingsphere.rules.sharding.tables.user.table-strategy.standard.sharding-algorithm-name=userId-inline
spring.shardingsphere.rules.sharding.sharding-algorithms.userId-inline.type= INLINE
spring.shardingsphere.rules.sharding.sharding-algorithms.userId-inline.props.algorithm-expression=user_$->{userid%2}

#配置分布式id的算法
spring.shardingsphere.rules.sharding.tables.user.key-generate-strategy.column=userid
spring.shardingsphere.rules.sharding.tables.user.key-generate-strategy.key-generator-name=alg_snow
spring.shardingsphere.rules.sharding.key-generators.alg_snow.type=SNOWFLAKE
spring.shardingsphere.rules.sharding.key-generators.alg_snow.props.worker-id=1

5.本地运行测试

image.png 标红的地方表示执行的sql,数据生成到了数据库中,下面去数据库中查看下sharding的user_0

image.png sharing_tmp的user_1

image.png

6.答疑:

  • 为什么sharding的user_1和sharding_tmp的user_0,没有数据。因为分库是按照userId %2来分的,所以分到sharding的数据尾号都是偶数,分到sharding_tmp的都是奇数。同理分表也是按照这个方案来分的,导致每个库的表的数据有倾斜。后续可以修改算法来映射,这只是一个简单的例子。
  • application.properties的属性配置的算法有哪些,需要填入哪些属性。下一节我们开始回答
  1. 代码中application.properties配置了sharding-algorithms,示例如下:那么总共有哪些呢?
spring.shardingsphere.rules.sharding.tables.user.database-strategy.standard.sharding-column=userid
spring.shardingsphere.rules.sharding.tables.user.database-strategy.standard.sharding-algorithm-name= user_alg
spring.shardingsphere.rules.sharding.sharding-algorithms.user_alg.type= HASH_MOD
spring.shardingsphere.rules.sharding.sharding-algorithms.user_alg.props.sharding-count=2

我去官方网站找了下文档,idea中可以搜索 org.apache.shardingsphere.sharding.spi.ShardingAlgorithm:共有下列算法

org.apache.shardingsphere.sharding.algorithm.sharding.inline.InlineShardingAlgorithm
org.apache.shardingsphere.sharding.algorithm.sharding.mod.ModShardingAlgorithm
org.apache.shardingsphere.sharding.algorithm.sharding.mod.HashModShardingAlgorithm
org.apache.shardingsphere.sharding.algorithm.sharding.range.VolumeBasedRangeShardingAlgorithm
org.apache.shardingsphere.sharding.algorithm.sharding.range.BoundaryBasedRangeShardingAlgorithm
org.apache.shardingsphere.sharding.algorithm.sharding.datetime.AutoIntervalShardingAlgorithm
org.apache.shardingsphere.sharding.algorithm.sharding.datetime.IntervalShardingAlgorithm
org.apache.shardingsphere.sharding.algorithm.sharding.classbased.ClassBasedShardingAlgorithm
org.apache.shardingsphere.sharding.algorithm.sharding.complex.ComplexInlineShardingAlgorithm
org.apache.shardingsphere.sharding.algorithm.sharding.hint.HintInlineShardingAlgorithm

其中

image.png

具体的用法官方网站没有举例~(ps:真的很不负责)