# 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 >= ? ```