前言
作为一名后端开发者,当你的系统用户量越来越多,数据库压力越来越大时,你一定会遇到"分库分表"这个概念。本文将用通俗易懂的方式,帮助小白开发者理解为什么需要分库分表,以及它能解决什么问题。
一、什么是分库分表?
1.1 核心概念
在大规模分布式系统中,单一数据库实例往往成为系统扩展的瓶颈。分库分表作为一种数据库水平扩展策略,通过将数据分散存储到多个物理节点上,从而突破单机存储和性能限制。
从系统架构角度看,分库分表本质上是一种数据分片(Sharding)策略,其核心思想是将原本集中式的数据存储改造为分布式存储架构,通过增加节点数量实现线性扩展能力。
1.2 技术定义
- 分库(Database Sharding):将一个数据库拆分成多个独立的数据库
- 分表(Table Sharding):将一张大表拆分成多张小表
- 垂直拆分:按业务功能拆分(如用户表、订单表分到不同库)
- 水平拆分:按数据行拆分(如用户表按ID范围分成多张表)
二、为什么需要分库分表?
2.1 单库单表的性能瓶颈
问题1:存储容量有限
MySQL单表建议:
- 数据量 < 2000万行
- 单表文件大小 < 10GB
超过这个量级,查询性能会显著下降
真实案例:某电商平台订单表,每天新增100万订单,一年就是3.65亿条数据,单表早已无法承受。
问题2:查询性能下降
sql
-- 在千万级数据表中查询
SELECT * FROM orders WHERE user_id = 12345;
-- 即使有索引,也可能需要扫描大量数据块
数据对比:
| 数据量 | B+树层级 | 查询耗时 |
|---|---|---|
| 100万 | 3层 | 10ms |
| 1000万 | 4层 | 50ms |
| 1亿 | 5层 | 200ms+ |
问题3:并发压力大
单数据库连接数限制:
- MySQL默认最大连接数:151
- 高并发场景:10000+ QPS
- 结果:连接池耗尽,请求排队或超时
问题4:备份恢复困难
- 单库100GB数据,全量备份需要数小时
- 故障恢复时间过长,影响业务连续性
2.2 分库分表的核心价值
价值1:提升性能
分表前:1亿数据查询 → 200ms
分表后:1000万/表 × 10张表 → 20ms
性能提升:10倍
价值2:横向扩展
传统方式(纵向扩展):
- 升级服务器硬件(CPU、内存、磁盘)
- 成本高,有上限
分库分表(横向扩展):
- 增加服务器数量
- 成本低,理论无上限
价值3:高可用保障
单库架构:
数据库宕机 → 整个系统不可用
分库架构:
某个库宕机 → 只影响部分用户(如1/10)
三、分库分表的具体方案
3.1 垂直拆分
垂直分库
场景:不同业务模块的数据分离
拆分前:
[单一数据库]
├── users(用户表)
├── orders(订单表)
├── products(商品表)
└── payments(支付表)
拆分后:
[用户中心DB] [订单中心DB] [商品中心DB]
├── users ├── orders ├── products
└── user_profiles └── order_items └── inventory
优点:
✓ 业务解耦,独立维护
✓ 故障隔离
✓ 便于微服务改造
缺点:
✗ 跨库JOIN困难
✗ 分布式事务复杂
垂直分表
场景:大字段拆分
go
// 拆分前:用户表字段过多
type User struct {
ID int64
Username string
Password string
Email string
Avatar []byte // 头像(大字段)
Description string // 个人简介(大字段)
CreateTime time.Time
UpdateTime time.Time
}
// 拆分后
// 主表:高频访问字段
type User struct {
ID int64
Username string
Email string
CreateTime time.Time
}
// 扩展表:低频访问字段
type UserProfile struct {
UserID int64
Avatar []byte
Description string
UpdateTime time.Time
}
// 优点:减少IO,提升查询速度
3.2 水平拆分
水平分表
场景:单表数据量过大
方案1:范围分片(Range-Based Sharding)
按数据的连续区间进行分片,常见维度包括:
- 主键ID范围(如:0-1000万、1000万-2000万)
- 时间范围(如:按月、按季度、按年)
- 地理区域(如:华北、华东、华南)
路由策略:
分片规则映射表:
Range 1: [0, 10000000) → Shard_0
Range 2: [10000000, 20000000) → Shard_1
Range 3: [20000000, 30000000) → Shard_2
适用场景:
- 时序数据(日志、监控指标)
- 具有明显区间特征的业务(区域分布)
- 需要频繁进行范围查询的场景
优势与局限:
- ✓ 范围查询效率高,无需跨片扫描
- ✓ 扩容操作简单,仅需新增分片
- ✗ 数据分布可能不均(数据倾斜问题)
- ✗ 热点数据集中(新增数据通常落在最新分片)
方案2:哈希分片(Hash-Based Sharding)
通过哈希算法将数据均匀分散到各分片,最常用的是取模算法。
路由策略:
算法:shard_id = hash(sharding_key) % shard_count
示例:
user_id: 12345 → hash(12345) % 10 = 5 → Shard_5
user_id: 98765 → hash(98765) % 10 = 5 → Shard_5
user_id: 11111 → hash(11111) % 10 = 1 → Shard_1
适用场景:
- 数据访问无明显热点
- 以单条记录查询为主的业务
- 要求数据均匀分布的系统
优势与局限:
- ✓ 数据分布均匀,避免热点问题
- ✓ 实现简单,路由逻辑清晰
- ✗ 范围查询需全表扫描(跨所有分片)
- ✗ 扩容成本高(需重新计算哈希并迁移数据)
方案3:一致性哈希(Consistent Hashing)
通过哈希环实现的分布式哈希算法,核心优势是动态扩缩容时仅需迁移少量数据。
算法原理:
1. 将哈希值空间映射为环形结构(0 ~ 2^32-1)
2. 将各物理节点映射到环上的某个位置
3. 数据按顺时针方向找到第一个节点作为存储位置
4. 引入虚拟节点解决数据倾斜问题
适用场景:
- 分布式缓存系统(Redis Cluster、Memcached)
- 需要频繁扩缩容的动态集群
- 对数据迁移成本敏感的场景
优势与局限:
- ✓ 扩缩容时数据迁移量最小(理论上仅1/N)
- ✓ 具备良好的容错能力(节点故障影响范围小)
- ✗ 实现复杂度较高
- ✗ 需要引入虚拟节点机制保证均匀性
水平分库
场景:单库连接数、IO瓶颈
多维度路由策略:
在既分库又分表的架构中,需要设计两级路由算法。
架构示意:
[DB_0] [DB_1] [DB_2]
├── orders_0 ├── orders_0 ├── orders_0
└── orders_1 └── orders_1 └── orders_1
路由规则(二次取模):
第一级:确定目标数据库
db_index = sharding_key % database_count
第二级:确定目标表
table_index = (sharding_key / database_count) % table_count
路由示例:
sharding_key: 12345, database_count: 3, table_count: 2
→ db_index = 12345 % 3 = 0 → DB_0
→ table_index = (12345 / 3) % 2 = 1 → orders_1
→ 最终路由:DB_0.orders_1
分库分表组合策略:
| 策略组合 | 适用场景 | 特点 |
|---|---|---|
| 分库不分表 | 单表数据量可控,但并发压力大 | 提升并发能力、隔离故障 |
| 分表不分库 | 存储瓶颈明显,并发压力一般 | 降低单表数据量、提升查询效率 |
| 既分库又分表 | 大规模高并发场景 | 全方位扩展能力、复杂度最高 |
四、分库分表带来的挑战
4.1 分布式主键问题
问题描述:在分库分表架构中,传统的数据库自增主键机制失效,不同分片中的自增ID会产生冲突。系统需要一种全局唯一的ID生成机制。
核心需求:
- 全局唯一性(Globally Unique)
- 趋势递增性(Sortable)
- 高性能(High Performance)
- 高可用(High Availability)
主流解决方案对比:
方案1:雪花算法(Snowflake Algorithm)
Twitter开源的分布式ID生成算法,通过64位整数保证全局唯一。
结构组成:
64位ID结构:
| 1位符号位 | 41位时间戳 | 10位机器标识 | 12位序列号 |
| 0 | 毫秒级 | 最多1024台 | 每毫秒4096个ID |
理论性能:单机QPS = 4096 × 1000 = 409万/秒
技术特性:
- ✓ 趋势递增,对B+树索引友好
- ✓ 本地生成,无网络依赖,性能极高
- ✓ 包含时间戳,可反向解析生成时间
- ✗ 依赖系统时钟,时钟回拨会导致ID重复
- ✗ 机器ID需要提前分配和管理
适用场景:高并发订单、消息、事件等需要趋势递增ID的业务
方案2:号段模式(Segment Allocation)
通过中心化的号段分发服务批量分配ID区间。
工作原理:
1. 中心服务维护各业务类型的当前最大ID
2. 应用启动时批量获取号段(如:[1000, 2000))
3. 应用本地使用该号段,用完再申请下一段
4. 采用双Buffer机制:当前号段使用到一定阈值时,异步加载下一号段
技术特性:
- ✓ 严格递增,符合业务习惯
- ✓ 数据库压力小(批量获取)
- ✓ 简单易实现,无时钟依赖
- ✗ 需要依赖数据库或独立服务
- ✗ 存在单点故障风险(可通过主备解决)
适用场景:对ID连续性有要求的业务(如订单号展示)
方案3:UUID / GUID
通用唯一识别码,基于随机数或MAC地址生成128位标识。
技术特性:
- ✓ 实现简单,各语言均有标准库支持
- ✓ 无需中心化服务,完全去中心化
- ✗ 占用空间大(32字符或16字节)
- ✗ 无序性导致数据库索引性能下降
- ✗ 不可读,业务语义弱
适用场景:分布式追踪ID、幂等Token等不需要作为主键的场景
方案4:数据库多主模式
通过设置不同的自增起始值和步长实现。
配置示例:
DB_0: auto_increment_offset=0, auto_increment_increment=10
DB_1: auto_increment_offset=1, auto_increment_increment=10
DB_2: auto_increment_offset=2, auto_increment_increment=10
生成序列:
DB_0: 0, 10, 20, 30 ...
DB_1: 1, 11, 21, 31 ...
DB_2: 2, 12, 22, 32 ...
技术特性:
- ✓ 利用数据库原生能力,无需额外组件
- ✓ 趋势递增
- ✗ 扩容困难(步长固定)
- ✗ ID不连续,存在空洞
适用场景:数据库实例数固定且较少的小规模分库场景
4.2 跨库JOIN问题
问题描述:在分库架构中,不同业务实体可能分布在不同物理数据库中,传统的SQL JOIN操作无法跨数据库执行,需要在应用层或中间件层解决关联查询问题。
典型场景:
场景:查询用户及其订单信息
传统架构:单库内直接JOIN
SELECT u.name, o.amount FROM users u JOIN orders o ON u.id = o.user_id
分库架构:users表在DB_0,orders表在DB_1,无法直接JOIN
解决方案对比:
方案1:应用层聚合(Application-Level Join)
核心思路:将JOIN操作拆解为多次独立查询,在应用层进行结果聚合。
实施步骤:
1. 查询主表数据(如:查询用户信息)
2. 提取关联键(如:user_id列表)
3. 批量查询关联表数据(使用IN条件)
4. 在内存中建立关联关系并组装结果
技术特性:
- ✓ 实现灵活,可控制查询策略
- ✓ 无需数据冗余,保持数据一致性
- ✓ 可针对性优化(如:缓存中间结果)
- ✗ 多次网络往返,延迟增加
- ✗ 应用层逻辑复杂度上升
- ✗ 大批量JOIN时内存开销大
适用场景:JOIN数据量可控(百条级别)、对实时性要求高的场景
方案2:数据冗余(Data Redundancy)
核心思路:在订单等从表中冗余用户名等主表的常用字段,避免关联查询。
设计原则:
冗余字段选择标准:
✓ 读频率远高于写频率的字段
✓ 数据变更频率低的字段(如用户名)
✓ 数据体积小的字段(避免冗余大字段)
✗ 不冗余频繁变更的字段(如余额、状态)
一致性保障:
- 消息队列异步同步(最终一致性)
- 分布式事务保障(强一致性,性能代价高)
- 定期对账与修复机制
技术特性:
- ✓ 查询性能最优,无JOIN开销
- ✓ 降低跨库查询复杂度
- ✗ 存储空间冗余
- ✗ 数据一致性维护成本高
- ✗ 冗余字段变更需要同步更新多表
适用场景:读多写少、数据一致性要求不高的展示类查询
方案3:全局表(Global Table)
核心思路:将字典表、配置表等小表在每个物理库中都保留完整副本。
适用表特征:
✓ 数据量小(通常<10000行)
✓ 变更频率低(配置类、地区码表等)
✓ 所有业务都需要关联的基础数据
同步策略:
- 配置中心推送变更
- Canal等工具监听binlog同步
- 定时全量同步
技术特性:
- ✓ 查询简单,与单库一致
- ✓ 无需跨库JOIN
- ✗ 数据同步延迟
- ✗ 存储空间浪费(N倍冗余)
适用场景:系统字典、配置参数、地理区域等基础数据表
方案4:中间件支持(Middleware Proxy)
部分分库分表中间件(如ShardingSphere)提供跨分片JOIN支持。
实现机制:
1. 中间件将SQL路由到所有相关分片
2. 收集各分片的查询结果
3. 在中间件层进行内存JOIN和聚合
4. 返回最终结果给应用
技术特性:
- ✓ 对应用透明,SQL语法兼容
- ✗ 性能开销大(全表扫描)
- ✗ 不适用于大数据量JOIN
适用场景:仅用于小数据量的报表查询、管理后台等非核心场景
4.3 分布式事务问题
问题描述:在分库分表架构中,单一业务操作可能涉及多个物理数据库,传统的ACID事务无法跨库保证,需要采用分布式事务解决方案。
典型场景:
电商下单流程:
1. 订单库:创建订单记录
2. 库存库:扣减商品库存
3. 账户库:扣减用户余额
要求:三个操作必须原子性完成(要么全成功,要么全失败)
CAP理论约束 :
在分布式系统中无法同时满足一致性(Consistency)、可用性(Availability)、分区容错性(Partition Tolerance),实践中通常选择牺牲强一致性以换取高可用。
解决方案对比:
方案1:最终一致性(Eventual Consistency)+ 可靠消息
核心思路:通过消息队列实现异步解耦,保证最终所有操作都会成功执行。
实施模式:
本地消息表方案:
1. 订单服务在本地事务中同时写入:订单记录 + 待发送消息
2. 定时任务扫描消息表,将消息发送到MQ
3. 库存服务、账户服务消费消息并执行各自的本地事务
4. 消费失败时进行重试(幂等性保障)
5. 最终所有服务都完成操作,达到最终一致性
技术特性:
- ✓ 性能高,无阻塞等待
- ✓ 可用性高,服务解耦
- ✓ 易于水平扩展
- ✗ 数据一致性存在延迟(通常秒级)
- ✗ 需要处理消息重复、顺序等问题
- ✗ 业务逻辑需要容忍中间状态
适用场景:互联网高并发业务、对实时一致性要求不严格的场景(推荐)
方案2:两阶段提交(2PC - Two-Phase Commit)
核心思路:引入事务协调器,通过准备和提交两个阶段保证强一致性。
执行流程:
阶段一:准备阶段(Prepare)
1. 协调器向所有参与者发送Prepare请求
2. 参与者执行事务但不提交,返回Yes/No
3. 协调器收集所有参与者的投票结果
阶段二:提交阶段(Commit)
1. 若所有参与者返回Yes,协调器发送Commit命令
2. 若任一参与者返回No,协调器发送Rollback命令
3. 参与者执行命令并返回确认
技术特性:
- ✓ 强一致性保障
- ✓ 符合传统事务语义
- ✗ 同步阻塞,性能差(RT提高数倍)
- ✗ 单点故障风险(协调器)
- ✗ 脑裂场景下可能数据不一致
适用场景:金融核心系统等对数据一致性要求极高的场景(慎用)
方案3:TCC模式(Try-Confirm-Cancel)
核心思路:将业务操作拆分为三个阶段,由业务代码保证一致性。
三阶段说明:
Try阶段:资源预留
- 检查资源是否充足
- 预留所需资源(如:冻结库存、冻结金额)
- 记录操作日志
Confirm阶段:执行业务
- 使用已预留的资源完成业务操作
- 释放资源锁定
Cancel阶段:回滚补偿
- 释放Try阶段预留的资源
- 恢复到操作前状态
实施示例:
下单场景TCC设计:
Try:
- 订单服务:创建"预下单"记录
- 库存服务:冻结库存(available_stock -= 1, frozen_stock += 1)
- 账户服务:冻结余额(available_balance -= 100, frozen_balance += 100)
Confirm:
- 订单服务:将订单状态改为"已下单"
- 库存服务:扣减冻结库存(frozen_stock -= 1)
- 账户服务:扣减冻结余额(frozen_balance -= 100)
Cancel:
- 订单服务:删除预下单记录
- 库存服务:释放冻结库存(available_stock += 1, frozen_stock -= 1)
- 账户服务:释放冻结余额(available_balance += 100, frozen_balance -= 100)
技术特性:
- ✓ 无长时间锁等待,性能优于2PC
- ✓ 适应复杂业务场景
- ✗ 实现复杂度高,需要业务侵入
- ✗ 需要设计反向补偿操作
- ✗ 对幂等性要求高
适用场景:金融支付、库存扣减等资金类、库存类核心业务
方案4:SAGA模式
核心思路:将长事务拆分为多个本地短事务,每个短事务都有对应的补偿事务。
执行模式:
协同式SAGA(Choreography):
服务间通过事件驱动进行协作,无中心协调器
编排式SAGA(Orchestration):
由中心编排器负责协调各服务的执行顺序
技术特性:
- ✓ 适合长流程业务(如旅游订单:机票+酒店+接送)
- ✓ 事务颗粒度灵活
- ✗ 需要设计补偿逻辑
- ✗ 不保证隔离性(脏读问题)
适用场景:微服务架构下的跨服务长流程事务
方案选型建议:
| 场景特征 | 推荐方案 | 原因 |
|---|---|---|
| 互联网高并发业务 | 最终一致性 | 性能优先,容忍短暂不一致 |
| 金融核心业务 | TCC | 平衡性能与一致性 |
| 银行转账清算 | 2PC | 强一致性要求 |
| 跨服务长流程 | SAGA | 灵活性与容错性 |
| 非核心辅助业务 | 最大努力通知 | 实现简单,成本低 |
4.4 数据迁移问题
问题描述:在已有业务系统中引入分库分表,需要在不停服的前提下完成海量数据迁移,同时保证数据一致性和业务连续性。
核心挑战:
- 存量数据迁移(TB级数据)
- 增量数据同步(实时写入)
- 业务切换无感知(零停机)
- 数据一致性校验
平滑迁移方案:
阶段1:双写策略(Dual Write)
架构状态:
应用层 ─┬─> 旧库(主写主读)✓ 保证业务连续性
└─> 新库(从写) ✓ 积累新增数据
实施要点:
- 新库写入失败不影响主流程(异步 + 日志记录)
- 记录双写开始时间点,作为数据对账基准
- 监控双写成功率,确保数据完整性
阶段2:历史数据迁移(Data Migration)
迁移策略:
1. 分批迁移:按分片键范围分批导出导入(每批10万-100万行)
2. 并行迁移:多线程/多进程并行处理不同分片
3. 限速控制:避免对生产库造成压力(限制QPS、IO)
4. 断点续传:记录迁移进度,支持中断恢复
工具选型:
- Canal:监听MySQL binlog实时同步
- DataX:阿里开源的离线数据同步工具
- 自研脚本:SELECT + INSERT批量迁移
阶段3:数据校验(Data Validation)
校验维度:
- 数据总量对比:count(*) 统计
- 关键业务数据抽样:随机抽取10%数据对比字段值
- 数据完整性:校验主键连续性、外键关联
- 业务指标对比:订单金额、用户数等核心指标
自动化对账:
- 定时任务扫描差异数据
- 不一致数据自动修复或告警
- 记录对账日志供审计
阶段4:读流量切换(Read Traffic Switch)
灰度切换策略:
1. 灰度1%:少量用户读新库,观察监控指标
2. 灰度10%:扩大范围,持续观察
3. 灰度50%:半数流量切换
4. 全量100%:完全切换到新库
快速回滚机制:
- 保留旧库数据
- 配置中心动态切换读库
- 异常情况秒级回滚
阶段5:下线旧库(Decommission)
清理步骤:
1. 停止双写:仅写新库
2. 保留观察期:保留旧库1-3个月用于应急
3. 数据归档:将旧库数据归档到冷存储
4. 资源回收:释放旧库服务器资源
关键风险与应对:
| 风险点 | 应对措施 |
|---|---|
| 迁移过程数据不一致 | 双写 + binlog同步 + 自动对账 |
| 切换过程业务中断 | 灰度切换 + 快速回滚 |
| 性能下降 | 压测验证 + 监控告警 |
| 数据丢失 | 全量备份 + 增量备份 |
五、典型业务场景设计
5.1 电商订单表分片设计
业务特征分析:
- 数据规模:日订单量100万,年订单量3.65亿
- 查询模式:95%按用户ID查询历史订单,5%按订单号查询
- 写入特征:高并发写入,峰值QPS达10000+
- 存储周期:热数据2年,冷数据归档
分片策略选择:
方案对比:
| 维度 | 按时间分片 | 按用户ID Hash分片 | 选择 |
|---|---|---|---|
| 查询效率 | 范围查询慢 | 单用户查询快 | ✓ Hash |
| 数据分布 | 不均匀(新表热) | 均匀 | ✓ Hash |
| 扩容难度 | 简单(新增月表) | 需数据迁移 | - |
| 业务匹配 | 不符合(按用户查询为主) | 符合 | ✓ Hash |
最终设计:
分片策略:按user_id哈希分表
分片数量:32张表(2的幂次,便于未来扩展到64/128)
路由算法:table_index = user_id % 32
表命名:orders_0, orders_1, ..., orders_31
核心表结构:
- order_id: BIGINT(雪花算法生成,全局唯一)
- user_id: BIGINT(分片键,建立索引)
- create_time: DATETIME(辅助索引,用于时间范围查询)
- amount: DECIMAL(订单金额)
- status: TINYINT(订单状态)
索引设计:
- PRIMARY KEY (order_id)
- INDEX idx_user_create (user_id, create_time) -- 组合索引,覆盖常用查询
- INDEX idx_status (status) -- 状态查询索引
查询场景适配:
场景1:查询用户订单列表(高频)
- 直接路由到目标表:orders_{user_id % 32}
- 单表查询,性能优异
场景2:按订单号查询(低频)
- 方案A:全表扫描(32张表并行查询)
- 方案B:订单号中包含分片信息(推荐)
如:订单号 = 时间戳 + 分片号 + 序列号
场景3:商家查询所有订单(后台管理)
- 使用ES等搜索引擎异步同步
- 避免跨分片查询影响核心业务
性能提升数据:
| 指标 | 分表前 | 分表后(32表) | 提升 |
|---|---|---|---|
| 单表数据量 | 3.65亿 | 1140万/表 | 减少97% |
| 用户订单查询RT | 180ms | 15ms | 提升12倍 |
| 写入吞吐QPS | 3000 | 30000+ | 提升10倍 |
| 磁盘IO使用率 | 85% | 25% | 降低71% |
5.2 社交平台用户表分库分表设计
业务特征分析:
- 数据规模:总用户量2亿,日活5000万
- 并发模式:读QPS 80000,写QPS 5000
- 查询模式:99%按用户ID精确查询
- 扩展需求:预计3年内用户量翻倍
架构设计:
总体方案:4库 × 16表 = 64个分片
第一级路由(库):db_index = user_id % 4
第二级路由(表):table_index = (user_id / 4) % 16
分片映射示例:
user_id: 12345
→ db_index = 12345 % 4 = 1 → DB_1
→ table_index = (12345 / 4) % 16 = 8 → users_8
→ 最终路由:DB_1.users_8
拆分收益:
- 单库并发:80000 / 4 = 20000 QPS(可承受)
- 单表数据量:2亿 / 64 = 312万/表(健康水平)
- 连接数分散:每库独立连接池,避免连接耗尽
垂直拆分优化:
主表(高频访问):user_base
- user_id, username, mobile, status, create_time
- 存储核心身份信息,轻量化
扩展表(低频访问):user_profile
- user_id, avatar_url, bio, location, birthday
- 存储详细资料,按需查询
统计表(独立维护):user_stats
- user_id, followers_count, following_count, posts_count
- 计数器字段,高频更新,独立分片避免锁竞争
六、最佳实践建议
6.1 何时需要分库分表?
触发条件(满足任一即可考虑):
✓ 单表数据量 > 1000万
✓ 单表文件大小 > 5GB
✓ 查询响应时间 > 100ms
✓ 数据库连接数接近上限
✓ 磁盘IO持续 > 80%
不要过早优化:
- 初创项目、用户量 < 10万:单表完全够用
- 优先通过索引、缓存优化
- 只有遇到明确瓶颈时再考虑分库分表
6.2 设计原则
1. 选好分片键(Sharding Key)
✓ 查询高频字段(如user_id)
✓ 数据分布均匀
✓ 尽量避免跨片查询
✗ 不要选时间字段(数据倾斜)
✗ 不要选低基数字段(如性别)
2. 预留扩展空间
go
// 设计表数量时,建议2的幂次
// 方便未来翻倍扩容
tableCount := 16 // 将来可扩展到32、64
3. 保持表结构一致
sql
-- 所有分表结构必须完全一致
-- 使用统一的DDL脚本管理
4. 避免分布式事务
✓ 业务上拆分,避免跨库操作
✓ 使用最终一致性代替强一致性
✗ 尽量不用两阶段提交