When accessing database tables, some information needs to be obtained from system tables. In order to improve retrieval efficiency, PostgreSQL provides caches, including SysCache and RelCache.
2.1 Some important data structures
SysCache is a CatCache array.
static CatCache *SysCache[SysCacheSize];
The SysCache structure formed by the above data structure is as follows:
2.2 Some important operation functions
Use the cacheinfo array to initialize the SysCache array, call the InitCatCache function to create and initialize the CacheHeader. But after this round of initialization is completed, there are no tuples in CatCache. Therefore, there is a second stage initialization.
cc_skey and cc_tupdesc will be filled
Find by different number of keys,they will invoke SearchCatCacheInternal function.
key1: For each iteration, the catcup structure is obtained. If we get catcup, similar to LRU, the node in the two-way list is deleted and inserted into the head (increase the priority of the next access).
key2: Negative tuple design: Solve the problem of cache penetration and improve the cache hit rate. Specifically, invalid tuples are also cached.
Tuple was not found in cache, so we have to try to retrieve it directly from the relation. If found, we will add it to the cache; if not found, we will add a negative cache entry instead.
static inline HeapTuple
SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1,
Datum v2,
Datum v3,
Datum v4)
{
// some initialization operations
if (unlikely(cache->cc_tupdesc == NULL))
CatalogCacheInitializeCache(cache);
arguments[0] = v1;
arguments[1] = v2;
arguments[2] = v3;
arguments[3] = v4;
// step2: get a hash bucket
hashValue = CatalogCacheComputeHashValue(cache, nkeys, v1, v2, v3, v4);
hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets);
bucket = &cache->cc_bucket[hashIndex];
// step3: Iterate over hash buckets
dlist_foreach(iter, bucket)
{
ct = dlist_container(CatCTup, cache_elem, iter.cur);
if (ct->dead)
continue; /* ignore dead entries */
if (ct->hash_value != hashValue)
continue; /* quickly skip entry if wrong hash val */
if (!CatalogCacheCompareTuple(cache, nkeys, ct->keys, arguments))
continue;
// LRU
dlist_move_head(bucket, &ct->cache_elem);
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
*/
if (!ct->negative)
{
ResourceOwnerEnlargeCatCacheRefs(CurrentResourceOwner);
ct->refcount++;
ResourceOwnerRememberCatCacheRef(CurrentResourceOwner, &ct->tuple);
CACHE_elog(DEBUG2, "SearchCatCache(%s): found in bucket %d",
cache->cc_relname, hashIndex);
return &ct->tuple;
}
else
{
CACHE_elog(DEBUG2, "SearchCatCache(%s): found neg entry in bucket %d",
cache->cc_relname, hashIndex);
return NULL;
}
}
// step4: cache miss
return SearchCatCacheMiss(cache, nkeys, hashValue, hashIndex, v1, v2, v3, v4);
}
static pg_noinline HeapTuple
SearchCatCacheMiss(CatCache *cache,
int nkeys,
uint32 hashValue,
Index hashIndex,
Datum v1,
Datum v2,
Datum v3,
Datum v4)
{
/* step1: init */
arguments[0] = v1;
arguments[1] = v2;
arguments[2] = v3;
arguments[3] = v4;
memcpy(cur_skey, cache->cc_skey, sizeof(ScanKeyData) * nkeys);
cur_skey[0].sk_argument = v1;
cur_skey[1].sk_argument = v2;
cur_skey[2].sk_argument = v3;
cur_skey[3].sk_argument = v4;
/*
* step2: tuple found
*/
relation = table_open(cache->cc_reloid, AccessShareLock);
scandesc = systable_beginscan(relation, cache->cc_indexoid, IndexScanOK(cache, cur_skey), NULL, nkeys, cur_skey);
ct = NULL;
while (HeapTupleIsValid(ntp = systable_getnext(scandesc)))
{
ct = CatalogCacheCreateEntry(cache, ntp, arguments, hashValue, hashIndex, false);
/* immediately set the refcount to 1 */
ResourceOwnerEnlargeCatCacheRefs(CurrentResourceOwner);
ct->refcount++;
ResourceOwnerRememberCatCacheRef(CurrentResourceOwner, &ct->tuple);
break; /* assume only one match */
}
systable_endscan(scandesc);
table_close(relation, AccessShareLock);
/*
* step3: tuple not found
* If tuple was not found, we need to build a negative cache entry
* containing a fake tuple.
*
* In bootstrap mode, we don't build negative entries.
*/
if (ct == NULL)
{
if (IsBootstrapProcessingMode())
return NULL;
ct = CatalogCacheCreateEntry(cache, NULL, arguments, hashValue, hashIndex, true);
return NULL;
}
// print something
return &ct->tuple;
}
3.RelCache
initializes the relation descriptor cache
load the shared relcache cache file
end