分库分表之ShardingSphere(一)

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 分库分表之ShardingSphere

分库分表诞生的前景

随着系统用户运行时间还有用户数量越来越多,整个数据库某些表的体积急剧上升,导致CRUD的时候性能严重下降,还容易造成系统假死

这时候系统都会做一些基本的优化,比如加索引、缓存、读写分离/主从复制,增删改都走主库,查询走从库。但是这样没法提升主库写的能力,因为主库只有一个。这时候就要考虑分库分表了,一般数据库在设计的时候就会提前考虑到是否有分库分表场景需要,避免后期带来迁移的问题,而且最好对表的查询足够简单,尽量避免跨表跨库查询。

阿里的开发规范中建议预估三年内单表数据量上500W,或者大小上2G,就要考虑分库分表

分库分表的方式(垂直拆分,水平复制)

一般系统模块都是访问同一个数据库资源,所有的表都存放在一个库里面

1.垂直拆分

1.1 垂直分库

把单一的数据库进行业务划分,专库专表

经过垂直拆分之后,每个模块都使用各自独立的数据库,减轻了数据库的压力,业务也更加清晰,拓展也更容易了,但是会增加连表查询以及事务处理的复杂度,无法解决单表数据量太大的问题

1.2 垂直分表

垂直拆分表主要解决一张表太多字段某个字段存储值为大文本会对io查询有损耗所以把本来属于同一个对象描述的属性拆分多个表,分布式微服务分库分表尽量不要严苛遵守数据库的3大范式,可参考不可严格遵循

相当于把一个大表根据字段拆分成多个小表 ,一个10w数据的表,变成两个10w数据的表

这样拆分的好处就是,假如只显示列表,不需要显示详细信息就很方便,例如一个订单是包含很多信息的,但是在后台通常不需要去获取订单的详情信息用作展示,一般只需要展示概要信息:下单用户、下单时间、金额等等重要信息。于是可以把一个订单表垂直拆分为两个表来处理

2.水平拆分

2.1 水平分库

把一个数据库分散成多个结构相同的数据库,本质就是复制操作

2.2 水平分表

一个表数据量太大,将一个表按不同的条件分散多个表中,把1000w的表拆分为两个500w的表

分表的规则有很多种:

  1. 取范围
    根据时间范围或者id范围分布到不同的库中,例如把2020年前的数据放到一个表中,之后的数据放到一个表中。把用户ID 0~100000放到一个表中,100000~200000的数据放到一个表中。
    优点:使用分片字段范围查询比较方便
    缺点:某段范围内热点数据可能被频繁读写,其他数据很少被查询
  2. 数值hash取模运算
    根据某个字段进行运算均匀的分配到不同的表中
    优点:分散比较均匀,不容易存在热点数据
    缺点:数据太分散,导致范围查询比较麻烦,需要查询分库之后再合并

水平拆分的时候会导致多库多表的联合查询难度变大,以及多数据源管理的问题

分库分库中间件

ShardingSphere

官网:https://shardingsphere.apache.org/

ShardingSphere是一个关系型数据库中间件,它由Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar(计划中)这3款相互独立的产品组成,主要提供数据库分片以及分布式事务

Sharding-JDBC

Sharding-JDBC简化分库分表之后数据相关操作,它一个轻量级的Java框架,是增强版的JDBC驱动,以jar包的形式提供引入非常简单,适用于很多ORM框架以及数据库连接池,Sharding-JDBC不做分库分表,主要做两个功能:数据分片和读写分离,简化分库分表后对数据的操作

实现水平分表

环境搭建:SpringBoot+mybatisPlus

1、首先创建一个springboot项目,引入相关依赖jar包

<!-- Sharding-JDBC -->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
            <version>4.0.0-RC1</version>
        </dependency>
        <!-- Mysql驱动依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.29</version>
        </dependency>
        <!-- Druid连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.8</version>
        </dependency>
        <!--mybatisPlus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

2、创建数据库表结构

创建一个mall数据库,然后里面创建两张表分别为:order0、order1

