PG高速缓冲区(Cache)——catcache构筑SysCache数据结构关系的骨干

  catcache代码位于src/backend/utils/cache/catcache.c,包含了对SysCache结构体的初始化和数据结构之间指针关系的链接以及操作。最重要的是提供了两个函数:精确匹配SearchCatCache和部分匹配SearchCatcacheList。提供的静态函数如下,这里不进行详细分析了。

 1 static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,  ScanKey cur_skey);
 2 static uint32 CatalogCacheComputeTupleHashValue(CatCache *cache,  HeapTuple tuple);
 3 
 4 #ifdef CATCACHE_STATS
 5 static void CatCachePrintStats(int code, Datum arg);
 6 #endif
 7 static void CatCacheRemoveCTup(CatCache *cache, CatCTup *ct);
 8 static void CatCacheRemoveCList(CatCache *cache, CatCList *cl);
 9 static void CatalogCacheInitializeCache(CatCache *cache);
10 static CatCTup *CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, uint32 hashValue, Index hashIndex, bool negative);
11 static HeapTuple build_dummy_tuple(CatCache *cache, int nkeys, ScanKey skeys);

  首先是提供两个初始化函数InitCatCache、InitCatCachePhase2、CatalogCacheInitializeCache。和创建内存上下文相关的函数CreateCacheMemoryContext。一些工具函数有CatalogCacheIdInvalidate、CatalogCacheCreateEntry、ResetCatalogCache、ResetCatalogCaches、CatalogCacheFlushRelation、ReleaseCatCache、build_dummy_tuple、ReleaseCatCacheList、PrepareToInvalidateCacheTuple

 

 

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

 1 HeapTuple SearchCatCache(CatCache *cache, Datum v1, Datum v2, Datum v3, Datum v4) {
 2     ScanKeyData cur_skey[4];
 3     uint32        hashValue;
 4     Index        hashIndex;
 5     Dlelem       *elt;
 6     CatCTup    *ct;
 7     Relation    relation;
 8     SysScanDesc scandesc;
 9     HeapTuple    ntp;
10 
11     if (cache->cc_tupdesc == NULL)
12         CatalogCacheInitializeCache(cache);
13 
14     memcpy(cur_skey, cache->cc_skey, sizeof(cur_skey));
15     cur_skey[0].sk_argument = v1;
16     cur_skey[1].sk_argument = v2;
17     cur_skey[2].sk_argument = v3;
18     cur_skey[3].sk_argument = v4;
19 
20     hashValue = CatalogCacheComputeHashValue(cache, cache->cc_nkeys, cur_skey);
21     hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets);
22 
23     for (elt = DLGetHead(&cache->cc_bucket[hashIndex]); elt;
24          elt = DLGetSucc(elt)) {
25         bool        res;
26         ct = (CatCTup *) DLE_VAL(elt);
27         if (ct->dead)
28             continue;            /* ignore dead entries */
29         if (ct->hash_value != hashValue)
30             continue;            
31         HeapKeyTest(&ct->tuple, cache->cc_tupdesc, cache->cc_nkeys, cur_skey, res);
32         if (!res)
33             continue;
34 
35         DLMoveToFront(&ct->cache_elem);
36         if (!ct->negative) {    ResourceOwnerEnlargeCatCacheRefs(CurrentResourceOwner);
37             ct->refcount++;
38 ResourceOwnerRememberCatCacheRef(CurrentResourceOwner, &ct->tuple);
39             return &ct->tuple;
40                 }else{
41             return NULL;
42         }
43     }
44 
45     relation = heap_open(cache->cc_reloid, AccessShareLock);
46     scandesc = systable_beginscan(relation,  cache->cc_indexoid, IndexScanOK(cache, cur_skey), SnapshotNow, cache->cc_nkeys, cur_skey);
47     ct = NULL;
48     while (HeapTupleIsValid(ntp = systable_getnext(scandesc)))
49     {
50         ct = CatalogCacheCreateEntry(cache, ntp,
51                                      hashValue, hashIndex,
52                                      false);
53         /* immediately set the refcount to 1 */
54         ResourceOwnerEnlargeCatCacheRefs(CurrentResourceOwner);
55         ct->refcount++;
56         ResourceOwnerRememberCatCacheRef(CurrentResourceOwner, &ct->tuple);
57         break;                    /* assume only one match */
58     }
59     systable_endscan(scandesc);
60     heap_close(relation, AccessShareLock);
61 
62     if (ct == NULL)
63     {
64         if (IsBootstrapProcessingMode())
65             return NULL;
66         ntp = build_dummy_tuple(cache, cache->cc_nkeys, cur_skey);
67         ct = CatalogCacheCreateEntry(cache, ntp,
68                                      hashValue, hashIndex,
69                                      true);
70         heap_freetuple(ntp);
71         return NULL;
72     }
73     return &ct->tuple;
74 }            

