2

1 概述

之前笔者写过两篇文章:

这里将两者结合起来,实现读写分离+分库分表的功能。关于环境的配置本文将进行简化叙述,详细可以参考前两篇文章。

2 环境

  • MySQL 8.0.29Docker
  • MyBatis Plus 3.5.1
  • MyBatis Plus Generator 3.5.2
  • Druid 1.2.10
  • ShardingSphereJDBC 5.1.1
  • Yitter 1.0.6(一个雪花id生成器)

3 数据库环境准备

由于环境准备不是本文的重点,一主一从的主从复制环境可以参考此处搭建。

准备好环境,本地启动两个MySQL,主节点环境:

  • 名字:master
  • 端口:3306
  • 数据库:两个库(test0test1
  • 数据表:六个表,每个库三个(test0.user0test0.user1test0.user2test1.user0test1.user1test1.user2

从节点环境:

  • 名字:slave
  • 端口:3307
  • 数据库:两个库(test0test1
  • 数据表:六个表,每个库三个(test0.user0test0.user1test0.user2test1.user0test1.user1test1.user2

主库配置文件:

[mysqld]
server-id=1
binlog-do-db=test0
binlog-do-db=test1

从库配置文件:

[mysqld]
server-id=2
replicate-do-db=test0
replicate-do-db=test1

主库+从库一共12个user库都是一样的,如下所示:

在这里插入图片描述

完整的数据库脚本和MySQL配置文件放在文末的源码链接中。

4 新建项目

新建项目并引入如下依赖:

  • Druid
  • MyBatis Plus starter
  • MyBaits Plus Generator
  • Velocity core
  • ShardingSphereJDBC
  • Yitter

Maven如下:

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.1</version>
</dependency>
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.5.2</version>
</dependency>
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.31</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.10</version>
</dependency>
<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
    <version>5.1.1</version>
</dependency>
<dependency>
    <groupId>com.github.yitter</groupId>
    <artifactId>yitter-idgenerator</artifactId>
    <version>1.0.6</version>
</dependency>

Gradle如下:

implementation 'com.alibaba:druid:1.2.10'
implementation 'com.baomidou:mybatis-plus-boot-starter:3.5.1'
implementation 'org.freemarker:freemarker:2.3.31'
implementation 'com.baomidou:mybatis-plus-generator:3.5.2'
implementation 'org.apache.shardingsphere:shardingsphere-jdbc-core-spring-boot-starter:5.1.1'
implementation 'com.github.yitter:yitter-idgenerator:1.0.6'

5 配置文件

spring:
  shardingsphere:
    mode:
      type: Memory                                                   # 内存模式,元数据保存在当前进程中
    datasource:
      names: master-test$->{0..1},slave-test$->{0..1}
      master-test0:                                                  # 跟上面的数据源对应
        type: com.alibaba.druid.pool.DruidDataSource                 # 连接池
        url: jdbc:mysql://127.0.0.1:3306/test0                       # 连接url
        username: root
        password: 123456
      master-test1:                                                  # 跟上面的数据源对应
        type: com.alibaba.druid.pool.DruidDataSource
        url: jdbc:mysql://127.0.0.1:3306/test1
        username: root
        password: 123456
      slave-test0:                                                   # 跟上面的数据源对应
        type: com.alibaba.druid.pool.DruidDataSource
        url: jdbc:mysql://127.0.0.1:3307/test0
        username: root
        password: 123456
      slave-test1:                                                   # 跟上面的数据源对应
        type: com.alibaba.druid.pool.DruidDataSource
        url: jdbc:mysql://127.0.0.1:3307/test1
        username: root
        password: 123456

    rules:                                                           # 配置分库分表以及读写分离的规则
      sharding:                                                      # 配置分库分表规则
        tables:
          user:                                                      # 这个可以随便取,问题不大
            actual-data-nodes: master-test$->{0..1}.user$->{0..2}    # 实际节点名称,格式为 库名$->{0..n1}.表名$->{0..n2}
                                                                     # 其中n1、n2分别为库数量-1和表数量-1
                                                                     # 也可以使用${0..n1}的形式,但是会与Spring属性文件占位符冲突
                                                                     # 所以使用$->{0..n1}的形式

            database-strategy:                                       # 分库策略
              standard:                                              # 标准分库策略
                sharding-column: age                                 # 分库列名
                sharding-algorithm-name: age-mod                     # 分库算法名字
            table-strategy:                                          # 分表策略
              standard:                                              # 标准分表策略
                sharding-column: id                                  # 分表列名
                sharding-algorithm-name: id-mod                      # 分表算法名字

        sharding-algorithms:                                         # 配置分库和分表的算法
          age-mod:                                                   # 分库算法名字
            type: MOD                                                # 算法类型为取模
            props:                                                   # 算法配置的键名,所有算法配置都需要在props下
              sharding-count: 2                                      # 分片数量
          id-mod:                                                    # 分表算法名字
            type: MOD                                                # 算法类型为取模
            props:                                                   # 算法配置的键名,所有算法配置都需要在props下
              sharding-count: 3                                      # 分片数量

      readwrite-splitting:                                           # 配置读写分离规则
        data-sources:                                                # 数据源
          master-test0:                                              # 这个可以随便取,带有区分意义即可,比如这里表示的是主库test0的规则
            type: Static                                             # 静态类型
            load-balancer-name: round_robin                          # 负载均衡算法名字
            props:                                                   # 具体读写数据源需要配置在props下
              write-data-source-name: master-test0                   # 写数据源
              read-data-source-names: slave-test0                    # 读数据源
          master-test1:
            type: Static                                             # 静态类型
            load-balancer-name: round_robin                          # 负载均衡算法名字
            props:                                                   # 具体读写数据源需要配置在props下
              write-data-source-name: master-test1                   # 写数据源
              read-data-source-names: slave-test1                    # 读数据源
        load-balancers:                                              # 负载均衡配置
          round_robin:                                               # 跟上面负载均衡算法的名字对应
            type: ROUND_ROBIN                                        # 负载均衡算法
    props:
      sql-show: true                                                 # 打印SQL

配置文件已经配上了详尽的注释,如果还有疑惑可以查看:

6 准备测试代码

使用MyBatis Plus Generator生成器类生成代码:

public class CodeGenerator {
    public static void main(String[] args) {
        final String USER_TABLE_NAME = "User";
        FastAutoGenerator.create("jdbc:mysql://localhost:3306/test0", "root", "123456")
                .globalConfig(builder ->
                        builder.author("author").outputDir(System.getProperty("user.dir") + "/src/main/java").build())
                .packageConfig(builder ->
                        builder.parent("com.example.demo").moduleName("user").build())
                .strategyConfig(builder ->
                        builder.addInclude("user0")
                                .entityBuilder().enableLombok().disableSerialVersionUID().idType(IdType.ASSIGN_ID).convertFileName((entityName -> USER_TABLE_NAME))
                                .mapperBuilder().convertMapperFileName((entityName -> USER_TABLE_NAME + "Mapper")).convertXmlFileName((entityName -> USER_TABLE_NAME + "Mapper")).enableMapperAnnotation()
                                .serviceBuilder().convertServiceFileName((entityName -> "I" + USER_TABLE_NAME + "Service")).convertServiceImplFileName((entityName -> USER_TABLE_NAME + "Service"))
                                .controllerBuilder().enableRestStyle().convertFileName(entityName -> USER_TABLE_NAME + "Controller"))
                .templateEngine(new FreemarkerTemplateEngine())
                .execute();
    }
}

实体类加上@Builder

//...
@Builder
public class User {
    //...
}

修改UserController类:

@RestController
@RequestMapping("/user")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class UserController {
    private final IUserService userService;
    private final Random random = new Random();

    @GetMapping("/insert")
    public boolean insert() {
        return userService.save(User.builder().name("name").age(random.nextInt(100) + 1).build());
    }

    @GetMapping("/select")
    public List<User> select() {
        return userService.list();
    }
}

同时新增一个雪花id生成器类(具体配置方法可以参考MyBatis Plus官方文档):

public class IdGenerator {
    @Bean
    public IdentifierGenerator identifierGenerator() {
        return entity -> YitIdHelper.nextId();
    }
}

7 测试

刷新几次插入页面:

http://localhost:8080/user/insert

从输出可以看到插入都是在主节点中进行的:

在这里插入图片描述

而查询的时候:

http://localhost:8080/user/select

输出如下:

在这里插入图片描述

是在从节点查询的。

8 参考代码

Java版:

Kotlin版:


氷泠
420 声望647 粉丝