说明:Sharding-jdbc是常见的分库分表工具,本文介绍Sharding-jdbc的基础使用。
分库分表
首先,介绍一下分库分表:
(1)分库
水平分库:以字段为依据,按照一定策略(hash、range),将一个库中的数据拆分到多个库中;
垂直分库:以表为依据,按照业务归属不同,将不同的表拆分到不同的库中;
(2)分表
水平分表:按照策略,将记录路由到不同的表上,常见的策略有范围、hash、字段值;
垂直分表:根据业务相关性,拆分表字段,将一张表拆分为多张表;
水平分就是横着一刀,垂直分就是竖着一刀。
垂直分库、垂直分表,我认为是设计时考虑的,像微服务架构,根据业务场景拆分多个小的服务,每个服务都可以有自己的数据库,是垂直分库。
而垂直分表,某张表的字段过长,可以考虑将表字段按照业务相关性拆分成多张表,另外,当表中有text类型的字段时,为了避免影响其他字段索引率,也需要独立出来一张表,用主键对应(阿里巴巴《Java开发手册》),是垂直分表的体现。
(3)需要考虑的问题
分库:
-
跨库事务;
-
跨库的JOIN;
分表:
-
多张表如何保证主键不重复;
-
多张表的count、order by、group by 及 聚合函数问题;
Sharding-jdbc使用
这里介绍使用Sharding-jdbc实现水平分表
(1)创建数据库表
先创建两张数据库表
sql
# 创建数据库
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 = InnoDB
DEFAULT CHARSET = utf8mb3
ROW_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 = InnoDB
DEFAULT CHARSET = utf8mb3
ROW_FORMAT = DYNAMIC;
(2)创建项目
创建一个Spring Boot项目,pom如下:
xml
<?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
,没有加后缀
java
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 {
@TableId
private Long orderId;
private BigDecimal price;
private Long userId;
private String status;
}
创建Service和Mapper
(OrderService)
java
import com.baomidou.mybatisplus.extension.service.IService;
import com.hezy.pojo.Order;
public interface OrderService extends IService<Order> {
}
(OrderServiceImpl)
java
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)
java
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> {
}
启动类
java
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)的方式
yml
spring:
main:
# 允许Bean重复定义覆盖
allow-bean-definition-overriding: true
shardingsphere:
datasource:
# 数据源名称,多个数据源时使用逗号(,)分割
names: m1
m1:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/order_db?serverTimezone=Asia/Shanghai&characterEncoding=utf8
username: root
password: 123456
sharding:
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: SNOWFLAKE
table-strategy:
inline:
# 配置分片键
sharding-column: order_id
# 配置分片策略( order_id % 2 + 1 的值就是数据实际要进入的数据表 )
# 利用"取模"计算的方式进行分片,将分片键除以分片表的个数,得到的模就是该数据要进入的数据表
# 示例中,共有2张分片表,则此处求取分片键的值与分片表的模数,表示为 order_id % 2,又因为分片表的初始值以 1 开始,则再加上1
algorithm-expression: t_order_$->{order_id % 2 + 1}
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 配置数据库字段与实体类映射方式,是否是小驼峰命名法
map-underscore-to-camel-case: true
global-config:
db-config:
# 配置数据表前缀
table-prefix: t_
(5)测试
写一个测试类,如下:
java
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 {
@Autowired
private OrderService orderService;
@Test
public 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);
}
}
@Test
public void findAllTest() {
List<Order> list = orderService.list();
System.out.println(list);
}
}
执行插入方法,插入完成

可见数据分散插入到对应的表中

试一下查询,也是查出10条记录,而不是单张表的5条记录。

注意
如果是手写SQL,不用Mybatis-Plus的API,那么写SQL的时候注意不要带上表名后缀
java
@Select("select * from t_order")
List<Order> findALl();
不要写成下面这样,这样就是查单张表的5条记录了。
java
@Select("select * from t_order_1")
List<Order> findALl();
参考
代码:
博客: