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

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


文章目录

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

相关推荐
三言老师15 小时前
K8s集群运行时自动化运维全覆盖落地实操(下)
linux·运维·服务器·网络
Super 含15 小时前
Android 启动优化(五):线程、GC 与 IO 为什么会拖慢启动?
java·服务器·数据库
ltl16 小时前
向量检索引擎选型:决策树、RAG 回链与开放问题
数据库
80s77716 小时前
住宅代理解析:动态住宅IP与静态住宅IP如何选型?
网络·网络协议·tcp/ip
坚持学习前端日记16 小时前
Python SQLAlchemy ORM 从0到1精通实战手册(基础到复杂高阶)
数据库·python·oracle
灯澜忆梦16 小时前
【MySQL17】进阶篇 | InnoDB引擎
数据库·mysql
吴声子夜歌16 小时前
网络安全——网络渗透测试(网络扫描)
网络·web安全·php
fish-man16 小时前
ceshi ceshi yixia
网络·安全·web安全
anxiao_m17 小时前
2026制造业云桌面选型攻略,不同生产场景适配方案汇总
大数据·网络·数据库
许彰午17 小时前
# 数据库配拦截器,不用改BPMN
数据库