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

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


文章目录

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

相关推荐
小辰记事本20 小时前
从零读懂RoCEv2数据包构造:从WQE到线缆上的完整旅程
服务器·网络·网络协议·rdma
廿一夏20 小时前
MySql存储引擎与索引
数据库·sql·mysql
Mahir0820 小时前
Spring 循环依赖深度解密:从问题本质到三级缓存源码级解析
java·后端·spring·缓存·面试·循环依赖·三级缓存
北京耐用通信21 小时前
全域适配工业场景耐达讯自动化Modbus TCP 转 PROFIBUS 网关轻松实现以太网与现场总线互通
网络·人工智能·网络协议·自动化·信息与通信
在角落发呆1 天前
Linux转发配置:解锁网络互联的核心密码
linux·运维·网络
lzhdim1 天前
SQL 入门 15:SQL 事务:从 ACID 到四种常见的并发问题
数据库·sql
瀚高PG实验室1 天前
瀚高企业版V9.1.1在pg_restore还原备份文件时提示extract函数语法问题
数据库·瀚高数据库
TDengine (老段)1 天前
TDengine Tag 设计哲学与 Schema 变更机制
大数据·数据库·物联网·时序数据库·iot·tdengine·涛思数据
YOU OU1 天前
Spring IoC&DI
java·数据库·spring
YMWM_1 天前
UDP协议详解:从原理到Python实践
网络·网络协议·udp