一、客户端分库分表解决方案
核心特点:
- 部署位置:与应用程序在同一个进程中(无独立服务),例如作为 Java 应用的一个 JAR 包依赖。
- 工作流程:
- 典型代表:Sharding-JDBC、MyCat Client(轻量版)。
优点:
- 性能损耗低:无网络转发开销(本地方法调用),适合高并发场景。
- 部署简单:无需额外部署独立服务,随应用一起发布,运维成本低。
- 响应速度快:减少 “应用→服务端代理→数据库” 的网络链路,延迟更低。
缺点:
- 对应用有侵入性:需在应用中引入特定依赖(如 Sharding-JDBC 的 JAR 包),并配置分片规则。
- 语言限制:通常只支持特定语言(如 Sharding-JDBC 仅支持 Java,因依赖 JDBC 规范)。
- 配置分散:分片规则配置在应用中,若多个应用共用一套分库分表集群,需保证配置一致(否则会出现数据路由错误)。
适用场景:
- 纯单一语言技术栈(如全 Java 微服务)。
- 高并发、低延迟需求(如电商订单、支付系统)。
- 应用团队与数据库团队协同紧密,可统一维护分片规则。
二、服务端分库分表解决方案
核心特点:
- 部署位置:作为独立服务进程部署(可集群化),与应用程序分离。
- 工作流程:
- 典型代表:Sharding-Proxy、MyCat、DBProxy。
优点:
- 应用无侵入:应用无需修改代码,仅需修改数据库连接地址(指向代理服务),适合遗留系统改造。
- 语言无关:支持所有能连接数据库的语言(Java、Python、Go、PHP 等),适合多语言混合架构。
- 配置集中管理:分片规则、权限控制等在代理服务中统一配置,避免多应用配置不一致。
- 便于运维:可在代理层统一实现监控、审计、限流等功能,无需在每个应用中单独开发。
缺点:
- 性能损耗较高:增加 “应用→代理→数据库” 的网络链路,存在网络延迟和代理服务的计算开销。
- 部署复杂:需额外维护代理服务的集群(高可用、负载均衡),增加运维成本。
- 代理成为瓶颈:若代理服务性能不足或出现故障,会影响所有连接它的应用。
适用场景:
- 多语言混合架构(如 Java 微服务 + Python 数据分析 + Go 后台任务)。
- 遗留系统分库分表改造(无法修改应用代码)。
- 需集中管控分库分表规则(如运维团队统一管理)。
- 对侵入性敏感的场景(如第三方系统接入分库分表集群)。
三、核心区别总结
对比维度 | 客户端分库分表解决方案 | 服务端分库分表解决方案 |
---|---|---|
逻辑实现位置 | 应用程序内部(同进程) | 独立代理服务(单独进程) |
应用侵入性 | 有(需引入依赖、配置规则) | 无(仅改连接地址) |
语言支持 | 通常只支持特定语言(如 Java) | 支持所有语言(基于数据库协议) |
性能损耗 | 低(无网络转发) | 中(网络转发 + 代理计算) |
部署复杂度 | 简单(随应用部署) | 复杂(需部署代理集群) |
典型代表 | Sharding-JDBC | Sharding-Proxy、MyCat |
四、选择建议
- 优先选客户端方案:纯 Java 技术栈、高并发低延迟、应用可改造、追求性能。
- 优先选服务端方案:多语言混合、应用不可改造(遗留系统)、需集中管理、对侵入性敏感。
-------------------------------------------------------------------------------------------------------------------
一、核心差异(架构层面)
维度 | Sharding-JDBC | Sharding-Proxy |
---|---|---|
定位 | 客户端分库分表解决方案(JDBC 驱动增强) | 服务端分库分表解决方案(独立代理服务器) |
接入方式 | 嵌入应用程序,作为 JDBC 驱动层组件存在 | 独立部署为代理服务,应用通过数据库协议(如 MySQL 协议)连接 |
对应用的侵入性 | 需在应用中引入依赖并配置,有一定侵入性 | 应用无感知,像连接普通数据库一样使用(仅需修改连接地址) |
支持语言 | 仅支持 Java(基于 JDBC 规范) | 支持所有语言(基于数据库协议,如 Java、Python、Go 等) |
部署形式 | 与应用程序同进程部署(无独立进程) | 独立进程部署(可集群化) |
性能开销 | 无网络转发开销(本地方法调用),性能损耗极低 | 存在网络通信开销(应用 → 代理 → 数据库),性能损耗略高 |
二、功能与特性对比
1. 核心功能(分库分表能力)
- 水平分表、垂直分表、水平分库、垂直分库
- 多种分片策略(行表达式、哈希、范围、自定义等)
- 读写分离、分布式事务(XA/BASE)
- 数据加密、脱敏、SQL 审计
2. 差异化特性
特性 | Sharding-JDBC | Sharding-Proxy |
---|---|---|
配置管理 | 配置分散在各应用中(需通过注册中心实现集中管理) | 配置集中在代理服务,支持动态更新(无需重启应用) |
数据库协议支持 | 依赖 JDBC 驱动,支持所有兼容 JDBC 的数据库 | 支持 MySQL、PostgreSQL 等主流数据库协议(需适配) |
连接池管理 | 由应用程序自身的连接池(如 HikariCP)管理 | 代理服务统一管理数据库连接池 |
监控与运维 | 需在应用层集成监控(如通过 Micrometer) | 可通过代理层统一监控所有接入应用的分库分表行为 |
版本兼容性 | 与应用使用的 JDBC 版本强关联 | 与应用解耦,仅需兼容数据库协议版本 |
三、适用场景
1. 选择 Sharding-JDBC 的场景
- 纯 Java 技术栈:如 Spring Boot/Cloud 应用,可直接集成 JDBC 驱动。
- 高性能要求:无网络转发开销,适合高并发场景(如电商订单系统)。
- 应用部署轻量化:无需额外部署代理服务,减少运维成本。
- 灵活的配置更新:可通过应用配置中心(如 Nacos)动态更新分片规则。
2. 选择 Sharding-Proxy 的场景
- 多语言混合架构:系统包含 Java、Python、Go 等多语言应用(如微服务 + 大数据组件)。
- 应用无侵入需求:不希望修改应用代码(如遗留系统分库分表改造)。
- 集中式管理:需要统一管控所有应用的分库分表规则、连接池、权限等(如运维团队统一管理)。
- 非 Java 应用接入:如 Python 数据分析程序需要访问分库分表后的数据库。
四、总结:核心区别一句话概括
- Sharding-JDBC 是「嵌入应用的客户端分库分表组件」,适合 Java 应用,追求高性能,需应用层集成。
- Sharding-Proxy 是「独立部署的服务端代理」,适合多语言场景,应用无侵入,需额外维护代理服务。
-------------------------------------------------------------------------------------------------------------------
一、概念定义与关系
1. ShardingSphere(生态 / 平台)
- Sharding-JDBC:客户端分库分表组件(嵌入应用)
- Sharding-Proxy:服务端分库分表代理(独立部署)
- Sharding-Sidecar:基于 Kubernetes 的云原生代理(较少用)
- 其他辅助组件:如分布式事务组件(Atomikos/XA)、监控工具等
2. Sharding-JDBC(客户端组件)
- 本质:对 JDBC 接口的增强(如
DataSource
、Connection
、Statement
等),应用程序通过它直接操作底层数据库。 - 特点:与应用同进程运行,无独立服务,性能损耗低。
3. Sharding-Proxy(服务端组件)
- 本质:一个 “中间代理层”,隔离了应用与底层数据库,应用无需感知分库分表细节。
- 特点:独立进程运行,需单独部署,支持多语言接入。
二、核心区别对比
维度 | ShardingSphere(生态) | Sharding-JDBC(客户端组件) | Sharding-Proxy(服务端组件) |
---|---|---|---|
定位 | 分布式数据库中间件生态(包含多个组件) | 客户端分库分表工具(嵌入应用) | 服务端分库分表代理(独立部署) |
形态 | 包含多个组件的平台 / 标准 | Java 库(JAR 包),需嵌入应用 | 独立服务(可集群化部署) |
接入方式 | 需选择具体组件(如 JDBC 或 Proxy) | 应用引入依赖,配置分片规则,直接调用 JDBC 接口 | 应用通过数据库连接串连接代理(如 jdbc:mysql://proxy:3307/db ) |
对应用的侵入性 | 取决于选择的组件 | 有侵入性(需修改应用依赖和配置) | 无侵入性(应用无需修改代码,仅改连接地址) |
支持语言 | 无限制(取决于组件) | 仅支持 Java(基于 JDBC 规范) | 支持所有语言(基于数据库协议,如 Java、Python、Go 等) |
性能损耗 | 无(本身是生态) | 极低(本地方法调用,无网络开销) | 略高(应用→代理→数据库,存在网络转发) |
适用场景 | 所有分布式数据库场景 | 纯 Java 应用、高并发场景(如电商订单) | 多语言混合架构、遗留系统改造、需集中管理的场景 |
三、功能覆盖范围
- ShardingSphere 生态的所有功能(如分片、读写分离、事务),Sharding-JDBC 和 Sharding-Proxy 均支持(功能上基本一致)。
- 差异主要体现在 架构设计和接入方式,而非功能本身。
四、如何选择?
- 纯 Java 技术栈 + 高性能需求:选 Sharding-JDBC(无网络开销,适合高并发)。
- 多语言混合架构(如 Java + Python):选 Sharding-Proxy(支持跨语言)。
- 遗留系统改造(不想改代码):选 Sharding-Proxy(应用无侵入)。
- 需要集中管理分片规则:选 Sharding-Proxy(配置集中在代理,方便运维)。
总结
- ShardingSphere 是 “生态”,定义了分布式数据库的标准和解决方案。
- Sharding-JDBC 和 Sharding-Proxy 是该生态中实现分库分表的 “两种工具”:前者是 “客户端嵌入型”,后者是 “服务端代理型”。
- 两者互补共存,可根据技术栈、性能需求、运维模式选择,甚至在同一系统中混合使用(如 Java 应用用 JDBC,Python 应用用 Proxy 连接同一套分库分表集群)。
-------------------------------------------------------------------------------------------------------------------
一、客户端集成方案(推荐纯 Java 技术栈)
1. Sharding-JDBC(Apache ShardingSphere)
- 支持水平分表、水平分库、垂直分表、垂直分库
- 内置多种分片策略(哈希、范围、行表达式、自定义等)
- 支持读写分离、分布式事务(XA/BASE)、数据加密
- 兼容 Spring Boot 自动配置,无缝集成
-
引入 Starter 依赖:xml
<dependency><groupId>org.apache.shardingsphere</groupId><artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId><version>5.3.2</version> </dependency>
-
配置分片规则(
application.yml
):yamlspring:shardingsphere:datasource:names: db0,db1 # 分库名称db0: # 数据库0配置type: com.zaxxer.hikari.HikariDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/db0username: rootpassword: rootdb1: # 数据库1配置(同db0)rules:sharding:tables:t_order: # 订单表分片规则actual-data-nodes: db${0..1}.t_order${0..1} # 实际分表:db0.t_order0, db0.t_order1, db1.t_order0, db1.t_order1database-strategy: # 分库策略(按用户ID哈希)standard:sharding-column: user_idsharding-algorithm-name: order_db_inlinetable-strategy: # 分表策略(按订单ID哈希)standard:sharding-column: order_idsharding-algorithm-name: order_table_inlinesharding-algorithms:order_db_inline: # 分库算法(行表达式)type: INLINEprops:algorithm-expression: db${user_id % 2}order_table_inline: # 分表算法type: INLINEprops:algorithm-expression: t_order${order_id % 2}
- 性能优异(无网络开销,本地方法调用),适合高并发场景
- 与 Spring Boot 无缝集成,配置简单
- 支持动态调整分片规则(结合配置中心)
- 仅支持 Java 语言(依赖 JDBC 规范)
- 需在应用中配置分片规则,对应用有一定侵入性
2. Dynamic Datasource + 自定义分片
dynamic-datasource-spring-boot-starter
),手动实现简单分片逻辑,适合规则简单的场景。核心思路:- 用动态数据源切换不同分库
- 在 SQL 执行前拦截并修改表名(如
t_order
→t_order_001
)
-
引入动态数据源依赖:xml
<dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>3.5.2</version> </dependency>
-
配置多数据源并自定义分片逻辑:java运行
// 分片工具类(按用户ID路由分库分表) public class ShardingUtils {public static String getDbName(Long userId) {return "db" + (userId % 2); // 分2个库}public static String getTableName(String baseTable, Long orderId) {return baseTable + "_" + (orderId % 4); // 分4个表} }// 业务层使用 @Service public class OrderService {@Autowiredprivate JdbcTemplate jdbcTemplate;public void saveOrder(Order order) {// 1. 切换分库DynamicDataSourceContextHolder.push(ShardingUtils.getDbName(order.getUserId()));// 2. 生成分表名String tableName = ShardingUtils.getTableName("t_order", order.getOrderId());// 3. 执行SQLjdbcTemplate.update("insert into " + tableName + "(...) values(...)", ...);// 4. 清除数据源上下文DynamicDataSourceContextHolder.poll();} }
二、服务端代理方案(推荐多语言 / 无侵入场景)
1. Sharding-Proxy(Apache ShardingSphere)
- 部署 Sharding-Proxy 服务(独立进程),配置分片规则(
conf/server.yaml
和conf/config-sharding.yaml
)。 - Spring Boot 应用直接连接代理,无需引入额外依赖:
yaml
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://sharding-proxy:3307/order_db # 连接代理地址username: rootpassword: root
- 应用无侵入(仅改连接地址),支持多语言(Java、Python、Go 等)
- 分片规则集中管理,适合多应用共用一套分库分表集群
- 便于统一运维(监控、审计、限流)
- 存在网络开销(应用→代理→数据库),性能略低于 Sharding-JDBC
- 需额外部署代理集群(保证高可用),增加运维成本
2. MyCat
- 部署 MyCat 服务,配置
schema.xml
(逻辑库表映射)、rule.xml
(分片规则)。 - 应用连接 MyCat 代理(同连接普通 MySQL):
yaml
spring:datasource:url: jdbc:mysql://mycat-server:8066/order_schema # MyCat 逻辑库username: rootpassword: 123456
三、云原生方案(推荐容器化 / 云环境)
ShardingSphere-Operator(云原生部署)
- 与 K8s 生态深度集成(ConfigMap 管理配置、Service 暴露服务)
- 支持分片集群的自动扩缩容(如新增分库时自动更新规则)
- 适合大规模、动态变化的云原生场景
四、技术选择建议
场景需求 | 推荐技术 | 核心原因 |
---|---|---|
纯 Java 技术栈 + 高并发 | Sharding-JDBC | 性能优异,与 Spring Boot 无缝集成 |
多语言混合架构 | Sharding-Proxy | 应用无侵入,支持跨语言 |
遗留系统改造(无代码修改) | MyCat / Sharding-Proxy | 仅改连接地址,零侵入 |
分片规则简单(中小数据) | Dynamic Datasource 自定义 | 实现简单,灵活度高 |
云原生 / 容器化部署 | ShardingSphere-Operator | 适配 K8s 生态,支持动态扩缩容 |
总结
- 追求性能和 Java 生态适配:优先 Sharding-JDBC
- 需多语言支持或无侵入:优先 Sharding-Proxy
- 复杂企业级场景:可考虑 MyCat
- 云原生环境:首选 ShardingSphere-Operator
-------------------------------------------------------------------------------------------------------------------
一、架构设计目标
- 解决海量数据问题:单表数据量超过 1000 万行时,通过分库分表降低单库 / 单表压力。
- 提升查询性能:减少单库 / 单表的 IO 和锁竞争,优化查询响应速度(目标:90% 查询 < 100ms)。
- 支持水平扩展:可通过新增数据库节点快速扩容,无需重构核心架构。
- 保证数据一致性:在分布式场景下,确保分库分表后的事务完整性和数据准确性。
- 业务无感知:通过中间件封装分库分表逻辑,减少对业务代码的侵入。
二、核心架构选型
1. 分库分表中间件
- 与 Spring Boot 无缝集成,适配 Java 生态。
- 无独立服务开销,性能损耗低(本地方法调用)。
- 支持动态分片规则(结合 Nacos 配置中心实时更新)。
- 兼容 Spring Data JPA/MyBatis 等 ORM 框架。
2. 数据存储层
- 主数据库:MySQL 8.0(支持原生分表、CTE 等特性,适配 ShardingSphere)。
- 从数据库:与主库一致,用于读写分离(减轻主库查询压力)。
- 存储引擎:InnoDB(支持事务和行级锁,适合高并发写入)。
3. 配套中间件
- 配置中心:Nacos(存储分片规则、数据源配置,支持动态更新)。
- 分布式事务:Seata(解决跨库事务问题,支持 AT 模式(自动补偿))。
- 全局 ID 生成:Snowflake 算法(保证分库分表后 ID 全局唯一)。
- 监控告警:Prometheus + Grafana(监控分库分表性能、数据源负载)。
三、数据分片策略设计
1. 分片维度选择
分片类型 | 适用场景 | 示例 |
---|---|---|
水平分表 | 单表数据量大,但访问频率低(如历史订单) | t_order → t_order_00 ~t_order_15 |
水平分库 | 单库压力大(高并发读写),需分摊存储 / 计算 | 按用户 ID 哈希分为 db_user_0 ~db_user_3 |
垂直分库 | 业务模块耦合低,可按业务拆分(如用户、订单) | 订单库 db_order 、用户库 db_user |
垂直分表 | 表字段过多,冷热数据分离(如大字段拆分) | t_user → t_user_base (基础信息)+ t_user_ext (扩展信息) |
2. 分片键设计(核心)
- 高频查询字段:确保多数查询能通过分片键路由到单一分库 / 分表(避免全库扫描)。
- 分布均匀性:避免数据倾斜(如某分表数据量是其他的 10 倍以上)。
- 订单表(t_order):
- 分库键:
user_id
(用户 ID,确保同一用户的订单在同一库,便于查询用户所有订单)。 - 分表键:
order_id
(订单 ID,按哈希分表,均匀分布数据)。
- 分库键:
- 商品表(t_product):
- 分表键:
category_id
(分类 ID,同类商品集中存储,适合分类查询)。
- 分表键:
3. 分片算法选择
算法类型 | 适用场景 | 示例 |
---|---|---|
哈希分片 | 分片键为数字 / 字符串,需均匀分布 | user_id % 4 → 分为 4 个库 |
范围分片 | 分片键为时间 / 自增 ID,需按区间查询 | 按月份分表:t_order_202401 、t_order_202402 |
枚举分片 | 分片键值有限且固定(如地区、状态) | 按地区分库:db_shanghai 、db_beijing |
自定义分片 | 复杂业务规则(如多字段组合分片) | 按 user_id % 2 + order_time 混合分片 |
4. 全局 ID 生成策略
- 结构:
1 位符号位 + 41 位时间戳 + 10 位机器 ID + 12 位序列号
(支持单节点每秒生成 4096 个 ID)。 - 集成方式:通过 ShardingSphere 内置
SNOWFLAKE
算法,或接入分布式 ID 服务(如 Leaf)。
# Sharding-JDBC 全局 ID 配置
spring:shardingsphere:rules:sharding:key-generators:snowflake:type: SNOWFLAKEprops:worker-id: 1 # 机器 ID(集群部署需唯一)
四、核心架构组件设计
1. 数据源管理层
# application.yml 数据源配置(Nacos 中管理)
spring:shardingsphere:datasource:names: db0,db1,db2,db3 # 4 个分库db0:type: com.zaxxer.hikari.HikariDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://db0:3306/db_order_0username: ${DB_USERNAME}password: ${DB_PASSWORD}# db1/db2/db3 配置同上(略)rules:sharding:tables:t_order: # 订单表分片规则actual-data-nodes: db${0..3}.t_order${0..7} # 4 库 × 8 表 = 32 个分表database-strategy: # 分库策略(user_id 哈希)standard:sharding-column: user_idsharding-algorithm-name: order_db_inlinetable-strategy: # 分表策略(order_id 哈希)standard:sharding-column: order_idsharding-algorithm-name: order_table_inlinekey-generate-strategy: # 全局 ID 生成column: order_idkey-generator-name: snowflakesharding-algorithms:order_db_inline:type: INLINEprops:algorithm-expression: db${user_id % 4} # 4 个分库order_table_inline:type: INLINEprops:algorithm-expression: t_order${order_id % 8} # 每个库 8 个分表
2. 业务接入层
// 实体类(与逻辑表名一致)
@Data
@TableName("t_order") // 逻辑表名,Sharding-JDBC 自动路由到分表
public class Order {private Long orderId; // 全局 ID(Snowflake 生成)private Long userId; // 分库键private BigDecimal amount;private LocalDateTime createTime;
}// Mapper 层(正常编写 SQL,无需关心分表)
public interface OrderMapper extends BaseMapper<Order> {// 按 user_id 查询(自动路由到对应分库)List<Order> selectByUserId(@Param("userId") Long userId);// 按 order_id 查询(自动路由到对应分表)Order selectByOrderId(@Param("orderId") Long orderId);
}// 服务层(正常调用,Sharding-JDBC 自动处理路由)
@Service
public class OrderService {@Autowiredprivate OrderMapper orderMapper;public void createOrder(Order order) {orderMapper.insert(order); // 自动路由到 db${user_id%4}.t_order${order_id%8}}public List<Order> getOrdersByUserId(Long userId) {return orderMapper.selectByUserId(userId); // 仅查询用户所在分库的分表}
}
3. 分布式事务处理
- 引入 Seata 依赖:
<dependency><groupId>io.seata</groupId><artifactId>seata-spring-boot-starter</artifactId><version>1.6.1</version>
</dependency>
- 配置 Seata(注册到 Nacos):
seata:tx-service-group: my_test_tx_group # 事务分组service:vgroup-mapping:my_test_tx_group: defaultgrouplist:default: seata-server:8091registry:type: nacosnacos:server-addr: nacos:8848group: SEATA_GROUP
- 业务层添加
@GlobalTransactional
注解:
@Service
public class OrderTransactionService {@Autowiredprivate OrderService orderService;@Autowiredprivate InventoryService inventoryService; // 库存服务(可能在另一分库)@GlobalTransactional // 分布式事务注解public void createOrderWithInventory(Order order, Long productId, Integer quantity) {// 1. 创建订单(分库 A)orderService.createOrder(order);// 2. 扣减库存(分库 B)inventoryService.deduct(productId, quantity);// 若步骤 2 失败,步骤 1 会自动回滚}
}
4. 读写分离设计
spring:shardingsphere:rules:readwrite-splitting:data-sources:order-db-group: # 主从分组type: Staticprops:write-data-source-name: db0 # 主库(写入)read-data-source-names: db0-slave1,db0-slave2 # 从库(读取)load-balancer-name: round_robin # 负载均衡策略(轮询)load-balancers:round_robin:type: ROUND_ROBIN # 轮询策略
五、部署与高可用设计
1. 部署架构
- 应用层:Spring Boot 应用集群(多实例部署,通过 Nginx 负载均衡)。
- 数据层:
- 主库:一主多从(主库写入,从库同步数据并承担查询)。
- 分库集群:按分片规则部署多个数据库节点(如 4 个分库,每个分库 1 主 2 从)。
- 中间件:
- Sharding-JDBC:嵌入应用内部(无独立部署)。
- Seata/ Nacos:集群部署(保证高可用)。
2. 扩容策略
- 例:从 4 个分库扩容到 8 个分库,新分片规则为
user_id % 8
,原分库数据按user_id % 4
迁移到新分库(如原 db0 数据迁移到新 db0 和 db4)。 - 扩容过程:通过 ShardingSphere 动态更新分片规则(无需停服),配合双写工具同步新旧分片数据。
六、监控与运维
-
性能监控:
- 接入 Prometheus 采集 Sharding-JDBC 指标(如分库查询耗时、路由成功率)。
- Grafana 配置看板,监控分库 / 分表的 QPS、延迟、连接数。
-
日志追踪:
- 集成 SkyWalking 跟踪分布式 SQL 执行链路(从应用到分库分表的完整路径)。
- 记录分片路由日志(
shardingsphere.sql
日志级别设为 INFO)。
-
配置管理:
- 所有分片规则、数据源配置存储在 Nacos,支持动态更新(无需重启应用)。
- 配置变更需通过审批流程,避免误操作导致数据路由错误。
七、注意事项与优化
- 避免跨库查询:设计时尽量通过分片键确保查询落在单一分库 / 分表,跨库 Join 需谨慎使用(可通过全局表、广播表优化)。
- 索引优化:分表后索引仅对当前分表有效,需保证分片键和查询字段均有索引。
- 历史数据归档:对超过保留期的数据(如 1 年前的订单),迁移到冷存储(如阿里云 OSS + 表格存储)。
- 压测验证:上线前通过 JMeter 模拟高并发场景,验证分库分表后的性能瓶颈(如单分表 QPS 是否满足需求)。
总结
-------------------------------------------------------------------------------------------------------------------
下面我将详细介绍 Spring Boot 集成 ShardingSphere 的代码配置,包括分库分表、读写分离、全局 ID 生成等核心功能的实现。
配置说明
-
数据源配置:
- 配置了 2 个主库(master0, master1)和 2 个从库(slave0, slave1)
- 使用 HikariCP 连接池管理数据库连接
-
读写分离:
- 配置了两个主从分组(order-db-group-0, order-db-group-1)
- 写操作路由到主库,读操作路由到从库
- 支持轮询和权重两种负载均衡策略
-
分库分表:
- 按 userId 进行分库(2 个库)
- 按 orderId 进行分表(每个库 4 个表)
- 实际数据节点为:order-db-group-0.t_order_0 到 order-db-group-1.t_order_3
-
全局 ID 生成:
- 使用雪花算法(Snowflake)生成全局唯一的 orderId
- 避免了分库分表环境下的 ID 冲突问题
代码结构说明
-
实体类(Order.java):
- 使用逻辑表名
t_order
- 主键字段使用 MyBatis-Plus 的
@TableId
注解
- 使用逻辑表名
-
数据访问层(OrderMapper.java):
- 继承 MyBatis-Plus 的
BaseMapper
接口 - 定义了按 userId 和 orderId 查询的方法
- SQL 语句中使用逻辑表名,ShardingSphere 会自动路由到实际分表
- 继承 MyBatis-Plus 的
-
服务层(OrderService.java):
- 业务代码完全不需要关心分库分表逻辑
- 正常使用 MyBatis-Plus 提供的 CRUD 方法
- 支持事务管理
-
控制器层(OrderController.java):
- 提供 RESTful API 接口
- 接收前端请求并调用服务层处理
动态配置(可选)
- 添加 Nacos 配置依赖(已在 pom.xml 中包含)
- 创建
bootstrap.yml
配置文件指定 Nacos 地址 - 将 ShardingSphere 配置迁移到 Nacos 配置中心
- 配置变更时,ShardingSphere 会自动感知并更新规则
注意事项
-
SQL 限制:
- 避免使用复杂的跨库 JOIN 操作
- 分页查询需要注意使用分片键,否则可能导致结果不准确
- 不支持某些聚合函数的跨库计算
-
事务管理:
- 单库事务可以直接使用 Spring 的
@Transactional
注解 - 跨库事务需要集成 Seata 等分布式事务框架
- 单库事务可以直接使用 Spring 的
-
性能优化:
- 合理设计分片键,避免数据倾斜
- 为分片键和查询频繁的字段建立索引
- 生产环境关闭 SQL 日志输出(
sql-show: false
)
-
监控与运维:
- 集成 ShardingSphere 的监控功能
- 定期检查各分库分表的数据分布情况
- 制定数据归档和清理策略
package com.example.shardingspheredemo.entity;import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data;import java.math.BigDecimal; import java.time.LocalDateTime;/*** 订单实体类* 注意:表名是逻辑表名,实际分表由ShardingSphere管理*/ @Data @TableName("t_order") // 逻辑表名 public class Order {/*** 订单ID,使用ShardingSphere的雪花算法生成*/@TableId(type = IdType.AUTO) // 这里AUTO是假象,实际由ShardingSphere的key-generator生成private Long orderId;/*** 用户ID,用于分库*/private Long userId;/*** 订单金额*/private BigDecimal amount;/*** 订单状态*/private Integer status;/*** 创建时间*/private LocalDateTime createTime;/*** 更新时间*/private LocalDateTime updateTime; }
package com.example.shardingspheredemo.controller;import com.example.shardingspheredemo.entity.Order; import com.example.shardingspheredemo.service.OrderService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*;import java.util.List;/*** 订单控制器*/ @RestController @RequestMapping("/orders") public class OrderController {@Autowiredprivate OrderService orderService;/*** 创建订单*/@PostMappingpublic Long createOrder(@RequestParam Long userId) {return orderService.createOrder(userId);}/*** 批量创建订单*/@PostMapping("/batch")public String batchCreateOrders(@RequestParam Long userId, @RequestParam int count) {orderService.batchCreateOrders(userId, count);return "Successfully created " + count + " orders";}/*** 根据用户ID查询订单*/@GetMapping("/user/{userId}")public List<Order> getOrdersByUserId(@PathVariable Long userId) {return orderService.getOrdersByUserId(userId);}/*** 根据订单ID查询订单*/@GetMapping("/{orderId}")public Order getOrderByOrderId(@PathVariable Long orderId) {return orderService.getOrderByOrderId(orderId);} }
package com.example.shardingspheredemo.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.shardingspheredemo.entity.Order; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param;import java.util.List;/*** 订单Mapper接口* 注意:SQL中使用的是逻辑表名t_order,ShardingSphere会自动路由到实际分表*/ @Mapper public interface OrderMapper extends BaseMapper<Order> {/*** 根据用户ID查询订单列表* 会被路由到该用户ID对应的分库*/List<Order> selectByUserId(@Param("userId") Long userId);/*** 根据订单ID查询订单* 会被路由到该订单ID对应的分库分表*/Order selectByOrderId(@Param("orderId") Long orderId);/*** 批量插入订单*/int batchInsert(@Param("orders") List<Order> orders); }
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!-- 注意:这里使用的是逻辑表名t_order --> <mapper namespace="com.example.shardingspheredemo.mapper.OrderMapper"><!-- 根据用户ID查询订单 --><select id="selectByUserId" resultType="com.example.shardingspheredemo.entity.Order">SELECT * FROM t_order WHERE user_id = #{userId} ORDER BY create_time DESC</select><!-- 根据订单ID查询订单 --><select id="selectByOrderId" resultType="com.example.shardingspheredemo.entity.Order">SELECT * FROM t_order WHERE order_id = #{orderId}</select><!-- 批量插入订单 --><insert id="batchInsert">INSERT INTO t_order (user_id, amount, status, create_time, update_time)VALUES<foreach collection="orders" item="order" separator=",">(#{order.userId}, #{order.amount}, #{order.status}, #{order.createTime}, #{order.updateTime})</foreach></insert> </mapper>
package com.example.shardingspheredemo.service;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.example.shardingspheredemo.entity.Order; import com.example.shardingspheredemo.mapper.OrderMapper; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional;import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List;/*** 订单服务实现类* 业务代码中无需关心分库分表逻辑,ShardingSphere会自动处理*/ @Service public class OrderService extends ServiceImpl<OrderMapper, Order> {/*** 创建订单*/@Transactionalpublic Long createOrder(Long userId) {Order order = new Order();order.setUserId(userId);order.setAmount(new BigDecimal("99.99"));order.setStatus(1); // 1-待支付order.setCreateTime(LocalDateTime.now());order.setUpdateTime(LocalDateTime.now());// 插入订单,ShardingSphere会根据userId和orderId自动路由到相应的分库分表baseMapper.insert(order);return order.getOrderId();}/*** 批量创建订单*/@Transactionalpublic void batchCreateOrders(Long userId, int count) {List<Order> orders = new ArrayList<>(count);for (int i = 0; i < count; i++) {Order order = new Order();order.setUserId(userId);order.setAmount(new BigDecimal((i + 1) * 10));order.setStatus(1);order.setCreateTime(LocalDateTime.now());order.setUpdateTime(LocalDateTime.now());orders.add(order);}// 批量插入,ShardingSphere会自动分发到相应的分库分表baseMapper.batchInsert(orders);}/*** 根据用户ID查询订单*/public List<Order> getOrdersByUserId(Long userId) {// 查询会被路由到该userId对应的分库return baseMapper.selectByUserId(userId);}/*** 根据订单ID查询订单*/public Order getOrderByOrderId(Long orderId) {// 查询会被路由到该orderId对应的分库分表return baseMapper.selectByOrderId(orderId);} }
package com.example.shardingspheredemo;import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.transaction.annotation.EnableTransactionManagement;/*** 应用启动类*/ @SpringBootApplication @MapperScan("com.example.shardingspheredemo.mapper") // 扫描Mapper接口 @EnableTransactionManagement // 启用事务管理 public class ShardingsphereDemoApplication {public static void main(String[] args) {SpringApplication.run(ShardingsphereDemoApplication.class, args);} }
spring:profiles:active: sharding # 激活分片配置# ShardingSphere 配置shardingsphere:# 数据源配置datasource:# 数据源名称列表,多个用逗号分隔names: master0,master1,slave0,slave1# 主库0配置master0:type: com.zaxxer.hikari.HikariDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/order_db_0?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghaiusername: rootpassword: roothikari:maximum-pool-size: 10minimum-idle: 5idle-timeout: 300000connection-timeout: 20000# 主库1配置master1:type: com.zaxxer.hikari.HikariDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/order_db_1?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghaiusername: rootpassword: roothikari:maximum-pool-size: 10minimum-idle: 5# 从库0配置(主库0的从库)slave0:type: com.zaxxer.hikari.HikariDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3307/order_db_0?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghaiusername: rootpassword: root# 从库1配置(主库1的从库)slave1:type: com.zaxxer.hikari.HikariDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3307/order_db_1?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghaiusername: rootpassword: root# 规则配置rules:# 读写分离规则readwrite-splitting:data-sources:# 主从数据源组合名称order-db-group-0:type: Staticprops:write-data-source-name: master0 # 写数据源read-data-source-names: slave0 # 读数据源,多个用逗号分隔load-balancer-name: round_robin # 负载均衡策略名称order-db-group-1:type: Staticprops:write-data-source-name: master1read-data-source-names: slave1load-balancer-name: round_robin# 负载均衡策略配置load-balancers:round_robin:type: ROUND_ROBIN # 轮询策略weight:type: WEIGHT # 权重策略(示例)props:slave0: 1slave1: 2# 分片规则配置sharding:# 表分片规则tables:# 订单表分片配置t_order:# 实际数据节点:数据库.表actual-data-nodes: order-db-group-${0..1}.t_order_${0..3}# 数据库分片策略database-strategy:standard:sharding-column: user_id # 分片列sharding-algorithm-name: order_db_inline # 分片算法名称# 表分片策略table-strategy:standard:sharding-column: order_id # 分片列sharding-algorithm-name: order_table_inline # 分片算法名称# 主键生成策略key-generate-strategy:column: order_id # 主键列key-generator-name: snowflake # 主键生成器名称# 订单item表分片配置t_order_item:actual-data-nodes: order-db-group-${0..1}.t_order_item_${0..3}database-strategy:standard:sharding-column: user_idsharding-algorithm-name: order_db_inlinetable-strategy:standard:sharding-column: order_idsharding-algorithm-name: order_table_inlinekey-generate-strategy:column: order_item_idkey-generator-name: snowflake# 分片算法配置sharding-algorithms:# 数据库分片算法order_db_inline:type: INLINE # 行表达式分片算法props:algorithm-expression: order-db-group-${user_id % 2} # 分片表达式,分2个库# 表分片算法order_table_inline:type: INLINEprops:algorithm-expression: t_order_${order_id % 4} # 分片表达式,每个库分4个表# 主键生成器配置key-generators:snowflake:type: SNOWFLAKE # 雪花算法props:worker-id: 1 # 工作节点ID,集群部署时需唯一max-tolerate-time-difference-milliseconds: 300000 # 最大容忍时间差# 属性配置props:# 展示SQL,开发环境开启,生产环境关闭sql-show: true# 执行引擎类型executor-size: 10# 检查表元数据一致性check-table-metadata-enabled: false# 数据库类型database-type: MySQL# MyBatis-Plus 配置 mybatis-plus:mapper-locations: classpath*:mapper/**/*.xmltype-aliases-package: com.example.shardingspheredemo.entityconfiguration:map-underscore-to-camel-case: truelog-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 日志输出# 日志配置 logging:level:org.apache.shardingsphere: INFOcom.example.shardingspheredemo: DEBUGcom.zaxxer.hikari: WARNroot: INFO
<?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.7.5</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.example</groupId><artifactId>shardingsphere-demo</artifactId><version>0.0.1-SNAPSHOT</version><name>shardingsphere-demo</name><description>Demo project for Spring Boot + ShardingSphere</description><properties><java.version>11</java.version><shardingsphere.version>5.3.2</shardingsphere.version></properties><dependencies><!-- Spring Boot 核心依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- ShardingSphere 核心依赖 --><dependency><groupId>org.apache.shardingsphere</groupId><artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId><version>${shardingsphere.version}</version></dependency><!-- 数据库驱动 --><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope></dependency><!-- ORM 框架 - MyBatis-Plus --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3.1</version></dependency><!-- 连接池 --><dependency><groupId>com.zaxxer</groupId><artifactId>HikariCP</artifactId></dependency><!-- 分布式事务 (可选) --><dependency><groupId>io.seata</groupId><artifactId>seata-spring-boot-starter</artifactId><version>1.6.1</version></dependency><!-- 配置中心 (可选,用于动态配置) --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId><version>2021.0.5.0</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build> </project>
-------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------