Sharding-jdbc是在java代码层面实现分库分表,不需要额外的软件安装

关于分库分表阿里巴巴开发手册上有明确说明: 单表行数超过500万行或者单表容量超过2GB,才推荐进行分库分表 如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表

概念

垂直拆分

数据库设计时优先考虑的

垂直分表

把表的一部分字段数据存到一张新表中
一张表的数据分到两个表中存储,由此提高IO效率
实现垂直分表不需要额外的组件实现,更多是数据库设计层面解决的问题
image-20201207150433310.png

垂直分库

把单一数据库按照业务进行划分,多个业务对应多个数据库
image-20201207150941530.png

水平拆分

水平分库

比如:根据id把不同的数据存到不同的库中
image-20201207151836043.png

水平分表

在同一个数据库中根据行数创建多个相同的表,这样一个表存放的数据就没那么大
image-20201207152120697.png

SpringBoot整合Sharding-Jdbc使用

SpringBoot整合主要改配置文件就可以使用了

Maven依赖

使用springboot 2.2.4版本为例

使用mybatisplus 3.3.1整合sharding-jdbc 4.0.0-RC1,使用druid连接池

  1. <properties>
  2. <java.version>1.8</java.version>
  3. <!-- MP版本配置 -->
  4. <mybatis-plus-boot-starter.version>3.3.1</mybatis-plus-boot-starter.version>
  5. <sharding-jdbc.version>4.0.0-RC1</sharding-jdbc.version>
  6. <druid-version>1.1.20</druid-version>
  7. </properties>
  8. <dependencies>
  9. <dependency>
  10. <groupId>org.apache.shardingsphere</groupId>
  11. <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
  12. <version>${sharding-jdbc.version}</version>
  13. </dependency>
  14. <dependency>
  15. <groupId>com.alibaba</groupId>
  16. <artifactId>druid-spring-boot-starter</artifactId>
  17. <version>${druid-version}</version>
  18. </dependency>
  19. <dependency>
  20. <groupId>mysql</groupId>
  21. <artifactId>mysql-connector-java</artifactId>
  22. </dependency>
  23. <!-- 定义mybatis-plus依赖 -->
  24. <dependency>
  25. <groupId>com.baomidou</groupId>
  26. <artifactId>mybatis-plus-boot-starter</artifactId>
  27. <version>${mybatis-plus-boot-starter.version}</version>
  28. </dependency>
  29. </dependencies>

整合MybatisPlus注意事项

application.yml配置

mybatis-plus:
  mapper-locations: classpath:mapper/**/*.xml
  configuration:
    # 是否开启自动驼峰命名规则映射:从数据库列名到Java属性驼峰命名的类似映射
    map-underscore-to-camel-case: true
    # 如果查询结果中包含空值的列,则 MyBatis 在映射的时候,不会映射这个字段
    call-setters-on-nulls: false

POJO类注意点

比如我需要对course_1和course_2表进行操作

但是我只需要创建一个po类就行了,course_1和course_2都只对应一个CoursePO类

与yml配置文件配置的逻辑表对应,指定表名为course就行,虽然数据库中并没有名为course的表

@TableName("course")
public class CoursePO implements Serializable {
    //...
}

在application.yml中要注意写上此配置

spring:
  main:
    # 一个实体类对应两张表,覆盖
    allow-bean-definition-overriding: true

mapper.xml注意点

CoursePO类对应course_1和course_2两张表

操作时也只需要与yml配置文件对应,指定表名为course就行

    <select id="findById" resultType="com.halayang.course.po.CoursePO">
        select * from course where id = #{id}
    </select>

水平分表实现

在一个数据库中根据策略将数据存放到结构相同的两个表中

demo设计:在本地名为sharding-learning数据库中创建两个结构一样的表course_1表和course_2表

application.yml配置

官方文档给的properties的配置,但是分库分表所要写的配置会很多,yml更有层次性,个人更喜欢yml

一定要注意层级!!!