CREATE DATABASE `mall` 
CREATE TABLE `orders0` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `order_number` varchar(50) NOT NULL,
  `create_time` date NOT NULL,
  `creater` varchar(20) NOT NULL,
  `money` decimal(10,0) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `orders1` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `order_number` varchar(50) NOT NULL,
  `create_time` date NOT NULL,
  `creater` varchar(20) NOT NULL,
  `money` decimal(10,0) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

3、约定分片规则

如果订单号order_number为奇数添加到orders0,否则添加到orders1

4、建立相关实体类、DAO

/**
 * @author yanglingcong
 */
@Data
public class Orders implements Serializable {
    private Long id;
    private String orderNumber;
    private Timestamp createTime;
    private String creater;
    private double money;
}

OrderDao

/**
 * @author yanglingcong
 */
@Mapper
public interface OrderDao extends BaseMapper<Orders> {
}

5、配置分片策略以及数据源

application.properties

server.port=8099
spring.application.name=sharding-jdbc-demo
spring.profiles.active=dev
#指定数据库连接信息
spring.shardingsphere.datasource.names=db0
spring.shardingsphere.datasource.db0.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.db0.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.db0.url=jdbc:mysql://xxx:3306/mall?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.db0.username=root
spring.shardingsphere.datasource.db0.password=xxx
# 一个实体类对应两张表,覆盖
spring.main.allow-bean-definition-overriding=true
#指定主键id列的生成策略 SNOWFLAKE表示雪花
spring.shardingsphere.sharding.tables.orders.key-generator.column=id
spring.shardingsphere.sharding.tables.orders.key-generator.type=SNOWFLAKE
#指定表的分布  order数据库的order1和order2表
spring.shardingsphere.sharding.tables.orders.actual-data-nodes=db0.orders$->{0..1}
#指定分片策略  约定根据订单号order_number分片 奇数添加到`order1`,否则添加到`order2`  表索引从1开始的,所以加上1
spring.shardingsphere.sharding.tables.orders.table-strategy.inline.sharding-column=id
spring.shardingsphere.sharding.tables.orders.table-strategy.inline.algorithm-expression=orders$->{id % 2}
#sql输出日志
spring.shardingsphere.props.sql.show=true

6、测试生成

@RunWith(SpringRunner.class)
@SpringBootTest
public class ShardingJdbcDemoApplicationTests {
    @Autowired
    OrderDao orderDao;
    @Test
    void insertTest() {
        for (int i = 0; i < 1; i++) {
            Orders order=new Orders();
            order.setCreater("ylc");
            order.setOrderNumber("6");
            order.setCreateTime(new Timestamp(System.currentTimeMillis()));
            order.setMoney(30.01);
            orderDao.insert(order);
        }
    }
    @Test
    void  selectTest(){
        QueryWrapper<Orders> queryWrapper=new QueryWrapper<>();
        queryWrapper.eq("id",1553218859002007553L);
        Orders orders = orderDao.selectOne(queryWrapper);
        System.out.println(orders);
    }
}

最后根据生成的雪花id插入到了数据库中

查询也会根据雪花id找到对应的表

sharding-jdbc水平分表就是提前做好分表,然后配置分表规则,就可以让数据插入在不同的表中了,这里只是指定了一个数据源,还可以指定多个数据源

实现水平分库

水平分库规则为:根据表里面的订单号字段orderNumber,奇数就路由到mall_1库,偶数路由到mall_2库

1、更改配置文件

