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

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


文章目录

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

相关推荐
逸Y 仙X1 小时前
文章十九: ElasticSearch Full Text 全文本查询
java·大数据·数据库·elasticsearch·搜索引擎·全文检索
STER labo1 小时前
mysql配置环境变量——(‘mysql‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件解决办法)
数据库·mysql·adb
微软技术分享1 小时前
本地部署千问 2.5-1.5B-GGUF + LangChain 封装学习
数据库·学习·langchain
七夜zippoe1 小时前
DolphinDB分区策略:VALUE分区详解
数据库·oracle·分区·value·dolphindb
rKWP8gKv71 小时前
数据库连接池选型:HikariCP与Druid的性能对比
数据库
dreamZhanglx1 小时前
MySQL进阶
数据库·mysql
有浔则灵1 小时前
GORM 日志与调试完全指南:从基础配置到生产实践
服务器·数据库·gorm
xmjd msup1 小时前
MySQL 函数
数据库·mysql
时空系2 小时前
第7篇:功能——打造你的工具箱 Rust中文编程
开发语言·网络·rust
PaperData2 小时前
2003-2026.1北大法宝地方数字经济政策数据
数据库·数据分析·学习方法·经管