数据库与缓存--分库分表实战指南

系列导读:本篇将深入讲解分库分表的核心原理、设计策略与最佳实践。


文章目录

    • 一、分库分表概述
      • [1.1 为什么需要分库分表?](#1.1 为什么需要分库分表?)
      • [1.2 分库 vs 分表](#1.2 分库 vs 分表)
      • [1.3 架构图](#1.3 架构图)
    • 二、分片策略
      • [2.1 分片键选择](#2.1 分片键选择)
      • [2.2 分片算法](#2.2 分片算法)
      • [2.3 分片配置](#2.3 分片配置)
    • 三、主键设计
      • [3.1 主键问题](#3.1 主键问题)
      • [3.2 雪花算法](#3.2 雪花算法)
    • 四、中间件选型
      • [4.1 中间件对比](#4.1 中间件对比)
      • [4.2 ShardingSphere 优势](#4.2 ShardingSphere 优势)
    • 五、实战案例
      • [5.1 订单分库分表](#5.1 订单分库分表)
      • [5.2 跨库查询](#5.2 跨库查询)
    • 总结

一、分库分表概述

1.1 为什么需要分库分表?

问题 说明
数据量过大 单表超过千万级,查询变慢
并发量过大 单库连接数有限
单点故障 单库宕机影响全局

1.2 分库 vs 分表

方式 说明 解决问题
垂直分库 按业务拆分数据库 业务解耦、独立扩展
垂直分表 按字段拆分表 减少IO、提高性能
水平分库 按数据拆分到多个库 解决高并发、大数据量
水平分表 按数据拆分到多个表 解决单表数据量过大

1.3 架构图

复制代码
垂直分库:
┌─────────┐   ┌─────────┐   ┌─────────┐
│ 用户库   │   │ 订单库   │   │ 商品库   │
└─────────┘   └─────────┘   └─────────┘

水平分库:
┌─────────┐   ┌─────────┐   ┌─────────┐
│ 订单库0  │   │ 订单库1 │   │ 订单库2  │
│ order_0 │   │ order_1 │   │ order_2 │
│ order_1 │   │ order_2 │   │ order_0 │
└─────────┘   └─────────┘   └─────────┘

二、分片策略

2.1 分片键选择

复制代码
分片键选择原则:
1. 数据分布均匀
2. 查询命中率高
3. 业务关联性强

常见分片键:
- 用户ID:用户相关业务
- 订单ID:订单相关业务
- 时间:日志、流水类业务

2.2 分片算法

算法 说明 优点 缺点
Hash hash(key) % N 分布均匀 扩容困难
Range 按范围划分 扩容简单 热点问题
一致性Hash 环形Hash 扩容影响小 实现复杂
取模 key % N 简单 扩容困难

2.3 分片配置

yaml 复制代码
# ShardingSphere 配置
spring:
  shardingsphere:
    rules:
      sharding:
        tables:
          t_order:
            actual-data-nodes: ds0.t_order_$->{0..1}
            table-strategy:
              standard:
                sharding-column: order_id
                sharding-algorithm-name: order-inline
            key-generate-strategy:
              column: order_id
              key-generator-name: snowflake
        
        sharding-algorithms:
          order-inline:
            type: INLINE
            props:
              algorithm-expression: t_order_$->{order_id % 2}
        
        key-generators:
          snowflake:
            type: SNOWFLAKE

三、主键设计

3.1 主键问题

复制代码
单库自增ID问题:
- 分库后ID冲突
- 无法保证全局唯一

解决方案:
- UUID:无序、太长
- 雪花算法:推荐
- 号段模式:数据库批量获取

3.2 雪花算法

java 复制代码
// 雪花算法生成分布式ID
public class SnowflakeIdWorker {
    private final long workerId;
    private final long datacenterId;
    private long sequence = 0L;
    private long lastTimestamp = -1L;
    
    public synchronized long nextId() {
        long timestamp = System.currentTimeMillis();
        
        if (timestamp < lastTimestamp) {
            throw new RuntimeException("时钟回拨");
        }
        
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & 4095;
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }
        
        lastTimestamp = timestamp;
        
        return ((timestamp - 1288834974657L) << 22)
            | (datacenterId << 17)
            | (workerId << 12)
            | sequence;
    }
}

四、中间件选型

4.1 中间件对比

中间件 类型 优点 缺点
ShardingSphere 客户端 轻量、功能全 侵入应用
MyCat 服务端 对应用透明 性能有损耗
Vitess 服务端 大厂背书 学习成本高

4.2 ShardingSphere 优势

复制代码
✅ 功能丰富:分片、读写分离、加密、影子库
✅ 生态完善:支持 Spring Boot、Namespace
✅ 性能优秀:客户端直连数据库
✅ 社区活跃:Apache 顶级项目

五、实战案例

5.1 订单分库分表

java 复制代码
// 订单实体
@Data
@TableName("t_order")
public class Order {
    @TableId(type = IdType.ASSIGN_ID)  // 雪花算法
    private Long orderId;
    private Long userId;
    private String orderNo;
    private BigDecimal amount;
    private Integer status;
    private LocalDateTime createTime;
}

// 分片配置
@Configuration
public class ShardingConfig {
    
    @Bean
    public DataSource dataSource() throws SQLException {
        // 数据源配置
        Map<String, DataSource> dataSourceMap = new HashMap<>();
        dataSourceMap.put("ds0", createDataSource("192.168.1.100:3306/order_db0"));
        dataSourceMap.put("ds1", createDataSource("192.168.1.101:3306/order_db1"));
        
        // 分片规则
        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
        shardingRuleConfig.getTables().add(getOrderTableRuleConfiguration());
        
        return ShardingDataSourceFactory.createDataSource(
            dataSourceMap, 
            shardingRuleConfig, 
            new Properties()
        );
    }
    
    private ShardingTableRuleConfiguration getOrderTableRuleConfiguration() {
        ShardingTableRuleConfiguration result = new ShardingTableRuleConfiguration("t_order", "ds${0..1}.t_order_${0..1}");
        result.setTableShardingStrategy(new StandardShardingStrategyConfiguration("order_id", "t_order_inline"));
        return result;
    }
}

5.2 跨库查询

java 复制代码
// 跨库查询问题解决方案
@Service
public class OrderQueryService {
    
    // 方案1:绑定表(关联分片键)
    public List<Order> queryByUserId(Long userId) {
        return orderMapper.selectByUserId(userId);
    }
    
    // 方案2:冗余数据
    public List<Order> queryByOrderNo(String orderNo) {
        // 从冗余表查询
        return orderIndexMapper.selectByOrderNo(orderNo);
    }
    
    // 方案3:广播表
    public Order queryWithRegion(Long orderId) {
        Order order = orderMapper.selectById(orderId);
        // 区域信息从广播表获取
        Region region = regionMapper.selectById(order.getRegionId());
        order.setRegionName(region.getName());
        return order;
    }
}

总结

✅ 分库分表概述 :为什么需要、分库vs分表

✅ 分片策略 :分片键、分片算法

✅ 主键设计 :雪花算法

✅ 中间件选型 :ShardingSphere、MyCat

✅ 实战案例:订单分库分表

下篇预告 :Redis 集群架构与优化


作者 :刘~浪地球
系列 :数据库与缓存(二)
更新时间:2026-04-11

相关推荐
lsc_blog8 分钟前
年终自评别贴提交记录,把技术产出讲成变化
数据库·面试·职场和发展·跳槽·求职招聘
衡石科技6 小时前
兼顾查询速度与数据时效:衡石的缓存和分析加速
缓存·数据缓存·数据时效
Zan8 小时前
SQLite + SQLx 实战:为什么已经发布的数据库迁移文件,绝对不能再修改?
数据库·后端·面试
feng_you_ying_li8 小时前
linux之数据链路层
linux·网络·智能路由器
这个DBA有点耶9 小时前
数据同步的三个工程底线:DDL同步、断点续传、一致性校验——开源CDC的边界与KFS的路径
数据库·dba·代码规范
SelectDB10 小时前
列式存储为什么适合分析类负载?原理、限制与日志场景应用
数据库
国科安芯10 小时前
人在回路中的总线:商业空间站环境控制与生命保障系统CANFD网络的可靠性与功能安全
网络·嵌入式硬件·安全·架构·php·抗辐射
数据库小学妹10 小时前
MySQL大字段治理:行溢出机制、覆盖索引与拆表
数据库·mysql·性能优化
爱吃奥利奥_wen11 小时前
SQL Server 基础语法详解:从建库建表到约束与增删改查
服务器·数据库·oracle