server.port=8099
spring.application.name=sharding-jdbc-demo
spring.profiles.active=dev
spring.shardingsphere.sharding.default-data-source-name=db1
#指定数据库连接信息
spring.shardingsphere.datasource.names=db1,db2
spring.shardingsphere.datasource.db1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.db1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.db1.url=jdbc:mysql://xxx:3306/mall_1?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.db1.username=root
spring.shardingsphere.datasource.db1.password=xxx
spring.shardingsphere.datasource.db2.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.db2.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.db2.url=jdbc:mysql://xxx:3306/mall_2?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.db2.username=root
spring.shardingsphere.datasource.db2.password=xxx
# 一个实体类对应两张表,覆盖
spring.main.allow-bean-definition-overriding=true
#指定主键id列的生成策略 SNOWFLAKE表示雪花
spring.shardingsphere.sharding.tables.orders.key-generator.column=id
spring.shardingsphere.sharding.tables.orders.key-generator.type=SNOWFLAKE
#指定库的分布
spring.shardingsphere.sharding.tables.orders.actual-data-nodes=db$->{1..2}.orders0
#指定数据库分表策略  根据order_number奇数就路由到mall_1库,偶数路由到mall_2库
spring.shardingsphere.sharding.tables.orders.database-strategy.inline.sharding-column=order_number
spring.shardingsphere.sharding.tables.orders.database-strategy.inline.algorithm-expression=db$->{order_number % 2+1}
#sql输出日志
spring.shardingsphere.props.sql.show=true

2、更改实体

/**
 * @author yanglingcong
 */
@Data
public class Orders implements Serializable {
    private Long id;
    private long orderNumber;
    private Timestamp createTime;
    private String creater;
    private double money;
}

3、sql脚本

CREATE DATABASE `mall_0`
CREATE TABLE `orders0` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `order_number` bigint NOT NULL,
  `create_time` date NOT NULL,
  `creater` varchar(20) NOT NULL,
  `money` decimal(10,0) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE DATABASE `mall_1`
CREATE TABLE `orders0` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `order_number` bigint NOT NULL,
  `create_time` date NOT NULL,
  `creater` varchar(20) NOT NULL,
  `money` decimal(10,0) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

4、插入数据

@Test
    void insertTest() {
        for (int i = 0; i < 3; i++) {
            Orders order=new Orders();
            order.setCreater("ylc");
            order.setOrderNumber(i);
            order.setCreateTime(new Timestamp(System.currentTimeMillis()));
            order.setMoney(30.01);
            orderDao.insert(order);
        }
    }
    @Test
    void  selectTest(){
        QueryWrapper<Orders> queryWrapper=new QueryWrapper<>();
        queryWrapper.eq("order_number",1553314824824299522L);
        Orders orders = orderDao.selectOne(queryWrapper);
        System.out.println(orders);
    }

很简单就实现了分库功能,分库分表就是把这两种方式结合到了一起

分库分表查询,分库分表字段为必传字段,否则不知道去哪个数据库哪张表去查询数据,所以根据非主键查询的不支持。

实现垂直分库

垂直分库相当于专库专表,可以使用多数据源来处理,也可以通过sharding-Jdbc

垂直分库的规则为:往订单表orders插入数据,会自动找到订单表所在的库mall进行操作。往用户表插入数据,会自动找到所在库User进行操作

1、数据库脚本

CREATE DATABASE `user`
CREATE TABLE `user`.userInfo (
  id BIGINT auto_increment NOT NULL,
  user_name varchar(20) NOT NULL,
  password varchar(50) NOT NULL,
  CONSTRAINT userinfo_PK PRIMARY KEY (id)
)
ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_0900_ai_ci
AUTO_INCREMENT=1;
CREATE DATABASE `user`
CREATE DATABASE `mall`
CREATE TABLE `orders` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `order_number` bigint NOT NULL,
  `create_time` date NOT NULL,
  `creater` varchar(20) NOT NULL,
  `money` decimal(10,0) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

2、创建相关实体类

UserInfo

@Data
public class UserInfo {
    private Long id;
    private String userName;
    private String password;
}

Orders

@Data
public class Orders implements Serializable {
    private Long id;
    private long orderNumber;
    private Timestamp createTime;
    private String creater;
    private double money;
}

3、Dao类

@Mapper
public interface UserInfoDao extends BaseMapper<UserInfo> {
}

4、配置垂直分库策略