v1\v2\v3\v4都用于查找元组的键值,分别对应该Cache中记录的元组搜索键。在第一次进入该函数时,由于系统表还没有加载到SysCache的相关结构体中,需要调用一次CatalogCacheInitializeCache(cache)。

1)将v1\v2\v3\v4设置到cur_skey数组相应元素中,调用CatalogCacheComputeHashValue(cache, cache->cc_nkeys, cur_skey)计算哈希值,并使用HASH_INDEX宏计算哈希桶的索引,按照该索引得到该CatCache在cc_bucket数组中对应的Hash桶的下标。

2)遍历Hash桶链找到满足查询需求的Dlelem,并将其结构体中dle_val属性强制转换为CatCTup类型,使用HeapKeyTest测试缓存的tuple是否匹配输入的键。如果找到,使用DLMoveToFront将该元组放到Hash桶的首位。如果是正元组,refcount和cc_hits都加1,返回元组。如果为负元组,cc_neg_hits加1,返回NULL。

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

 扫描物理系统表的代码如下:

 1     relation = heap_open(cache->cc_reloid, AccessShareLock);
 2     scandesc = systable_beginscan(relation, cache->cc_indexoid,IndexScanOK(cache, cur_skey), SnapshotNow, cache->cc_nkeys, cur_skey);
 3     ct = NULL;
 4     while (HeapTupleIsValid(ntp = systable_getnext(scandesc)))
 5     {
 6         ct = CatalogCacheCreateEntry(cache, ntp, hashValue, hashIndex, false);
 7         /* immediately set the refcount to 1 */        ResourceOwnerEnlargeCatCacheRefs(CurrentResourceOwner);
 8         ct->refcount++;
 9         ResourceOwnerRememberCatCacheRef(CurrentResourceOwner, &ct->tuple);
10         break;                    /* assume only one match */
11     }
12     systable_endscan(scandesc);
13     heap_close(relation, AccessShareLock);

  部分匹配使用函数SearchCatcacheList,该函数产生一个CatCList结构,其中以链表的方式存放了在Cache中找到的元组。CatCList中的tuple字段记录的是一个负元组,它仅仅用来存放该CatCList所用到的键值,没有其他用户。CatCList中所包含的元组实际通过members字段表示的变长数据来记录,该数组的实际长度由n_members字段记录。SearchCatCacheList函数也会先计算查找键的Hash值,不过该函数首先会在CatCache的cc_lists字段中记录的CatCList链表中查找以前是否缓存了该查找键的结果,该查找过程将使用CatCList中tuple字段指向的元组与查找键进行Hash值比较。如果能够找到匹配该Hash值的CatCList,则cc_lhits加1并将该CatCList移到cc_lists所指向链表的头部,然后返回找到的CatCList。如果在CatCache中找不到CatCList,则扫描物理系统表并构建相应的CatCList并将它加入到cc_lists所指向链表的头部。

  1 CatCList *SearchCatCacheList(CatCache *cache, int nkeys, Datum v1, Datum v2, Datum v3, Datum v4) {
  2     ScanKeyData cur_skey[4];
  3     uint32        lHashValue;
  4     Dlelem       *elt;
  5     CatCList   *cl;
  6     CatCTup    *ct;
  7     List       *volatile ctlist;
  8     ListCell   *ctlist_item;
  9     int            nmembers;
 10     bool        ordered;
 11     HeapTuple    ntp;
 12     MemoryContext oldcxt;
 13     int            i;
 14 
 15     /*
 16      * one-time startup overhead for each cache
 17      */
 18     if (cache->cc_tupdesc == NULL)
 19         CatalogCacheInitializeCache(cache);
 20 #ifdef CATCACHE_STATS
 21     cache->cc_lsearches++;
 22 #endif
 23     /*
 24      * initialize the search key information
 25      */
 26     memcpy(cur_skey, cache->cc_skey, sizeof(cur_skey));
 27     cur_skey[0].sk_argument = v1;
 28     cur_skey[1].sk_argument = v2;
 29     cur_skey[2].sk_argument = v3;
 30     cur_skey[3].sk_argument = v4;
 31     lHashValue = CatalogCacheComputeHashValue(cache, nkeys, cur_skey);
 32 
 33     /* scan the items until we find a match or exhaust our list */
 34     for (elt = DLGetHead(&cache->cc_lists);
 35          elt;
 36          elt = DLGetSucc(elt)) {
 37         bool        res;
 38         cl = (CatCList *) DLE_VAL(elt);
 39 
 40         if (cl->dead)
 41             continue;            /* ignore dead entries */
 42 
 43         if (cl->hash_value != lHashValue)
 44             continue;            /* quickly skip entry if wrong hash val */
 45 
 46         /*
 47          * see if the cached list matches our key.
 48          */
 49         if (cl->nkeys != nkeys)
 50             continue;
 51         HeapKeyTest(&cl->tuple,
 52                     cache->cc_tupdesc,
 53                     nkeys,
 54                     cur_skey,
 55                     res);
 56         if (!res)
 57             continue;
 58 
 59         /*
 60          * We found a matching list.  Move the list to the front of the
 61          * cache's list-of-lists, to speed subsequent searches.  (We do not
 62          * move the members to the fronts of their hashbucket lists, however,
 63          * since there's no point in that unless they are searched for
 64          * individually.)
 65          */
 66         DLMoveToFront(&cl->cache_elem);
 67 
 68         /* Bump the list's refcount and return it */
 69         ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner);
 70         cl->refcount++;
 71         ResourceOwnerRememberCatCacheListRef(CurrentResourceOwner, cl);
 72 
 73         CACHE2_elog(DEBUG2, "SearchCatCacheList(%s): found list",
 74                     cache->cc_relname);
 75 
 76 #ifdef CATCACHE_STATS
 77         cache->cc_lhits++;
 78 #endif
 79 
 80         return cl;
 81     }
 82 
 83     /*
 84      * List was not found in cache, so we have to build it by reading the
 85      * relation.  For each matching tuple found in the relation, use an
 86      * existing cache entry if possible, else build a new one.
 87      *
 88      * We have to bump the member refcounts temporarily to ensure they won't
 89      * get dropped from the cache while loading other members. We use a PG_TRY
 90      * block to ensure we can undo those refcounts if we get an error before
 91      * we finish constructing the CatCList.
 92      */
 93     ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner);
 94 
 95     ctlist = NIL;
 96 
 97     PG_TRY();
 98     {
 99         Relation    relation;
100         SysScanDesc scandesc;
101 
102         relation = heap_open(cache->cc_reloid, AccessShareLock);
103 
104         scandesc = systable_beginscan(relation,
105                                       cache->cc_indexoid,
106                                       true,
107                                       SnapshotNow,
108                                       nkeys,
109                                       cur_skey);
110 
111         /* The list will be ordered iff we are doing an index scan */
112         ordered = (scandesc->irel != NULL);
113 
114         while (HeapTupleIsValid(ntp = systable_getnext(scandesc)))
115         {
116             uint32        hashValue;
117             Index        hashIndex;
118 
119             /*
120              * See if there's an entry for this tuple already.
121              */
122             ct = NULL;
123             hashValue = CatalogCacheComputeTupleHashValue(cache, ntp);
124             hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets);
125 
126             for (elt = DLGetHead(&cache->cc_bucket[hashIndex]);
127                  elt;
128                  elt = DLGetSucc(elt))
129             {
130                 ct = (CatCTup *) DLE_VAL(elt);
131 
132                 if (ct->dead || ct->negative)
133                     continue;    /* ignore dead and negative entries */
134 
135                 if (ct->hash_value != hashValue)
136                     continue;    /* quickly skip entry if wrong hash val */
137 
138                 if (!ItemPointerEquals(&(ct->tuple.t_self), &(ntp->t_self)))
139                     continue;    /* not same tuple */
140 
141                 /*
142                  * Found a match, but can't use it if it belongs to another
143                  * list already
144                  */
145                 if (ct->c_list)
146                     continue;
147 
148                 break;            /* A-OK */
149             }
150 
151             if (elt == NULL)
152             {
153                 /* We didn't find a usable entry, so make a new one */
154                 ct = CatalogCacheCreateEntry(cache, ntp,
155                                              hashValue, hashIndex,
156                                              false);
157             }
158 
159             /* Careful here: add entry to ctlist, then bump its refcount */
160             /* This way leaves state correct if lappend runs out of memory */
161             ctlist = lappend(ctlist, ct);
162             ct->refcount++;
163         }
164 
165         systable_endscan(scandesc);
166 
167         heap_close(relation, AccessShareLock);
168 
169         /*
170          * Now we can build the CatCList entry.  First we need a dummy tuple
171          * containing the key values...
172          */
173         ntp = build_dummy_tuple(cache, nkeys, cur_skey);
174         oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
175         nmembers = list_length(ctlist);
176         cl = (CatCList *)
177             palloc(sizeof(CatCList) + nmembers * sizeof(CatCTup *));
178         heap_copytuple_with_tuple(ntp, &cl->tuple);
179         MemoryContextSwitchTo(oldcxt);
180         heap_freetuple(ntp);
181 
182         /*
183          * We are now past the last thing that could trigger an elog before we
184          * have finished building the CatCList and remembering it in the
185          * resource owner.    So it's OK to fall out of the PG_TRY, and indeed
186          * we'd better do so before we start marking the members as belonging
187          * to the list.
188          */
189 
190     }
191     PG_CATCH();
192     {
193         foreach(ctlist_item, ctlist)
194         {
195             ct = (CatCTup *) lfirst(ctlist_item);
196             Assert(ct->c_list == NULL);
197             Assert(ct->refcount > 0);
198             ct->refcount--;
199             if (
200 #ifndef CATCACHE_FORCE_RELEASE
201                 ct->dead &&
202 #endif
203                 ct->refcount == 0 &&
204                 (ct->c_list == NULL || ct->c_list->refcount == 0))
205                 CatCacheRemoveCTup(cache, ct);
206         }
207 
208         PG_RE_THROW();
209     }
210     PG_END_TRY();
211 
212     cl->cl_magic = CL_MAGIC;
213     cl->my_cache = cache;
214     DLInitElem(&cl->cache_elem, cl);
215     cl->refcount = 0;            /* for the moment */
216     cl->dead = false;
217     cl->ordered = ordered;
218     cl->nkeys = nkeys;
219     cl->hash_value = lHashValue;
220     cl->n_members = nmembers;
221 
222     i = 0;
223     foreach(ctlist_item, ctlist)
224     {
225         cl->members[i++] = ct = (CatCTup *) lfirst(ctlist_item);
226         Assert(ct->c_list == NULL);
227         ct->c_list = cl;
228         /* release the temporary refcount on the member */
229         Assert(ct->refcount > 0);
230         ct->refcount--;
231         /* mark list dead if any members already dead */
232         if (ct->dead)
233             cl->dead = true;
234     }
235     Assert(i == nmembers);
236 
237     DLAddHead(&cache->cc_lists, &cl->cache_elem);
238 
239     /* Finally, bump the list's refcount and return it */
240     cl->refcount++;
241     ResourceOwnerRememberCatCacheListRef(CurrentResourceOwner, cl);
242 
243     CACHE3_elog(DEBUG2, "SearchCatCacheList(%s): made list of %d members",
244                 cache->cc_relname, nmembers);
245 
246     return cl;
247 }

 

 

 

