在 MySQL 的性能优化中,一个经常被提及的经验是:
“单表数据量不宜超过 2000 万。”
这并非随意的数字,而是结合了 B+Tree 索引结构、单行数据大小、页结构与缓存命中率 等多个因素所形成的经验总结。
本篇将从索引结构出发,深入解析单表数据量、索引层数、每行数据大小三者之间的关系,帮助你科学判断:你的表还能承受多大的数据量?
一、MySQL 中索引的本质是什么?
MySQL(InnoDB 引擎)使用 B+Tree 作为主键索引和辅助索引的底层结构。
- 每一个 叶子节点 是一个 数据页,默认大小是 16KB;
- 索引结构就是一棵 多路平衡查找树;
- 每一层 B+Tree 索引页存储多个 key → 形成“树的高度”;
- 树越高,查询路径越长,IO 越多,性能越差。
二、单行数据大小对索引结构的影响
一个数据页(16KB)中能容纳多少条记录,取决于每条记录占用的字节数。
示例:假设行大小不同,数据页能容纳的记录数
| 每行数据大小 | 每页记录数(大致) |
|---|---|
| 100B | 160 行左右 |
| 500B | 32 行左右 |
| 1000B | 16 行左右 |
| 2000B | 8 行左右 |
每页不能被完全填满,还包括页目录、槽位、指针等元数据开销。 实际上数据页大约有 15KB 空间可以存放数据,假如 一条数据占用 1K,那么一个数据页最大也就存放 15 条数据
三、非叶子节点个数估算
非叶子节点的数据页也是 16KB,除去数据页的结构信息占用 1K,大约有 15K 可以存储数据,非叶子节点不存储数据,只存储主键信息和页号 假如数据表主键是 bigint类型,占用 8byte空间,页号占用 4byte,那么一条数据是12byte数据,那么一个非叶子节点的数据是:
15KB * 1024 / 12 = 1280
四、索引树高度的估算方法
设:
- D = 每个非叶子页能容纳的指针个数(子节点个数);
- L = 树的层数;
- N = 总数据条数;
- 则理论上:
N≤D(L−1)×R 其中 R 为每页记录数。
例如:若每页可容纳 15 条记录,非叶子节点每页能容纳 1280 个主键数据(key),那 3 层 B+Tree 可容纳约 1280 × 1280 × 100 = 2,500 万条记录。
五、结合数据大小估算不同层数可支持的数据量
假设配置:
- 数据页大小:16KB,其中只有 15K 空间存放数据(除去页目录、槽位等)
- 索引页:每个节点约容纳 1280 个指针(非叶子页)
- 数据页:每行数据分别为 100B、500B、1KB、2KB
| 每行大小 | 每页记录数 | 3 层 B+Tree 可支持数据量 |
|---|---|---|
| 100B | 160 | 1280 × 1280 × 150 ≈ 24576 万行 |
| 500B | 32 | 1280 × 1280 × 30 ≈ 4915 万行 |
| 1KB | 16 | 1280 × 1280 × 15 ≈ 2458 万行 |
| 2KB | 8 | 1280 × 1280 × 8 ≈ 1311 万行 |
实际上,假如一条数据有 1KB 大小,三层 B+Tree可以存放的数据大约是 2458 万行。2000W 只是一个推荐值。 在设计表的时候,可以通过纵向拆表,减少单条数据的占用空间来增大单表的存储量,如上表格中,如果单条数据只有 100Byte,单表可以存放 2.45 亿数据,查询性能也不会太差