分库分表前须满足三个前提:单表超5000万行、单库qps持续>2000、主从延迟长期>1s,至少两个成立才考虑;业务主键必须为int64/uint64;跨分片join和order by limit必须下线或改应用层合并。

分库分表前必须确认的三个前提
直接上 shard 或 vitess 很容易翻车。先看清楚数据是否真到了非拆不可的地步:单表行数超 5000 万、单库 QPS 持续 > 2000、主从延迟长期 > 1s,这三个指标中至少满足两个,才值得动分库分表。否则加索引、读写分离、缓存预热更省事。另外,业务主键必须是 int64 或 uint64(不能是 UUID),否则分片键难做路由;最后,所有跨分片的 JOIN 和 ORDER BY ... LIMIT 查询必须下线或改写成应用层合并。
用 sharding-sphere-proxy 还是自研分片逻辑?
Go 微服务里不建议用 sharding-sphere-proxy —— 它是 Java 生态的,Go client 与它的协议兼容性差,且无法感知服务内部事务上下文。推荐在 Go 层做轻量分片路由:
- 用
github.com/bradfitz/slice或golang.org/x/exp/slices做分片键哈希(比如shardID := userID % 16) - 每个分库连接池独立初始化:
dbShards[shardID] = sql.Open("mysql", dsn),别共用一个*sql.DB - 事务只能落在单分片内;跨分片更新必须用最终一致性 + 消息队列补偿
- 避免用
SELECT * FROM user WHERE id IN (1,2,3,4)这类语句——IN 列表可能横跨多个分片,得先查路由再并发 fetch
archive 表迁移时如何不停服
归档不是简单 INSERT INTO archive SELECT ... 然后 DELETE。线上表锁住几秒就可能触发熔断。正确做法是分批+低峰+校验:
- 每次只处理 1000 行:
DELETE FROM orders WHERE create_time - 用
pt-archiver(Percona Toolkit)比手写脚本更稳,它自带 sleep 控制和 checksum 校验 - 归档表必须带原始分片字段(如
shard_id),否则后续查历史数据时无法定位物理库 - 归档后立刻执行
ANALYZE TABLE orders,避免优化器因统计信息过期选错执行计划
时间分区表(PARTITION BY RANGE)在 MySQL 8.0+ 的实际限制
MySQL 原生时间分区看着方便,但 Go 微服务调用时容易踩坑:
-
PARTITION BY RANGE (TO_DAYS(create_time))要求create_time不能为 NULL,否则插入失败报ERROR 1526 (HY000): Table has no partition for value NULL - 每月新增分区得手动
ALTER TABLE orders ADD PARTITION ...,没人维护就会卡住——建议用 cron job 自动执行,别靠 DBA - Go 的
database/sql不识别分区名,SELECT仍走全表扫描,除非你在 WHERE 条件里明确写出分区字段范围 - 备份时
mysqldump --no-create-info --where="create_time >= '2023-01-01'"才能跳过冷分区,否则 dump 文件爆炸
分库分表不是一锤子买卖,最麻烦的是后续新增字段要同步到所有分片库,归档策略要随业务生命周期动态调整——这些没法靠工具自动完成,得在 service 层埋好钩子和开关。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











