33.进阶-SQL优化-主键优化

33.进阶-SQL优化-主键优化

(1)主键优化

在上一小节,我们提到,主键顺序插入的性能是要高于乱序插入的。 这一小节,就来介绍一下具体的原因,然后再分析一下主键又该如何设计。

1). 数据组织方式

在InnoDB存储引擎中,表数据都是根据主键顺序组织存放的,这种存储方式的表称为索引组织表(index organized table IOT)。

表数据都是根据主键顺序组织存放的怎么理解这句话?

我们之前讲解索引的时候提到,InnoDB存储引擎的表,它的索引分为两类,聚集索引和二级索引。一张表默认主键索引就是聚集索引。所以一张表当中最终我们表当中的数据存放的时候他就是根据主键顺序存放的。比如下面的6 12 16,6这个主键下面存放的就是6这个主键对应的行数据,12这个主键下面存放的就是12这个主键对应的行数据,16这个主键下面存放的就是16这个主键对应的行数据。所以在InnoDB存储引擎中,表数据都是根据主键顺序存放的。而在这幅图当中,这个b+tree的数据结构当中所有的数据都会出现在叶子结点,而非叶子结点它仅仅起到索引数据的作用。而非叶子结点中的索引以及叶子结点中的数据最终都是存放在一个逻辑结构“页Page”当中的。而大家所看到的这些黄色的这些块,实际上都是一个一个的Page页。一个页的大小是固定的16K,对于也来说,他是InnoDB磁盘管理的最小单元,也就意味着一个区当中,他可以包含64个页。

image-20240702234058237

行数据,都是存储在聚集索引的叶子节点上的。而我们之前也讲解过InnoDB的逻辑结构图:

image-20240702234114666

InnoDB的逻辑存储结构最外层是表空间Tablespace,表空间中存储的是一个一个的Segment段,段里面存放的是一个一个的Extent区,而一个区的大小是固定的1M,在区当中存放的是一个一个的页Page,页当中存放的是一个一个的行,行当中存放具体的字段值。

在InnoDB引擎中,数据行是记录在逻辑结构 page 页中的,而每一个页的大小是固定的,默认16K。那也就意味着, 一个页中所存储的行也是有限的,如果插入的数据行row在该页存储不小,将会存储到下一个页中,页与页之间会通过指针连接。

2). 页分裂
1、当我们在往表结构当中插入数据的时候,流程是怎么样的?

首先需要明确,我们刚刚提到的Page页,页当中存放的就是具体的行数据。而一个页他是可以为空的,也就是说一个页当中可以没有存放数据,也可以存储一半,也可以存储满。每一个页至少包含两行数据,而这些行数据是根据主键排列的。为什么至少包含两行数据呢?原因是因为一个页当中如果只包含一行的话,就相当于是一个链表了。所以在InnoDB当中规定,每一个页当中至少包含两行数据。如果某一行数据比较大,此时超出了一定的阈值之后,就会产生行溢出的现象。

2、看一下主键顺序插入的时候,我们的页是什么样的情况?

这一块已经从磁盘中申请了一个页Page,我们在主键顺序插入的时候,首先先插入id为1的数据,再插入id为2的,再插入id为3、4、5…直到8的数据,会发现第一个页写满了,然后去申请第二个页,当第一个页写满的时候,再去写入第二个页。第一个页下来就是第二个页了,所以他们之间会维护一个双向指针。第二页写满了再去写第三页就可以了。

3、看一下主键乱序插入的时候,我们的页是什么样的情况?

image-20240703190749195

如上图所示,假如现在两个页都插满了,接下来再去插入id为50的这条数据。并不是直接写入第三个数据页,因为我们提到叶子结点是有序的,50插入进去应该存放在47之后。第一页数据写满了吗?差一点点,但是50这一行数据写不下,此时他就会去做一个操作,开辟一个新的数据页,但是50不会直接写到这个页上,他会找到第一个数据页50%的位置,34和47是超出了是超出了50%,此时他会先将这两个数据先移动,移动到我们新开辟的数据页中,然后将50插入到第一个数据页。此时需要对链表指针进行重新设置。

image-20240703191157670

他会设置一号数据页它的下一个数据页是3号,3号的下一个数据页才是2号,而3号的上一个是一号,重写设置链表指针。这种现象称为页分裂。所以逐渐乱序插入的情况下,可能就会发生页分裂的现象。

页可以为空,也可以填充一半,也可以填充100%。每个页包含了2-N行数据(如果一行数据过大,会行溢出),根据主键排列。

A. 主键顺序插入效果

①. 从磁盘中申请页, 主键顺序插入

②. 第一个页没有满,继续往第一页插入

image-20240702234208964

③. 当第一个页写满之后,再写入第二个页,页与页之间会通过指针连接

image-20240702234220450

④. 当第二页写满了,再往第三页写入