# 水平分表
spring:
  main:
    # 一个实体类对应两张表,覆盖
    allow-bean-definition-overriding: true
  shardingsphere:
    datasource:
      # 配置数据源,给数据源起名称 可以起多个
      names: m1
      m1:
        driver-class-name: com.mysql.cj.jdbc.Driver
        username: root
        password: 1234
        type: com.alibaba.druid.pool.DruidDataSource
        url: jdbc:mysql://localhost:3306/sharding-learning?useSSL=true&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&autoReconnect=true&failOverReadOnly=false&allowMultiQueries=true&serverTimezone=GMT%2B8
    props:
      sql:
          # 显示解析的sql
        show: true
    sharding:
      tables:
        course:
          # 指定 course 表分布情况,配置表在哪个数据库里面,表名称都是什么 m1.course_1 , m1.course_2
          actual-data-nodes: m1.course_$->{1..2}
          key-generator:
            column: id
            # 主键id使用雪花算法,自动插入
            type: SNOWFLAKE
          table-strategy:
            inline:
              # 指定主键列命
              sharding-column: id
              # 根据id的奇偶性来确定存放到哪个表中
              algorithm-expression: course_$->{id % 2 + 1}

水平分库实现

根据策略使数据存放到不同数据库的相同表中

demo设计:建两个数据库edu_db1和edu_db2,每个数据库都有course_1和course_2表

application.yml配置

在此将水平分表和水平分库同时使用

# 水平分库
spring:
  main:
    # 一个实体类对应两张表,覆盖
    allow-bean-definition-overriding: true
  shardingsphere:
    datasource:
      # 配置数据源,给数据源起名称 可以起多个
      names: m1,m2
      # 第一个数据源
      m1:
        driver-class-name: com.mysql.cj.jdbc.Driver
        username: root
        password: 1234
        type: com.alibaba.druid.pool.DruidDataSource
        url: jdbc:mysql://localhost:3306/edu_db1?useSSL=true&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&autoReconnect=true&failOverReadOnly=false&allowMultiQueries=true&serverTimezone=GMT%2B8
      # 第一个数据源
      m2:
        driver-class-name: com.mysql.cj.jdbc.Driver
        username: root
        password: 1234
        type: com.alibaba.druid.pool.DruidDataSource
        url: jdbc:mysql://localhost:3306/edu_db2?useSSL=true&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&autoReconnect=true&failOverReadOnly=false&allowMultiQueries=true&serverTimezone=GMT%2B8
    props:
      sql:
        # 显示解析的sql
        show: true
    sharding:
      # 所有表都按照此规则做分库
      # default-database-strategy:
      #   inline:
      #   # 根据user_id奇偶数分库
      #     sharding-column: user_id
      #     algorithm-expression: m$->{user_id % 2}

      tables:
        # 指定表名
        course:
          # 在指定表下制定分库策略
          database-strategy:
            inline:
              # 根据user_id奇偶数分库
              sharding-column: user_id
              algorithm-expression: m$->{user_id % 2 + 1}
          # 指定数据库分库,以及数据库里表的分布
          actual-data-nodes: m$->{1..2}.course_$->{1..2}
          # 主键生成策略
          key-generator:
            column: id
            type: SNOWFLAKE
          table-strategy:
            # 指定分表策略
            inline:
              # 根据id奇偶数分表
              algorithm-expression: course_$->{id % 2 + 1}
              sharding-column: id

垂直分库实现

根据业务需求将不同的表存放在不同的数据库中

现有两个数据库edu_db1和edu_db2 新增一个t_user表只存放在edu_db1中 所以涉及到t_user表的操作时只需要访问edu_db1就行了 course_1和course_2的分库分表操作同上

application.yml配置

