跳转至

MySQL每张表最好不超过2000万数据,是真的吗?

一、前言

作为后端开发工程师,是不是经常听到过: - "MySQL单表最好不要超过2000万" - "单表超过2000万就要考虑数据迁移了" - "表数据快到2000万了,查询速度当然慢"

这些说法究竟有没有科学依据?本文将带你深入分析这个"2000万"数字的来源,并从原理层面解答这个问题。


二、背景故事

面试中的经典问题

面试官:你为什么要分三张表,两张不行吗?四张不行吗?

应聘者:因为MySQL每张表最好不超过2000万条数据,否则会导致查询速度降低。

面试官:那你先回去等通知吧。

这个回答有什么问题?让我们继续往下看。

阿里Java开发手册的建议

单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。

注意:500万或2GB只是一个参考值,并不适用于所有场景。


三、单表数量限制

主键类型的限制

MySQL表的主键大小决定了表的上限:

主键类型 取值范围 最大记录数
INT 2^32-1 约21亿
BIGINT 2^64-1 约3689万亿

BIGINT能用多久?

无符号BIGINT自增,按每秒新增一条记录计算: - 最大值:18446744073709551615 - 大约需要58万年才能用完

所以,主键类型不是限制单表数量的主要因素。


四、InnoDB存储结构

B+树结构

InnoDB引擎使用B+树作为索引结构:

  1. 非叶子节点:只存储索引数据(索引列的值)
  2. 叶子节点
  3. 聚簇索引:存储完整数据行
  4. 非聚簇索引:存储索引列和主键值

聚簇索引 vs 非聚簇索引

  • 聚簇索引(主键索引):叶子节点存储完整数据行
  • 非聚簇索引(二级索引):叶子节点存储索引列+主键值,查询时需要回表

五、B+树与数据存储

B+树的高度

理论上,B+树保持在3层以内比较合适: - 第1层:根节点(常驻内存) - 第2层:索引节点 - 第3层:数据节点

如果变成4层,每次查询需要4次磁盘IO,性能会下降。


六、页的数据结构

页大小

InnoDB默认页大小为16KB,可以修改(最大64KB,最小4KB)。

页的组成

每个数据页包含: 1. 文件头:记录前后页地址 2. 页号:唯一标志 3. 数据记录:存储实际数据 4. 页目录:提高查询效率 5. 校验码:数据完整性校验

行格式

在COMPACT行格式下,一条记录的额外信息包括: - 变长字段长度列表:2字节 - NULL标志位:1字节 - 记录头信息:5字节 - Transaction ID:6字节 - Roll Pointer:7字节

一条记录至少占用2 + 1 + 5 + 6 + 7 = 21字节的额外开销。


七、最大数据量计算

计算前提

假设: - 主键为BIGINT(8字节) - 页大小为16KB - B+树高度为3层

计算步骤

第一层(根节点): - 每个指针:6字节 - 每个索引值:8字节(BIGINT) - 每个节点可存:16KB / 14B ≈ 1170 个索引项 - 最多指向:1170 个子节点

第二层(中间层): - 最多索引数:1170 × 1170 ≈ 136.89万

第三层(叶子节点): - 每条记录假设:1KB(实际取决于字段大小) - 每页可存:16条记录 - 总记录数:1170 × 1170 × 16 ≈ 2000万条

结论

所以"2000万"这个数字是基于: - 主键为BIGINT(8字节) - 每条记录约1KB - B+树高度为3层 - 页大小为16KB

计算公式:约等于 (16384 / 14)² × (16384 / 1024)


八、实际影响因素

每张表的最优数据量不同的原因

  1. 字段大小
  2. 字段少的表(如只有id和status),每条记录可能只有几十字节
  3. 字段多的表(如包含TEXT、BLOB等),每条记录可能几KB甚至更大

  4. 行格式

  5. COMPACT
  6. REDUNDANT
  7. DYNAMIC
  8. COMPRESSED

  9. 索引数量

  10. 每增加一个索引,就会多一棵B+树
  11. 索引也会占用大量空间

  12. 页填充因子

  13. InnoDB不会100%填满页面,一般为15/16

  14. 数据密度

  15. NULL值不占用实际存储空间
  16. VARCHAR字段实际占用与内容长度相关

实际案例

记录大小 3层B+树可存数据量
500字节 约4000万条
1KB 约2000万条
2KB 约1000万条
4KB 约500万条

九、总结与建议

关于"2000万"的正确理解

  1. 2000万不是铁律
  2. 它是基于特定条件(BIGINT主键、1KB/条记录)的计算结果
  3. 实际取决于字段大小、索引数量等因素

  4. 阿里规范参考

  5. 单表行数超过500万行单表容量超过2GB
  6. 才推荐进行分库分表

  7. 性能监控比数字更重要

  8. 关注查询响应时间
  9. 监控磁盘IO
  10. 观察CPU使用率

何时考虑分库分表

  1. 查询性能明显下降
  2. 单表容量超过2GB
  3. 单表行数超过500万(字段较少时)
  4. 数据写入量过大

分表策略

  1. 按时间分表
  2. 适用于日志、订单等有时间特点的数据

  3. 按哈希分表

  4. 适用于需要均匀分布的数据

  5. 按地区/业务分表

  6. 适用于业务边界清晰的数据

优化建议

  1. 合理设计索引
  2. 避免过多索引
  3. 选择合适的索引列

  4. 定期归档历史数据

  5. 迁移冷数据到归档表

  6. 使用分区表

  7. MySQL原生分区功能
  8. 对应用透明

  9. 升级硬件

  10. SSD硬盘
  11. 更大内存(提高缓存命中率)

附录:相关参数

-- 查看页大小
SHOW VARIABLES LIKE 'innodb_page_size';

-- 查看行格式
SHOW VARIABLES LIKE 'innodb_default_row_format';

-- 查看缓冲池大小
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';

-- 查看索引统计信息
SHOW INDEX FROM table_name;