server.port=8099
spring.application.name=sharding-jdbc-demo
spring.profiles.active=dev
spring.shardingsphere.sharding.default-data-source-name=db1
#指定数据库连接信息
spring.shardingsphere.datasource.names=db1,db2
spring.shardingsphere.datasource.db1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.db1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.db1.url=jdbc:mysql://xxx:3306/mall?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.db1.username=root
spring.shardingsphere.datasource.db1.password=xxx
spring.shardingsphere.datasource.db2.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.db2.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.db2.url=jdbc:mysql://xxx:3306/user?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.db2.username=root
spring.shardingsphere.datasource.db2.password=xxx
# 一个实体类对应两张表,覆盖
spring.main.allow-bean-definition-overriding=true
#指定主键id列的生成策略 SNOWFLAKE表示雪花
spring.shardingsphere.sharding.tables.orders.key-generator.column=id
spring.shardingsphere.sharding.tables.orders.key-generator.type=SNOWFLAKE
spring.shardingsphere.sharding.tables.userInfo.key-generator.column=id
spring.shardingsphere.sharding.tables.userInfo.key-generator.type=SNOWFLAKE
#指定orders表所在的库 userInfo表所在的库
spring.shardingsphere.sharding.tables.orders.actual-data-nodes=db1.orders
spring.shardingsphere.sharding.tables.userInfo.actual-data-nodes=db2.userInfo
#指定orders表分片策略
spring.shardingsphere.sharding.tables.orders.table-strategy.inline.sharding-column=id
spring.shardingsphere.sharding.tables.orders.table-strategy.inline.algorithm-expression=orders
#指定数userInfo分片策略
spring.shardingsphere.sharding.tables.userInfo.table-strategy.inline.sharding-column=id
spring.shardingsphere.sharding.tables.userInfo.table-strategy.inline.algorithm-expression=userInfo
#sql输出日志
spring.shardingsphere.props.sql.show=true

5、插入数据

Orders表在mall库

@Test
    void insertOrdersTest() {
            Orders order=new Orders();
            order.setCreater("ylc");
            order.setOrderNumber(1);
            order.setCreateTime(new Timestamp(System.currentTimeMillis()));
            order.setMoney(30.01);
            orderDao.insert(order);
    }

UserInfo表在user库

@Test
    void insertUserTest() {
        UserInfo userInfo=new UserInfo();
        userInfo.setUserName("ylc");
        userInfo.setPassword("123456");
        userInfoDao.insert(userInfo);
    }

这样就实现了不同的表插入了不同的数据库,不过使用多数据源方式会更方便


相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
Java 关系型数据库 中间件
分库分表(3)——ShardingJDBC实践
分库分表(3)——ShardingJDBC实践
326 0
分库分表(3)——ShardingJDBC实践
|
4月前
|
Java Apache Maven
Apache ShardingSphere 实现分库分表及读写分离
Apache ShardingSphere 实现分库分表及读写分离
79 0
|
6月前
|
SQL 负载均衡 算法
使用ShardingJDBC实现分库分表
使用ShardingJDBC实现分库分表
|
5月前
|
算法 Java 数据库连接
【分库分表】基于mysql+shardingSphere的分库分表技术
【分库分表】基于mysql+shardingSphere的分库分表技术
171 0
|
SQL 存储 算法
SpringBoot整合ShardingSphere实现分表分库&读写分离&读写分离+数据库分表
SpringBoot整合ShardingSphere实现分表分库&读写分离&读写分离+数据库分表
1661 0
SpringBoot整合ShardingSphere实现分表分库&读写分离&读写分离+数据库分表
|
关系型数据库 MySQL Java
ShardingSphere 实战之读写分离
采用 ShardingShpere 的 Sharding-Porxy(透明化的数据库代理端) 模式在本地实现 mysql 数据库读写分离,并通过 java 应用程序连接.
ShardingSphere 实战之读写分离
|
SQL 存储 算法
聊聊 Sharding-JDBC 分库分表
聊聊 Sharding-JDBC 分库分表
|
SQL cobar 算法
分库分表之ShardingSphere(二)
分库分表之ShardingSphere
|
存储 缓存 中间件
ShardingSphere-分库分表小结和问题 | 学习笔记
快速学习ShardingSphere-分库分表小结和问题。
ShardingSphere-分库分表小结和问题 | 学习笔记
|
关系型数据库 中间件 数据库
ShardingSphere-分库分表介绍 | 学习笔记
快速学习ShardingSphere-分库分表介绍。
ShardingSphere-分库分表介绍 | 学习笔记