openGauss-server/src/gausskernel/storage/cstore/cstore_allocspace.cpp

755 lines
33 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
*
* openGauss is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* ---------------------------------------------------------------------------------------
*
* cstore_allocspace.cpp
*
*
* IDENTIFICATION
* src/gausskernel/storage/cstore/cstore_allocspace.cpp
*
* ---------------------------------------------------------------------------------------
*/
#include "access/cstore_am.h"
#include "access/genam.h"
#include "access/sdir.h"
#include "access/skey.h"
#include "storage/cstore/cstorealloc.h"
#include "storage/cu.h"
#include "storage/custorage.h"
#include "storage/lmgr.h"
#include "storage/lock/lwlock.h"
#include "utils/aiomem.h"
#include "utils/fmgroids.h"
#include "utils/hsearch.h"
#include "utils/snapmgr.h"
HTAB* CStoreColspaceCache = NULL;
//用于描述CStore列文件的相关信息
typedef struct {
CStoreColumnFileTag tag;//用于标识CStore列文件的标签
uint64 maxOffset;//表示CStore列文件的最大偏移量
uint32 maxCuid;//表示CStore列文件的最大CUIDColumn Unique Identifier
uint64 extendOffset;//表示CStore列文件的扩展偏移量
} CStoreColFileDesc;
//计算CStore分配器共享内存空间的大小
Size CStoreAllocatorShmSize()
{
Size size = 0;// 初始化共享内存大小为0
// 估算以256为预期哈希表项数和CStoreColFileDesc结构体大小为基础的哈希表占用内存空间的估算值
// 并将估算值累加到size变量中
size = add_size(size, hash_estimate_size(256, sizeof(CStoreColFileDesc)));
return size;// 返回CStore分配器共享内存空间的大小
}
//初始化CStore列空间缓存CStoreColspaceCache的哈希表
void CStoreAllocator::InitColSpaceCache(void)
{
HASHCTL ctl;// 哈希表控制结构体
if (CStoreColspaceCache == NULL) {// 如果CStoreColspaceCache为空
errno_t rc = memset_s(&ctl, sizeof(ctl), 0, sizeof(ctl));// 将ctl结构体初始化为0
securec_check(rc, "", "");
ctl.keysize = sizeof(CStoreColumnFileTag);// 设置哈希表键的大小为CStoreColumnFileTag结构体的大小
ctl.entrysize = sizeof(CStoreColFileDesc);// 设置哈希表项的大小为CStoreColFileDesc结构体的大小
ctl.hash = tag_hash;// 设置哈希函数为tag_hash
// 创建CStore Column Space Cache哈希表并初始化为指定的大小和配置项
CStoreColspaceCache =
HeapMemInitHash("CStore Column Space Cache", 40960, 81920, &ctl, HASH_ELEM | HASH_FUNCTION);
if (CStoreColspaceCache == NULL)// 如果创建哈希表失败
ereport(PANIC, (errmsg("could not initialize CStore Column space desc hash table")));
}
}
//重置CStore列空间缓存CStoreColspaceCache的哈希表
void CStoreAllocator::ResetColSpaceCache(void)
{
if (CStoreColspaceCache != NULL) {// 如果CStoreColspaceCache不为空
HeapMemResetHash(CStoreColspaceCache, "CStore Column Space Cache");// 重置CStore列空间缓存的哈希表
}
}
CStoreAllocator::CStoreAllocator()
{}
CStoreAllocator::~CStoreAllocator()
{}
//获取下一个可用的CUIDColumn Unit ID
uint32 CStoreAllocator::GetNextCUID(Relation rel)
{
bool found = false;
CStoreColFileDesc* entry = NULL;
uint32 cuid = InValidCUID;
CStoreColumnFileTag tag(rel->rd_node, VirtualSpaceCacheColID, MAIN_FORKNUM);// 创建CStore列文件标记
(void)LWLockAcquire(CStoreColspaceCacheLock, LW_EXCLUSIVE);// 获取CStore列空间缓存锁
// 在CStore列空间缓存中查找指定的CStore列文件标记并返回对应的哈希表项
entry = (CStoreColFileDesc*)hash_search(CStoreColspaceCache, (void*)&tag, HASH_FIND, &found);
// 断言找到了指定的哈希表项
Assert(found);
// 获取CStore列文件的最大CUID
cuid = entry->maxCuid;
if (cuid == MaxCUID)// 如果没有剩余的CUID可用
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_RESOURCES),
errmsg("No CUID is left for new CU in relation \"%u\". Please execute the VACUUM FULL before do "
"anything else",
rel->rd_id)));
if (cuid > CUIDWarningThreshold && cuid < MaxCUID)// 如果CUID接近极限值
ereport(WARNING, (errmsg("CUID is almost to be used up in relation \"%u\"", rel->rd_id)));
entry->maxCuid++;// 将CStore列文件的最大CUID增加1
LWLockRelease(CStoreColspaceCacheLock);// 释放CStore列空间缓存锁
return cuid;// 返回获取的CUID值
}
/*
* @Description: calculate extend size when allocate space
* @Param[IN] cu_offset: max cu offset
* @Param[IN] cu_size: write cu size
* @Param[IN] extend_offset: record extend offset
* @Return:0 -- no need extend, others extend size
* @See also:
*/
/*
* @Description: 根据已分配空间的情况计算扩展大小
* @Param[IN] cu_offset: 最大CU偏移量
* @Param[IN] cu_size: 写入的CU大小
* @Param[IN] extend_offset: 记录的扩展偏移量
* @Return: 0 -- 不需要扩展,其他值表示需要扩展的大小
* @See also:
*/
//根据已分配空间的情况计算扩展大小
uint32 CStoreAllocator::CalcExtendSize(uint64 cu_offset, uint32 cu_size, uint64 extend_offset)
{
uint32 extend_segment = (uint32)(u_sess->attr.attr_storage.fast_extend_file_size * 1024LL);// 定义扩展段大小(以字节为单位)
uint32 extend_size = 0;// 初始化扩展大小为0
uint32 need_file_size = 0;// 初始化需求文件大小为0
uint32 left_extend_size = 0;// 初始化剩余扩展大小为0
Assert(cu_offset <= extend_offset);// 断言最大CU偏移量小于等于记录的扩展偏移量
left_extend_size = extend_offset - cu_offset;// 计算剩余扩展大小
if (cu_size <= left_extend_size) {
return 0; // no need fast entend // 不需要快速扩展
}
need_file_size = cu_size - left_extend_size;// 计算需求文件大小
// 若需求文件大小小于等于扩展段大小,则直接扩展到扩展段大小;否则,计算合适的扩展大小
if (need_file_size <= extend_segment) {
extend_size = extend_segment;
} else {
uint32 remainder = need_file_size % extend_segment;
extend_size = need_file_size + extend_segment - remainder;
}
return extend_size;// 返回计算得到的扩展大小
}
/*
* @Description: allocate file size
* @Param[IN] extend_offset: cu pointer
* @Param[IN] size: cu size
* @See also:
*/
/*
* @Description: 分配文件空间
* @Param[IN] cnode: 文件节点
* @Param[IN] extend_offset: CU指针
* @Param[IN] cu_offset: 最大CU偏移量
* @Param[IN] cu_size写入的CU大小
* @Return: 实际分配的文件空间大小
* @See also:
*/
//分配文件空间
uint32 CStoreAllocator::AcquireFileSpace(const CFileNode& cnode, uint64 extend_offset, uint64 cu_offset, uint32 cu_size)
{
uint32 extend_size = 0;
CUStorage* cuStorage = New(CurrentMemoryContext) CUStorage(cnode);
// 根据配置参数external_enable决定使用libaio还是pread()/pwrite()来读写文件
ADIO_RUN()
{
if (u_sess->attr.attr_sql.enable_fast_allocate) {
// 如果开启了快速内存分配则调用CalcExtendSize函数计算扩展大小
extend_size = CStoreAllocator::CalcExtendSize(cu_offset, (uint32)cu_size, extend_offset);
if (extend_size != 0) {
cuStorage->FastExtendFile(extend_offset, extend_size, true);// 先进行快速扩展
}
cuStorage->FastExtendFile(cu_offset, cu_size, false);// 再写入数据
} else {
// 如果没有开启快速内存分配则使用palloc0函数分配内存并将分配的空间清零。然后将数据写入该空间最后释放内存。
char* buffer = (char*)adio_align_alloc(cu_size);
errno_t rc = memset_s(buffer, cu_size, 0, cu_size);
securec_check(rc, "\0", "\0");
cuStorage->SaveCU(buffer, cu_offset, cu_size, true);
adio_align_free(buffer);
extend_size = cu_size;
}
}
ADIO_ELSE()
{
// 如果external_enable为false则使用pread()/pwrite()进行文件读写
char* buffer = (char*)palloc0(cu_size);
cuStorage->SaveCU(buffer, cu_offset, cu_size, false, true);
pfree(buffer);
buffer = NULL;
extend_size = cu_size;
}
ADIO_END();
DELETE_EX(cuStorage);// 删除CUStorage对象释放内存
return extend_size;// 返回实际分配的文件空间大小
}
//为一个列存储文件节点cnode分配大小为size的空间返回分配的空间在文件中的偏移量
uint64 CStoreAllocator::AcquireSpace(const CFileNode& cnode, Size size, int align_size)
{
Assert(align_size > 0);
bool found = false;
CStoreColFileDesc* entry = NULL;
uint64 offset = InvalidCStoreOffset;
uint32 extend_size = 0;
LWLockAcquire(CStoreColspaceCacheLock, LW_EXCLUSIVE);// 获取互斥锁
/* 在哈希表中查找文件节点 */
entry = (CStoreColFileDesc*)hash_search(CStoreColspaceCache, (const void*)&cnode, HASH_FIND, &found);
Assert(found);
if (found) {
offset = entry->maxOffset;
// 当升级时最后一个CU需要添加填充。因此cu_point必须向后对齐
// when upgrade, last cu need add padding. so cu_point must align backward
int remainder = offset % align_size;
if (remainder != 0) {
ereport(WARNING, (errmsg("AcquireSpace: find un align size(%lu)", offset)));
offset = offset + align_size - remainder;// 对齐offset
entry->maxOffset = offset;// 更新maxOffset
}
// 必须在更新entry之前完成快速扩展因为不能留下空洞
// must finish fast extend here before update, because we can not leave hole
extend_size = CStoreAllocator::AcquireFileSpace(cnode, entry->extendOffset, entry->maxOffset, size);
entry->maxOffset += size;// 更新maxOffset
entry->extendOffset += extend_size;// 更新maxOffset
}
LWLockRelease(CStoreColspaceCacheLock);// 释放互斥锁
return offset;// 返回offset
}
//从自由空间映射fsm中尝试获取大小为size的空间并进行对齐返回分配的空间在文件中的偏移量
uint64 CStoreAllocator::TryAcquireSpaceFromFSM(CStoreFreeSpace* fsm, Size size, int align_size)
{
CStoreFreeSpaceDesc desc;
uint64 offset = InvalidCStoreOffset;
Assert(fsm != NULL);
Assert(align_size > 0);
// 检查自由空间映射fsm是否有足够的空间来满足需求
if (!fsm->HasEnoughSpace(size + align_size))
return offset;// 如果没有足够的空间返回InvalidCStoreOffset表示分配失败
// 从自由空间映射fsm中弹出大小最大的空闲块描述符desc
fsm->PopDescWithMaxSize(desc);
offset = desc.beginOffset;// 获取该空闲块的起始偏移量
// when upgrade, last cu need add padding. so cu_point must align backward
// 当进行升级时最后一个CU需要添加填充。因此cu_point必须向后对齐
int remainder = offset % align_size;
if (remainder != 0) {
ereport(WARNING, (errmsg("TryAcquireSpaceFromFSM: find un align size(%lu)", offset)));
offset = offset + align_size - remainder;// 对齐offset
desc.beginOffset = offset;// 更新desc的起始偏移量
desc.size -= remainder;// 更新desc的大小
}
// 更新desc的起始偏移量和大小
desc.beginOffset += size;
desc.size -= size;
// 如果仍然有剩余空间将剩余空间的描述符压入自由空间映射fsm
if (desc.size > 0)
fsm->Push(desc);
return offset;// 返回分配的空间在文件中的偏移量
}
//为了获取空间而锁定关系rel
void CStoreAllocator::LockRelForAcquireSpace(Relation rel)
{
LockRelationForExtension(rel, ExclusiveLock);//使用独占锁ExclusiveLock来锁定关系
}
//为了释放获取空间而锁定的关系rel
void CStoreAllocator::ReleaseRelForAcquireSpace(Relation rel)
{
UnlockRelationForExtension(rel, ExclusiveLock);//使用独占锁ExclusiveLock来释放关系的锁
}
//无效化列空间缓存CStoreColspaceCache中与给定cnode相关的缓存条目
void CStoreAllocator::InvalidColSpaceCache(const CFileNode& cnode)
{
LWLockAcquire(CStoreColspaceCacheLock, LW_EXCLUSIVE);
hash_search(CStoreColspaceCache, (void*)&cnode, HASH_REMOVE, NULL);
LWLockRelease(CStoreColspaceCacheLock);
}
// build space cache for attrno[ attrNum ].
// 函数作用:为给定关系(`heapRel`)和属性编号数组(`attrIds`)中的属性创建列空间缓存。
// 缓存是针对每个属性单独创建的,并且使用锁定方式保证同步性。
// 如果缓存已经存在,则不会重复创建。
// 在构建缓存之前该函数会锁定关系以防止插入新的元组并找到最大的CU ID`maxCUID`和CU指针的偏移量`offset`)。
// 如果存在Gin/BTree索引还需要查找存储在BTree索引中的最大CU ID`maxIdxCUID`并将其与maxCUID比较选择最大的值。
// 使用这些信息,缓存将被创建,然后释放关系锁。
// 最后,释放分配的内存。
void CStoreAllocator::BuildColSpaceCacheForRel(_in_ Relation heapRel,
_in_ AttrNumber* attrIds, // equal to attrno[]
_in_ int attrNum, _in_ List* indexRel)
{
// 创建文件节点结构的数组
CFileNode* cFileNode = (CFileNode*)palloc(sizeof(CFileNode) * attrNum);
// 根据给定的属性编号构造cFileNode数组中每个属性的文件节点
for (int i = 0; i < attrNum; ++i) {
cFileNode[i].m_rnode = heapRel->rd_node;
cFileNode[i].m_forkNum = MAIN_FORKNUM;
cFileNode[i].m_attid = attrIds[i];
}
// 如果缓存不存在,就需要构建缓存
if (!CStoreAllocator::ColSpaceCacheExist(cFileNode, attrNum)) {
uint64* offset = (uint64*)palloc(sizeof(uint64) * attrNum);
// it's very important to make maxCUID and all the maxCUPointers the newest and biggest.
// so lock and forbit this relation inserting new tuples, see also SaveAll() method.
// 防止关系在创建缓存期间插入新的元组,使用独占锁
LockRelationForExtension(heapRel, ExclusiveLock);
// 获取最大CU ID
Oid cudesOid = heapRel->rd_rel->relcudescrelid;
uint32 maxCUID = CStore::GetMaxCUID(cudesOid, heapRel->rd_att) + 1;
// 如果存在Gin/BTree索引则获取BTree索引中的最大CU ID用于比较选择最大的CU ID
/* If there is gin/btree index, we need to find the biggest CU ID stored in the btree index. */
if (indexRel != NULL) {
uint32 maxIdxCUID = CStore::GetMaxIndexCUID(heapRel, indexRel) + 1;
if (maxIdxCUID > maxCUID)
maxCUID = maxIdxCUID;
}
// 查找每个属性的CU指针偏移量
for (int i = 0; i < attrNum; ++i) {
if (!heapRel->rd_att->attrs[i]->attisdropped) {
offset[i] = CStore::GetMaxCUPointer(attrIds[i], heapRel);
} else {
offset[i] = 0;
}
}
// 创建缓存
CStoreAllocator::BuildColSpaceCacheForRel(cFileNode, attrNum, offset, maxCUID);
// 释放关系锁
UnlockRelationForExtension(heapRel, ExclusiveLock);
// 释放分配的内存
pfree_ext(offset);
}
// 释放分配的内存
pfree_ext(cFileNode);
}
/*
* @Description: calc fast extend offset
* @Param[IN] max_offset: max cu_pointer of the file
* @Return: extend offset
* @See also:
*/
/*
* @Description: 计算快速扩展的偏移量
* @Param[IN] max_offset: 文件的最大CU指针
* @Return: 扩展的偏移量
* @See also:
*/
//根据文件的最大CU指针计算快速扩展的偏移量。偏移量是按照预定义的段大小对齐的以提高存储空间的利用率
uint64 CStoreAllocator::GetExtendOffset(uint64 max_offset)
{
// 获取快速扩展的段大小(单位:字节)
int extend_segment = (int)(u_sess->attr.attr_storage.fast_extend_file_size * 1024LL);
// 计算当前偏移量所在的段的起始偏移量
uint64 offset = CU_FILE_OFFSET(max_offset);
uint64 remainder = offset % extend_segment;
if (remainder != 0) {
// 如果当前偏移量不是段的起始位置,则计算下一个段的起始偏移量
max_offset = max_offset + extend_segment - remainder;
}
return max_offset;
}
//构建列空间的全局缓存,将列对应的文件偏移量写入缓存,以提供分配空间时的参考
void CStoreAllocator::BuildColSpaceCacheForRel(const CFileNode* cnodes, int nColumn, uint64* offsets, uint32 maxCUID)
{
CFileNode tag(cnodes[0].m_rnode, VirtualSpaceCacheColID, MAIN_FORKNUM);
bool found = false;
CStoreColFileDesc* entry = NULL;
LWLockAcquire(CStoreColspaceCacheLock, LW_EXCLUSIVE);
// We should check if all cache entries of the relation are valid.
// If not, update them.
// If yes, skip.
//
// 构建列空间的全局缓存
// 将列对应的文件偏移量写入缓存,以提供分配空间时的参考
// 缓存中记录的是每个列的最大文件偏移量、对应的最大CU指针和扩展偏移量
//
entry = (CStoreColFileDesc*)hash_search(CStoreColspaceCache, (void*)&tag, HASH_ENTER, &found);
if (entry == NULL)
ereport(PANIC, (errmsg("build global column space cache hash table failed")));
// Other session has insert some columns or all columns into hash table
// !!!Note that we reuse variable 'found'
//
// 判断是否需要更新缓存
// 如果存在已经缓存的数据,则需要检查其是否已经完整
//
if (found) {
for (int i = 0; i < nColumn; i++) {
hash_search(CStoreColspaceCache, (void*)&cnodes[i], HASH_FIND, &found);
// Other session has insert some columns
// It is incomplete
//
// 如果某个列的空间信息不存在,则代表其他进程还没有往缓存中插入
// 全部信息不完整,需要更新缓存
//
if (!found)
break;
}
}
// 如果缓存中没有列对应的空间信息,则需要更新缓存
if (!found) {
entry->maxCuid = maxCUID;
entry->maxOffset = InvalidCStoreOffset;
// 逐一将每个列的空间信息填入缓存
for (int i = 0; i < nColumn; i++) {
entry = (CStoreColFileDesc*)hash_search(CStoreColspaceCache, (void*)&cnodes[i], HASH_ENTER, NULL);
if (entry == NULL)
ereport(PANIC, (errmsg("build global column space cache hash table failed")));
entry->maxOffset = offsets[i];
entry->maxCuid = InValidCUID;
entry->extendOffset = CStoreAllocator::GetExtendOffset(entry->maxOffset);
}
}
LWLockRelease(CStoreColspaceCacheLock);
}
//用于检查给定的列空间信息是否存在于缓存中
bool CStoreAllocator::ColSpaceCacheExist(const CFileNode* cnodes, int nColumn)
{
bool found = false;
// 获取共享锁,以防止其他线程对缓存进行修改
LWLockAcquire(CStoreColspaceCacheLock, LW_SHARED);
// 遍历每个列的CFileNode查找其在缓存中是否存在
// 如果某个列的空间信息不存在则found为false退出循环
for (int i = 0; i < nColumn; i++) {
hash_search(CStoreColspaceCache, (void*)&cnodes[i], HASH_FIND, &found);
if (!found)
break;
}
// 释放共享锁
LWLockRelease(CStoreColspaceCacheLock);
// 返回是否所有列的空间信息都存在于缓存中的布尔值
return found;
}
/**
* science we loose lock after get max cuid we will doubt the max cuid system
* here we recheck max cuid located in index
* we want to make sure if there is on another larger cuid in index
*/
//用于重新检查最大的cuid值
uint32 CStoreAllocator::recheck_max_cuid(Relation m_rel, uint32 max_cuid, int index_num, Relation* m_idxRelation)
{
bool find = false;
List* index_rel_list = NIL;
// 筛选出索引类型为B树或GIN的关联关系将其添加到索引关联关系列表中
for (int i = 0; i < index_num; ++i) {
Oid am_oid = m_idxRelation[i]->rd_rel->relam;
if (am_oid == CBTREE_AM_OID || am_oid == CGIN_AM_OID) {
index_rel_list = lappend(index_rel_list, m_idxRelation[i]);
}
}
// 若索引关联关系列表为空则直接返回原始的最大cuid值
if (list_length(index_rel_list) == 0) {
return max_cuid;
}
// 获取索引关联关系列表中的最大索引cuid并在释放列表内存后返回
uint32 max_idx_cuid = CStore::GetMaxIndexCUID(m_rel, index_rel_list) + 1;
list_free_ext(index_rel_list);
// 如果最大索引cuid等于MaxCUID表示没有剩余的cuid可供新的CU使用报错
if (max_idx_cuid == MaxCUID) {
ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_RESOURCES),
errmsg("No CUID is left for new CU in relation \"%u\".", m_rel->rd_id)));
}
// 如果最大索引cuid大于原始的最大cuid值更新缓存中对应的列文件描述项的最大cuid并返回最大索引cuid
if (max_idx_cuid > max_cuid) {
CStoreColFileDesc* entry = NULL;
CStoreColumnFileTag tag(m_rel->rd_node, VirtualSpaceCacheColID, MAIN_FORKNUM);
(void)LWLockAcquire(CStoreColspaceCacheLock, LW_EXCLUSIVE);
entry = (CStoreColFileDesc*)hash_search(CStoreColspaceCache, (void*)&tag, HASH_FIND, &find);
Assert(find);
entry->maxCuid = max_idx_cuid + 1;
LWLockRelease(CStoreColspaceCacheLock);
return max_idx_cuid;// 如果最大索引cuid不大于原始的最大cuid值则直接返回原始的最大cuid值
}
return max_cuid;
}
//初始化CStoreFreeSpace对象
void CStoreFreeSpace::Initialize(int maxSize)
{
m_maxSize = maxSize;// 设置最大尺寸
m_descNum = 0;// 描述项数量初始化为0
m_descs = (CStoreFreeSpaceDesc*)palloc0(sizeof(CStoreFreeSpaceDesc) * (m_maxSize + 1));// 分配描述项数组内存
}
}
CStoreFreeSpace::~CStoreFreeSpace()
{
m_descs = NULL;// 将描述项数组指针置空
}
//销毁CStoreFreeSpace对象
void CStoreFreeSpace::Destroy()
{
pfree(m_descs);// 释放描述项数组内存
m_descs = NULL;// 将描述项数组指针置空
}
//向CStoreFreeSpace对象的描述项数组中压入一个新的描述项
void CStoreFreeSpace::Push(const CStoreFreeSpaceDesc& desc)
{
int i;
if (m_maxSize == m_descNum)// 描述项数量已达到最大尺寸,无法继续添加
return;
i = ++m_descNum;// 描述项数量加1并将当前位置索引赋值给变量i
while (i != 1 && desc.size > m_descs[i / 2].size) {// 描述项的尺寸比父节点的尺寸大,进行上移操作
m_descs[i] = m_descs[i / 2];// 将父节点的描述项下移到当前位置
i /= 2;// 更新索引为父节点的索引
}
m_descs[i] = desc;// 将待插入的描述项存放到最终位置
}
//用于弹出具有最大尺寸的描述项并将其赋值给传入的参数desc
void CStoreFreeSpace::PopDescWithMaxSize(CStoreFreeSpaceDesc& desc)
{
CStoreFreeSpaceDesc tmp;
int i = 1;
int subi = 2;
if (m_descNum == 0)// 描述项数量为0无法弹出
return;
desc = m_descs[1];// 将根节点的描述项赋值给传入的参数desc
tmp = m_descs[m_descNum--];// 将最后一个描述项赋值给临时变量tmp并将描述项数量减1
while (subi <= m_descNum) {// 子节点索引未超出描述项数组范围
if (subi < m_descNum && m_descs[subi].size < m_descs[subi + 1].size)// 右子节点的尺寸更大,选择右子节点
subi++;
if (tmp.size >= m_descs[subi].size)// 临时描述项的尺寸大于等于子节点的尺寸,退出循环
break;
m_descs[i] = m_descs[subi];// 将子节点的描述项上移到当前位置
i = subi;// 更新索引为子节点索引
subi *= 2;// 计算下一个子节点的索引
}
m_descs[i] = tmp;// 将临时描述项存放到最终确定的位置
}
//用于获取具有最大尺寸的描述项并将其赋值给传入的参数desc
void CStoreFreeSpace::GetDescWithMaxSize(_out_ CStoreFreeSpaceDesc& desc)
{
if (m_descNum == 0)// 如果描述项数量为0则说明空间已满返回size最大值
desc.size = ~0;
else// 否则获取具有最大尺寸的描述项并赋值给传入的参数desc
desc = m_descs[1];
}
//用于判断空闲空间是否足够放下指定大小的数据块。如果当前为空闲空间
bool CStoreFreeSpace::HasEnoughSpace(Size size)
{
return IsEmpty() ? false : size <= m_descs[1].size;// 判断空闲空间是否足够放下指定大小的数据块
}
// compute free space data for the *attrno* attribute, which
// belongs to the relation specified by *cudescHeapRel*.
// *cudescIndexRel* used to index-scan.
//计算指定关系中指定属性的空闲空间数据
void CStoreFreeSpace::ComputeFreeSpace(
_in_ AttrNumber attrno, _in_ Relation cudescHeapRel, _in_ Relation cudescIndexRel, __inout CStoreFreeSpace* fspace)
{
bool isnull = false;// 是否为NULL值
List* beginOffsetOrderedList = NIL;// 按beginoffset排序的列表
ListCell* currCell = NULL;// 当前列表项
ListCell* prevCell = NULL;// 前一个列表项
ListCell* nextCell = NULL;// 后一个列表项
TupleDesc cudescTupDesc = RelationGetDescr(cudescHeapRel);// CUDesc表的元组描述符
#ifdef USE_ASSERT_CHECKING
List* tupList = NIL;// 用于断言检查的元组列表
#endif
// Setup scan key to fetch from the index by col_id.
// 设置扫描键按照col_id从索引中获取数据
ScanKeyData key;
ScanKeyInit(&key, (AttrNumber)CUDescColIDAttr, BTEqualStrategyNumber, F_INT4EQ, Int32GetDatum(attrno));
// DIRTY snapshot will be used so that we can get the newest data.
// 使用DIRTY快照初始化扫描描述符
SnapshotData SnapshotDirty;
InitDirtySnapshot(SnapshotDirty);
SysScanDesc cudescScan = systable_beginscan_ordered(cudescHeapRel, cudescIndexRel, &SnapshotDirty, 1, &key);
// Step 1:
// Scan the CUDesc of column from the CUDesc table, and put them in a list
// ordered by beginoffset. And then merge the CUDesc if
// 1). desc1.beginoffset + desc1.size == desc2.beginoffset, or
// 2). desc2.beginoffset + desc2.size == desc1.beginoffset
// Note: if the number of holds in the column > MaxNumOfHoleFSM, we should
// give up the scan, and go back to the 'APPEND_ONLY'.
//
//从CUDesc表中扫描列的CUDesc并按beginoffset排序放入列表中然后合并相邻的CUDesc
HeapTuple tup = NULL;
while ((tup = systable_getnext_ordered(cudescScan, BackwardScanDirection)) != NULL) {
CStoreSpaceDesc spaceDesc;
char* cuPointer = DatumGetPointer(fastgetattr(tup, CUDescCUPointerAttr, cudescTupDesc, &isnull));
// skip cuPointer is null
// 跳过cuPointer为NULL的情况
if (isnull)
continue;
Assert(cuPointer);
spaceDesc.beginOffset = *((uint64*)VARDATA_ANY(cuPointer));
spaceDesc.size = DatumGetInt32(fastgetattr(tup, CUDescSizeAttr, cudescTupDesc, &isnull));
// skip those special CUs with total NULL or the SAME value.
// 跳过特殊情况下size为0的CUDesc
if (spaceDesc.size == 0)
continue;
#ifdef USE_ASSERT_CHECKING
HeapTuple tupForCheck = heap_copytuple(tup);
tupList = lappend(tupList, tupForCheck);
#endif
// try to merge the descs.
// 尝试合并CUDesc
for (currCell = list_head(beginOffsetOrderedList), prevCell = NULL; currCell != NULL; currCell = nextCell) {
CStoreSpaceDesc* curEntry = (CStoreSpaceDesc*)lfirst(currCell);
nextCell = lnext(currCell);
Assert(spaceDesc.beginOffset != curEntry->beginOffset);
// if |-- curEntry --|-- spaceDesc --|-- nextEntry --|
// then |------ curEntry ------|-- nextEntry --|
// then |------------ curEntry ------------|
if (curEntry->beginOffset + curEntry->size == spaceDesc.beginOffset) {
curEntry->size += spaceDesc.size;
if (nextCell != NULL) {
CStoreSpaceDesc* nextEntry = (CStoreSpaceDesc*)lfirst(nextCell);
if (nextEntry->beginOffset == curEntry->beginOffset + curEntry->size) {
curEntry->size += nextEntry->size;
beginOffsetOrderedList = list_delete_cell(beginOffsetOrderedList, nextCell, currCell);
pfree(nextEntry);
}
}
// mark spaceDesc as handled.
// 标记spaceDesc已处理
spaceDesc.beginOffset = InvalidCStoreOffset;
break;
}
// 使用插入排序
// do Insertion sort
if (spaceDesc.beginOffset < curEntry->beginOffset) {
// if |-- spaceDesc --|-- curEntry --|
// then |------ curEntry ------|
if (spaceDesc.beginOffset + spaceDesc.size == curEntry->beginOffset) {
curEntry->beginOffset = spaceDesc.beginOffset;
curEntry->size += spaceDesc.size;
} else {
CStoreSpaceDesc* newEntry = (CStoreSpaceDesc*)palloc0(sizeof(CStoreSpaceDesc));
newEntry->beginOffset = spaceDesc.beginOffset;
newEntry->size = spaceDesc.size;
if (prevCell == NULL) {
beginOffsetOrderedList = list_concat(lappend(NIL, newEntry), beginOffsetOrderedList);
} else {
lappend_cell(beginOffsetOrderedList, prevCell, newEntry);
}
// check the size of beginOffsetOrderedList to avoid too many space
// 检查beginOffsetOrderedList的大小避免空间过多
if (list_length(beginOffsetOrderedList) > MaxNumOfHoleFSM)
goto scan_end;
}
// 标记spaceDesc已处理
// mark spaceDesc as handled.
spaceDesc.beginOffset = InvalidCStoreOffset;
break;
}
Assert(spaceDesc.beginOffset > curEntry->beginOffset + curEntry->size);
prevCell = currCell;
}
// 在此处将spaceDesc追加到列表末尾
// by here, we should append the spaceDesc at the end of list.
if (spaceDesc.beginOffset != InvalidCStoreOffset) {
CStoreSpaceDesc* newEntry = (CStoreSpaceDesc*)palloc0(sizeof(CStoreSpaceDesc));
newEntry->beginOffset = spaceDesc.beginOffset;
newEntry->size = spaceDesc.size;
beginOffsetOrderedList = lappend(beginOffsetOrderedList, newEntry);
if (list_length(beginOffsetOrderedList) > MaxNumOfHoleFSM)
goto scan_end;
}
}
// Step2 : Calculate the space hole of the column
// Check if there's a hole at the begin of Column.
// 步骤2计算列的空洞
// 检查列的开头是否存在空洞
currCell = list_head(beginOffsetOrderedList);
if (currCell != NULL) {
CStoreSpaceDesc* entry = (CStoreSpaceDesc*)lfirst(currCell);
if (entry->beginOffset > MinAvailableCStoreFSMSize) {
CStoreFreeSpaceDesc desc;
desc.beginOffset = 0;
desc.size = entry->beginOffset;
fspace->Push(desc);
}
}
// 计算列的空洞
// Calculate the space hole of the column
for (currCell = list_head(beginOffsetOrderedList); currCell != NULL; currCell = nextCell) {
CStoreSpaceDesc *curEntry = NULL, *nextEntry = NULL;
nextCell = lnext(currCell);
if (nextCell == NULL)
break;
curEntry = (CStoreSpaceDesc*)lfirst(currCell);
nextEntry = (CStoreSpaceDesc*)lfirst(nextCell);
Assert(curEntry->beginOffset < nextEntry->beginOffset);
if (curEntry->beginOffset + curEntry->size + MinAvailableCStoreFSMSize < nextEntry->beginOffset) {
CStoreFreeSpaceDesc freeSpace;
freeSpace.beginOffset = curEntry->beginOffset + curEntry->size;
freeSpace.size = nextEntry->beginOffset - (curEntry->beginOffset + curEntry->size);
fspace->Push(freeSpace);
if (fspace->IsFull())
break;
}
}
scan_end:
#ifdef USE_ASSERT_CHECKING
list_free_deep(tupList);// 释放断言检查用的元组列表
tupList = NIL;
#endif
list_free_deep(beginOffsetOrderedList);// 释放排序后的列表
beginOffsetOrderedList = NIL;
systable_endscan_ordered(cudescScan);// 结束扫描
cudescScan = NULL;
}