shardingSphere分库分表简单实现
准备
环境
- mysql 5.7 +
- Springboot 3.2 +
- mybatis-plus 3.5.5
- druid 1.2.23
- shardingsphere-jdbc 5.5
- JDK 21
Pom
xml
<dependencies>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.5</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.23</version>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc</artifactId>
<version>5.5.0</version>
<exclusions>
<exclusion>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-test-util</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.54</version>
</dependency>
</dependencies>
数据库准备
新建两个库、分别建四张表
sql
CREATE DATABASE `order_db_0` /*!40100 DEFAULT CHARACTER SET utf8 */;
CREATE DATABASE `order_db_1` /*!40100 DEFAULT CHARACTER SET utf8 */;
CREATE TABLE `order_0` (
`order_id` bigint(20) NOT NULL,
`user_id` bigint(20) NOT NULL,
`amount` decimal(10,2) NOT NULL,
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`order_id`),
KEY `idx_user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 其他四张表结构一致即可
代码部分
配置文件
在resource目录下新建两个文件:
application.yaml
yaml
spring:
main:
allow-bean-definition-overriding: true
application:
name: ls-backend
datasource:
driver-class-name: org.apache.shardingsphere.driver.ShardingSphereDriver
url: jdbc:shardingsphere:classpath:sharding.yaml
servlet:
multipart:
max-file-size: 100MB
max-request-size: 100MB
server:
port: 8085
servlet:
context-path: /
encoding:
force-response: true
#Mybatis扫描
mybatis:
config-location: classpath:/mybatis-config.xml
mybatis-plus:
mapper-locations: classpath:/mapper/*Mapper.xml
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
sharding.yaml
yaml
dataSources:
ds0:
driverClassName: com.mysql.cj.jdbc.Driver
dataSourceClassName: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://xxx
username: test
password: test
ds1:
driverClassName: com.mysql.cj.jdbc.Driver
dataSourceClassName: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://yyy
username: test
password: test
rules:
- !SHARDING
tables:
order:
actualDataNodes: ds${0..1}.order_${0..3}
tableStrategy:
standard:
shardingColumn: order_id
shardingAlgorithmName: order-id-inline
databaseStrategy:
standard:
shardingColumn: user_id
shardingAlgorithmName: db-inline
bindingTables:
- order
shardingAlgorithms:
db-inline:
type: INLINE
props:
algorithm-expression: ds${user_id % 2}
order-id-inline:
type: INLINE
props:
algorithm-expression: order_${order_id % 4}
props:
sql-show: true
实体类
java
@Data
@TableName("`order`") // MyBatis-Plus 表名(与分片表名一致)
public class Order {
@TableId(type = IdType.ASSIGN_ID) // 手动输入主键(由雪花算法生成)
private Long orderId;
private Long userId;
private BigDecimal amount;
private LocalDateTime createTime;
}
Mapper
java
@Mapper
public interface OrderMapper extends BaseMapper<Order> {
}
Service
java
@Service
@Slf4j
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Transactional
public int createOrder(Long userId, BigDecimal amount) {
SnowflakeIdGenerator generator = new SnowflakeIdGenerator(1, 1);
Order order = new Order();
order.setOrderId(generator.nextId()); // 自动生成 BIGINT 类型的雪花 ID
order.setUserId(userId);
order.setAmount(amount);
log.info("即将插入的order: {}", JSON.toJSONString(order));
// MyBatis-Plus 插入
return orderMapper.insert(order);
}
public List<Order> getOrderById(Long orderId, Long userId) {
log.info("查询订单,orderId: {}", orderId);
QueryWrapper<Order> wrapper = new QueryWrapper<>();
wrapper.eq("user_id", userId);
wrapper.eq("order_id", orderId);
return orderMapper.selectList(wrapper);
}
}
雪花算法工具
java
public class SnowflakeIdGenerator {
// 起始时间戳(2024-01-01 00:00:00)
private static final long TWEPOCH = 1704067200000L;
// 工作机器 ID(10位,范围:0~1023)
private final long workerId;
// 数据中心 ID(10位中的高位,范围:0~1023)
private final long datacenterId;
// 序列号(12位,范围:0~4095)
private long sequence = 0L;
// 上次生成 ID 的时间戳(毫秒)
private long lastTimestamp = -1L;
/**
* 构造函数(需指定唯一的工作机器 ID 和数据中心 ID)
* @param workerId 工作机器 ID(0~1023)
* @param datacenterId 数据中心 ID(0~1023)
*/
public SnowflakeIdGenerator(long workerId, long datacenterId) {
// 校验参数范围
if (workerId > 1023 || workerId < 0) {
throw new IllegalArgumentException("Worker ID must be between 0 and 1023");
}
if (datacenterId > 1023 || datacenterId < 0) {
throw new IllegalArgumentException("Datacenter ID must be between 0 and 1023");
}
this.workerId = workerId;
this.datacenterId = datacenterId;
}
/**
* 生成下一个 ID
* @return 64位 BIGINT 类型的雪花算法 ID
*/
public synchronized long nextId() {
long timestamp = System.currentTimeMillis();
// 处理时间戳回拨(可选)
if (timestamp < lastTimestamp) {
throw new RuntimeException("Clock moved backwards. Refusing to generate ID");
}
// 同一毫秒内生成多个 ID
if (timestamp == lastTimestamp) {
sequence = (sequence + 1) & 0xFFF; // 12位掩码(0~4095)
if (sequence == 0) { // 序列号溢出,等待下一毫秒
timestamp = waitNextMillis(lastTimestamp);
}
} else {
sequence = 0L; // 新毫秒,序列号重置
}
lastTimestamp = timestamp;
// 计算 ID(按位拼接)
return ((timestamp - TWEPOCH) << 22) // 时间戳(41位)
| (datacenterId << 17) // 数据中心 ID(5位)
| (workerId << 12) // 工作机器 ID(5位)
| sequence; // 序列号(12位)
}
/**
* 等待下一毫秒(解决同一毫秒内序列号溢出)
*/
private long waitNextMillis(long lastTimestamp) {
long timestamp = System.currentTimeMillis();
while (timestamp <= lastTimestamp) {
timestamp = System.currentTimeMillis();
}
return timestamp;
}
// 测试示例
public static void main(String[] args) {
// 示例:工作机器 ID=1,数据中心 ID=1(需根据实际环境调整)
SnowflakeIdGenerator generator = new SnowflakeIdGenerator(1, 1);
for (int i = 0; i < 10; i++) {
System.out.println("Generated ID: " + generator.nextId());
}
}
}
实测
创建
bash
INFO 35088 --- [ls-backend] [nio-8085-exec-1] ShardingSphere-SQL : Logic SQL: INSERT INTO `order` ( order_id, user_id, amount ) VALUES ( ?, ?, ? )
INFO 35088 --- [ls-backend] [nio-8085-exec-1] ShardingSphere-SQL : Actual SQL: ds1 ::: INSERT INTO `order_0` ( order_id, user_id, amount ) VALUES (?, ?, ?) ::: [191145194425552896, 987654321095, 8]
查询
bash
INFO 35088 --- [ls-backend] [nio-8085-exec-3] ShardingSphere-SQL : Logic SQL: SELECT order_id,user_id,amount,create_time FROM `order` WHERE (user_id = ? AND order_id = ?)
INFO 35088 --- [ls-backend] [nio-8085-exec-3] ShardingSphere-SQL : Actual SQL: ds1 ::: SELECT order_id,user_id,amount,create_time FROM `order_0` WHERE (user_id = ? AND order_id = ?) ::: [987654321095, 191145194425552896]