1. 分库分表技术背景与核心挑战
当单表数据量突破千万级时,传统关系型数据库的性能瓶颈开始显现。我经历过一个电商项目,订单表每月增长300万条记录,不到一年就面临查询响应超时、写入队列堆积的问题。这正是分库分表技术要解决的核心痛点——通过水平拆分将数据分散到多个物理节点,实现存储容量和计算能力的线性扩展。
分库分表本质上是一种数据分片(Sharding)技术,主要解决三大问题:
- 存储瓶颈:单机磁盘容量有限
- 性能瓶颈:高并发下IOPS和CPU成为瓶颈
- 可用性问题:单点故障影响全局
但分库分表也带来了新的技术挑战:
- 分布式事务一致性
- 跨库JOIN查询效率
- 全局唯一ID生成
- 数据迁移与扩容
2. Sharding-JDBC架构解析
2.1 核心设计理念
Sharding-JDBC采用轻量级Java框架设计,与MyBatis、Hibernate等ORM框架完美兼容。其核心工作原理是通过重写JDBC接口,在SQL解析阶段完成路由改写:
// 传统JDBC流程 Statement -> Driver -> Database // Sharding-JDBC流程 Statement -> ShardingDriver -> SQL解析 -> 路由计算 -> 改写SQL -> 执行引擎 -> 结果归并这种设计带来两大优势:
- 无侵入性:应用代码几乎零改造
- 高性能:相比代理模式(如MyCat)减少网络开销
2.2 分片策略详解
Sharding-JDBC提供5种分片策略,实际项目中常用的有三种组合:
- 标准分片策略(StandardShardingStrategy)
// 按用户ID分库,订单ID分表 shardingRuleConfig.setDefaultDatabaseShardingStrategyConfig( new StandardShardingStrategyConfiguration("user_id", new DatabaseShardingAlgorithm())); shardingRuleConfig.setDefaultTableShardingStrategyConfig( new StandardShardingStrategyConfiguration("order_id", new TableShardingAlgorithm()));- 复合分片策略(ComplexShardingStrategy)
// 多字段联合分片(如地域+时间) shardingRuleConfig.setDefaultDatabaseShardingStrategyConfig( new ComplexShardingStrategyConfiguration("region_id,create_time", new ComplexShardingAlgorithm()));- 行表达式分片(InlineShardingStrategy)
# 简单取模分片配置 spring.shardingsphere.sharding.tables.t_order.actual-data-nodes=ds$->{0..1}.t_order_$->{0..15} spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.sharding-column=order_id spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.algorithm-expression=t_order_$->{order_id % 16}关键经验:分片键的选择需要满足两个条件:高基数(值分布均匀)和高频查询(避免跨库查询)
3. 生产级配置实战
3.1 数据源配置模板
spring: shardingsphere: datasource: names: ds0,ds1 ds0: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.jdbc.Driver jdbc-url: jdbc:mysql://db01:3306/order_db?useSSL=false username: admin password: 加密密码 ds1: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.jdbc.Driver jdbc-url: jdbc:mysql://db02:3306/order_db?useSSL=false username: admin password: 加密密码3.2 分表规则配置
订单表按月分表实战配置:
sharding: tables: t_order: actual-data-nodes: ds$->{0..1}.t_order_$->{202301..202312} table-strategy: standard: sharding-column: create_time precise-algorithm-class-name: com.example.MonthShardingAlgorithm key-generator: column: order_id type: SNOWFLAKE配套的Java分片算法实现:
public class MonthShardingAlgorithm implements PreciseShardingAlgorithm<Date> { private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyyMM"); @Override public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Date> shardingValue) { LocalDate createDate = shardingValue.getValue().toInstant() .atZone(ZoneId.systemDefault()).toLocalDate(); String suffix = createDate.format(FORMATTER); return availableTargetNames.stream() .filter(name -> name.endsWith(suffix)) .findFirst() .orElseThrow(() -> new IllegalArgumentException("无效分表")); } }4. 性能优化与问题排查
4.1 常见性能陷阱
全路由问题:当分片键未出现在查询条件中,会导致广播查询
-- 错误示例(缺少user_id条件) SELECT * FROM t_order WHERE status = 1; -- 优化方案 SELECT * FROM t_order WHERE user_id = 123 AND status = 1;分页性能:LIMIT 10000,10 会先获取10010条记录
-- 优化方案:使用上次查询的最大ID SELECT * FROM t_order WHERE user_id = 123 AND order_id > 10000 ORDER BY order_id LIMIT 10;
4.2 监控指标
通过Spring Boot Actuator暴露的关键指标:
shardingsphere_request_total{name="sql_execute",status="success"} 监控SQL执行量 shardingsphere_latency_seconds_max{name="sql_execute"} 最大延迟 shardingsphere_connection_total 连接池使用情况5. 进阶实践:分布式事务
对于跨库事务,推荐采用Seata的AT模式:
@GlobalTransactional public void placeOrder(Order order) { orderMapper.insert(order); // 操作分库A inventoryMapper.deduct(order); // 操作分库B pointsMapper.add(order); // 操作分库C }配置要点:
# Seata配置 seata.tx-service-group=my_tx_group seata.service.vgroup-mapping.my_tx_group=default # ShardingSphere集成 spring.shardingsphere.props.seata.tx.enable=true6. 数据迁移方案
当需要扩容分片时,推荐采用双写迁移方案:
- 应用层双写:同时写入新旧分片
- 数据校验:通过定时任务比对差异
- 流量切换:逐步将读请求切到新分片
- 旧数据清理:确认无误后停用旧分片
// 双写示例代码 @Transactional public void createOrder(Order order) { // 新分片写入 shardingOrderMapper.insert(order); // 旧分片写入(可异步) legacyOrderMapper.insert(order); }在电商秒杀系统中,我们通过Sharding-JDBC将订单表分散到16个物理分片,配合本地缓存,成功将下单TPS从800提升到15000。关键点在于:分片键选择用户ID哈希(保证用户维度查询效率)+ 热点数据检测(自动路由到特殊分片)。