如何增加一个SysCache
如果你阅读过Postgres源码,就肯定会看到这样的用法:GetSysCacheOid1,SearchSysCache1之类的,望文生义,显然是通过系统缓存查找某些东西,如果一些常用的数据存放在内存中,肯定能极大的提高查找效率。
SysCache中缓存的是什么东西?
元数据,也就是系统表中数据。经常会用到的系统表,都有会缓存起来,提高性能,是不是每个系统表都有cache没仔细核查过。例如pg_class表中数据,第一次查某表,需求从磁盘上加载,第二次就直接从syscache中找到,但这只是表的属性信息,并非表的内容,如select *fromt1; 从t1这个名字找oid的过程可以从cache中找到,但t1的内容不行,t1在内容在另外的buffer中,而且该内容的share_buf是全局的,而元数据的cache是局部的。
另外,syscahce中缓存的对象单位是tupple,或者说叫元组,通俗点说就是行,按行缓存,对普通表内容是按页缓存在buf中。
SysCache是怎么找的?
来看一个例子:
Select *from t1;
内部会通过t1从Search_path找Schema name 去一起找这个表所在oid,有这oid才能找到表数据,找这个oid就会用到syscache,会走到如下接口:
Oid
get_relname_relid(constchar *relname, Oid relnamespace) //通过schema + name找oid
{
return GetSysCacheOid2(RELNAMENSP,
PointerGetDatum(relname),
ObjectIdGetDatum(relnamespace));
}
然后会进入下面函数:
Oid
GetSysCacheOid(intcacheId, //对应上面的 RELNAMENSP
Datum key1, //对面上面的relname
Datum key2, //对面上面的relnamespace
Datum key3,
Datum key4)
{
HeapTuple tuple;
Oid result;
tuple = SearchSysCache(cacheId, key1, key2, key3,key4); //从cache中找到tuple
if (!HeapTupleIsValid(tuple))
return InvalidOid;
result = HeapTupleGetOid(tuple); //取出元组中oid即可
ReleaseSysCache(tuple);
return result;
}
对于一个表来说,如果确定了模式名和表名,那显然能唯一确定一个表,对上面函数来说,RELNAMENSP已经确定了是从哪个系统表中找,并且也确定了唯一索引是什么,这个唯一索引有两个索引键,是relname和relnamespace,那么就能根据它们找出一个唯一tuple出来。
SearchCatCache函数逻辑相对复杂,不过多深入,大致流程介绍下:首先会按hash的方式去cache中找,通过hash计算出一个位置,因为hash算法不是唯一的,所以找到的是一个链表,然后遍历这个链表找到最终值,如果要缓存的元组相对较多,建议桶多搞点,毕竟现在memory is cheap,空间换时间合算。能找到并且没有失效就返回,找不到则打开系统表去找,找到后挂到cache上并返回,下次可直接找到。这里的链表是一个有意思的结构,存的并不是直接的tuple数据,而是一个包含有这个tuple数据的一个CatCTup对象的另外一个成员的地址。CatCTup也是一个有趣的结构,内部的 CT_MAGIC宏很魔幻。
如何增加一个SysCache?
四个要点:
1. 在SysCacheIdentifier中增加一个枚举值
2. 在cacheinfo数组增加一个元素
3. 在indexing.h中增加一个唯一索引
4. 在修改元组的地方更新。
实例分析:
下面以一个postgres10的分区增加的syscache作为具体的例子来详细介绍各个细节。
对于分区系统表pg_partitioned_table,在SysCacheIdentifier增加了枚举PARTRELID,注意它的位置,是排序的,名字自己取,注意望文生义即可。
在cacheinfo数组中,增加了元素
{PartitionedRelationId, /* PARTRELID */
PartitionedRelidIndexId,
1,
{
Anum_pg_partitioned_table_partrelid,
0,
0,
0
},
32
},
其中PartitionedRelationId 为要缓存的表oid, PartitionedRelidIndexId为要缓存的表上对应的唯一索引oid,1为前面的索引的索引键个数,下面的Anum_pg_partitioned_table_partrelid为对应的索引键为表中哪列,最多4个,再下面的32为hash桶个数,如果要缓存的元组多,不妨把这个桶个数设大点,提高效率。注意,这里在cacheinfo中新加的元素位置必须和SysCacheIdentifier中加的枚举位置相同,因为使用时就是以枚举作为数组下标引用。
indexing.h中的索引为PartitionedRelidIndexId,定义如下:
DECLARE_UNIQUE_INDEX(pg_partitioned_table_partrelid_index,3351, onpg_partitioned_table usingbtree(partrelid oid_ops));
#define PartitionedRelidIndexId 3351
也就是说,这是只有一个索引键的唯一索引,对应的列为partrelid列。
对于最后一步,在修改的地方更新cache,则在以下接口中有体现:
StorePartitionKey 函数,插入元组后,会调用CatalogUpdateIndexes更新,而对于删除元组,在RemovePartitionKeyByRelId中,删除之后会ReleaseSysCache 更新。
如果我们要增加一个cache,完成以上步骤即可。
补充一点
可能有你看到了,上面有几个字描红加大并且加粗了:没有失效,对于cache这是一个非常关键的点。假如有这样的场景:A会话中select 表t1,那么会加到cache中,B 会话中把这个表drop掉,再从A会话select这个表。显然,是查不到这个表,因为这个cache会失效。当有某个连接中有更新事务提交后,当前所有活跃连接在事务开始时以及语句开始时(注意,begin事务中每个语句开始时),都会主动去接收一个全局共享的消息队列,把一些其它连接中发生改变的对象对应的本地cache进行更新删除,再找时需要重新去表上找,cache中将不存在,保证一致性。
Postgres SysCache解析

本文详细介绍了Postgres数据库中SysCache的工作原理及实现方式,包括如何查找和增加SysCache的具体步骤,以及SysCache如何保证数据的一致性。
6038

被折叠的 条评论
为什么被折叠?



