InnoDB 采用 B+ 树存储,真正的数据行存在聚簇索引的叶子节点上

B+ 树的每个节点是一页,一页固定大小是 16k ,其中 1k 是页头,15k 是可以存放 索引/数据行 的空间

B+ 树的层数要尽可能小,以减少查找数据时磁盘 IO 的次数(磁盘 IO 很慢),实际中一般是 3 层

 

下面假设 B+ 树一共是三层,第一、二层用来存主键索引,第三层用来存数据行(聚簇索引)

用来存储索引的一页(非叶子节点):

由于是聚簇索引,一般都是主键作为索引列,假设主键为 bigInt 类型(8byte),每个索引列还需要页号(4byte),用来指向下一层的B+树节点(页),所以一个索引列大小是 12byte

那么一页可以存储 15*1024/12 = 1024 行索引列

B+ 树必有一个根节点,根节点可以存 1024 行索引列,那么也就会指向第二层的 1024 个 B+ 树节点(一个节点是一页)

第二层每个页也用来存索引,每个页可存 1024 行索引,那么每页就会指向 1024 个第三层的叶子节点(1024页),所以第三层叶子节点页的数量为 1024*1024

用来存储数据行的一页(叶子节点):

假设一行数据是 1k ,那么一页的可用空间 15k 可以存储 15 个数据行

如果一行数据大于或小于 1k ,那么一页可以存储的数据行还会小于或大于 15

综上,第三层叶子节点的数量总共为 1024*1024,每个叶子节点可以存 15 行数据,第三层可以存 1024*1024*15 = 24576000 (约 2.45kw)个数据行

所以,三层可以存的数据量大约就是 2000w,如果要存储更多的数据,那么意味着:

  • 要增加 B+ 树高度,每次查询磁盘 IO 的次数就会增加,速度就会变慢
  • MySQL 为了提高性能,会将表的索引装载到内存中,在 InnoDB buffer size 足够的情况下,其能完成全加载进内存,查询不会有问题。但是,当单表数据库到达某个量级的上限时,导致内存无法存储其索引,使得之后的 SQL 查询会产生磁盘 IO,从而导致性能下降。