Rowstore存储中SQL算子如何识别行内列的边界?
你看到的磁盘上连续拼接的行数据,是行存储引擎的底层物理存储形态,但SQL算子读取时完全不需要依赖列分隔符,核心靠的是表的**元数据(Schema定义)**来精准拆分每一列,具体逻辑分这几种情况:
固定长度列的解析逻辑
对于INT、CHAR(N)、DATE这类固定长度的列,元数据里会明确记录每个列的字节长度。读取行数据时,引擎会按Schema定义的顺序,依次截取对应长度的字节段:比如第一列是INT(占4字节),就取行数据的前4字节作为该列的值;第二列是CHAR(8)(占8字节),接着取后面8字节,以此类推,完全不需要分隔符来标记边界。可变长度列的处理方式
针对VARCHAR、TEXT这类长度不固定的列,元数据会记录列的类型标识,同时行数据中会额外携带长度前缀:比如VARCHAR列的开头会用1~2字节存储该列实际的字节长度,引擎先读取这个长度值,再往后截取对应长度的字节作为列值。部分引擎还会在每行的行头里维护一个「偏移数组」,直接记录每个可变长度列在该行中的起始和结束位置,进一步提升解析效率。行头的辅助作用
多数行存储引擎的每行数据开头都有一个行头区域,里面存储着行的状态(比如是否被标记删除)、可变长度列的数量、偏移数组等辅助信息,帮助引擎快速定位列的位置,避免逐个计算长度的额外开销。
举个简单的实际例子:假设表结构是(id INT, username VARCHAR(16), age TINYINT),某行在磁盘上的二进制数据可能是:[行头] 0x00000005 0x0004 0x616C6963 0x1F
拆解后:
- 行头:包含该行的辅助解析信息
0x00000005:4字节INT类型,对应id=50x0004:1字节长度前缀,说明username实际占4字节;0x616C6963是"alic"的二进制编码,对应username="alic"0x1F:1字节TINYINT类型,对应age=31
通过元数据里的Schema定义,引擎就能准确拆分出每个列的值,完全不需要逗号这类分隔符来分隔列。
内容的提问来源于stack exchange,提问作者Yan Fung

