当前位置: 首页 > 科技观察

InnoDB行过长时如何选择溢出字段?

时间:2023-03-12 07:27:14 科技观察

InnoDB索引页大小默认为16K。但是varchar、text、blob类型的单个字段的内容长度可能会超过16K。在这种情况下,整个索引页无法存储单个字段的内容。解决这个问题的方法是找到那些内容比较长的字段作为溢出字段,将它们的内容存放在溢出页中,减少索引页记录中剩余的内容。接下来说一下InnoDB选择溢出字段的逻辑。本文内容基于MySQL8.0.29源码。在正文进入正题之前,大家可以思考一个问题:一个表中每条记录的溢出字段是否相同?1、建表时的限制单从字段数来看,MySQL的server层限制了建表最多1024个字段。InnoDB限制最多创建1023个字段,但是如果我们在建表的时候真的要创建1023个字段,我们会很荣幸收到这个错误:1117-Toomanycolumns。因为InnoDB会给表增加2到3个隐藏字段:DB_ROW_ID、DB_TRX_ID、DB_ROLL_PTR。DB_ROW_ID字段只有在表没有主键的情况下才会添加,并且在建表时没有创建所有字段都允许为NULL的唯一索引。建表的时候可以定义1023-3=1020个字段吗?还是不行,因为在crashrecovery的过程中,在解析Redolog的时候,REDUNDANT记录也会在表的内存对象(dict_table_t)中增加3个字段。从上面的介绍我们可以看出InnoDB需要预留6个字段给自己使用。因此,我们在建表的时候,最多可以创建的字段数是:1023-3*2=1017。一个表实际可以创建的字段数不仅仅受表中字段数的限制服务器层和存储引擎,也是由字段的长度决定的。在建表的时候,InnoDB会问自己一个问题:如果我把这条DDL语句放开,让它建表成功,以后插入或者更新这张表的时候,会不会因为记录是太长?要回答这个问题,总不能凭着自己的本心去想象和去做吧?所以,要有规矩,要按规矩办事。规则如下:假设那些有资格被选为溢出字段的字段已经被视为溢出字段,并且其字段内容部分或全部存储在溢出页面中。答对了。根据记录的格式,溢出字段的内容部分或全部存储在溢出页上。REDUNDANT,COMPACT记录只会将溢出字段的部分内容存储到溢出页。DYNAMIC和COMPRESSED记录会将溢出字段的全部内容存储在溢出页中。在这个规则下,计算索引页中剩余记录内容的长度,看看会不会太长?如果还是太长,InnoDB不会放过这条DDL语句。这时候建表会失败,报如下错误:1118-Rowsizetoolarge(>8126)。将一些列改为TEXT或BLOB可能会有帮助。在当前行格式中,0字节的BLOB前缀以内联方式存储。为什么判断超长的条件大于8126字节?别着急,后面会有介绍。如果不是太长,自然建表成功。2.索引页是什么样子的?InnoDB的索引页,无论是非叶子节点还是B+树的叶子节点,初始化完成后,插入记录前,包含以下部分:38-byteFileHeader56-byteindex页头信息1313-byteInfimumrecord13-byteSupremerecord2-byteSupremeSlot2-byteInfimumSlot8-byteFileTrailer共占用132字节,如下图:3.如何判断线路太长?通过上一节我们知道,一个索引页初始化后,会占用132字节的空间。索引页的默认大小为16K。初始化后,索引页剩余空间为:16*1024-132=16252字节。InnoDB规定:一个索引页中至少要存储2条记录。因此,索引页中一条记录的最大长度为:16252/2=8126字节。在插入或更新记录时,如果插入记录的长度或更新记录的长度大于8126字节,记录中的某些字段将被选为溢出字段。一条记录的长度是以下部分长度的总和:字段NULL标记区,标记每个字段的内容是否为NULL。如果表中的所有字段都定义为NOTNULL,则记录中不存在该区域。字段内容长度区,存放每个变长字段的内容长度。如果表中的所有字段都不是变长字段,则该区域不存在于记录中。记录头信息,REDUNDANT格式:6字节;COMPACT、DYNAMIC、COMPRESSED格式:5个字节。用户字段内容。DB_ROW_ID,6bytes,InnoDB在建表时既没有主键也没有唯一索引且所有字段都定义为NOTNULL的情况下,会添加该列作为表的主键。DB_TRX_ID,8字节,最后修改记录的交易ID。DB_ROLL_PTR,7字节,指向上一个事务产生的undolog。4.选择溢出字段的逻辑选择溢出字段的环节可能会经过一轮或多轮循环,每一轮循环从表中选择一个字段作为溢出字段,直到索引页中剩余记录的长度小于等于8126字节,选择溢出字段的过程结束。选择溢出字段时,会排除一些字段,符合以下规则的字段不会被选为溢出字段:主键字段。固定长度字段(字符、二进制字段除外)。内容为NULL的字段。REDUNDANT、COMPACT记录,字段内容长度<=788字节。DYNAMIC,COMPRESSED记录,字段内容长度<=40字节,字段类型为BLOB,GEOMETRY,VAR_POINT。DYNAMIC,COMPRESSED记录,字段内容长度<=255字节,且字段类型不是BLOB、GEOMETRY、VAR_POINT。不符合上述规则的字段才有资格被选为溢出字段。每次循环都会遍历表中的所有字段,根据上述规则从那些有资格被选为溢出字段的字段中找出内容最长的字段,即为溢出字段。5、页地址字段被选择为溢出字段后,该字段的部分或全部内容将存储在溢出页中。然后,在索引页记录中,字段末尾会有一个20字节的区域,存放Overflow页地址。20字节的溢出页地址由以下4部分组成:表空间ID,4字节,溢出页所在的表空间ID。页码,4字节,第一个溢出页的页码。当一个溢出页不能存放该字段的溢出内容时,就会有多个溢出页组成溢出页链表。字段内容Offset,4字节,在第一个溢出页中,字段内容在页中为Offset。根据是否启用压缩页,溢出页中字段内容的Offset会有所不同,需要记下。溢出页内容长度,溢出页存储的当前字段的内容长度,8个字节,实际上只有最后4个字节用来存储溢出页内容长度的总和,如下图:溢出域留在索引页记录中的内容根据记录格式的不同而不同:REDUNDANT,COMPACTrecords,索引页记录中溢出域的长度为788字节,由以下两部分组成:768字节的字段内容。20字节溢出页地址。溢出字段中768字节之后的内容将存储在溢出页中。对于DYNAMIC和COMPRESSED记录,溢出字段的全部内容将存储在溢出页中,只有20字节的溢出页地址存储在索引页记录中。6.回答文章开头的问题经过前面的介绍,相信你已经回答了本文开头的问题。回到问题:问:一个表中每条记录的溢出字段是否相同?回答:每条记录的溢出字段可能相同也可能不相同。记录中哪些字段会成为溢出字段取决于每条记录中所有符合条件的溢出字段的内容长度。7.综上所述,在一条记录中,当所有字段的内容长度之和超过8126字节时,就会有部分字段被选为溢出字段。选择一个溢出字段可能会经过多轮,每一轮都会从那些有资格被选为溢出字段的字段中选择内容最长的字段作为溢出字段,直到索引页中剩余记录的长度小于或等于8126字节。对于REDUNDANT和COMPACT记录,溢出字段内容的前768字节存放在索引页记录中,其余内容存放在溢出页中。对于DYNAMIC和COMPRESSED记录,溢出字段的所有内容都存储在溢出页中。本文转载自微信公众号“一树一溪”,可通过以下二维码关注。转载本文请联系艺书艺熙公众号。