tidb中,如果某些字段为空,行存和列存会占用空间?

tidb中,如果某些字段为空,行存和列存会占用空间?

1 个赞

:thinking:刚搜索了一下,null值是要占用空间的,占一个字节。一直以为不占用。

官网中有提到这个吗?没找到啊

别的地方看到的,等会我实践一下,正好还有点时间

验证了一下,确实是占存储空间的 :joy:
插入100w的null值,表预估从1MB,涨到了115MB

CREATE TABLE `tmp_20230721001` (
  `a` varchar(100) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin

-- test	tmp_20230721001	1.00MB	0.08MB
SELECT
    db_name,
    table_name,
    ROUND(SUM(total_size/cnt), 2) '压缩前表的单副本大小(MB)',
    ROUND(SUM(total_size/cnt/(SELECT ROUND(AVG(value),2) FROM METRICS_SCHEMA.store_size_amplification WHERE value > 0)), 2) '压缩后表的大小(MB)'
FROM (
	SELECT
        db_name,table_name,region_id,SUM(Approximate_Size) total_size,COUNT(*) cnt
    FROM information_schema.TIKV_REGION_STATUS
    WHERE db_name = 'test'
	AND table_name IN ('tmp_20230721001')
    GROUP BY db_name,table_name,region_id
) tabinfo
GROUP BY db_name,table_name;

INSERT INTO tmp_20230721001
SELECT NULL FROM INFORMATION_SCHEMA.TABLES a,INFORMATION_SCHEMA.TABLES b
LIMIT 1048576

SELECT count(1),1024*1024 FROM tmp_20230721001

-- test	tmp_20230721001	115.00MB	8.98MB

测试不准,这个测试表有隐藏主键要占空间的,你要测试应该建
2个表
第一个表一列id int 主键

第二个表两列 id int 主键, a varchar(100),然后分别插入相同的条数,对比空间

:astonished:有道理

行存和列存都会为字段为空的情况分配一定的存储空间

可能需要较多数据量,我这个测试案例100万条记录根本看不出区别

在 4.0 后, TiDB 的数据行使用的如下新格式:

  • version: 首 byte 用于标识编解码版本,在上面老格式介绍中我们看到首自己是表示类型的 Type 只会使用(1-10),所以我们需要选择大于 10 的值作为版本号就能和老版 表数据编码格式区分开
  • flag: 之后的 1 byte 是可扩展的标识 flag, 目前只用到 1 bit 来标识是否是 large 行,当有 ColumnID 超过 255 或 Value 部分数据总长度有超过 65535 时会标记 large 行并在后面 column id array 和 offset array 部分使用足够的字节来存储
  • num of not null columns/num of null columns: 之后是 2 bytes 用于记录行中非 NULL 列的数目,再往后 2 bytes 则记录行中 Null 列的数目,这 4 bytes 主要用于确定之后 column id array 的读取长度
  • not null column id array: 接下来是对应长度的非 NULL 列 ColumnID 数组,数组元素按照升序排序,便于二分查找定位用于 offset 数组的 index。如果不是 large 行,每个 ColumnID 需 1 byte ,否则需 2 bytes
  • null column id array: 之后是对应长度的 NULL 列 ColumnID 数组,数组元素按照升序排序,便于二分查找确定是否有 NULL 列, NULL 值在新格式中只需要保存 ColumnID 不需保存 ColumnData
  • not null column offset array: 指定当前列在 not null column data 段 byte 数组的结束 offset, 假设当前列通过 column id array 定位到的 index 是 i, 则改列的数据将是 data[offset[i-1]:offset[i]]。如果不是 large 行,一个 offset 需 2 bytes, 否则需 4 bytes
  • not null column data: 非空列数据依次保存的 byte 数组,需要注意的是不再保存 Type 信息,除 使用 int 来代替 varint 外,单个列数据格式和原格式中的单列格式保持一致。

在编码时,会将行数据整理为格式定义的格式进行写入。

而在解码时,调用者会提供当前数据行中所需列的 ColumnID 数组和列的类型信息做为输入,解码处理会先根据首 byte version 判断是否按照新格式解码,如果是新格式则预先将除了最后一段 not null column data 外的其他段信息在内存中整理成 flag 或 array,然后依次对输入的 ColumnID 数组在 not null column id array 中二分查找,找到则通过 not null column offset array 定位 not null column data 中的数据并根据输入中提供的列类型信息解码,如未在 not null column id array 段找到则继续在 null column id array 进行二分查找,找到则直接使用 NULL 值作为列解码结果,还是未找到则使用 schema 定义的列默认值作为列解码结果。

3 个赞

:+1:专业解读

copy的

good

此话题已在最后回复的 60 天后被自动关闭。不再允许新回复。