表的模式信息存放在系统表中,因此要访问表,就需要首先在系统表中取得表的模式信息。对于一个PostgreSQL系统来说,对于系统表和普通表模式的访问是非常频繁的。为了提高这些访问的效率,PostgreSQL设立了高速缓存(Cache)来提高访问效率。
Cache中包括一个系统表元组Cache(SysCache)和一个表模式信息Cache(RelCache)。其中:
SysCache中存放的是最近使用过的系统表的元组;
RelCache中包含所有最近访问过的表的模式信息(包含系统表的信息)。RelCache中存放的不是元组,而是RelationData数据结构,每一个RelationData结构表示一个表的模式信息,这些信息都由系统表元组中的信息构造而来。
值得注意的是,两种Cache都不是所有进程共享的。每一个PostgreSQL的进程都维护着自己的SysCache和RelCache。
1.SysCache
SysCache主要用于缓存系统表元组。从实现上看SysCache就是一个数组,数组的长度为预定义的系统表的个数。在PostgreSQL9.5.6中实现了55个系统表,因此SysCache数组具有55个元素,每个元素的数据结构为CatCache,该结构体内使用Hash来存储被缓存的系统表元组,每一个系统表唯一地对应一个SysCache数组中的CatCache结构。每个CatCache都有若干个(不超过4个)査找关键字,这些关键字及其组合可以用来在CatCache中查找系统表元组,在初始化数据集簇时会在这些关键字上为系统表创建索引。
其中涉及到的数据类型的简要意义如下,我们先有个印象,以免后面遇到了感到奇怪和陌生:
/*
*
struct catctup:
individual tuple in the cache.
*
struct catclist:
list of tuples matching a partial key.
*
struct catcache:
information for managing a cache.
*
struct catcacheheader: information for managing all the caches.
*/
1.1 SysCache初始化
在Postgres进程初始化时(在InitProgres中),将会对SysCache进行初始化。SysCache的初始化实际上是填充SysCache数组中每个元素的CatCache结构的过程,主要任务是将査找系统表元组的关键字信息写入SysCache数组元素中。这样通过指定的关键字可以快速定位到系统表元组的存储位置。
CatCache的数据结构如下:
typedef struct catcache
{
int
id;
/* cache identifier --- see syscache.h */
slist_node cc_next;
/* list link */
const char *cc_relname;
/* name of relation the tuples come from */
Oid
cc_reloid;
/* OID of relation the tuples come from */
Oid
cc_indexoid; /* OID of index matching cache keys */
bool
cc_relisshared; /* is relation shared across databases? */
TupleDesc cc_tupdesc;
/* tuple descriptor (copied from reldesc) */
int
cc_ntup;
/* # of tuples currently in this cache */
int
cc_nbuckets; /* # of hash buckets in this cache */
int
cc_nkeys;
/* # of keys (1..CATCACHE_MAXKEYS) */
int
cc_key[CATCACHE_MAXKEYS];
/* AttrNumber of each key */
PGFunction cc_hashfunc[CATCACHE_MAXKEYS]; /* hash function for each key */
ScanKeyData cc_skey[CATCACHE_MAXKEYS];
/* precomputed key info for
* heap scans */
bool
cc_isname[CATCACHE_MAXKEYS]; /* flag "name" key columns */
dlist_head cc_lists;
/* list of CatCList structs */
#ifdef CATCACHE_STATS
long
cc_searches; /* total # searches against this cache */
long
cc_hits;
/* # of matches against existing entry */
long
cc_neg_hits; /* # of matches against negative entry */
long
cc_newloads; /* # of successful loads of new entry */
/*
* cc_searches - (cc_hits + cc_neg_hits + cc_newloads) is number of failed
* searches, each of which will result in loading a negative entry
*/
long
cc_invals;
/* # of entries invalidated from cache */
long
cc_lsearches; /* total # list-searches */
long
cc_lhits;
/* # of matches against existing lists */
#endif
dlist_head *cc_bucket;
/* hash buckets */
} CatCache;
在SysCache.c文件中已经将所有系统表的CatCache信息存储在一个名为cacheinfo的静态数组
中,每个系统表的CatCache信息用一个数组元素来描述,其数据类型为cachedesc:
struct cachedesc
{
Oid
reloid;
/* OID of the relation being cached */
Oid
indoid;
/* OID of index relation for this cache */
int
nkeys;
/* # of keys needed for cache lookup */
int
key[4];
/* attribute numbers of key attrs */
int
nbuckets;
/* number of hash buckets for this cache */
};
在Postgres进程初始化时,会调用InitCatalogCache函数对SysCache数组进行初始化,并建立由CacheHdr记录的CatCache链表。
InitCatalogCache函数中对SysCache的初始化主要分为以下儿个步骤:
1)根据cacheinfo为SysCache数组分配空间,这里将SysCache的长度设置为和cacheinfo数组相同。
2)循环调用InitCatcache函数根据cacheinfo中的每一个元素生成CatCache结构并放人SysCache数组的对应位置中。InitCatcache每调用一次将处理一个cachedesc结构。该函数根据cachedesc中要求的Hash桶的数量
为即将建立的CatCache结构分配内存,并根据cachedesc结构中的信息填充CatCache的各个字段。
最后将生成的CatCache链接在CacheHdr所指向的链表的头部。