5. cstore缓存机制
考虑到cstore列存储格式主要面向只读查询居多的OLAP类业务,因此openGauss提供只读的共享CU缓冲区机制。
openGauss中CU只读共享缓冲区的结构如图4-30所示。和行存储页面粒度的共享缓冲区类似,最上层为共享哈希表,哈希表键值为CU的slot类型、relfilenode、colid、cuid、cupointer构成的五元组,哈希表的记录值为该CU对应的缓冲区槽位slot id(对应行存储共享缓区的buffer id)。在全局CacheDesc数组中,用CacheDesc结构体记录与slot id对应的缓存槽位的状态信息(对应行存储缓冲区的BufferDesc结构体)。在共享CU数组中,用CU结构体记录与slot id对应的缓存CU的结构体信息。
与行存储固定的页面大小不同,不同CU的大小可能是不同的(行存储页面大小都是8 K),因此上述CU槽位只记录指向实际内存中CU数据的指针。另一方面为了保证共享内存大小可控,通过另外的全局变量来记录已经申请的有效槽位中所有CU的大小总和。
图4-30 CU只读共享缓存结构示意图
CU只读共享缓冲区的工作机制如图4-31所示。
(1) 当从磁盘读取一个CU放如Cache Mgr时,需要从FreeSlotList里拿到一个free slot(空闲槽位)存放CU,然后插入到哈希表中。
(2) 当FreeSlotList为NULL的时,需要根据LRU算法淘汰掉一个slot(槽位),释放CU data占的内存,减小CU总大小计数,并从哈希表中删除,然后存放新的CU,再插入哈希表中。
(3) 缓存大小可以配置。如果内存超过设置的大小,需要淘汰掉适量的slot,并释放CU data占用的内存。
(4) 支持缓存压缩态的CU或解压态的CU两种模式,可以通过配置文件修改,同时只能存在一种模式。
图4-31 CU只读共享缓存读取示意图
与CU只读共享缓冲区相关的关键数据结构代码如下:
RelFileNodeOld m_rnode;
int m_colId;
int32 m_CUId;
uint32 m_padding;
CUPointer m_cuPtr;
} CUSlotTag;
/* slot id哈希表键值主要部分,各个成员的含义从命名中可以清晰看出 */
typedef struct DataSlotTag {
DataSlotTagKey slotTag;
CacheType slotType;
} DataSlotTag;
/* slot id哈希表键值结构体,成员包括CUSlotTag与slot类型(CU、OBS外表等) */
typedef struct CacheLookupEnt {
CacheTag cache_tag;
CacheSlotId_t slot_id;
} CacheLookupEnt;
/* slot id哈希表记录结构体,成员包括哈希表键值和对应的slot id */
typedef struct CacheDesc {
uint16 m_usage_count;
uint16 m_ring_count;
uint32 m_refcount;
CacheTag m_cache_tag;
CacheSlotId_t m_slot_id;
CacheSlotId_t m_freeNext;
LWLock *m_iobusy_lock;
LWLock *m_compress_lock;
/*The data size in the one slot.*/
int m_datablock_size;
bool m_refreshing;
slock_t m_slot_hdr_lock;
CacheFlags m_flag;
} CacheDesc;
/* CU共享缓冲区槽位状态结构体,其中m_usage_count、m_ring_count为LRU淘汰算法需要的使用计数,m_refcount为判断能否淘汰的被引用计数,m_freeNext指向下一次空闲的slot槽位(如果本槽位在free list中的话,否则m_freeNext恒等于-2),m_iobusy_lock为I/O并发控制锁,m_compress_lock为压缩并发控制锁,m_datablock_size为CU实际数据的大小,m_slot_hdr_lock保护整个CacheDesc的并发读写操作,m_flag表示槽位状态(包括全新、有效、freelist中、空闲、I/O中、错误等状态)*/