去年开发一个项目的时候,因为系统的核心数据是定时从外界发送过来的,数据量比较大,后来很快单表就达到了千万级别,这就需要分库分表,最后选择了ShardingSphere,原因就是比较容易上手。
官网地址:https://shardingsphere.apache.org/
如上图所示,当前版本是4.x,并且官网支持中文阅读。点击文档下拉4.x版本:
简介如下:
定位为轻量级Java框架,在Java的JDBC层提供的额外服务。 它使用客户端直连数据库,以jar包形式提供服务,无需额外部署和依赖,可理解为增强版的JDBC驱动,完全兼容JDBC和各种ORM框架。
这两部分的介绍可以阅读官网,这里就不介绍了。
本项目基于SpringBoot 2.1.5使用ShardingSphere和Mybatis-Plus实现分库分表。
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.shardingsphere</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>基于 Spring Boot 2.1.15 使用sharding-sphere + Mybatis-Plus 实现分库分表</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.1</version>
</dependency>
<dependency>
<groupId>io.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>io.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-namespace</artifactId>
<version>3.1.0</version>
</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>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
ds0
├── user_0
└── user_1
ds1
├── user_0
└── user_1
既然是分库分表 库结构与表结构一定是一致的。
数据库: ds0
CREATE DATABASE IF NOT EXISTS `ds0`/*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci */;
USE `ds0`;
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for user_0
-- ----------------------------
DROP TABLE IF EXISTS `user_0`;
CREATE TABLE `user_0`(
`id` INT(11) NOT NULL,
`name` VARCHAR(255) DEFAULT NULL,
`age` INT(11) DEFAULT NULL,
PRIMARY KEY (`id`)
)ENGINE=INNODB DEFAULT charset=utf8mb4;
-- ----------------------------
-- Table structure for user_1
-- ----------------------------
DROP TABLE IF EXISTS `user_1`;
CREATE TABLE `user_1`(
`id` INT(11) NOT NULL,
`name` VARCHAR(255) DEFAULT NULL,
`age` INT(11) DEFAULT NULL,
PRIMARY KEY (`id`)
)ENGINE=INNODB DEFAULT charset=utf8mb4;
SET FOREIGN_KEY_CHECKS = 1;
数据库:ds1
CREATE DATABASE IF NOT EXISTS `ds1`/*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci */;
USE `ds1`;
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for user_0
-- ----------------------------
DROP TABLE IF EXISTS `user_0`;
CREATE TABLE `user_0`(
`id` INT(11) NOT NULL,
`name` VARCHAR(255) DEFAULT NULL,
`age` INT(11) DEFAULT NULL,
PRIMARY KEY (`id`)
)ENGINE=INNODB DEFAULT charset=utf8mb4;
-- ----------------------------
-- Table structure for user_1
-- ----------------------------
DROP TABLE IF EXISTS `user_1`;
CREATE TABLE `user_1`(
`id` INT(11) NOT NULL,
`name` VARCHAR(255) DEFAULT NULL,
`age` INT(11) DEFAULT NULL,
PRIMARY KEY (`id`)
)ENGINE=INNODB DEFAULT charset=utf8mb4;
SET FOREIGN_KEY_CHECKS = 1;
# 数据源 ds0,ds1
sharding:
jdbc:
datasource:
names: ds0,ds1
# 第一个数据库
ds0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://192.168.67.1:3306/ds0?characterEncoding=utf-8
username: root
password: 123
# 第二个数据库
ds1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://192.168.67.1:3306/ds1?characterEncoding=utf-8
username: root
password: 123
# 水平拆分的数据库(表) 配置分库 + 分表策略 行表达式分片策略
config:
sharding:
# 分库策略
default-database-strategy:
inline:
sharding-column: id
algorithm-expression: ds$->{id % 2}
# 分表策略 其中user为逻辑表 分表主要取决于age行
tables:
user:
actual-data-nodes: ds$->{0..1}.user_$->{0..1}
table-strategy:
inline:
sharding-column: age
# 分片算法表达式
algorithm-expression: user_$->{age % 2}
# 主键 UUID 18位数 如果是分布式还要进行一个设置 防止主键重复
# key-generator-column-name: id
# 打印执行的数据库
props:
sql:
show: true
# 打印执行的sql语句
spring:
main:
allow-bean-definition-overriding: true
使用配置文件方式实现分库以及分表,配置说明:
实体类:
package com.shardingsphere.demo.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;
@Data
@TableName("user")
public class User extends Model<User> {
/**
* 主键id
*/
private int id;
/**
* 名称
*/
private String name;
/**
* 年龄
*/
private int age;
}
mapper(记得在启动类上添加mapper扫描路径@MapperScan(“com.shardingsphere.demo.dao”)):
package com.shardingsphere.demo.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.shardingsphere.demo.entity.User;
public interface UserMapper extends BaseMapper<User> {
}
service:
package com.shardingsphere.demo.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.shardingsphere.demo.entity.User;
public interface UserService extends IService<User> {
}
serviceImpl:
package com.shardingsphere.demo.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.shardingsphere.demo.dao.UserMapper;
import com.shardingsphere.demo.entity.User;
import com.shardingsphere.demo.service.UserService;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
controller:
package com.shardingsphere.demo.controller;
import com.shardingsphere.demo.entity.User;
import com.shardingsphere.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/select")
public List<User> select(){
return userService.list();
}
@GetMapping("/insert")
public Boolean insert(User user){
return userService.save(user);
}
}
启动项目,然后打开浏览器分别访问:
http://localhost:8080/insert?id=1&name=1hd&age=12
http://localhost:8080/insert?id=2&name=1hd&age=13
http://localhost:8080/insert?id=3&name=1hd&age=14
http://localhost:8080/insert?id=4&name=1hd&age=15
然后查看控制台日志:
根据分片算法和分片策略,不同的id以及age取模后添加到不同库的不同表里,这就达到了分库分表的结果。
查询测试,访问http://localhost:8080/select,查看控制台日志:
分别从ds0数据库两张表和ds1两张表查询结果 然后汇总结果返回。