分库分表之前,先算清楚这笔账

米阳 2023-8-12 5 8/12

“数据量大了要分库分表”这句话害了不少项目。分片带来的复杂度是长期的:跨库 join 没了、事务要改成最终一致、扩容要迁移数据、运维要管更多实例。动手之前先算几个数。

第一步:现在到底多大

SELECT table_name,
       table_rows,
       ROUND(data_length/1024/1024, 1) AS data_mb,
       ROUND(index_length/1024/1024, 1) AS index_mb
FROM information_schema.tables
WHERE table_schema = 'app'
ORDER BY data_length DESC LIMIT 10;

注意 table_rows 是估算值,InnoDB 可能偏差很大,用 COUNT(*) 核对一遍。

第二步:单表能撑多大

经验区间:

单表行数 状态
百万级 无压力
千万级 需要好索引,正常
亿级 开始吃力,考虑归档或分片
十亿级 必须分片

但行数不是唯一指标。一行 200 字节和一行的 2KB,差距很大。判断标准是索引能否装进内存:InnoDB 的 Buffer Pool 装得下热数据索引,查询就快;装不下就开始频繁磁盘 IO。

所以先看 innodb_buffer_pool_size 和数据+索引总量。4G buffer pool 撑 20G 数据,命中率还行;撑 100G 就必然抖动。

第三步:增长有多快

SELECT DATE_FORMAT(created_at,'%Y-%m') AS m, COUNT(*)
FROM orders GROUP BY m ORDER BY m DESC LIMIT 12;

每月增长 10 万行和每月 500 万行,应对策略完全不同。前者三年才 3600 万,加个归档就能跑很多年;后者一年就得上分片。

第四步:能不能不分

按性价比排序的替代方案:

  1. 加索引、改 SQL:八成慢查询是这个原因,不需要分片
  2. 归档冷数据:把两年前的订单挪到历史表,主表立刻瘦下来,业务代码几乎不用改
  3. 读写分离:读多写少的场景,加从库能扛很久
  4. 分区表:按时间分区,查询自动裁剪分区,对应用透明
  5. 换存储:日志类数据放 ES 或 ClickHouse,别硬塞 MySQL

这些做完还不够,再考虑分片。

真要分,怎么分

  • 分片键:必须能覆盖绝大多数查询条件。订单表按 user_id 分,按订单号查就要扫描所有分片
  • 分片数量:一次分够,比如 16 或 32,后续扩容用“双倍扩容 + 数据迁移”,比一开始分 4 个再慢慢加省事
  • 跨片查询:提前想好“按时间统计”“按状态查”这类需求怎么处理,通常要异构一份到宽表或搜索引擎
  • 分布式事务:尽量用“本地事务 + 消息最终一致”,别上 XA

我的结论

绝大多数项目在千万级以内,靠索引 + 归档 + 读写分离就能撑很多年。分片是最后一张牌,打出去就收不回来。先把前面几步做扎实,比急着分片更有价值。

- THE END -

米阳

8月12日10:05

最后修改:2023年8月12日
0

非特殊说明,本博所有文章均为博主原创。

共有 0 条评论