postgresql中如何新增加一个SysCache

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

如何增加一个SysCache

如果你阅读过Postgres源码,就肯定会看到这样的用法:GetSysCacheOid1SearchSysCache1之类的,望文生义,显然是通过系统缓存查找某些东西,如果一些常用的数据存放在内存中,肯定能极大的提高查找效率。

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中将不存在,保证一致性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值