AtEOXact_CatCache用于在main事务(commit或abort)结束后清理catcache

 1 void
 2 AtEOXact_CatCache(bool isCommit)
 3 {
 4 #ifdef USE_ASSERT_CHECKING
 5     if (assert_enabled)
 6     {
 7         CatCache   *ccp;
 8 
 9         for (ccp = CacheHdr->ch_caches; ccp; ccp = ccp->cc_next)
10         {
11             Dlelem       *elt;
12             int            i;
13 
14             /* Check CatCLists */
15             for (elt = DLGetHead(&ccp->cc_lists); elt; elt = DLGetSucc(elt))
16             {
17                 CatCList   *cl = (CatCList *) DLE_VAL(elt);
18 
19                 Assert(cl->cl_magic == CL_MAGIC);
20                 Assert(cl->refcount == 0);
21                 Assert(!cl->dead);
22             }
23 
24             /* Check individual tuples */
25             for (i = 0; i < ccp->cc_nbuckets; i++)
26             {
27                 for (elt = DLGetHead(&ccp->cc_bucket[i]);
28                      elt;
29                      elt = DLGetSucc(elt))
30                 {
31                     CatCTup    *ct = (CatCTup *) DLE_VAL(elt);
32 
33                     Assert(ct->ct_magic == CT_MAGIC);
34                     Assert(ct->refcount == 0);
35                     Assert(!ct->dead);
36                 }
37             }
38         }
39     }
40 #endif
41 }

 

  

posted @ 2020-11-20 13:23  肥叔菌  阅读(815)  评论(0编辑  收藏  举报