本文是基于postgresql 14的代码进行分析解读,演示是在centos8系统上进行。
+---------------------+---------------+----------------+
| HeapTupleHeaderData | nulls bitmap | padding |
+------------+--------+---------------+----------------+
| object ID | value1 value2 value3 ... |
+------------+-----------------------------------------+
数据表里看到的每一行数据,在page中是以tuple的形式存储,tuple的结构有:
(1) HeapTupleHeaderData
(2)nulls bitmap ; 这是个可选项,当t_infomask有HEAP_HASNULL标志的话,这个数组就会有值;
(3) padding ; 前面null bitmap的长度不确定,为了后面数据能对齐,这里对HeapTupleHeaderData+nulls bitmap 字节对齐,这里会有留空。
(4) OID值,这是个可选项,当t_infomask有HEAP_HASOID_OLD时,会有此值;
(5)用户数据;也就是各列的数据;
struct HeapTupleHeaderData
{
union
{
HeapTupleFields t_heap;
DatumTupleFields t_datum;
} t_choice;
ItemPointerData t_ctid; /* current TID of this or newer tuple (or a
* speculative insertion token) */
/* Fields below here must match MinimalTupleData! */
#define FIELDNO_HEAPTUPLEHEADERDATA_INFOMASK2 2
uint16 t_infomask2; /* number of attributes + various flags */
#define FIELDNO_HEAPTUPLEHEADERDATA_INFOMASK 3
uint16 t_infomask; /* various flag bits, see below */
#define FIELDNO_HEAPTUPLEHEADERDATA_HOFF 4
uint8 t_hoff; /* sizeof header incl. bitmap, padding */
/* ^ - 23 bytes - ^ */
#define FIELDNO_HEAPTUPLEHEADERDATA_BITS 5
bits8 t_bits[FLEXIBLE_ARRAY_MEMBER]; /* bitmap of NULLs */
/* MORE DATA FOLLOWS AT END OF STRUCT */
};
(1) t_choice
由HeapTupleFields 结构和 DatumTupleFields 结构组成的联合体,分别在tuple构建和tuple在page中两种情况下选择使用,又不能同时存在。
当一个tuple在内 存中构建时,需要填充这个结构体。这时候还不需要可见性判断和并发控制的信息。
结构说明:
typedef struct DatumTupleFields
{
int32 datum_len_; /* varlena header (do not touch directly!) */
int32 datum_typmod; /* -1, or identifier of a record type */
Oid datum_typeid; /* composite type OID, or RECORDOID */
/*
* datum_typeid cannot be a domain over composite, only plain composite,
* even if the datum is meant as a value of a domain-over-composite type.
* This is in line with the general principle that CoerceToDomain does not
* change the physical representation of the base type value.
*
* Note: field ordering is chosen with thought that Oid might someday
* widen to 64 bits.
*/
} DatumTupleFields;
当把tuple加入到表的page中时,就必须填充这个结构体。将DatumTupleFields直接强转成HeapTupleFields结构来使用。
结构说明:
typedef struct HeapTupleFields
{
TransactionId t_xmin; /* inserting xact ID */
TransactionId t_xmax; /* deleting or locking xact ID */
union
{
CommandId t_cid; /* inserting or deleting command ID, or both */
TransactionId t_xvac; /* old-style VACUUM FULL xact ID */
} t_field3;
} HeapTupleFields;
在insert/update时都会组装一个新的tuple,然后找到一个剩余空间足够的page,加到里面。当然寻找空间足够的page有一定策略,这个后面再讲。
HeapTuple
heap_form_tuple(TupleDesc tupleDescriptor,
Datum *values,
bool *isnull);
在这个接口中计算tuple的len,包括tupleheader+各属性列的值的大小,
另外len还需要包括HeapTupleData结构体的大小,该结构体记录了 行指针数据和tuple的数据,以及他们的总长度;
typedef struct HeapTupleData
{
uint32 t_len; /* length of *t_data */
ItemPointerData t_self; /* SelfItemPointer */
Oid t_tableOid; /* table the tuple came from */
#define FIELDNO_HEAPTUPLEDATA_DATA 3
HeapTupleHeader t_data; /* -> tuple header and data */
} HeapTupleData;
然后t_data的 t_choice.t_datum会被赋值,长度取值为tuple长度向左移2位;
给 tupleheader中t_choice.t_datum赋值操作
HeapTupleHeaderSetDatumLength(td, len);
HeapTupleHeaderSetTypeId(td, tupleDescriptor->tdtypeid);
HeapTupleHeaderSetTypMod(td, tupleDescriptor->tdtypmod);
#define MaxTupleAttributeNumber 1664 /* 8 * 208 */
在内存中构建tuple时会判断是否超过这个上限
#define MaxHeapAttributeNumber 1600 /* 8 * 200 */
这里最大1600个列,当列的字段长时会小于这个数字;这个数字是留有一定余量。
作者邮箱:[email protected]
如有错误或者疏漏欢迎指出,互相学习。
注:未经同意,不得转载!