Sharding-Jdbc实战之四:分库分表

目录

1、原理

1.1 为什么要分库分表

1.2 分库分表

1.3 不停机分库分表数据迁移

2、 配置

 2.1 逻辑表

2.2 分库分表数据节点 - actual-data-nodes

2.3 inline分片策略

2.4 分布式主键配置

3、实战

3.1 需求

3.2 表结构

3.3 配置文件

 3.4 测试类

 3.5 验证


1、原理

1.1 为什么要分库分表

分库分表目的:解决高并发,和数据量大的问题。

1、高并发情况下,会造成IO读写频繁,自然就会造成读写缓慢,甚至是宕机。一般单库不要超过2k并发,NB的机器除外。
2、数据量大的问题。主要由于底层索引实现导致,MySQL的索引实现为B+TREE,数据量其他,会导致索引树十分庞大,造成查询缓慢。第二,innodb的最大存储限制64TB。

要解决上述问题。最常见做法,就是分库分表。
分库分表的目的,是将一个表拆成N个表,就是让每个表的数据量控制在一定范围内,保证SQL的性能。 一个表数据建议不要超过500W。

1.2 分库分表

水平拆分:同一个表的数据拆到不同的库不同的表中。可以根据时间、地区或某个业务键维度,也可以通过hash进行拆分,最后通过路由访问到具体的数据。拆分后的每个表结构保持一致 

垂直拆分:就是把一个有很多字段的表给拆分成多个表,或者是多个库上去。每个库表的结构都不一样,每个库表都包含部分字段。一般来说,可以根据业务维度进行拆分,如订单表可以拆分为订单、订单支持、订单地址、订单商品、订单扩展等表;也可以,根据数据冷热程度拆分,20%的热点字段拆到一个表,80%的冷字段拆到另外一个表。

1.3 不停机分库分表数据迁移

一般数据库的拆分也是有一个过程的,一开始是单表,后面慢慢拆成多表。那么我们就看下如何平滑的从MySQL单表过度到MySQL的分库分表架构

(1) 利用MySQL+Canal做增量数据同步,利用分库分表中间件,将数据路由到对应的新表中
(2) 利用分库分表中间件,全量数据导入到对应的新表中
(3) 通过单表数据和分库分表数据两两比较,更新不匹配的数据到新表中
(4) 数据稳定后,将单表的配置切换到分库分表配置上

2、 配置

 2.1 逻辑表

 逻辑表是指:水平拆分的数据库或者数据表的相同路基和数据结构表的总称。比如用户数据根据订单id%2拆分为2个表,分别是:t_order0和t_order1。他们的逻辑表名是:t_order。在shardingjdbc中的定义方式如下:

spring:
  shardingsphere:
    sharding:
      tables:
        # t_order逻辑表名
        t_order:

2.2 分库分表数据节点 - actual-data-nodes

 tables:
        # t_order 逻辑表名
        t_order:
          # 数据节点:多数据源$->{0..N}.逻辑表名$->{0..N} 相同表
          actual-data-nodes: ds$->{0..2}.t_order$->{0..1}
           # 数据节点:多数据源$->{0..N}.逻辑表名$->{0..N} 不同表
          actual-data-nodes: ds0.t_order$->{0..1},ds1.t_order$->{2..4}
          # 指定单数据源的配置方式
          actual-data-nodes: ds0.t_order$->{0..4}
          # 全部手动指定
          actual-data-nodes: ds0.t_order0,ds1.t_order0,ds0.t_order1,ds1.t_order1,

 数据分片是最小单元。由数据源名称和数据表组成,比如:ds0.t_order0。

寻找规则如下:

2.3 inline分片策略

spring.shardingsphere.sharding.tables.t_order.actual-data-nodes=ds$->{0..1}.t_order$->{0..1}
#数据源分片策略
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.sharding-column=user_id
#数据源分片算法
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.algorithm-expression=ds$->{user_id%2}
#表分片策略
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.sharding-column=order_id
#表分片算法
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.algorithm-expression=t_order$->{order_id%2}

上面的配置通过user_id%2来决定具体数据源,通过order_id%2来决定具体表

insert into t_order(user_id,order_id) values(2,3),user_id%2 = 0使用数据源ds0,order_id%2 = 1使用t_order1,insert语句最终操作的是数据源ds0的t_order1表

2.4 分布式主键配置

Sharding-Jdbc可以配置分布式主键生成策略。默认使用雪花算法(snowflake),生成64bit的长整型数据,也支持UUID的方式