# 垂直分库
spring:
  main:
    # 一个实体类对应两张表,覆盖
    allow-bean-definition-overriding: true
  shardingsphere:
    datasource:
      # 配置数据源,给数据源起名称 可以起多个
      names: m1,m2
      # 第一个数据源
      m1:
        driver-class-name: com.mysql.cj.jdbc.Driver
        username: root
        password: 1234
        type: com.alibaba.druid.pool.DruidDataSource
        url: jdbc:mysql://localhost:3306/edu_db1?useSSL=true&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&autoReconnect=true&failOverReadOnly=false&allowMultiQueries=true&serverTimezone=GMT%2B8
      # 第一个数据源
      m2:
        driver-class-name: com.mysql.cj.jdbc.Driver
        username: root
        password: 1234
        type: com.alibaba.druid.pool.DruidDataSource
        url: jdbc:mysql://localhost:3306/edu_db2?useSSL=true&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&autoReconnect=true&failOverReadOnly=false&allowMultiQueries=true&serverTimezone=GMT%2B8
    props:
      sql:
        # 显示解析的sql
        show: true
    sharding:
      tables:
        # 指定表名
        course:
          # 指定数据库分库,以及数据库里表的分布
          actual-data-nodes: m$->{1..2}.course_$->{1..2}
          # 主键生成策略
          key-generator:
            column: id
            type: SNOWFLAKE
          table-strategy:
            # 指定分表策略
            inline:
              sharding-column: id
              # 根据id奇偶数分表
              algorithm-expression: course_$->{id % 2 + 1}

        # 指定t_user表
        t_user:
          # 指定user的库和表
          actual-data-nodes: m1.t_user
          # 主键生成策略
          key-generator:
            column: id
            type: SNOWFLAKE
          table-strategy:
            # 指定分表策略
            inline:
              # 指定库和表 不对此表做分表操作 配置比较简单
              sharding-column: id
              algorithm-expression: t_user

公共表配置

有些表数据变化不是很大,不需要进行分库分表策略

新增一个t_udict表作为公共表 对垂直分库的配置文件进行一些小改动,加一点配置

application.yml配置

# 垂直分库
spring:
  main:
    # 一个实体类对应两张表,覆盖
    allow-bean-definition-overriding: true
  shardingsphere:
    datasource:
      # 配置数据源,给数据源起名称 可以起多个
      names: m1,m2
      # 第一个数据源
      m1:
        driver-class-name: com.mysql.cj.jdbc.Driver
        username: root
        password: 1234
        type: com.alibaba.druid.pool.DruidDataSource
        url: jdbc:mysql://localhost:3306/edu_db1?useSSL=true&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&autoReconnect=true&failOverReadOnly=false&allowMultiQueries=true&serverTimezone=GMT%2B8
      # 第一个数据源
      m2:
        driver-class-name: com.mysql.cj.jdbc.Driver
        username: root
        password: 1234
        type: com.alibaba.druid.pool.DruidDataSource
        url: jdbc:mysql://localhost:3306/edu_db2?useSSL=true&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&autoReconnect=true&failOverReadOnly=false&allowMultiQueries=true&serverTimezone=GMT%2B8
    props:
      sql:
        # 显示解析的sql
        show: true
    sharding:
      tables:
        # 指定表名
        course:
          # 指定数据库分库,以及数据库里表的分布
          actual-data-nodes: m$->{1..2}.course_$->{1..2}
          # 主键生成策略
          key-generator:
            column: id
            type: SNOWFLAKE
          table-strategy:
            # 指定分表策略
            inline:
              # 根据id奇偶数分表
              algorithm-expression: course_$->{id % 2 + 1}
              sharding-column: id
        t_user:
          # 指定user的库和表
          actual-data-nodes: m1.t_user
          # 主键生成策略
          key-generator:
            column: id
            type: SNOWFLAKE
          table-strategy:
            # 指定分表策略
            inline:
              # 指定库和表
              sharding-column: id
              algorithm-expression: t_user
        # 公共表配置
        t_udict:
          # 指定数据库和表
          actual-data-nodes: m1.t_udict
          key-generator:
            column: id
            type: SNOWFLAKE
      # 指定公共表
      broadcast-tables: t_udict

读写分离实现

首先要对mysql进行主从复制的配置

https://www.yuque.com/kelisidiyanuodi/dttdng/unkied

搭建完成之后使用Sharding-jdbc实现读写分离的操作

