简介
大字段表示varchar、blob、text等使用大空间的字段,下面先简单介绍大字段在行中的存储方式,然后再介绍数据页格式与溢出页格式了解其具体存储格式。由于大部分都是通过文档了解,代码看的不多,可能理解有错误的地方,欢迎提出。
(下文中图都是来自各文章)
行存储格式
mysql行存储格式
mysql上层(sql层)使用的行格式可用如下图表示,转载自MySQL Row Format(MySQL行格式详解)
innodb 行记录格式
innodb有多种行记录格式,有Compact、Redundant(Antelope文件格式)和Compressed、Dynamic(Barracuda文件格式),下面主要对compact和Dynamic(mysql5.7 default)与大字段相关的部分进行一些介绍。详情可参考[1][2]
Compact
Compact格式对于大字段处理如图:
由于innodb的表是按索引(B+树)来组织的,节点中放的是页(page),每个页至少需要存放两条数据,不然就成了链表了。当一个页中只能存放下一条数据时,就会放在溢出页中。
总之当字段长超过一个阈值就会把一部分存放在溢出页(Uncompressed BLOB Page)。
/** Determine if a record is so big that it needs to be stored externally.
@param[in] rec_size length of the record in bytes
@param[in] comp nonzero=compact format
@param[in] n_fields number of fields in the record; ignored if
tablespace is not compressed
@param[in] page_size page size
@return FALSE if the entire record can be stored locally on the page */
UNIV_INLINE
ibool
page_zip_rec_needs_ext(
ulint rec_size,
ulint comp,
ulint n_fields,
const page_size_t& page_size)
{
....
return(rec_size >= page_get_free_space_of_empty(comp) / 2);
}
当需要放在溢出页时,它会把前 768 byte 放在row中,其余字节放在溢出页。
Dynamic
InnoDB also encodes fixed-length fields greater than or equal to 768 bytes in length as variable-length fields. For example, a CHAR(255) column can exceed 768 bytes if the maximum byte length of the character set is greater than 3, as it is with utf8mb4.
Whether any columns are stored off-page depends on the page size and the total size of the row. When the row is too long, InnoDB chooses the longest columns for off-page storage until the clustered index record fits on the B-tree page. TEXT and BLOB columns that are less than or equal to 40 bytes are always stored in-line.
Dynamic 对溢出数据的处理可看下图
Dynamic 在数据需要放在溢出页时把数据全部放在溢出页。
Note
- 如上英文所述,innodb对于char等也有可能放在溢出页处理。详情可参考[3]。
- Compact和Dynamic 都用20byte指针表明溢出数据地址。其结构如下:4 byte space id;4 byte page id;4 byte blob header 在 page 中的 offset;8 byte 存在溢出页中的长度。
数据页格式
innodb 数据页大致分为3部分:page header、page body、page tailer。下面主要讲一下page body。不同类型的page其主要区别在于body结构不同。
page body
page body 主要用来存放实际record,主要可分为3部分(实际还有header等其他空间):user records、free records、free space。
- user records 为已经分配在实际使用的record。
- free records 为使用后删除的records,没有还给free space。其通过链表进行组织。新删除的放在链表头。
- free space 为尚未分配的空间
在插入一行时,先在free records链表中查找是否有可用的record,若有直接使用,若无再在free space中分配。
删除时,把删除的record放入free records链表。
从上述方式可知,若数据长度不定,必然会产生不能使用的极小碎片,因此需要进行page重组。
page 重组
page 重组有page内调整(去除碎片)和page合并。下面只对page内调整进行简单介绍。详情可参考[7] [8]。
页内调整
页内调整大致流程如下:
- 创建一个与page相同的tmp_page;
- 初始化page,把tmp_page中的record一行行拷到page。
- 删除tmp_page
外部存储页
外部存储页有多种,这里主要讲一下Uncompressed BLOB Page,其格式如上图所示。其body部分主要分成两部份:header和data。header记录数据长度、下一个page的位置等;data即实际数据。
对于此page,每一行每个溢出字段都会对于一个page,不共享,其data部分只属于某一行的某一字段。无需重组。
参考资料
- InnoDB的行记录格式, Compact, Redundant, Compressed, Dynamic
- https://dev.mysql.com/doc/refman/5.7/en/innodb-physical-record.html
- InnoDB的行溢出数据,Char的行结构存储
- mysql中InnoDB引擎中页的概念
- MySQL系列:innodb源码分析之page结构解析
- INNODB 数据页结构
- DML对innodb page空间的影响
- innodb page重组空间压缩函数(btr_page_reorganize_low)注释
- MySQL · 引擎特性 · InnoDB 文件系统之文件物理结构
- MySQL 如何存储长度较大的varchar与blob