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+树作为索引结构:
- 非叶子节点:只存储索引数据(索引列的值)
- 叶子节点:
- 聚簇索引:存储完整数据行
- 非聚簇索引:存储索引列和主键值
聚簇索引 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)
八、实际影响因素¶
每张表的最优数据量不同的原因¶
- 字段大小
- 字段少的表(如只有id和status),每条记录可能只有几十字节
-
字段多的表(如包含TEXT、BLOB等),每条记录可能几KB甚至更大
-
行格式
- COMPACT
- REDUNDANT
- DYNAMIC
-
COMPRESSED
-
索引数量
- 每增加一个索引,就会多一棵B+树
-
索引也会占用大量空间
-
页填充因子
-
InnoDB不会100%填满页面,一般为15/16
-
数据密度
- NULL值不占用实际存储空间
- VARCHAR字段实际占用与内容长度相关
实际案例¶
| 记录大小 | 3层B+树可存数据量 |
|---|---|
| 500字节 | 约4000万条 |
| 1KB | 约2000万条 |
| 2KB | 约1000万条 |
| 4KB | 约500万条 |
九、总结与建议¶
关于"2000万"的正确理解¶
- 2000万不是铁律
- 它是基于特定条件(BIGINT主键、1KB/条记录)的计算结果
-
实际取决于字段大小、索引数量等因素
-
阿里规范参考
- 单表行数超过500万行或单表容量超过2GB
-
才推荐进行分库分表
-
性能监控比数字更重要
- 关注查询响应时间
- 监控磁盘IO
- 观察CPU使用率
何时考虑分库分表¶
- 查询性能明显下降
- 单表容量超过2GB
- 单表行数超过500万(字段较少时)
- 数据写入量过大
分表策略¶
- 按时间分表
-
适用于日志、订单等有时间特点的数据
-
按哈希分表
-
适用于需要均匀分布的数据
-
按地区/业务分表
- 适用于业务边界清晰的数据
优化建议¶
- 合理设计索引
- 避免过多索引
-
选择合适的索引列
-
定期归档历史数据
-
迁移冷数据到归档表
-
使用分区表
- MySQL原生分区功能
-
对应用透明
-
升级硬件
- SSD硬盘
- 更大内存(提高缓存命中率)
附录:相关参数¶
-- 查看页大小
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;