从SearchCatCache的査找过程可以看到,由于CatCache只是一个缓存,因此即使在其中找不到某个元组也不能确定该元组是否存在于系统表中,还需要进一步扫描物理系统表来査找该元组。但是,如果在CatCache中为这个不存在的元组放置一个“负元组”则可避免这些额外的开销,因为每次査找同一个不存在的元组时将会得到这个“负元组”,此时即可判定要査找的元组并不存在于系统表中,因而不用进一步去扫描物理系统表确认,从而造成浪费。
要注意SearchCatCache的调用者不能修改返回的元组,并且使用完之后要调用ReleaseCatCache将其释放。
在CatCache中,部分匹配使用另外一个函数SearchCatcacheList,该函数产生一个CatCList结构。
typedef struct catclist { int cl_magic; /* for identifying CatCList entries */ #define CL_MAGIC 0x52765103 CatCache *my_cache; /* link to owning catcache */ dlist_node cache_elem; /* list member of per-catcache list */ int refcount; /* number of active references */ bool dead; /* dead but not yet removed? */ bool ordered; /* members listed in index order? */ short nkeys; /* number of lookup keys specified */ uint32 hash_value; /* hash value for lookup keys */ HeapTupleData tuple; /* header for tuple holding keys */ int n_members; /* number of member tuples */ CatCTup *members[FLEXIBLE_ARRAY_MEMBER]; /* members */ } CatCList;其中以链表的方式存放了在Cache中找到的元组。CatCLUt中的tuple字段记录的是一个“负元组”,它仅仅用来存放该CatCList所用到的键值,没有其他用途。CatCLUt中所包含的元组实际通过members字段表示的变长数据来记录,该数组的实际长度由n_membera字段记录。
SearchCatcacheList函数也会先计算査找键的Hash值,不过该函数首先会在CatCache的cc_lists字段中记录的CatCLlst链表中査找以前是否缓存了该査找键的结果,该査找过程将使用CatCList中tuple字段指向的元组与査找键进行Hash值比较。如果能够找到匹配该Hash值的CatCList,则cc_hits加1并将该CatCList移到ccjists所指向链表的头部,然后返回找到的CatCList。如果在CatCache中找不到CatCList,则扫描物理系统表并构建相应的CatCList并将它加人到ccjists所指向链表的头部。
同样,SearchCatcacheList的调用者不能修改返回的CatCList对象或者里面的元组,并且使用完之后要调用ReleaseCatCacheList将其释放。
最后,我们给一张SysCache相关的内存结构图吧:
2.RelCache对RelCache的管理比SysCache要简单许多,原因在于大多数时候RelCache中存储的RelationData 的结构是不变的,因此 PostgreSQL 仅用一个 Hash 表来维持这样一个结构。对 RelCache 的査找、插人、删除、修改等操作也非常简单。当需要打开一个表时,首先在RelCache中寻找该表的RelationData结构,如果没有找到,则创建该结构并加人到RelCache中。
和SysCache的初始化类似,RelCache的初始化同样也在InitPostgres函数中进行,同样分为两个阶段:RelationCachelnitialize 和 ReIationCacheInitializePhase2。
InitPostgres会调用函数RelationCachelnitialize对ReiCache进行第一阶段初始化,该函数将为该进程创建一个Hash表,其Hash键为表的OID,并设置Hash函数为oid_hash。Hash表的创建在函数hash_create中实现,该函数将创建一个标准Hash表结构体HTAB。
在完成了 Hash表的创建后,InitPostgres将调用RelationCachelnitializePhase进人第二阶段的初始化。该函数将必要的系统表和系统表索引的模式信息加人到RelCache中,这个过程通过函数RelationCacheInitializePhase2 来实现。这个阶段会确保 pg_class、pg_attribute、pg_proc、pg_type 四个系统表及相关索引的模式信息被加人到RelCache。在PostgreSQL中,使用一个文件pgJntemaLinit来记录系统表RelationData结构体,若该文件存在且未损坏,则将其内容直接读人RelCache中。否则,分别建立 pg_class、pg__atlribute、pg_proc、pg_type 及其索引的 RelationData 结构,加入到 RelCache上的Hash表中,并重写pg_internal.init文件。
当RelCache初始化完成后,我们就可以使用它来査找表的模式信息。RelCache的主要操作包括:
2.1 插人新打开的表