上篇文章说了,innoDB除了会记录真实数据外,会存储额外数据,额外数据就是描述真实数据的数据,额外数据分为超长字段长度列表,null列表,头部信息,null列表主要存储字段为null的数据,mysql规定是一个字节,8个字节为存储,当只有三个字段的时候,高位默认0填充。变长字段长度列表要看实际情况存储的字符集和定义的varchar大长度。
记录的真是数据
对于compact_tb表,除了我们 定义的c1,c2,c3,c4外,mysql会默认添加三个列(隐藏列):
Row_id:(DB_ROW_ID)不是必须的,占用6个字节,行列,标识符。
Transaction_id:(DB_TRX_ID)必须的,6个字节,事务id。
Roll_pointer:(DB_ROLL_POINTER)必须的,7个字节,回滚指针。
innoDB表生成过程:如果用户自定了primary key主键,则用用用户自定义的,若没有,则选一个unique建为主键,若都没有,则默认会添加一个row_id为主键。所以从上看到,innoDB的事务id和回滚指针是必须的,但row_id是可选的,若用户自定义了,则不需要使用row_id。
所以不为null的时候,记录的真实数据就存在这里,
行数据存的就是:row_id,transaction_id,roll_pointer,c1,c2,c3,c4
第二行数据:row_id,transaction_id,roll_pointer,c1,c2。
从第二行数据可以看到,因为c3,c4是null,存在额外信息的null值列表里,记录的真是数据就没存储了,从而节省内存空间,提高查询效率。
CHAR(M)的存储
在compact_tb表中,c3的是char(10),而前面说了,在compact行格式中,只会把变长类型的列存储在变长字段长度列表中,但这是因为我们采用的ascii字符集,改字符集定长字节,如果采用变长字节的字符集,如gbk(1~2个字节),utf8(1~3)个字节,这时候c3也会存储到变长字段字符集中。
若定长字符集acsii,存储的效果则是:c4,c2,c1。
若改为变长字符集utf8,存储的效果则是:c4,c3,c2,c2。
总结:当char(M)采用的是定长字符集时候,则字节长度不会存储到“变长字段长度列表”,若采用的是变长字符集的时候,则会存储到“变长字段长度列表”。
并且,在存储的时候,至少占用M个字节,而varchar没有这方面要求,如char(10)在当前字段只存储一个字符串,也会占用10个字节。这是为了将来更新的值小于10个字节时候可以直接更新,而不需要重新开辟新的内存空间记录数据,防止之前的的数据产生空间碎片。
文章来源:知乎平台 原文地址:https://zhuanlan.zhihu.com/p/398748506