Open Bpazy opened 1 year ago
在MySQL Innodb存储引擎中的B+树的一个节点大小为“1页”,也就是16k。也即代表B+树的每个节点可以存16KB数据。
非叶子结点:
叶子结点:
关于各种类型占据的空间大小,参考这里: https://github.com/Bpazy/blog/issues/292
由此,可以推算出公式:
两层总数 = 非叶子节点(根) 叶子节点。 三层总数 = 非叶子节点(根) 非叶子节点 * 叶子节点。
主键为bigint(约2000w): 2层B+树的话:可以存放1170个16条=18720条(行)数据。 3层B+树的话:可以存放1170个1170个*16条=21902400条(行)数据。
主键为int(约4000w): 2层B+树的话:可以存放1600个16条=25600条(行)数据。 3层B+树的话:可以存放1600个1600个*16条=40960000条(行)数据。 所以三层B+树也就差不多2000w条或4000w条数据。
2100w 行以后,真的会发生性能极速劣化吗?并不会!
其实,每一次 B+ 树增高,都只会增加两个索引页,修改一个索引页,加起来只修改了三个 16KB 的数据页,无论是磁盘 IO 还是 Buffer Pool 缓存失效,对性能的影响都微乎其微:
索引从三层转换到四层,只增加了一次 IO,绝对性能降低幅度的理论极限只有1/3,而且在有 Buffer Pool 存在的情况下,性能差异微乎其微,只增加了1~2次比大小的计算成本。
那是否意味着不需要再分表了呢?
虽然三层索引和四层索引看起来性能差异不大,但是如果你的单行数据比较大,例如达到了 5KB,还是建议做一下横向分表的,这才是效果最立竿见影的减少磁盘 IO 次数的优化方法:
2017 年发布的阿里巴巴 Java 开发手册中写道“单表行数超过 500 万行或者单表容量超过 2 GB ,才推荐进行分库分表”,被很多技术博文写成了:阿里巴巴推荐超过 500 万行的表进行分表,这种理解是错误的。
虽然经过我的实测,在每行数据定长 1024 字节,Buffer Pool 配置为 22GB,在单表体积 24GB 的情况下,四层索引和三层索引并没有任何性能差异,但是现实世界中的数据表可不是这么严丝合缝:
那么,该如何回答“到底该何时分表”这个问题呢?很遗憾并没有一个放之四海皆准的答案,这和每个表的读取、新增、更新的具体情况是分不开的。
虽然在数据库技术层面我们无法给出何时分表的答案,但是从软件工程层面我们可以得出一个结论:
能不分就不分,不到万不得已不搞分表,如果能靠加索引或者加内存来解决就不要考虑分表,分表会对业务代码造成根本性的影响,会产生长久的技术债务。
B+树是B树的一种变形形式。 网上各种资料上B+树的定义各有不同,一种定义方式是关键字个数和孩子节点个数相同。这里我们采取维基百科上所定义的方式,即关键字个数比孩子节点个数小1,这种方式是和B树基本等价的。 除了B树的性质,B+树还包括以下要求:
B+树的搜索、插入、删除操作参考原文。
B+树和B树的区别是:
B+树的优点在于:
但是B树也有优点,其优点在于: 由于B树的每一个节点都包含key和value,因此经常访问的元素可能离根节点更近,因此访问也更迅速。