#主键的列名
spring.shardingsphere.sharding.tables.t_order.key-generator.column=id
#主键生成策略
spring.shardingsphere.sharding.tables.t_order.key-generator.type=SNOWFLAKE

3、实战

3.1 需求

对1000w的用户数据进行分库分表,对用户表的数据进行分表和分库的操作。根据年龄奇数存储在t_user1,偶数t_user0,同时性别奇数存储在ds1,偶数ds0

3.2 表结构

CREATE TABLE `t_user0` (
  `id` bigint(20) DEFAULT NULL,
  `nickname` varchar(200) DEFAULT NULL,
  `password` varchar(200) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `sex` int(11) DEFAULT NULL,
  `birthday` varchar(100) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE `t_user1` (
  `id` bigint(20) DEFAULT NULL,
  `nickname` varchar(200) DEFAULT NULL,
  `password` varchar(200) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `sex` int(11) DEFAULT NULL,
  `birthday` varchar(100) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

两个数据库中都包含t_user0t_user1两张表

3.3 配置文件

application.yml

server:
  port: 8085
spring:
  main:
    allow-bean-definition-overriding: true
  shardingsphere:
    # 参数配置,显示sql
    props:
      sql:
        show: true
    # 配置数据源
    datasource:
      # 给每个数据源取别名,下面的ds1,ds2任意取名字
      names: ds0,ds1
      # 给master-ds1每个数据源配置数据库连接信息
      ds0:
        # 配置druid数据源
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://192.168.79.138:3306/shardingJdbc?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT
        username: root
        password: 123456
        maxPoolSize: 100
        minPoolSize: 5
      # 配置ds2-slave
      ds1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://192.168.79.138:3307/shardingJdbc?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT
        username: root
        password: 123456
        maxPoolSize: 100
        minPoolSize: 5
    # 配置默认数据源ds0
    sharding:
      # 默认数据源,主要用于写,注意一定要配置读写分离 ,注意:如果不配置,那么就会把三个节点都当做从slave节点,新增,修改和删除会出错。
      default-data-source-name: ds0
      # 配置分表的规则
      tables:
        # t_user 逻辑表名
        t_user:
          # 数据节点:数据源$->{0..N}.逻辑表名$->{0..N}
          actual-data-nodes: ds$->{0..1}.t_user$->{0..1}
          # 拆分库策略
          database-strategy:
            inline:
              sharding-column: sex    # 分片字段(分片键)
              algorithm-expression: ds$->{sex % 2} # 分片算法表达式
          # 拆分表策略
          table-strategy:
            inline:
              sharding-column: age    # 分片字段(分片键)
              algorithm-expression: t_user$->{age % 2} # 分片算法表达式
          key-generator:
            # 主键的列名
            column: id
            type: SNOWFLAKE
# 整合mybatis的配置XXXXX
mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.xuexiangban.shardingjdbc.entity

 3.4 测试类

package com.workhard.shardingjdbc;

import com.workhard.shardingjdbc.entity.User;
import com.workhard.shardingjdbc.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.Random;

@SpringBootTest
class ShardingJdbcApplicationTests {

    @Autowired
    private UserMapper userMapper;

    /**
     * sex:奇数
     * age:奇数
     * ds1.t_user1
     */
    @Test
    public void test01() {
        User user = new User();
        user.setNickname("zhangsan" + new Random().nextInt());
        user.setPassword("123456");
        user.setAge(17);
        user.setSex(1);
        user.setBirthday("1997-12-03");
        userMapper.addUser(user);
    }

    /**
     * sex:奇数
     * age:偶数
     * ds1.t_user0
     */
    @Test
    public void test02() {
        User user = new User();
        user.setNickname("lisi" + new Random().nextInt());
        user.setPassword("123456");
        user.setAge(18);
        user.setSex(1);
        user.setBirthday("1997-12-03");
        userMapper.addUser(user);
    }

    /**
     * sex:偶数
     * age:奇数
     * ds0.t_user1
     */
    @Test
    public void test03() {
        User user = new User();
        user.setNickname("wangwu" + new Random().nextInt());
        user.setPassword("123456");
        user.setAge(17);
        user.setSex(2);
        user.setBirthday("1997-12-03");
        userMapper.addUser(user);
    }

    /**
     * sex:偶数
     * age:偶数
     * ds0.t_user0
     */
    @Test
    public void test04() {
        User user = new User();
        user.setNickname("zhaoliu" + new Random().nextInt());
        user.setPassword("123456");
        user.setAge(18);
        user.setSex(2);
        user.setBirthday("1997-12-03");
        userMapper.addUser(user);
    }
}

 3.5 验证

  • 2
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值