# 为什么MySQL单表数据在2000W+后会明显下降
在中国互联网技术圈流传着这么一个说法:MySQL 单表数据量大于 2000 万行,性能会明显下降。事实上,这个传闻据说最早起源于百度。具体情况大概是这样的,当年的 DBA 测试 MySQL性能时发现,当单表的量在 2000 万行量级的时候,SQL 操作的性能急剧下降,因此,结论由此而来。然后又据说百度的工程师流动到业界的其它公司,随之也带去了这个信息,所以,就在业界流传开这么一个说法。再后来,阿里巴巴《Java 开发手册》提出单表行数超过 500 万行或者单表容量超过2GB,才推荐进行分库分表。对此,有阿里的黄金铁律支撑,所以,很多人设计大数据存储时,多会以此为标准,进行分表操作。
有业界传说和阿里巴巴的开发手册支撑,这个结论应该是靠谱的,毕竟实践出真知,但这背后的原理是什么呢,目前我们用的MYSQL大部分都是InnoDB引擎,现在我们就从InnoDB引擎说起来扒一扒为什么单表数据在2000W+后会明显下降。
- 最小储存单元:InnoDB存储引擎最小储存单元就是页(Page),页可以用于存放数据也可以用于存放键值+指针,一个页的大小默认是16K。也就是说InnoDB中不管你的数量量是多少,最终占用的存储空间肯定是16K的整数倍。
- InnoDB索引结构:为什么在关心索引结构呢,因为在千万级的数据查询中如果没有索引,根本就没法查询,索引的数据结构直接影响我们的查询效率。InnoDB的索引结构是B+树,B+树的特点是叶子节点存放数据,非叶子结点存放键值+指针。[这里我就不再分析MYSQL的索引原理了,感兴趣的同学可以看我的另一篇关于MYSQL索引原理解析的文章。]
- B+树数据存储计算:这里假设单条纪录的数据大小为1K(一般的业务数据记录也就在1K左右),那么单个叶子结节所能存储的纪录数:16K/1K=16。非叶子节点能够存储多少指针呢?一般我们的主键ID都是bigint类型,长度为8字节,而指针大小在InnoDB源码中设置为6字节,这样键值+指针占用的大小就是14字节,一页能够存储的指针数:16K/14=1170。那么一棵高度为2的B+树能够存放的纪录数:117016=18720,一棵高度为3的B+树能够存放的纪录数:11701170*16=21902400。在查找数据时,一次页的查找代表一次IO,而IO的字数又和B+树的高度有关,如果B+树为3层,那么通过主键索引数据时就需要3次IO,而IO的代价是非常高的,一般要控制在3以下,所以说一量数据量达到2000W+,那么B+树的高度将会变成4,从而导致每次主键索引都需要4次IO,IO次数的增加导致性能明显下降。
总结一下:单表数据量越大,B+树高度越高,查询需要IO次数越多,性能越差。 这里的几个分界值就是2W和2000W,也就是说1000W和100W通过主键来索引的性能其实是差不多的,都需要2次IO。