前言
这个问题可能比较抽象,如果对MySQL索引结构不理解的人来说,可能蒙,所以建议先去看看索引结构再来看这个问题。MySQL 选择将节点大小设置为 16KB 而不是更大的原因,主要是为了在内存管理、性能、磁盘 I/O 效率、适应性和兼容性之间取得平衡。本文将从讲解页的结构开始,然后分析为什么MySQL为什么把节点大小设置为16K,而不是更大?
页结构实战
页包括:前指针,后指针,页头,页目录,用户数据。默认插入数据按照主键排序,所以主键设计递增。这样查找的时候,由于排好序,查找更快。
但是用户数据越来越多,就是变成一个长链表,查找就会变得很慢。MySQL,就会优化,通过一个页目录(一个范围),指针执行对应的范围的数据。
当16k的页占满了,就要新开一页,结构也是一个页目录对应一个用户数据。页之间用指针链接。
但是当数据变多,页也变多了,这样页也就变成一个长链表,每次都要从头去遍历读取磁盘,所以查找的速度也会变慢。
同样用空间换时间。新开一页作为索引页继续分组,每一组指针关联,每一组最小的作为关联。
所以一开始找先找页的组(这一组也叫索引页),找的范围之后,对应到那一页(这一页叫做数据页),再按照页目录找到响应数据。
最后MySQL的索引结构就是:
为了兼容范围查询,b 树叶子节点是双向指针,所以用范围查询条件的时候,如果通过索引可以很快查到数据就走索引,不用走全表,比如大于5,通过索引走到5,大于5走右边遍历,<5左边遍历。
叶子节点双向的原因可以保证范围查询也走索引,直接在叶子节点左右遍历
总结
假设索引字段类型是Bigint,8byte,每两个元素之间存的是下一个节点的地址,mysql分配的是6byte,也就是说一个索引后面配对一个节点地址,成对出现(见B树), 我们一个页中能存放多少这样的单元,其实就代表有多少指针,可以算一下16K的节点可以存多少对也就是多少个索引,8b 6b=14b, 一棵高度为2的B 树,16K /14b=1170个索引。
叶子节点有索引有data元素,假设占1K(假设),那一个节点就放16K/1K=16个元素,假设树高是3,所有节点都放满,能放多少数据?可以算一下,1170*1170*16=21902400,2千多万数据。
高度为3,(第二层)有1170个子节点,(第二层)每个子节点又有1170个子节点,一共有1170*1170个指针(节点),每个指针(节点)放16个数据。
mysql设置16K的大小,数据就可以存2千多万就已经足够了吧,既能保证一次磁盘IO不要Load太多的数据 又能保证一次load的性能,即便表的数据在几千万的数量也能保证树的高度在一个可控的范围。
我正在参与2023腾讯技术创作特训营第四期有奖征文,快来和我瓜分大奖!