网站调优技能拉新工作室在哪里接项目
ShardingSphere 是一款开源的分布式数据库中间件,支持分库分表、读写分离、分布式事务等核心功能,兼容 MySQL、PostgreSQL、Oracle 等主流数据库。本文将从基础概念、环境搭建、核心配置、业务开发、常见问题五个维度,提供一套完整的分库分表实战教程。
一、分库分表核心概念
1. 为什么需要分库分表?
随着业务数据量增长,单库单表面临性能瓶颈(QPS 上限、存储容量限制),分库分表通过水平拆分(按规则将数据分散到多个库/表)解决以下问题:
- 存储瓶颈:单库存储容量有限(如 MySQL 单库建议不超过 500GB)。
- 查询性能:单表数据量过大(如超过 1000 万行)时,SQL 查询变慢。
- 写入压力:单库写入 QPS 受限于硬件(如单机 MySQL 写入约 2000~5000 QPS)。
2. 分库分表的核心术语
术语 | 说明 |
---|---|
数据分片 | 将数据分散到多个库/表的过程,分为垂直分片(按业务拆分库)和水平分片(按规则拆分表)。 |
分片键(Shard Key) | 决定数据分布的关键字段(如用户 ID、订单 ID),需保证数据均匀分布。 |
分片算法 | 定义数据如何根据分片键分配到具体库/表的规则(如哈希取模、范围划分、时间范围)。 |
分片规则 | 分片键与分片算法的组合,ShardingSphere 通过规则文件(rules 目录)配置。 |
二、环境搭建(以 Spring Boot + MySQL 为例)
1. 前置准备
- JDK 1.8+
- Maven 3.6+
- MySQL 5.7+(需创建 2 个库,每个库 2 张表,用于演示分库分表)
- ShardingSphere 5.3.2(最新稳定版,支持 SPI 扩展和云原生)
2. 创建测试数据库与表
假设业务需求:将 order
表按 user_id
分片到 2 个库(ds0
、ds1
),每个库内分 2 张表(order_0
、order_1
)。
步骤 1:创建库
CREATE DATABASE ds0;
CREATE DATABASE ds1;
步骤 2:在每个库中创建分表
-- ds0 库名
USE ds0;
CREATE TABLE order_0 (order_id BIGINT PRIMARY KEY,user_id BIGINT NOT NULL,amount DECIMAL(10,2),create_time DATETIME
);CREATE TABLE order_1 (order_id BIGINT PRIMARY KEY,user_id BIGINT NOT NULL,amount DECIMAL(10,2),create_time DATETIME
);-- ds1 库名(结构与 ds0 相同)
USE ds1;
CREATE TABLE order_0 (...); -- 同 ds0.order_0
CREATE TABLE order_1 (...); -- 同 ds0.order_1
3. 引入 ShardingSphere 依赖
在 pom.xml
中添加 Spring Boot Starter 依赖:
<dependency><groupId>org.apache.shardingsphere</groupId><artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId><version>5.3.2</version>
</dependency>
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope>
</dependency>
三、核心配置:分库分表规则
ShardingSphere 的配置分为数据源配置和分片规则配置,支持 YAML、Properties、Java Config 三种方式。以下以 YAML 为例(application.yml
):
1. 数据源配置(多数据源)
ShardingSphere 需要连接多个物理数据库(ds0
、ds1
),需在 dataSources
中定义:
spring:shardingsphere:datasources:ds0: # 第一个物理库driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/ds0?serverTimezone=UTC&useSSL=false&characterEncoding=utf-8username: rootpassword: 123456ds1: # 第二个物理库driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/ds1?serverTimezone=UTC&useSSL=false&characterEncoding=utf-8username: rootpassword: 123456
2. 分片规则配置(核心)
分片规则需定义库分片规则(将数据分配到哪个库)和表分片规则(将数据分配到库内的哪张表)。
示例:按 user_id 分片到 2 库 2 表
spring:shardingsphere:rules:sharding:# 库名分片规则(示例:user_id 哈希取模 2,分配到 ds0 或 ds1)database-strategy:standard:sharding-column: user_id # 分片键sharding-algorithm-name: db-inline # 分片算法名称(对应下方 algorithms)# 表名分片规则(示例:user_id 哈希取模 2,分配到 order_0 或 order_1)table-strategy:standard:sharding-column: user_id # 分片键(需与库分片键一致,也可不同)sharding-algorithm-name: table-inline # 分片算法名称# 分片算法定义(内联表达式)sharding-algorithms:db-inline:type: INLINE # 内联算法(支持 SpEL 表达式)props:algorithm-expression: ds$->{user_id % 2} # user_id % 2 结果为 0 → ds0,1 → ds1table-inline:type: INLINEprops:algorithm-expression: order_$->{user_id % 2} # user_id % 2 结果为 0 → order_0,1 → order_1
3. 关键配置说明
- 分片键(sharding-column):必须选择高频查询的字段(如
user_id
),避免跨分片查询(如按order_id
查询需全库扫描)。 - 分片算法(sharding-algorithm):
INLINE
:内联算法,通过 SpEL 表达式灵活定义(如ds$->{user_id % 2}
)。HASH_MOD
:哈希取模(等价于INLINE
的$->{... % 2}
)。RANGE
:范围划分(如按时间范围分片:2023-01-01 ~ 2023-06-01
到order_0
)。
- 库表分离策略:库分片和表分片可独立配置(如库按
user_id
分片,表按order_id
分片)。
四、业务开发:CRUD 操作
ShardingSphere 通过透明化路由屏蔽分库分表细节,业务代码无需感知分片逻辑,仅需操作逻辑表名(如 order
)即可。
1. 使用 MyBatis-Plus 操作分片表
假设使用 MyBatis-Plus 作为 ORM 框架,只需定义逻辑表对应的实体类和 Mapper:
实体类(Order):
@Data
@TableName("order") // 逻辑表名(无需写分表后缀)
public class Order {@TableId(type = IdType.AUTO)private Long orderId; // 主键(非分片键)private Long userId; // 分片键(必须与配置的 sharding-column 一致)private BigDecimal amount;private LocalDateTime createTime;
}
Mapper 接口:
public interface OrderMapper extends BaseMapper<Order> {
}
2. 插入数据(自动路由)
插入时,ShardingSphere 根据 user_id
计算分片键,自动路由到目标库和表:
@Service
public class OrderService {@Autowiredprivate OrderMapper orderMapper;public void createOrder(Long userId, BigDecimal amount) {Order order = new Order();order.setOrderId(SnowflakeUtil.nextId()); // 自增主键或雪花算法生成order.setUserId(userId);order.setAmount(amount);order.setCreateTime(LocalDateTime.now());orderMapper.insert(order); // 自动路由到 ds0/order_0 或 ds1/order_1}
}
3. 查询数据(路由与聚合)
-
按分片键查询(高效):
// 根据 user_id 查询订单(ShardingSphere 自动路由到对应库和表) List<Order> orders = orderMapper.selectList(Wrappers.<Order>lambdaQuery().eq(Order::getUserId, 123L));
-
跨分片查询(低效,需避免):
// 不按分片键查询(如按 order_id),需扫描所有库和表,结果合并 Order order = orderMapper.selectById(12345L);
五、高级功能与优化
1. 读写分离
ShardingSphere 支持主库写、从库读,通过 readwrite-splitting
规则配置:
spring:shardingsphere:rules:readwrite-splitting:data-sources:ds0:dynamic: true # 动态切换主从write-data-source-name: ds0-write # 主库read-data-source-names: [ds0-read-1, ds0-read-2] # 从库ds1:dynamic: truewrite-data-source-name: ds1-writeread-data-source-names: [ds1-read-1, ds1-read-2]load-balance:algorithm-type: ROUND_ROBIN # 从库负载均衡策略(轮询、随机等)
2. 分布式事务
ShardingSphere 支持 Seata AT 模式实现跨库事务,需引入 Seata 依赖并配置:
<dependency><groupId>org.apache.shardingsphere</groupId><artifactId>shardingsphere-integration-seata-core</artifactId><version>5.3.2</version>
</dependency>
业务代码示例:
@GlobalTransactional // Seata 全局事务注解
public void createOrderWithTx(Long userId, BigDecimal amount) {orderService.createOrder(userId, amount); // 写库操作accountService.deductBalance(userId, amount); // 跨库扣减余额(触发分布式事务)
}
3. 监控与管理
ShardingSphere 提供 Admin Console(管理控制台)用于监控和配置:
- 访问地址:
http://localhost:8088
(默认端口) - 功能:查看数据源状态、分片规则、SQL 执行统计、在线修改配置(无需重启)。
六、常见问题与解决方案
1. 跨分片查询性能差
原因:未按分片键查询时,需扫描所有库和表,导致网络开销和结果合并耗时。
解决方案:
- 业务设计时,尽量通过分片键(如
user_id
)过滤数据。 - 对非分片键字段建立索引(如
order_id
),减少全表扫描。
2. 分片键选择不当导致数据倾斜
原因:分片键分布不均(如 user_id
为连续递增 ID),导致部分库/表数据量过大。
解决方案:
- 选择高基数字段(如 UUID、雪花算法生成的 ID)作为分片键。
- 使用范围分片(如按时间范围)替代哈希分片,避免热点。
3. 分布式事务一致性
原因:跨库事务需协调多个数据源,可能出现部分提交、部分回滚。
解决方案:
- 使用 Seata AT 模式(自动补偿)或 TCC 模式(手动确认)。
- 优先保证最终一致性(如异步消息补偿)。
总结
ShardingSphere 分库分表的核心是通过分片规则定义数据分布,业务代码无需感知底层分片逻辑。关键步骤包括:
- 设计合理的分片键和分片算法(避免数据倾斜、跨分片查询)。
- 配置多数据源和分片规则(YAML/Java Config)。
- 使用 ORM 框架透明操作逻辑表(如 MyBatis-Plus)。
- 结合读写分离、分布式事务优化性能和一致性。
通过本文的实战教程,可快速掌握 ShardingSphere 分库分表的核心能力,应对大数据量场景下的性能挑战。