暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

PG/Opengauss/磐维的varlena类型解析

原创 Capulus 2025-09-18
502

前言

在Opengauss/磐维里,存在可变长的数据类型,那么这种类型在数据库存储中,到底是以什么形式存储的,是如何存储的,这篇文章带你了解深处的秘密。

varlena解析

首先,varlena有一个基础的通用结构:

struct varlena { char vl_len_[4]; char vl_dat[FLEXIBLE_ARRAY_MEMBER]; }; typedef struct varlena bytea; typedef struct varlena byteawithoutorderwithequalcol; typedef struct varlena text; typedef struct varlena BpChar; /* blank-padded char, ie SQL char(n) */ typedef struct varlena VarChar; /* var-length char, ie SQL varchar(n) */ typedef struct varlena NVarChar2; /* var-length char, ie SQL nvarchar2(n) */

由上面定义可见,bytea、text、bpchar、varchar、NVarChar2这些类型,都是基于varlena的。
具体的存储中,varlena分为几种细分类型:

  • varattrib_1b
  • varattrib_4b
  • varattrib_1b_e

varattrib_1b

先看一下varattrib_1b的定义:

typedef struct { uint8 va_header; char va_data[FLEXIBLE_ARRAY_MEMBER]; /* Data begins here */ } varattrib_1b;

有一个1字节长度的va_header,它里面记录了标志位和整个字符的长度,具体结构为:

tag length
1bit 7bit

这种类型可以存储不超过127字节长度的数据。如果超过127字节,那么会用下面这种形式存储。

varattrib_4b

看一下varattrib_4b的定义:

typedef union { struct /* Normal varlena (4-byte length) */ { uint32 va_header; char va_data[FLEXIBLE_ARRAY_MEMBER]; } va_4byte; struct /* Compressed-in-line format */ { uint32 va_header; uint32 va_tcinfo; /* Original data size (excludes header) and * compression method; see va_extinfo */ char va_data[FLEXIBLE_ARRAY_MEMBER]; /* Compressed data */ } va_compressed; } varattrib_4b;

这是一个union,里面包含了两种类型:

  • va_4byte:未压缩的varattrib_4b类型
  • va_compressed:压缩类型的varattrib_4b

va_4byte

va_header是一个4字节。其中,tag是标志位,compress标记是否是压缩类型,length是整个字符的长度。这种类型可以存储不超过1G的数据。

tag compress length
1 bit 1 bit 30 bit

va_compressed

和va_4byte类似,只不过compress标志位会标志该列数据为压缩类型。pg内部使用列pglz算法来做压缩的,后续有机会解析一下pglz_decompress部分的代码。

varattrib_1b_e

这种类型并不会把具体的数据存储在当前page中,他是一个指针,指向到了外部的数据地址

首先来看一下结构体定义:

typedef struct { uint8 va_header; /* Always 0x80 or 0x01 */ uint8 va_tag; /* Type of datum */ char va_data[FLEXIBLE_ARRAY_MEMBER]; /* Type-specific data */ } varattrib_1b_e;

同样的,结构体里定义了1个字节的va_header,1个字节的va_tag。
这里的va_tag很重要,标识了到底是哪种类型的external,来看下定义:

typedef enum vartag_external { VARTAG_INDIRECT = 1, VARTAG_EXPANDED_RO = 2, VARTAG_EXPANDED_RW = 3, VARTAG_ONDISK = 18 } vartag_external;

可以看到,1B_E类型可以分为4种小类,下面来逐一介绍:

VARTAG_ONDISK

这种类型会把数据存储在磁盘中,表现为一个toast表,指向了具体的oid

typedef struct varatt_external { int32 va_rawsize; /* Original data size (includes header) */ uint32 va_extinfo; /* External saved size (without header) and * compression method */ Oid va_valueid; /* Unique ID of value within TOAST table */ Oid va_toastrelid; /* RelID of TOAST table containing it */ } varatt_external;

从结构体中也可以清晰看到,va_rawsize代表了原始数据大小,包含了header。va_extinfo代表外部存储的数据大小,不包含header的。va_valueid记录了toast表的table oid号。va_toastrelid代表了数据文件的table real oid,可以通过它来找到具体的数据文件来解析。

varatt_expanded

这种类型会把数据存储在内存当中,对应的是VARTAG_EXPANDED_RO、VARTAG_EXPANDED_RW类型,从名字也可以看出,一个是只读一个是可读写的。

指针类型是ExpandedObjectHeader,也可以清晰看到这个结构体的内容,基本还是记录了va_header,但是这个值会始终是(-1),还记录了上下文信息,还有指针的信息等

#define EOH_HEADER_MAGIC (-1) typedef struct ExpandedObjectHeader ExpandedObjectHeader; struct ExpandedObjectHeader { /* Phony varlena header */ int32 vl_len_; /* always EOH_HEADER_MAGIC, see below */ /* Pointer to methods required for object type */ const ExpandedObjectMethods *eoh_methods; /* Memory context containing this header and subsidiary data */ MemoryContext eoh_context; /* Standard R/W TOAST pointer for this object is kept here */ char eoh_rw_ptr[EXPANDED_POINTER_SIZE]; /* Standard R/O TOAST pointer for this object is kept here */ char eoh_ro_ptr[EXPANDED_POINTER_SIZE]; }; typedef struct varatt_expanded { ExpandedObjectHeader *eohptr; } varatt_expanded;

vartag_indirect

最后一种类型,这种类型比较特殊,先看下定义:

typedef struct varatt_indirect { struct varlena *pointer; /* Pointer to in-memory varlena */ } varatt_indirect;

可以看到,指针类型的本质是一个struct varlena结构体,这又回到了最上面的那种基础类型了。也就是说,这种类型可以指向varatt_external,varatt_expanded,或varattrib_1b,varattrib_4b 类型

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论