列存储(cstore_vector)
声明:本文的部分内容参考了他人的文章。在编写过程中,我们尊重他人的知识产权和学术成果,力求遵循合理使用原则,并在适用的情况下注明引用来源。
本文主要参考了 OpenGauss1.1.0 的开源代码和《OpenGauss数据库源码解析》一书以及OpenGauss社区学习文档和一些参考资料
概述
在 OpenGauss 中,cstore_vector.h 文件与列存储(Columnar Storage)有着密切的联系,它主要定义了与列存储向量操作相关的数据结构和函数。列存储是一种数据存储格式,将表的每一列分别存储,相比传统的行存储格式,它有助于提高查询性能,特别是对于 OLAP(联机分析处理)场景。其中,在中分别定义了如下几个结构体:(路径:src/include/access/cstore_vector.h
)
/* 类型声明 */
struct bulkload_block; /* bulkload_block: 内存块结构体 */
struct bulkload_block_list; /* bulkload_block_list: 用于批量加载的内存块列表结构体 */
struct bulkload_minmax; /* bulkload_minmax: 在批量加载过程中用于存储最小值和最大值信息的结构体 */
struct bulkload_datums; /* bulkload_datums: 批量加载过程中用于存储值和NULL信息的结构体 */
struct bulkload_vector; /* bulkload_vector: 一列的一批值 */
struct bulkload_vector_iter; /* bulkload_vector_iter: 批量加载向量的迭代器结构体 */
struct bulkload_rows; /* bulkload_rows: 批量加载的批量行结构体 */
下面我们分别来看看这几个结构体是做什么的吧。
bulkload_block 结构体
bulkload_block 结构体表示用于批量加载的内存块,其中包含了指向下一个内存块的指针 next,指向内存缓冲区的指针 buff,已使用的空间大小 used 以及总空间大小 total。该结构体的主要作用是提供一种分块管理内存的机制,用于存储批量加载过程中的数据,其中 create 函数用于创建新的内存块,可以指定块的大小和数据单元的大小。这有助于有效地组织和管理大量的数据,提高批量加载的性能和效率。函数源码如下所示:(路径:src/include/access/cstore_vector.h
)
/* bulkload_block: 内存块结构体 */
struct bulkload_block {
struct bulkload_block *next; /* 下一个内存块 */
char *buff; /* 内存缓冲区 */
int used; /* 已使用的空间(字节) */
int total; /* 总空间大小(字节) */
/* data_unit 见 data_unit() 函数 */
static bulkload_block *create(int block_size, int data_unit);
};
bulkload_block_list 结构体
bulkload_block_list 结构体用于管理批量加载过程中的内存块列表,其中包含了指向第一个块的指针 m_head,指向当前块的指针 m_current,块的数量 m_block_num,以及最小块大小 m_block_size。该结构体的主要作用是提供一种组织和管理批量加载数据的机制,它可以动态地创建、销毁、扩展和重置内存块,以适应不同大小和类型的数据。通过这样的机制,可以更加高效地管理内存资源,提高批量加载的性能和效率。函数源码如下所示:(路径:src/include/access/cstore_vector.h
)
/* bulkload_block_list: 用于批量加载的内存块列表结构体 */
struct bulkload_block_list {
bulkload_block *m_head; /* 第一个块 */
bulkload_block *m_current; /* 当前块 */
int m_block_num; /* 块数量 */
/*
* 对于固定长度的数据类型,所有块都有相同的块大小;
* 对于可变长度的数据类型,不是所有块的大小都相同,
* 但我们记住了 m_block_size 中的最小块大小。
* 如果数据大小大于 m_block_size,则持有数据的块将采用与数据大小相等的块大小。
*/
int m_block_size;
void init(Form_pg_attribute attr);
void destroy();
void expand(void);
void expand(int needed);
void reset(bool reuse_blocks);
void configure(int attlen);
private:
template <bool to_free>
void reset_inner(void);
};
bulkload_minmax 结构体
bulkload_minmax 结构体用于在批量加载数据的过程中维护每个属性/字段的最小值和最大值的信息。它包含了比较函数指针、用于比较函数结束处理的函数指针、用于存储最小值和最大值的缓冲区以及其他必要的信息。该结构体的主要功能是在数据加载过程中动态地更新和维护各个属性的最小和最大值,以支持在构建索引和排序等操作中的性能优化。这通过比较不同数据值并更新相应的最小值和最大值缓冲区来实现,确保在加载大量数据时能够高效地获取属性的范围信息。函数源码如下所示:(路径:src/include/access/cstore_vector.h
)
/* bulkload_minmax: 在批量加载过程中用于存储最小值和最大值信息的结构体 */
struct bulkload_minmax {
CompareDatum m_compare; /* 数据比较函数 */
FinishCompareDatum m_finish_compare; /* 比较函数的结束处理函数 */
/* 使 min/max 缓冲区地址 8 字节对齐,
* 因为前 8 个字节将被访问为 Datum 指针。
* 所以将两个函数指针放在之后,MIN_MAX_LEN = 8*N 。
*/
char m_min_buf[MIN_MAX_LEN]; /* 存储最小值的缓冲区 */
char m_max_buf[MIN_MAX_LEN]; /* 存储最大值的缓冲区 */
int m_varstr_maxlen; /* 可变长度列的最大长度 */
bool m_first_compare; /* 表示第一次比较动作 */
void init(Oid atttypid);
void reset(void);
void configure(Oid atttypid);
};
bulkload_datums 结构体
bulkload_datums 结构体的主要作用是在批量加载过程中,为每个属性/字段维护对应的值和 NULL 信息,以支持高效的数据加载。它提供了初始化、销毁、重置、判断是否为 NULL、设置 NULL、获取值等操作,为批量加载提供了方便的接口和功能。函数源码如下所示:(路径:src/include/access/cstore_vector.h
)
/* bulkload_datums: 批量加载过程中用于存储值和NULL信息的结构体 */
struct bulkload_datums {
Datum *m_vals_points; /* 仅用于 attlen > 8 或 attlen < 0 */
char *m_null_bitmap; /* NULLs 位图 */
int m_vals_num; /* m_vals_points 中当前值的数量,包括NULLs */
bool m_has_null; /* 表示至少存在一个NULL值 */
bool m_all_null; /* 表示所有值均为NULL */
void init(Form_pg_attribute attr, int max_num);
void destroy();
void reset(int max_num);
bool is_null(int which);
void set_null(int which);
Datum get_datum(int which) const;
};
bulkload_vector 结构体
bulkload_vector 结构体用于表示一列数据的一批值,在批量加载数据的过程中,它包含了用于管理内存块、存储最小值和最大值信息、处理值和 NULL 信息的函数指针等成员。该结构体的主要功能是在数据加载过程中维护一列数据的值、NULL 信息以及该列的最小值和最大值,以便在后续的索引构建和排序等操作中能够高效地处理。结构体提供了一组 API,包括初始化、配置、重置、销毁等函数,用于对数据进行操作和管理。具体的实现涉及内存块的管理、数据的追加和解码、最小值和最大值的更新等操作,以确保在批量加载大量数据时能够有效地维护和处理数据列的相关信息。函数源码如下所示:(路径:src/include/access/cstore_vector.h
)
/* bulkload_vector: 一列的一批值 */
struct bulkload_vector : public BaseObject {
/* 内存块信息和 m_append 函数指针 */
bulkload_block_list m_blocks;
int m_attlen;
/* 函数指针 */
Datum (bulkload_vector::*m_decode)(ScalarVector *, int) const;
Datum (bulkload_vector::*m_append)(bulkload_rows *, Datum, int);
/* 值/NULL信息 */
bulkload_datums m_values_nulls;
/* 最小/最大信息 */
bulkload_minmax m_minmax;
/* ====== bulkload_vector API ====== */
void init(Form_pg_attribute attr, int max_values);
void configure(Form_pg_attribute attr);
void reset(int max_values, bool reuse_blocks);
void destroy(void);
Size data_size(void);
void data_copy(char *outbuf);
void new_fixedsize_block(int data_unit);
void choose_fixedsize_block(bulkload_rows *batch_rows, int data_unit);
void new_varsize_block(int data_len);
void choose_varsize_block(bulkload_rows *batch_rows, int data_len);
private:
/* 适用于所有数据类型的追加函数 */
Datum append_int8(bulkload_rows *batch_rows, Datum v, int len);
Datum append_int16(bulkload_rows *batch_rows, Datum v, int len);
Datum append_int32(bulkload_rows *batch_rows, Datum v, int len);
Datum append_int64(bulkload_rows *batch_rows, Datum v, int len);
Datum append_fixed_length(bulkload_rows *batch_rows, Datum v, int len);
template <int varlen>
Datum append_var_length(bulkload_rows *batch_rows, Datum v, int len);
/* 适用于所有数据类型的解码函数 */
Datum decode_integer(ScalarVector *pvector, int rowIdx) const;
Datum decode_fixed_length(ScalarVector *pvector, int rowIdx) const;
Datum decode_var_length(ScalarVector *pvector, int rowIdx) const;
};
bulkload_vector_iter 结构体
bulkload_vector_iter 结构体是批量加载向量的迭代器,用于遍历一列数据的一批值。该结构体包含了一组函数,用于开始迭代、获取下一个值、检查是否结束等操作。具体而言,它通过维护向量的指针、块的指针、当前块的位置、当前访问值的数量等信息,实现了对向量中值的逐个访问。迭代器中的函数指针 m_next 和对应的具体实现,使得在迭代过程中可以根据不同数据类型调用相应的处理函数,完成对值的获取。这个结构体的设计主要用于提供一个统一的接口,使得批量加载的过程中可以方便地遍历一列数据的值,并进行相应的处理。函数源码如下所示:(路径:src/include/access/cstore_vector.h
)
/* bulkload_vector_iter: 批量加载向量的迭代器结构体 */
struct bulkload_vector_iter {
void begin(bulkload_vector *vect, int max_values);
void next(Datum *value, bool *null);
bool not_end(void) const;
private:
bulkload_vector *m_vector; /* 当前访问的向量 */
bulkload_block *m_block; /* 当前访问的块 */
int m_block_pos; /* 当前块的位置 */
int m_cur_num; /* 当前访问的值的数量 */
int m_max_num; /* 最大访问值的数量 */
/* 下一个函数指针 */
void (bulkload_vector_iter::*m_next)(Datum *, bool *);
/* 下一个函数 */
void next_int8(Datum *value, bool *null);
void next_int16(Datum *value, bool *null);
void next_int32(Datum *value, bool *null);
void next_int64(Datum *value, bool *null);
void next_var_length(Datum *value, bool *null);
};
bulkload_rows 结构体
bulkload_rows 结构体是用于批量加载的批量行结构体。它包含了管理内存、值向量、属性数量、当前和最大值的数量、缓冲区的懒惰创建与重用等信息。该结构体设计的目的是为了在批量加载的过程中有效地组织和存储数据,提供一种高效的方式来处理大量的数据行。函数源码如下所示:(路径:src/include/access/cstore_vector.h
)
/* bulkload_rows: 批量加载的批量行结构体 */
struct bulkload_rows : public BaseObject {
/* 使所有使用的内存都在 m_context 中 */
MemoryContext m_context;
/* 我们必须控制该对象使用的总内存大小,
* 所以 m_using_blocks_total_rawsize 将记住使用的内存大小。
*/
Size m_using_blocks_total_rawsize;
Size m_using_blocks_init_rawsize;
/* 每个属性/字段的值向量 */
bulkload_vector *m_vectors;
int m_attr_num;
/* 每个向量中要保存的当前和最大值的数量 */
int m_rows_maxnum;
int m_rows_curnum;
/* 因为缓冲区是懒惰创建的,所以我们可以
* 1. 如果需要,创建所有缓冲区,
* 2. 重新创建所有缓冲区并在之前的销毁后重用。
*/
bool m_inited;
typedef bool (bulkload_rows::*FormAppendColumnFuncType)(TupleDesc tup_desc, VectorBatch *p_batch, int *start_idx);
FormAppendColumnFuncType m_form_append_column_func;
typedef Size (bulkload_rows::*FormSampleTupleSizeFuncType)(TupleDesc tup_desc, VectorBatch *p_batch,
int idx_sample);
FormSampleTupleSizeFuncType m_form_sample_tuple_size_func;
bool m_has_dropped_column;
/* 构造函数和析构函数 */
bulkload_rows(TupleDesc tuple_desc, int rows_maxnum, bool to_init = true);
~bulkload_rows()
{
}
/* 重要:
* Destroy 是释放所有资源的通用函数,
* 所以不要更改此函数的名称。
*/
void Destroy(void)
{
destroy();
}
void init(TupleDesc tup_desc, int rows_maxnum);
void reset(bool reuse_blocks);
bool append_one_vector(TupleDesc tup_desc, VectorBatch *p_batch, int *start_idx, MemInfoArg *m_memInfo = NULL);
bool append_one_tuple(Datum *values, const bool *isnull, TupleDesc tup_desc);
void append_one_column(Datum *values, const bool *isnull, int rows, TupleDesc tup_desc, int dest_idx);
Size total_memory_size(void) const;
/*
* @Description: 检查行数是否达到限制。
* @Return: 如果行数达到限制,则为 true;否则为 false。
* @See also:
*/
inline bool full_rownum(void)
{
return (m_rows_curnum == m_rows_maxnum);
}
/*
* @Description: 检查行大小是否达到限制。
* @Return: 如果行大小达到限制,则为 true;否则为 false。
* @See also:
*/
inline bool full_rowsize(void)
{
return (m_using_blocks_total_rawsize >= BULKLOAD_MAX_MEMSIZE);
}
Size calculate_tuple_size(TupleDesc tup_desc, Datum *tup_values, const bool *tup_nulls) const;
private:
template <bool hasDroppedColumn>
bool append_in_column_orientation(TupleDesc tup_desc, VectorBatch *p_batch, int *start_idx);
bool append_in_row_orientation(TupleDesc tup_desc, VectorBatch *p_batch, int *start_idx);
template <bool hasDroppedColumn>
Size sample_tuple_size(TupleDesc tup_desc, VectorBatch *p_batch, int start_idx);
void destroy(void);
};
总结
这几个结构体之间存在一定的层次关系和调用关系,主要用于支持批量加载过程中的数据组织、存储、和处理。以下是它们之间的关系:
- bulkload_block(内存块结构体):
- 用于表示一个内存块,包含指向下一个内存块的指针和相关的内存信息。
- bulkload_block_list(内存块列表结构体):
- 用于管理多个 bulkload_block 结构体,形成内存块的链表。
- 包含指向第一个内存块的指针(m_head)和指向当前内存块的指针(m_current)等信息。
- 提供了一些管理内存块链表的函数,如初始化、销毁、扩展等。
- bulkload_minmax(最小值和最大值信息结构体):
- 用于存储在批量加载过程中的最小值和最大值的信息。
- 包含了比较函数和相关的缓冲区,用于对比较结果进行处理。
- bulkload_datums(值和NULL信息结构体):
- 用于在批量加载过程中存储值和 NULL 信息。
- 包含了值数组、NULL 位图等信息,提供了一些用于初始化、销毁和重置的函数。
- bulkload_vector(一列的一批值结构体):
- 继承自 BaseObject,包含内存块信息、函数指针、值/NULL信息和最小/最大信息等。
- 提供了一系列的 API 函数用于初始化、配置、重置、销毁等操作。
- 通过 bulkload_rows 中的 append_one_vector 函数来追加数据。
- bulkload_vector_iter(批量加载向量的迭代器结构体):
- 用于迭代访问 bulkload_vector 中的数据。
- 提供了开始、下一个、是否结束等函数。
- bulkload_rows(批量加载的批量行结构体):
- 继承自 BaseObject,包含内存上下文、值向量、属性数量、当前和最大值数量、缓冲区的懒惰创建与重用等信息。
- 提供了一系列的函数用于初始化、重置、追加数据、计算内存大小等操作。
- 调用了 bulkload_vector 中的一些函数来处理数据。
调用关系可以用以下简化的图示表示:
+-------------------------+
| bulkload_vector_iter |
+------------+------------+
|
v
+-------------------------+
| bulkload_vector |
+-----------+-------------+
/|\
|
+-----------------+-----------------+
| bulkload_rows |
+-----------+-----------+-----------+
| |
v v
+----------------++-----------------+
| bulkload_block || bulkload_datums |
+----------------++-----------------+
上述图示中箭头表示调用关系,例如 bulkload_rows 调用了 bulkload_vector 中的函数,而 bulkload_vector 中也可能调用了 bulkload_block 和 bulkload_datums 中的函数。这些结构体之间通过函数调用来完成数据的组织、存储和处理。更加详细的调用关系如下图所示: