说明:Sharding-jdbc是常见的分库分表工具,本文介绍Sharding-jdbc的基础使用。
分库分表
首先,介绍一下分库分表:
(1)分库
水平分库:以字段为依据,按照一定策略(hash、range),将一个库中的数据拆分到多个库中;
垂直分库:以表为依据,按照业务归属不同,将不同的表拆分到不同的库中;
(2)分表
水平分表:按照策略,将记录路由到不同的表上,常见的策略有范围、hash、字段值;
垂直分表:根据业务相关性,拆分表字段,将一张表拆分为多张表;
水平分就是横着一刀,垂直分就是竖着一刀。
垂直分库、垂直分表,我认为是设计时考虑的,像微服务架构,根据业务场景拆分多个小的服务,每个服务都可以有自己的数据库,是垂直分库。
而垂直分表,某张表的字段过长,可以考虑将表字段按照业务相关性拆分成多张表,另外,当表中有text类型的字段时,为了避免影响其他字段索引率,也需要独立出来一张表,用主键对应(阿里巴巴《Java开发手册》),是垂直分表的体现。
(3)需要考虑的问题
分库:
-
跨库事务;
-
跨库的JOIN;
分表:
-
多张表如何保证主键不重复;
-
多张表的count、order by、group by 及 聚合函数问题;
Sharding-jdbc使用
这里介绍使用Sharding-jdbc实现水平分表
(1)创建数据库表
先创建两张数据库表
# 创建数据库
create schema order_db collate utf8mb3_general_ci;# 创建表
use order_db;CREATE TABLE `t_order_1`
(`order_id` bigint NOT NULL COMMENT '订单id',`price` decimal(10, 2) NOT NULL COMMENT '订单价格',`user_id` bigint NOT NULL COMMENT '下单用户id',`status` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '订单状态',PRIMARY KEY (`order_id`) USING BTREE
) ENGINE = InnoDBDEFAULT CHARSET = utf8mb3ROW_FORMAT = DYNAMIC;CREATE TABLE `t_order_2`
(`order_id` bigint NOT NULL COMMENT '订单id',`price` decimal(10, 2) NOT NULL COMMENT '订单价格',`user_id` bigint NOT NULL COMMENT '下单用户id',`status` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '订单状态',PRIMARY KEY (`order_id`) USING BTREE
) ENGINE = InnoDBDEFAULT CHARSET = utf8mb3ROW_FORMAT = DYNAMIC;
(2)创建项目
创建一个Spring Boot项目,pom如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.hezy</groupId><artifactId>sharding-jdbc-demo</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><!-- shardingJDBC核心依赖 --><dependency><groupId>org.apache.shardingsphere</groupId><artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId><version>5.2.1</version><exclusions><exclusion><artifactId>snakeyaml</artifactId><groupId>org.yaml</groupId></exclusion><exclusion><artifactId>cosid-core</artifactId><groupId>me.ahoo.cosid</groupId></exclusion></exclusions></dependency><!-- 版本冲突 --><dependency><groupId>org.yaml</groupId><artifactId>snakeyaml</artifactId><version>1.33</version></dependency><dependency><groupId>me.ahoo.cosid</groupId><artifactId>cosid-core</artifactId><version>1.19.3</version></dependency><!--XA 分布式事务 --><dependency><groupId>org.apache.shardingsphere</groupId><artifactId>shardingsphere-transaction-xa-core</artifactId><version>5.2.1</version><exclusions><exclusion><artifactId>transactions-jdbc</artifactId><groupId>com.atomikos</groupId></exclusion><exclusion><artifactId>transactions-jta</artifactId><groupId>com.atomikos</groupId></exclusion></exclusions></dependency><!-- SpringBoot依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><exclusions><exclusion><artifactId>snakeyaml</artifactId><groupId>org.yaml</groupId></exclusion></exclusions></dependency><!--测试类依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency><!--durid数据库连接池--><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.20</version></dependency><!-- mysql连接驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!-- mybatisplus依赖 --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.3.3</version></dependency></dependencies>
</project>
(3)编写代码
创建pojo对象,注意表名是t_order
,没有加后缀
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;
import java.math.BigDecimal;@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("t_order")
public class Order implements Serializable {@TableIdprivate Long orderId;private BigDecimal price;private Long userId;private String status;
}
创建Service和Mapper
(OrderService)
import com.baomidou.mybatisplus.extension.service.IService;
import com.hezy.pojo.Order;public interface OrderService extends IService<Order> {
}
(OrderServiceImpl)
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hezy.mapper.OrderMapper;
import com.hezy.pojo.Order;
import com.hezy.service.OrderService;
import org.springframework.stereotype.Service;@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
}
(OrderMapper)
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.hezy.pojo.Order;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface OrderMapper extends BaseMapper<Order> {
}
启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class App {public static void main(String[] args) {SpringApplication.run(App.class, args);}
}
(4)配置文件
配置文件中,定义了数据库配置及水平分表的策略,采用取模(%2)的方式
spring:main:# 允许Bean重复定义覆盖allow-bean-definition-overriding: trueshardingsphere:datasource:# 数据源名称,多个数据源时使用逗号(,)分割names: m1m1:type: com.alibaba.druid.pool.DruidDataSourcedriverClassName: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://127.0.0.1:3306/order_db?serverTimezone=Asia/Shanghai&characterEncoding=utf8username: rootpassword: 123456sharding:tables:t_order:# 配置数据节点,m1.t_order_1,m1.t_order_2,使用上面names的配置名称actual-data-nodes: m1.t_order_$->{1..2}key-generator:# 配置主键,及主键生成算法column: order_id# 雪花算法type: SNOWFLAKEtable-strategy:inline:# 配置分片键sharding-column: order_id# 配置分片策略( order_id % 2 + 1 的值就是数据实际要进入的数据表 )# 利用“取模”计算的方式进行分片,将分片键除以分片表的个数,得到的模就是该数据要进入的数据表# 示例中,共有2张分片表,则此处求取分片键的值与分片表的模数,表示为 order_id % 2,又因为分片表的初始值以 1 开始,则再加上1algorithm-expression: t_order_$->{order_id % 2 + 1}
mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl# 配置数据库字段与实体类映射方式,是否是小驼峰命名法map-underscore-to-camel-case: trueglobal-config:db-config:# 配置数据表前缀table-prefix: t_
(5)测试
写一个测试类,如下:
import com.hezy.pojo.Order;
import com.hezy.service.OrderService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.math.BigDecimal;
import java.util.List;@SpringBootTest
public class OrderMapperTest {@Autowiredprivate OrderService orderService;@Testpublic void insertTest() {for (int i = 0; i < 10; i++) {Order order = new Order();order.setStatus("正常");order.setPrice(new BigDecimal(20));order.setUserId(1L);boolean save = orderService.save(order);System.out.println(save);}}@Testpublic void findAllTest() {List<Order> list = orderService.list();System.out.println(list);}
}
执行插入方法,插入完成
可见数据分散插入到对应的表中
试一下查询,也是查出10条记录,而不是单张表的5条记录。
注意
如果是手写SQL,不用Mybatis-Plus的API,那么写SQL的时候注意不要带上表名后缀
@Select("select * from t_order")List<Order> findALl();
不要写成下面这样,这样就是查单张表的5条记录了。
@Select("select * from t_order_1")List<Order> findALl();
参考
代码:
-
https://gitee.com/learning_demo/sharding-jdbc
-
https://gitee.com/xscodeit/xushu_springboot_demos/tree/master/Sharding
博客:
- Sharding-JDBC分库分表