声明:本文的部分内容参考了他人的文章。在编写过程中,我们尊重他人的知识产权和学术成果,力求遵循合理使用原则,并在适用的情况下注明引用来源。
本文主要参考了 OpenGauss1.1.0 的开源代码和《OpenGauss数据库源码解析》一书以及OpenGauss社区学习文档和一些参考资料
CStoreAllocator 类在列式存储引擎中担任关键角色,负责管理列存储表的空间分配和释放。它通过提供静态方法来获取列存储文件和列存储单元的空间,以及执行空间锁定和释放操作,有效地支持列存储表的扩展和管理。此外,该类还涉及CUID(Column UID)的管理,包括获取下一个 CUID 和重新检查最大 CUID 等操作,为列存储引擎提供了对列的唯一标识和顺序管理的支持。通过这些功能,CStoreAllocator 实现了在列存储引擎中的高效空间管理,为列存储表的数据组织和访问提供了基础支持。
以下是该类的主要功能:
- 空间分配和释放: 提供了静态方法来获取列存储的文件空间和列存储单元(CU)的空间。这包括对文件空间的扩展和对空闲空间管理器(Free Space Manager,FSM)的查询。
- AcquireFileSpace为列存储文件获取空间。
- AcquireSpace:为列存储单元获取空间。
- TryAcquireSpaceFromFSM:尝试从自由空间管理器获取空间。
- 空间锁定和释放 :提供了静态方法来在执行空间分配前后对表进行锁定和解锁。
- LockRelForAcquireSpace:在执行空间分配前锁定表。
- ReleaseRelForAcquireSpace:在执行空间分配后解锁表。
- 其他空间管理操作: 提供了其他一些与空间管理相关的操作,如无效列空间缓存、构建列空间缓存等。
- InvalidColSpaceCache:使列空间缓存无效。
- BuildColSpaceCacheForRel:为表构建列空间缓存。
- CUID(Column UID)管理: 提供了获取下一个CUID、重新检查最大 CUID 等方法。
- GetNextCUID:获取下一个 CUID。
- recheck_max_cuid:重新检查最大 CUID。
CStoreAllocator 类源码如下所示:(路径:src/gausskernel/storage/cstore/cstore_allocspace.cpp
)
class CStoreAllocator : public BaseObject {
private:
CStoreAllocator();
virtual ~CStoreAllocator();
public:
// 初始化列空间缓存
static void InitColSpaceCache(void);
// 重置列空间缓存
static void ResetColSpaceCache(void);
// 获取下一个 CUID
static uint32 GetNextCUID(Relation rel);
// 在获取空间之前需要锁定关系
static void LockRelForAcquireSpace(Relation rel);
// 释放获取空间前的关系锁
static void ReleaseRelForAcquireSpace(Relation rel);
// 获取文件空间
static uint32 AcquireFileSpace(const CFileNode& cnode, uint64 extend_offset, uint64 cu_offset, uint32 cu_size);
// 获取 CU 的空间
static uint64 AcquireSpace(const CFileNode& cnode, Size size, int align_size);
// 从自由空间中尝试获取 CU 的空间
static uint64 TryAcquireSpaceFromFSM(CStoreFreeSpace* fsm, Size size, int align_size);
// 使列空间缓存无效
static void InvalidColSpaceCache(const CFileNode& cnode);
// 为关系构建列空间缓存
static void BuildColSpaceCacheForRel(const CFileNode* cnodes, int nColumn, uint64* offsets, uint32 maxCUID);
// 为关系构建列空间缓存
static void BuildColSpaceCacheForRel(
_in_ Relation heapRel, _in_ AttrNumber* attrIds, _in_ int attrNum, _in_ List* btreeIndex = NIL);
// 检查列空间缓存是否存在
static bool ColSpaceCacheExist(const CFileNode* cnodes, int nColumn);
// 获取扩展偏移量
static uint64 GetExtendOffset(uint64 max_offset);
// 计算扩展大小
static uint32 CalcExtendSize(uint64 cu_offset, uint32 cu_size, uint64 extend_offset);
// 重新检查最大 CUID
static uint32 recheck_max_cuid(Relation m_rel, uint32 max_cuid, int index_num, Relation* m_idxRelation);
};
我们可能不会对每个成员函数都进行分析,感兴趣的读者可以自行阅读源码。
CStoreAllocator::InitColSpaceCache 函数用于初始化列式存储引擎中的列空间缓存,即用于管理列存储文件空间信息的哈希表。在初始化之前,它首先检查是否已经存在列空间缓存,如果不存在,则创建一个新的哈希表。哈希表的键是 CStoreColumnFileTag 结构,用于唯一标识列存储文件的标签,而哈希表的值是 CStoreColFileDesc 结构,用于存储列存储文件的描述信息。初始化完成后,该哈希表将用于存储和检索列存储文件的空间信息。函数源码如下所示:(路径:src/gausskernel/storage/cstore/cstore_allocspace.cpp
)
void CStoreAllocator::InitColSpaceCache(void)
{
// 初始化哈希表控制结构
HASHCTL ctl;
// 检查是否已经存在列空间缓存
if (CStoreColspaceCache == NULL) {
errno_t rc = memset_s(&ctl, sizeof(ctl), 0, sizeof(ctl));
securec_check(rc, "", "");
// 设置哈希表键的大小为 CStoreColumnFileTag 结构的大小
ctl.keysize = sizeof(CStoreColumnFileTag);
// 设置哈希表项的大小为 CStoreColFileDesc 结构的大小
ctl.entrysize = sizeof(CStoreColFileDesc);
// 设置哈希函数为 tag_hash
ctl.hash = tag_hash;
// 创建并初始化列空间缓存的哈希表
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")));
}
}
CStoreAllocator::ResetColSpaceCache 函数的作用是重置列存储中用于管理列空间的缓存哈希表。在列存储中,每个列都有对应的空间描述信息,用于记录该列的文件偏移等信息。重置过程中,会清空哈希表中的所有数据,通常在一些需要重新加载列信息的场景下调用,确保列信息的准确性。函数源码如下所示:(路径:src/gausskernel/storage/cstore/cstore_allocspace.cpp
)
void CStoreAllocator::ResetColSpaceCache(void)
{
// 如果列空间缓存已经初始化
if (CStoreColspaceCache != NULL) {
// 重置列空间缓存哈希表
HeapMemResetHash(CStoreColspaceCache, "CStore Column Space Cache");
}
}
CStoreAllocator::GetNextCUID 函数的作用是获取下一个可用的CUID(Column ID),用于标识列存储中的新的列单元(Column Unit)。在获取过程中,会检查 CUID 是否已用尽,如果已用尽则抛出错误。同时,如果 CUID 接近耗尽阈值,会发出警告。最后,增加最大 CUID,为下一个列单元做准备。函数源码如下所示:(路径:src/gausskernel/storage/cstore/cstore_allocspace.cpp
)
uint32 CStoreAllocator::GetNextCUID(Relation rel)
{
// 标识是否在哈希表中找到对应项
bool found = false;
// 用于存储哈希表中的项
CStoreColFileDesc* entry = NULL;
// 用于存储获取到的CUID
uint32 cuid = InValidCUID;
// 构建标识列文件的哈希表键
CStoreColumnFileTag tag(rel->rd_node, VirtualSpaceCacheColID, MAIN_FORKNUM);
// 获取列空间哈希表的锁,以进行写操作
(void)LWLockAcquire(CStoreColspaceCacheLock, LW_EXCLUSIVE);
// 在列空间哈希表中查找对应的项
entry = (CStoreColFileDesc*)hash_search(CStoreColspaceCache, (void*)&tag, HASH_FIND, &found);
// 断言确保找到对应项
Assert(found);
// 获取到当前的最大CUID
cuid = entry->maxCuid;
// 如果CUID已用尽,则抛出错误
if (cuid == MaxCUID)
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_RESOURCES),
errmsg("No CUID is left for new CU in relation \"%u\". Please execute the VACUUM FULL before doing "
"anything else",
rel->rd_id)));
// 如果CUID接近耗尽阈值,则发出警告
if (cuid > CUIDWarningThreshold && cuid < MaxCUID)
ereport(WARNING, (errmsg("CUID is almost used up in relation \"%u\"", rel->rd_id)));
// 增加最大CUID,为下一个CU做准备
entry->maxCuid++;
// 释放列空间哈希表的锁
LWLockRelease(CStoreColspaceCacheLock);
// 返回获取到的CUID
return cuid;
}
CStoreAllocator::AcquireSpace 函数的作用是为列文件分配空间,其中包括在列空间哈希表中查找对应的列文件描述项,并根据对齐要求和文件扩展大小等信息计算出列文件中新的偏移量。在更新最大偏移量之前,通过 CStoreAllocator::AcquireFileSpace 函数完成对列文件的扩展。函数源码如下所示:(路径:src/gausskernel/storage/cstore/cstore_allocspace.cpp
)
uint64 CStoreAllocator::AcquireSpace(const CFileNode& cnode, Size size, int align_size)
{
// 断言:对齐大小必须大于0
Assert(align_size > 0);
// 用于标识是否在哈希表中找到对应项
bool found = false;
// 用于存储找到的列文件描述项
CStoreColFileDesc* entry = NULL;
// 初始偏移量为无效值
uint64 offset = InvalidCStoreOffset;
// 用于存储文件扩展大小
uint32 extend_size = 0;
// 获取列空间哈希表的独占锁
LWLockAcquire(CStoreColspaceCacheLock, LW_EXCLUSIVE);
// 在列空间哈希表中查找与给定CFileNode相对应的项
entry = (CStoreColFileDesc*)hash_search(CStoreColspaceCache, (const void*)&cnode, HASH_FIND, &found);
// 断言:必须找到对应项
Assert(found);
// 如果找到对应项
if (found) {
// 将偏移量设置为找到的最大偏移量
offset = entry->maxOffset;
// 在升级时,最后一个列单元需要添加填充,因此cu_point必须向后对齐
int remainder = offset % align_size;
// 如果remainder不为0,表示需要进行对齐操作
if (remainder != 0) {
// 发出警告消息
ereport(WARNING, (errmsg("AcquireSpace: find un align size(%lu)", offset)));
// 进行对齐操作
offset = offset + align_size - remainder;
// 更新最大偏移量
entry->maxOffset = offset;
}
// 必须在更新之前完成快速扩展,因为我们不能留下空洞
extend_size = CStoreAllocator::AcquireFileSpace(cnode, entry->extendOffset, entry->maxOffset, size);
// 更新最大偏移量和扩展偏移量
entry->maxOffset += size;
entry->extendOffset += extend_size;
}
// 释放列空间哈希表的独占锁
LWLockRelease(CStoreColspaceCacheLock);
// 返回偏移量
return offset;
}
CStoreAllocator::AcquireFileSpace 该函数的作用是为列文件分配文件空间,其中包括对列文件进行扩展操作。在快速分配策略下,通过 CUStorage 对象的 FastExtendFile 函数实现快速扩展;在非快速分配策略下,通过分配缓冲区、填充 0,并通过 CUStorage 对象的 SaveCU 函数保存列单元。最后,释放 CUStorage 对象,返回扩展大小。
注:其中 CUStorage 相关内容可以阅读【 OpenGauss源码学习 —— 列存储(CUStorage)】一文
CStoreAllocator::AcquireFileSpace 函数源码如下所示:(路径:src/gausskernel/storage/cstore/cstore_allocspace.cpp
)
/*
* @Description: 分配文件大小
* @Param[IN] extend_offset: 扩展偏移量
* @Param[IN] size: 列单元大小
* @See also:
*/
uint32 CStoreAllocator::AcquireFileSpace(const CFileNode& cnode, uint64 extend_offset, uint64 cu_offset, uint32 cu_size)
{
// 用于存储扩展大小
uint32 extend_size = 0;
// 创建CUStorage对象,用于对列文件进行操作
CUStorage* cuStorage = New(CurrentMemoryContext) CUStorage(cnode);
// 在ADIO模式下运行
ADIO_RUN()
{
// 如果启用了快速分配策略
if (u_sess->attr.attr_sql.enable_fast_allocate) {
// 计算扩展大小
extend_size = CStoreAllocator::CalcExtendSize(cu_offset, (uint32)cu_size, extend_offset);
// 如果扩展大小不为0,则使用快速扩展文件
if (extend_size != 0) {
cuStorage->FastExtendFile(extend_offset, extend_size, true);
}
// 使用快速扩展文件
cuStorage->FastExtendFile(cu_offset, cu_size, false);
} else {
// 分配缓冲区,填充0,然后保存CU
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模式下运行
ADIO_ELSE()
{
// 分配缓冲区,填充0,然后保存CU
char* buffer = (char*)palloc0(cu_size);
cuStorage->SaveCU(buffer, cu_offset, cu_size, false, true);
pfree(buffer);
buffer = NULL;
extend_size = cu_size;
}
// 结束ADIO模式
// 释放CUStorage对象
DELETE_EX(cuStorage);
// 返回扩展大小
return extend_size;
}
该函数的作用是从给定的空闲空间管理器(Free Space Manager,FSM)中尝试获取指定大小的空间,并确保满足对齐要求。函数通过弹出最大大小的空闲空间描述项,计算偏移量,并在升级时进行对齐操作。如果成功获取空间,则更新描述项的起始偏移量和大小,并将其重新推入空闲空间管理器。最终返回成功获取的偏移量。函数源码如下所示:(路径:src/gausskernel/storage/cstore/cstore_allocspace.cpp
)
uint64 CStoreAllocator::TryAcquireSpaceFromFSM(CStoreFreeSpace* fsm, Size size, int align_size)
{
// 存储空闲空间的描述项
CStoreFreeSpaceDesc desc;
// 初始偏移量为无效值
uint64 offset = InvalidCStoreOffset;
// 断言:空闲空间管理器不能为空
Assert(fsm != NULL);
// 断言:对齐大小必须大于0
Assert(align_size > 0);
// 如果空闲空间管理器没有足够的空间
if (!fsm->HasEnoughSpace(size + align_size))
return offset;
// 从空闲空间管理器中弹出最大大小的描述项
fsm->PopDescWithMaxSize(desc);
// 将偏移量设置为描述项的起始偏移量
offset = desc.beginOffset;
// 在升级时,最后一个列单元需要添加填充,因此cu_point必须向后对齐
int remainder = offset % align_size;
// 如果remainder不为0,表示需要进行对齐操作
if (remainder != 0) {
// 发出警告消息
ereport(WARNING, (errmsg("TryAcquireSpaceFromFSM: find un align size(%lu)", offset)));
// 进行对齐操作
offset = offset + align_size - remainder;
// 更新描述项的起始偏移量和大小
desc.beginOffset = offset;
desc.size -= remainder;
}
// 更新描述项的起始偏移量和大小
desc.beginOffset += size;
desc.size -= size;
// 如果描述项的大小仍然大于0,将其重新推入空闲空间管理器
if (desc.size > 0)
fsm->Push(desc);
// 返回偏移量
return offset;
}
其中,CStoreAllocator::TryAcquireSpaceFromFSM 函数由 CUStorage::AllocSpace 函数调用,因为在【 OpenGauss源码学习 —— 列存储(CUStorage)】一文中没有对 CUStorage::AllocSpace 函数进行介绍,这里顺带简述一下。
CUStorage::AllocSpace 函数根据存储策略选择不同的分配方式,其中包括使用自由空间管理器(Free Space Manager)或者使用追加模式。函数尝试从自由空间管理器中获取指定大小的空间,如果获取失败则使用追加模式从 CStore 分配器中获取。最终返回成功获取的偏移量,并根据分配模式标记是否为追加模式。函数源码如下所示:(路径:src/gausskernel/storage/cstore/custorage.cpp
)
uint64 CUStorage::AllocSpace(_in_ int size)
{
// 初始偏移量为无效值
uint64 offset = InvalidCStoreOffset;
// 对齐大小,默认为2字节对齐
int align_size = is_2byte_align ? ALIGNOF_TIMESERIES_CUSIZE : ALIGNOF_CUSIZE;
// 根据策略选择不同的分配方式
switch (m_strategy) {
// 使用自由空间管理器(Free Space Manager)
case USING_FREE_SPACE:
// 尝试从自由空间管理器中获取指定大小的空间
offset = CStoreAllocator::TryAcquireSpaceFromFSM(m_freespace, size, align_size);
// 如果成功获取到空间
if (offset != InvalidCStoreOffset) {
// 标记为非追加模式
append_only = false;
break;
}
// 如果没有合适的空闲空间,使用追加模式
/* if no free space fits, use the APPEND_ONLY way */
case APPEND_ONLY:
// 从CStore分配器中获取指定大小的空间
offset = CStoreAllocator::AcquireSpace(m_cnode, size, align_size);
// 标记为追加模式
append_only = true;
break;
// 默认情况下,断言失败
default:
Assert(false);
break;
}
// 返回最终的偏移量
return offset;
}
CStoreAllocator::InvalidColSpaceCache 函数用于使指定文件节点的列存空间缓存项失效,首先获取列存空间缓存锁,然后在列存空间缓存中查找并移除指定文件节点的缓存项,最后释放列存空间缓存锁。其中,CStoreAllocator::InvalidColSpaceCache 函数分别由:CStore::InvalidRelSpaceCache、CStore::UnlinkColDataFile 和 CStore::TruncateStorageInSameXact 函数调用。其中 CStore 的介绍可以浏览【 OpenGauss源码学习 —— 列存储(CStore)】系列相关文章。
CStoreAllocator::InvalidColSpaceCache 函数源码如下所示:(路径:src/gausskernel/storage/cstore/custorage.cpp
)
void CStoreAllocator::InvalidColSpaceCache(const CFileNode& cnode)
{
// 获取列存空间缓存锁
LWLockAcquire(CStoreColspaceCacheLock, LW_EXCLUSIVE);
// 在列存空间缓存中查找并移除指定文件节点的缓存项
hash_search(CStoreColspaceCache, (void*)&cnode, HASH_REMOVE, NULL);
// 释放列存空间缓存锁
LWLockRelease(CStoreColspaceCacheLock);
}
CStoreAllocator::BuildColSpaceCacheForRel 函数用于构建关联关系的列存空间缓存,首先创建虚拟文件节点标签表示全局列存空间缓存,然后获取列存空间缓存锁。接着,检查关联关系的所有缓存项是否有效,如果无效则进行更新,遍历所有列,将它们的信息插入到列存空间缓存中,最后释放列存空间缓存锁。函数源码如下所示:(路径:src/gausskernel/storage/cstore/custorage.cpp
)
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);
// 检查关联关系的所有缓存项是否有效
// 如果无效,进行更新;如果有效,跳过
entry = (CStoreColFileDesc*)hash_search(CStoreColspaceCache, (void*)&tag, HASH_ENTER, &found);
if (entry == NULL)
ereport(PANIC, (errmsg("build global column space cache hash table failed")));
// 其他会话已经插入了关联关系的一些列或所有列到哈希表中
// !!!注意我们重用了变量 'found'
if (found) {
for (int i = 0; i < nColumn; i++) {
hash_search(CStoreColspaceCache, (void*)&cnodes[i], HASH_FIND, &found);
// 其他会话已经插入了一些列
// 这是不完整的
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);
}
CStoreAllocator::ColSpaceCacheExist 函数用于检查关联关系的列是否存在于列存空间缓存中,首先获取列存空间缓存锁,然后遍历所有列,检查它们是否在列存空间缓存中,最后释放列存空间缓存锁。函数源码如下所示:(路径:src/gausskernel/storage/cstore/custorage.cpp
)
bool CStoreAllocator::ColSpaceCacheExist(const CFileNode* cnodes, int nColumn)
{
// 标记是否在列存空间缓存中找到相关项
bool found = false;
// 获取列存空间缓存锁
LWLockAcquire(CStoreColspaceCacheLock, LW_SHARED);
// 遍历所有列,检查它们是否在列存空间缓存中
for (int i = 0; i < nColumn; i++) {
hash_search(CStoreColspaceCache, (void*)&cnodes[i], HASH_FIND, &found);
if (!found)
break;
}
// 释放列存空间缓存锁
LWLockRelease(CStoreColspaceCacheLock);
return found;
}
CStoreAllocator::GetExtendOffset 函数用于计算快速扩展的偏移量,首先获取快速扩展文件大小的段大小,然后计算当前最大偏移量对应的文件偏移量,并计算偏移量对段大小的余数。如果余数不为0,则将最大偏移量调整到下一个段的起始位置,最后返回调整后的最大偏移量作为扩展偏移量。函数源码如下所示:(路径:src/gausskernel/storage/cstore/custorage.cpp
)
/*
* @Description: 计算快速扩展偏移量
* @Param[IN] max_offset: 文件的最大CU指针
* @Return: 扩展偏移量
* @See also:
*/
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;
// 如果余数不为0,则将最大偏移量调整到下一个段的起始位置
if (remainder != 0) {
max_offset = max_offset + extend_segment - remainder;
}
// 返回调整后的最大偏移量作为扩展偏移量
return max_offset;
}
CStoreAllocator::CalcExtendSize 函数用于在分配空间时计算快速扩展的大小。首先获取快速扩展文件大小的段大小,然后计算剩余的扩展大小。如果 CU 大小小于等于剩余的扩展大小,则不需要进行快速扩展,返回 0。接着计算需要的文件大小,如果需要的文件大小小于等于段大小,则扩展大小设为段大小。最后,计算需要的文件大小对段大小的余数,计算扩展大小,使其为段大小的倍数,并返回扩展大小。函数源码如下所示:(路径:src/gausskernel/storage/cstore/custorage.cpp
)
/*
* @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 left_extend_size = extend_offset - cu_offset;
// 如果CU大小小于等于剩余的扩展大小,不需要进行快速扩展
if (cu_size <= left_extend_size) {
return 0;
}
// 计算需要的文件大小
uint32 need_file_size = cu_size - left_extend_size;
// 如果需要的文件大小小于等于段大小,扩展大小设为段大小
if (need_file_size <= extend_segment) {
return extend_segment;
}
// 计算需要的文件大小对段大小的余数
uint32 remainder = need_file_size % extend_segment;
// 计算扩展大小,使其为段大小的倍数
return need_file_size + extend_segment - remainder;
}
CStoreAllocator::recheck_max_cuid 函数用于在获取最大 CUID 后重新检查最大 CUID。首先,遍历所有索引,找到支持 CBTREE 和 CGIN 的索引。然后,如果没有支持 CBTREE 和 CGIN 的索引,直接返回原始的最大 CUID。接着,获取支持 CBTREE 和 CGIN 的索引中的最大 CUID,并释放索引列表。如果索引中的最大 CUID等于 MaxCUID,报错。最后,如果索引中的最大 CUID 大于原始最大 CUID ,更新缓存中的最大 CUID,并返回索引中的最大 CUID;如果索引中的最大 CUID 不大于原始最大CUID,则返回原始最大 CUID。函数源码如下所示:(路径:src/gausskernel/storage/cstore/custorage.cpp
)
/**
* 由于在获取最大CUID后我们释放了锁,我们会怀疑系统的最大CUID,因此在此重新检查位于索引中的最大CUID。
* 我们希望确保索引中没有更大的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;
// 遍历所有索引,找到支持CBTREE和CGIN的索引
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]);
}
}
// 如果没有支持CBTREE和CGIN的索引,则返回原始的最大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,报错
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;
}