开启两台虚拟机启动mysql服务 两个服务器上的mysql都有一个名为edu_db1的数据库 创建一个t_user表,对t_user表的操作实现读写分离

application.yml配置

# 读写分离配置
# select执行从库 insert update delete执行从库
spring:
  main:
    # 一个实体类对应两张表,覆盖
    allow-bean-definition-overriding: true
  shardingsphere:
    datasource:
      # 配置数据源,给数据源起名称 可以起多个
      names: m1,s1
      m1:
        driver-class-name: com.mysql.cj.jdbc.Driver
        username: root
        password: 1234
        type: com.alibaba.druid.pool.DruidDataSource
        url: jdbc:mysql://192.168.246.103:3306/edu_db1?useSSL=true&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&autoReconnect=true&failOverReadOnly=false&allowMultiQueries=true&serverTimezone=GMT%2B8
        # 第一个数据源
      s1:
        driver-class-name: com.mysql.cj.jdbc.Driver
        username: root
        password: 1234
        type: com.alibaba.druid.pool.DruidDataSource
        url: jdbc:mysql://192.168.246.104:3306/edu_db1?useSSL=true&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&autoReconnect=true&failOverReadOnly=false&allowMultiQueries=true&serverTimezone=GMT%2B8
    props:
      sql:
        # 显示解析的sql
        show: true
    sharding:
      master-slave-rules:
        # 给数据库取个名字
        edu_db1:
          # 主库
          master-data-source-name: m1
          # 从库
          slave-data-source-names: s1
      tables:
        t_user:
          # 对主从库的表进行操作
          actual-data-nodes: edu_db1.t_user
          # 主键生成策略
          key-generator:
            column: id
            type: SNOWFLAKE
          table-strategy:
            # 指定分表策略
            inline:
              # 指定库和表
              sharding-column: id
              algorithm-expression: t_user

待解决的问题

demo都是用阿里巴巴的druid连接池实现的
如果改为使用Hikari连接池就会启动报错

demo使用的sharding-jdbc版本是4.0.0-RC1,升级到4.0.0-RC2时启动报错 UnsatisfiedDependencyException


4.0.0-RC2时启动报错 解决方案

在上面的maven依赖中

删除druid starter依赖
解决自动配置依赖冲突

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>${druid-version}</version>
        </dependency>

改用druid依赖即可

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>${druid-version}</version>
        </dependency>

使用Hikari连接池不会出现这种问题

HikariCP连接池 解决方案

只需要在application.yml配置文件中微调
在涉及数据库连接池的配置中只需要把url改成jdbc-url即可

在此放出hikari的一些配置

spring:
  shardingsphere:
    datasource:
      # 配置数据源,给数据源起名称 可以起多个
      names: m1,m2
      # 第一个数据源
      m1:
        driver-class-name: com.mysql.cj.jdbc.Driver
        username: root
        password: 1234
        type: com.zaxxer.hikari.HikariDataSource
        jdbc-url: jdbc:mysql://localhost:3306/edu_db1?useSSL=true&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&autoReconnect=true&failOverReadOnly=false&allowMultiQueries=true&serverTimezone=GMT%2B8
        hikari:
          connection-timeout: 30000
          minimum-idle: 5
          maximum-pool-size: 30
          idle-timeout: 30000
          max-lifetime: 120000
          auto-commit: true
          connection-test-query: SELECT 1
      # 第二个数据源
      m2:
        driver-class-name: com.mysql.cj.jdbc.Driver
        username: root
        password: 1234
        type: com.zaxxer.hikari.HikariDataSource
        jdbc-url: jdbc:mysql://localhost:3306/edu_db2?useSSL=true&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&autoReconnect=true&failOverReadOnly=false&allowMultiQueries=true&serverTimezone=GMT%2B8
        hikari:
          connection-timeout: 30000
          minimum-idle: 5
          maximum-pool-size: 30
          idle-timeout: 30000
          max-lifetime: 120000
          auto-commit: true
          connection-test-query: SELECT 1