什么是页分裂?
如果我们使用非自增主键,由于每次插入主键的索引值都是随机的(比如 UUID),因此每次插入新的数据时,就可能会插入到现有数据页中间的某个位置,这将不得不移动其它数据来满足新数据的插入,甚至需要从一个页面复制数据到另外一个页面,我们通常将这种情况称为页分裂。
页分裂还有可能会造成大量的内存碎片,导致索引结构不紧凑,从而影响查询效率。
举个例子,假设某个数据页中的数据是1、5、9、10,且数据页满了,现在准备插入一个数据6,则需要把数据页分割为两个数据页:
出现页分裂时,需要将一个页的记录移动到另外一个页,性能会受到影响,同时页空间的利用率下降,造成存储空间的浪费。
而如果记录是顺序插入的,例如插入数据11,则只需开辟新的数据页,也就不会发生页分裂:
因此,在使用 InnoDB 存储引擎时,如果没有特别的业务需求,建议使用自增字段作为主键。
insert 操作对 B+ 树结构的改变是什么样的?
-
如果我们使用主键是顺序递增,那么每次插入的新数据就会顺序插入到叶子节点最右边的节点里,如果该页面满了,就会自动开辟一个新页面,将新数据插入到新页面。因为每次插入一条新记录,都是追加操作,不需要重新移动数据,因此这种插入数据的方法效率非常高。
-
如果我们使用主键不是顺序递增,由于每次插入主键的索引值都是随机的,因此每次插入新的数据时,就可能会插入到现有数据页中间的某个位置,这时候为了保证B+ 树的有序性,要移动其它数据来满足新数据的插入如果该页面满了,就发生页分裂,这时候要从一个页面复制数据到另外一个页面,目的是保证后一个数据页中的所有行主键值比前一个数据页中主键值大,页分裂可能会造成大量的内存碎片,导致索引结构不紧凑,从而影响查询效率。
所以,我们在设计主键的时候,最好采用自增的方式,或者顺序递增主键值。另外,主键字段的长度不要太大,因为主键字段长度越小,意味着二级索引的叶子节点越小(二级索引的叶子节点存放的数据是主键值),这样二级索引占用的空间也就越小。