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

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


文章目录

    • 一、分库分表概述
      • [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

相关推荐
A_humble_scholar10 小时前
Linux(七)调度器:从硬件矛盾到进程切换的底层逻辑
linux·服务器·网络
AOwhisky10 小时前
Redis 学习笔记(第一期):概述、安装配置与核心理论
运维·数据库·redis·笔记·学习·云计算
ytttr87310 小时前
C# 定时数据库备份工具
开发语言·数据库·c#
❀抽抽10 小时前
证件照制作API接入指南:700+规格一键生成
大数据·网络·人工智能
Promise微笑10 小时前
绝缘油介损(油介损)测试仪的深层机理、技术演进与精准诊断策略
大数据·网络·人工智能
睡不醒男孩03082310 小时前
自建 Prometheus+Grafana 与 CLUP 深度监控 PG 集群有什么区别?
数据库·oracle
AOwhisky11 小时前
Redis 学习笔记(第四期):高可用与集群(哨兵 + Cluster + 容器化)
linux·运维·数据库·redis·笔记·学习·缓存
测试仪器廖生1359025638511 小时前
罗德与施瓦茨 FSP13频谱分析仪FSP30
网络·人工智能·算法
上海锝秉工控11 小时前
省线型增量编码器:用“减法思维“重构工业控制的未来
网络·人工智能·重构
猫猫聚会Ing11 小时前
数据库设计 Prompt 提示词 - 构建与迭代
数据库