forked from huawei/openGauss-server
755 lines
33 KiB
C++
755 lines
33 KiB
C++
/*
|
||
* 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列文件的最大CUID(Column 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()
|
||
{}
|
||
//获取下一个可用的CUID(Column 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;
|
||
}
|