再说Postgres中的高速缓存(cache) (2)

在InitCatalogCache函数中实际只完成了SysCache初始化的第一个阶段,在稍后被调用的函数
RelationCachelnitializePhase2(负责 RelCache 的初始化)还将调用InitCalcachePhase2进行第二阶段也是最后的SysCache初始化工作。InitCatcachePhase2将依次完善SysCache数组中的CatCache结构,
主要是根据对应的系统表填充CatCache结构中的元组描述符(cc_tupdesc)、系统表名(cc_relname)、査找关键字的相关字段(cc_hashfunc、cc_isname、cc_skey)等。

SysCache数组初始化完成之后,CalCache内是没有任何元组的,但是随着系统运行时对于系统表元组的访问,CatCache中的系统表元组会逐渐增多。

1.2 CatCache中缓存元组的组织

CatCache中对缓存元组的组织可以看上图。CatCache中的cc_bucket是一个可变数组。cc_bucket数组中的每一个元素都表示一个Hash桶,元组的键值通过Hash函数可以映射到cc_bucket数组的下标。每一个Hash桶都被组织成一个双向链表(Dlist),其中的节点为Dlist_node类型。

struct dlist_node { dlist_node *prev; dlist_node *next; };

具有同一个hash值得元组被缓存在同一个hash桶中,每一个hash桶中的缓存元组都被先包装成Dlist_node结构并链接成一个链表。因此在査找某一个元组时,需要先计算其Hash键值并通过键值找到其所在的Hash桶,之后要遍历Hash桶的链表逐一比对缓存元组。为了尽最减少遍历Hash桶的代价,在组织Hash桶中链表时,会将这一次命中的缓存元组移动到链表的头部,这样下一次査找同一个元组时可以在尽可能少的时间内命中。

CatCache中的缓存元组将先被包装成CatCTup形式,然后加人到其所在Hash桶的链表中。在CatCTup中通过my_cache和cache_elem分别指向该缓存元组所在的CatCache及Hash桶链表中的节点。一个被标记为“死亡”的CalCTup(dead字段为真)并不会实际从CatCache中删除,但是在后续的査找中它不会被返回。“死亡"的缓存元组将一直被保留在CatCache中,直到没有人访问它,即其refcount变为0。但如果“死亡”元组同时也属于一个CatCList,则必须等到CatCList和CatCTup的refcount都变为0时才能将其从CatCache中清除。CatCTup的negative字段表明该缓存元组是否为一个“负元组”,所谓负元组就是实际并不存在于系统表中,但是其键值曾经用于在CatCache中进行査找的元组。负元组只有键值,其他属性均为空。负元组的存在是为了避免反复到物理表中去査找不存在的元组所带来的I/O开销。

catctup的数据结构如下:

typedef struct catctup { int ct_magic; /* for identifying CatCTup entries */ #define CT_MAGIC 0x57261502 CatCache *my_cache; /* link to owning catcache */ dlist_node cache_elem; /* list member of per-bucket list */ struct catclist *c_list; /* containing CatCList, or NULL if none */ int refcount; /* number of active references */ bool dead; /* dead but not yet removed? */ bool negative; /* negative cache entry? */ uint32 hash_value; /* hash value for this tuple's keys */ HeapTupleData tuple; /* tuple management header */ } CatCTup; 1.3 在CatCache中査找元组

在CatCache中査找元组有两种方式:精确匹配部分匹配。前者用于给定CatCache所需的所有键值,并返回CatCache中能完全匹配这个键值的元组;而后者只需要给出部分键值,并将部分匹配的元组以一个CatCList的方式返回。

精确匹配査找由函数SearchCatCache函数实现,其函数原型如下:

SearchCatcache (CatCache* Cache .Datum vl, Datum v2 .Datum v3, Datum v4)

其中,vl、v2、v3和v4都用于査找元组的键值,分别对应该Cache中记录的元组搜索键。可以看到,SearehCatcache最多可以使用4个属性的键值进行査询,4个参数分别对应该CatCache数据结构中CC_key字段定义的査找键。

SearchCatCache需要在一个给定的CatCache中査找元组,为了确定要在哪个CatCache中进行査找,还需要先通过CacheHdr遍历SysCache中所有的CatCache结构体,并根据査询的系统表名或系
统表OID找到对应的CatCache。

SearchCatCache在给定的CatCache中査找元组的过程如下:

1) 对所査找元组的键值进行Hash,按照Hash值得到该CatCache在cc_bucket数组中对应的
Hash桶的下标。

2) 遍历Hash桶链找到满足査询需求的Dlist(CatCTup类型的属性cache_elem就是Dlist的节点dlist_node)并遍历,CatCTup中的HeapTupleData就是要査找的元组头部。另外,还要将该dlist_node移到链表头
部并将CatCache的cc_hits(命中计数器)加1。

3) 如果在Hash桶链中无法找到满足条件的元组,则需要进一步对物理系统表进行扫描,以确认要査找的元组是确实不存在还是没有缓存在CatCache中。如果扫描物理系统表能够找到满足条件的元组,则需要将该元组包装成Dlelem之后加人到其对应的Hash桶内链表头部。如果在物理系统表中找不到要査找的元组,则说明该元组确实不存在,此时构建一个只有键值但没有实际元组的“负元组”,并将它包装好加人到Hash桶内链表头部。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wppsfg.html