image-20240702234232438

B. 主键乱序插入效果

①. 加入1#,2#页都已经写满了,存放了如图所示的数据

image-20240702234244088

②. 此时再插入id为50的记录,我们来看看会发生什么现象

会再次开启一个页,写入新的页中吗?

image-20240702234300831

不会。因为,索引结构的叶子节点是有顺序的。按照顺序,应该存储在47之后。

image-20240702234314133

但是47所在的1#页,已经写满了,存储不了50对应的数据了。 那么此时会开辟一个新的页 3#。

image-20240702234326546

但是并不会直接将50存入3#页,而是会将1#页后一半的数据,移动到3#页,然后在3#页,插入50。

image-20240702234338831

移动数据,并插入id为50的数据之后,那么此时,这三个页之间的数据顺序是有问题的。 1#的下一个页,应该是3#, 3#的下一个页是2#。 所以,此时,需要重新设置链表指针。

image-20240702234353471

上述的这种现象,称之为 “页分裂”,是比较耗费性能的操作。

3). 页合并

image-20240703191728714

上面这幅图展示出来了当前叶子结点的情况,接下来要执行删除操作:在InnoDB当中,如果我们要去删除一个数据,比如删除id为16的数据,他并不会真正的物理删除,也就是说,并不会直接从磁盘中将数据页中的这一行数据删除,而只是对这行数据做一个标记,标识它被删除了。一旦这一块的空间被标识为删除状态,那么这一块的空间就允许其他记录声明使用。比如接下来再删15、14,再往下删可能会发生一个现象,当页中所删除的记录达到一定的阈值之后,接下来InnoDB他就会去开始查找它的上一页或下一页,有没有合并的可能性。如果可以合并,他会合并两个页以优化空间。这个阈值默认是页的50%,也就是这个页中所删除的记录占到了这个页的50%,InnoDB就会找上一页及其下一页,看看是否可以合并。

image-20240703192319477

如上图所示,已经超过了50%,就说明当前这个数据页可以合并了,然后去找上一个数据页,发现上一个数据页是存储满的,而且没有标识删除状态,那么和1号数据页合并不了。再去看下一个数据页,会发现还有50%以上都是空闲的,所以2号和3号这两个谁也,InnoDB引擎会自动合并,会将三号数据页中的记录移动到2号数据页,此时3号数据页就贡献出来了。如下

image-20240703192545532

此时再去插入20这条数据,他就直接往3号数据页插入就可以了。这种现象称为叶合并。

目前表中已有数据的索引结构(叶子节点)如下:

image-20240702234412970

当我们对已有数据进行删除时,具体的效果如下:

当删除一行记录时,实际上记录并没有被物理删除,只是记录被标记(flaged)为删除并且它的空间变得允许被其他记录声明使用。

image-20240702234427349

当我们继续删除2#的数据记录

image-20240702234438659

当页中删除的记录达到 MERGE_THRESHOLD(默认为页的50%),InnoDB会开始寻找最靠近的页(前或后)看看是否可以将两个页合并以优化空间使用。

image-20240702234450513

删除数据,并将页合并之后,再次插入新的数据21,则直接插入3#页

image-20240702234501922

这个里面所发生的合并页的这个现象,就称之为 “页合并”。

知识小贴士:
 	MERGE_THRESHOLD:合并页的阈值,可以自己设置,在创建表或者创建索引时指定。
4). 索引设计原则

1、满足业务需求的情况下,尽量降低主键的长度。

解释:得从聚集索引和二级索引的结构说起,因为对于一张表来说,主键索引或者聚集索引他只有一个,但是二级索引会有很多个,在二级索引的叶子结点中挂的就是数据的主键。所以说如果主键过长,二级索引比较多,那么将会占用大量的磁盘空间,而且在搜索的时候会耗费大量的磁盘IO。所以要尽量降低主键的长度。

2、插入数据时,尽量选择顺序插入,选择使用AUTO_INCREMENT自增主键。

解释:顺序插入:一个一个页写满再写下一个页;乱序插入:可能出现页分裂现象。

3、尽量不要使用UUID做主键或者是其他自然主键,如身份证号。

解释:因为每一次生成的UUID是无序的,无序的时候插入就会乱序插入,可能出现页分裂的现象。如果是身份证号,长度会比较长,检索的时候会耗费大量的磁盘IO。

4、业务操作时,避免对主键的修改。

解释:因为修改主键还要去懂对应的索引结构,代价是比较大的。

D做主键或者是其他自然主键,如身份证号。

解释:因为每一次生成的UUID是无序的,无序的时候插入就会乱序插入,可能出现页分裂的现象。如果是身份证号,长度会比较长,检索的时候会耗费大量的磁盘IO。

4、业务操作时,避免对主键的修改。

解释:因为修改主键还要去懂对应的索引结构,代价是比较大的。

image-20240702234553786

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值