面试官:都说 MySQL 单表超过 2000 万行就要分库分表,这个结论到底是怎么来的?

0 阅读6分钟

面试后端开发的时候,经常会遇到一个关于数据库优化的经典问题:“MySQL 单表数据量到多少就需要分库分表了?”

很多人会直接回答:“2000 万左右。”

如果面试官接着问:“这个 2000 万是怎么算出来的?如果我的表只有几个整数字段,到了 5000 万查询会慢吗?”

这个时候如果只知道一个死数字,就没法往下聊了。今天我们就直接通过计算,看看这个“2000万”的结论到底是怎么来的。

计算过程:2000 万的由来

要弄清楚这个问题,我们要看 MySQL InnoDB 引擎的底层数据结构:B+ 树。

在 InnoDB 里,数据是按“页(Page)”来存的,默认一页的大小是 16KB。 B+ 树有一个特点:非叶子节点只存主键 ID 和指向下一层的指针,真正的行数据全都在最底层的叶子节点上。

我们现在来算一下,一棵 3 层高度的 B+ 树,大概能存多少数据:

1. 设定几个前提条件:

  • 主键 ID 我们用 bigint 类型,占 8 个字节。
  • 指针在 InnoDB 里占 6 个字节。
  • 我们假设一行数据(包含表里所有字段)加起来大概是 1KB。

2. 算一下非叶子节点能存多少指针:

  • 一个非叶子节点是 16KB。
  • 一对“主键+指针”是 14 字节(8+6)。
  • 那么一个节点能放的数量就是:16KB / 14字节 = 16384 / 14 ≈ 1170 个
  • 也就是说,一个根节点可以分出 1170 个分支。

3. 算一下叶子节点能存多少行数据:

  • 一个叶子节点也是 16KB。
  • 按我们假设的一行数据 1KB 算,一个叶子节点能存:16KB / 1KB = 16 行

4. 算一下 3 层 B+ 树的总容量:

  • 第 1 层(根节点):1 个节点,有 1170 个指针。
  • 第 2 层:1170 个节点,每个节点又有 1170 个指针。算下来就是 1170 * 1170 = 1,368,900 个指针,指向最底层的叶子节点。
  • 第 3 层(叶子节点):有 1,368,900 个节点,每个存 16 行数据。
  • 总数据量1,368,900 * 16 ≈ 2190 万行

所以,所谓的“2000万”,其实是基于“一行数据大小为 1KB”的假设下,一棵 3 层 B+ 树能存下的数据量估算值。

为什么建议 B+ 树保持在 3 层?

有人可能会问:那如果数据真的到了 5000 万,让 B+ 树变成 4 层不行吗?

可以是可以,但这会增加磁盘 I/O 的次数。

在以前大家都用机械硬盘的时候,磁盘读取数据是很慢的(大约需要几毫秒到十几毫秒)。

  • 在 3 层 B+ 树里,查一条数据最多需要 3 次磁盘 I/O(如果根节点在内存里,实际上只要 2 次)。
  • 如果树变成了 4 层,查一次数据就要多一次磁盘 I/O。并发量大的时候,多一次 I/O 就会导致数据库响应变慢。

为了不让查询变慢,大家总结出的经验就是:尽量把 B+ 树的高度控制在 3 层以内。

现在的实际情况:2000 万还是绝对标准吗?

其实“2000万”只是一个经验值,在现在的实际开发中,它并不是一条死规定。单表到底能存多少,主要看两点:

第一,看你的单行数据到底有多大。 如果你的表只有三个字段:iduser_idstatus,一行数据算下来才几十个字节。 那一个 16KB 的叶子节点就能存几百行数据。这时候一棵 3 层的 B+ 树存个七八千万甚至上亿条数据都没问题。这种表到了 5000 万查询依然很快,根本不需要分库分表。

反过来,如果你的表里有好几个很长的字符串字段(比如 text 或者很长的 varchar),一行数据占了 4KB。那一个叶子节点只能存 4 行数据,可能表里才几百万条数据,B+ 树就已经变成 4 层了。

第二,看你的服务器硬件。 “控制 3 层”的主要原因是怕机械硬盘的随机读取太慢。 但现在的数据库服务器基本都用上了固态硬盘(SSD)。固态硬盘的随机读取速度比机械硬盘快很多。所以在 SSD 环境下,就算 B+ 树真的变成了 4 层,多出来的那一点读取时间,很多业务也是完全可以接受的。

总结与面试回答建议

如果你在面试时被问到“单表多大需要分库分表”,可以参考下面的思路来回答:

  1. 先说明 2000 万不是一个绝对的限制,而是一个基于经验的估算。
  2. 解释这个数字是怎么来的:在 InnoDB 默认页大小 16KB、单行数据约 1KB 的假设下,一棵 3 层的 B+ 树刚好能存大概 2000 万条数据。为了避免树长到 4 层增加磁盘 I/O 次数,所以有了这个建议。
  3. 说明实际情况因表而异。如果单行数据很小(比如简单的关联表),3 层 B+ 树存几千万条数据毫无压力;如果单行数据很大(包含大文本),可能几百万条就达到瓶颈了。
  4. 最后可以提一下硬件的影响。现在 SSD 普及了,多一次磁盘 I/O 的影响没有以前那么大。所以在考虑分库分表之前,应该先评估是否可以优化表结构(比如拆分大字段)或者升级硬件,因为分库分表会给系统带来很大的复杂性。

END

写在最后:

最近私信问我面试题的小伙伴实在太多了,一个个回有点回不过来。

我花了两个周末,把星球里大家公认最容易挂的 AI/Go/Java 面试坑点 整理成了一份 PDF 文档。里面不光有题,还有解题思路和避坑指南。

想要的同学,直接关注并私信我 【面试】,我统一发给大家。

wangzhongyang.com 也欢迎大家直接访问我的官网,里面有AI / Go / Java 的资料,免费学习