# mx-tableshard
**Repository Path**: fejay-love/mx-tableshard
## Basic Information
- **Project Name**: mx-tableshard
- **Description**: 基于mybati的轻量级分表组件
- **Primary Language**: Java
- **License**: MIT
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2026-06-19
- **Last Updated**: 2026-06-22
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# mx-tableshard
`mx-tableshard` 是一个基于 **Spring Boot 3 + MyBatis / MyBatis-Plus** 的轻量级分表组件。
组件通过 MyBatis 拦截器自动识别实体类上的 `@TableShard` 注解,从 SQL 参数中提取分片字段值,并将逻辑表名替换为实际物理表名。
## 功能特性
- 支持 Spring Boot 自动装配。
- 支持 MyBatis / MyBatis-Plus 查询和更新 SQL 拦截。
- 支持实体类注解声明分表规则。
- 内置三种分表策略:
- `HashShardStrategy`:一致性 Hash 分表。
- `ModShardStrategy`:取模分表。
- `DateShardStrategy`:日期归档分表。
- 支持从多种参数形态中提取分片值:
- 普通实体对象。
- `Map` 参数。
- MyBatis `ParamMap`。
- MyBatis-Plus `Wrapper`。
- MyBatis `ParameterMapping`。
- SQL 条件中的 `?` 占位符。
- 日期分片支持按年、月、日、季度生成表名后缀。
- SQL 分片字段条件支持:
- `=`
- `IN (...)`
- `LIKE ?`
- `>`
- `<`
- `>=`
- `<=`
## 环境要求
- JDK 21+
- Spring Boot 3.x
- MyBatis Spring Boot Starter 3.x
- MyBatis-Plus 3.5.x(可选,使用 MyBatis-Plus 时需要)
## 安装使用
当前项目版本:
```xml
com.mx
mx-tableshard
1.0-SNAPSHOT
```
如果在本地项目中使用,先安装到本地 Maven 仓库:
```bash
mvn clean install
```
业务项目中引入依赖:
```xml
com.mx
mx-tableshard
1.0-SNAPSHOT
```
## 配置说明
组件默认启用。
```yaml
shendu:
shard:
enabled: true
```
如需关闭分表组件:
```yaml
shendu:
shard:
enabled: false
```
自动装配入口:
```text
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
```
## 快速开始
### 1. 准备物理表
以订单表为例,逻辑表名为 `t_order`,按月份进行日期分表:
```sql
CREATE TABLE t_order_202406 (
id BIGINT PRIMARY KEY,
order_no VARCHAR(64),
user_id BIGINT,
create_time DATETIME
);
CREATE TABLE t_order_202407 (
id BIGINT PRIMARY KEY,
order_no VARCHAR(64),
user_id BIGINT,
create_time DATETIME
);
```
业务 SQL 中仍然写逻辑表名:
```sql
SELECT * FROM t_order WHERE create_time >= ?
```
组件会在执行前替换为:
```sql
SELECT * FROM t_order_202406 WHERE create_time >= ?
```
### 2. 在实体类上声明分表规则
```java
import com.baomidou.mybatisplus.annotation.TableName;
import annotation.org.wf.mx.tableshard.TableShard;
import strategy.org.wf.mx.tableshard.DateShardStrategy;
import java.time.LocalDateTime;
@TableName("t_order")
@TableShard(tableName = "t_order", shardColumn = "create_time", strategy = DateShardStrategy.class,
shardPattern = "yyyyMM")
public class Order {
private Long id;
private String orderNo;
private Long userId;
private LocalDateTime createTime;
// getter / setter
}
```
### 3. 编写 Mapper
```java
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
public interface OrderMapper extends BaseMapper {
}
```
正常使用 MyBatis / MyBatis-Plus 查询即可:
```java
LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
wrapper.ge(Order::getCreateTime, LocalDateTime.of(2024, 6, 1, 0, 0));
List orders = orderMapper.selectList(wrapper);
```
若 `createTime = 2024-06-01T00:00`,则会路由到:
```text
t_order_202406
```
## `@TableShard` 注解说明
```java
@TableShard(
tableName = "t_order",
shardNum = 4,
shardColumn = "order_no",
strategy = HashShardStrategy.class,
shardPattern = ""
)
```
| 属性 | 说明 | 必填 | 默认值 |
| --- | --- | --- | --- |
| `tableName` | 逻辑表名。为空时尝试从 `@TableName` 或 MyBatis-Plus 表信息中获取。 | 否 | `""` |
| `shardNum` | 分表数量。Hash / Mod 策略需要配置。 | 否 | `0` |
| `shardColumn` | 分片字段名,支持驼峰和下划线匹配。 | 是 | 无 |
| `strategy` | 分表策略类。 | 否 | `HashShardStrategy.class` |
| `shardPattern` | 日期分片格式,主要用于 `DateShardStrategy`。 | 否 | `""` |
## 内置分表策略
### 一致性 Hash 分表
适合订单号、用户 ID 等希望分布相对均匀的字段。
```java
@TableShard(
tableName = "t_order",
shardNum = 4,
shardColumn = "order_no",
strategy = HashShardStrategy.class
)
public class Order {
}
```
表名示例:
```text
t_order_1
t_order_2
t_order_3
t_order_4
```
### 取模分表
适合分片数量固定、分片键分布相对均匀的场景。
```java
@TableShard(
tableName = "t_user",
shardNum = 8,
shardColumn = "user_id",
strategy = ModShardStrategy.class
)
public class User {
}
```
表名示例:
```text
t_user_1
t_user_2
...
t_user_8
```
### 日期归档分表
适合日志、订单、流水等按时间归档的数据。
```java
@TableShard(
tableName = "t_log",
shardColumn = "create_time",
strategy = DateShardStrategy.class,
shardPattern = "yyyyMM"
)
public class LogRecord {
}
```
支持的 `shardPattern`:
| 模式 | 说明 | 示例日期 | 物理表名 |
| --- | --- | --- | --- |
| `yyyy` | 按年分表 | `2024-06-15` | `t_log_2024` |
| `yyyyMM` | 按月分表 | `2024-06-15` | `t_log_202406` |
| `yyyyMMdd` | 按日分表 | `2024-06-15` | `t_log_20240615` |
| `yyyyQ` | 按季度分表 | `2024-05-15` | `t_log_2024q2` |
日期分片值支持:
- `LocalDateTime`
- `LocalDate`
- `java.util.Date`
- 毫秒时间戳 `Long`
- 日期字符串,例如:
- `2024-06-15`
- `2024-06-15 10:30:00`
- `2024-06-15T10:30:00`
## SQL 条件支持
分片字段可以出现在以下条件中:
```sql
WHERE order_no = ?
WHERE order_no IN (?)
WHERE create_time > ?
WHERE create_time >= ?
WHERE create_time < ?
WHERE create_time <= ?
WHERE create_time >= #{startTime}
WHERE create_time <= #{endTime}
```
日期分片示例:
```sql
SELECT * FROM t_order WHERE status = ? AND create_time >= ?
```
参数:
```text
status = 1
create_time = 2024-06-01T00:00
```
`shardPattern = "yyyyMM"` 时路由到:
```text
t_order_202406
```
## 重要限制
### 1. 当前是单表路由,不是多表路由
组件当前会根据提取到的一个分片值计算一个物理表名。
例如:
```sql
WHERE create_time >= '2024-06-30'
AND create_time < '2024-07-02'
```
如果按月分表,真实数据可能分布在:
```text
t_order_202406
t_order_202407
```
当前组件不会自动生成 `UNION ALL`,也不会自动查询多个物理表。此类跨分片范围查询需要业务方自行拆分,或在后续版本中支持多表路由。
### 2. SQL 解析基于规则匹配
当前组件没有引入完整 SQL Parser,分片字段定位主要基于 SQL 字符串规则匹配。复杂 SQL 场景需谨慎验证,例如:
```sql
WHERE DATE(create_time) >= ?
WHERE ? <= create_time
WHERE EXISTS (SELECT 1 FROM ...)
```
## 自定义分表策略
实现 `ShardStrategy` 接口:
```java
import strategy.org.wf.mx.tableshard.ShardStrategy;
import org.springframework.stereotype.Component;
@Component
public class TenantShardStrategy implements ShardStrategy {
@Override
public String computeTableName(String logicTableName, int shardNum, String shardKey) {
int index = Math.abs(shardKey.hashCode()) % shardNum + 1;
return logicTableName + "_" + index;
}
}
```
实体类中使用自定义策略:
```java
@TableShard(
tableName = "t_tenant_order",
shardNum = 16,
shardColumn = "tenant_id",
strategy = TenantShardStrategy.class
)
public class TenantOrder {
}
```
自定义策略注册为 Spring Bean 后,组件会通过 `ShardStrategyRegistry` 自动发现并使用。
## 测试与 Demo
运行全部测试:
```bash
mvn test
```
运行日期分片比较操作符 Demo:
```bash
mvn -Dtest=DateShardComparisonDemoTest test
```
Demo 文件:
```text
src/test/java/com/mx/tableshard/demo/DateShardComparisonDemoTest.java
```
运行 SQL 参数定位测试:
```bash
mvn -Dtest=ShardSqlParameterLocatorTest test
```
测试文件:
```text
src/test/java/com/mx/tableshard/extractor/ShardSqlParameterLocatorTest.java
```
## 项目结构
```text
src/main/java/com/mx/tableshard
├── annotation # @TableShard 注解
├── config # Spring Boot 自动配置与配置属性
├── extractor # 分片值提取链路
├── interceptor # MyBatis 分表拦截器
├── strategy # 分表策略
└── utils # 工具类
```
## 工作原理
核心执行流程:
```text
MyBatis 执行 SQL
↓
ShardTableInterceptor 拦截
↓
解析 Mapper 对应实体类上的 @TableShard
↓
ShardValueExtractor 提取分片字段值
↓
ShardStrategy 计算物理表名
↓
替换 SQL 中的逻辑表名
↓
继续执行替换后的 SQL
```
示例:
```sql
SELECT * FROM t_order WHERE create_time >= ?
```
替换后:
```sql
SELECT * FROM t_order_202406 WHERE create_time >= ?
```