前言

pg客户端通过libpq连接到服务器后,postmaster进程会为此客户端创建一个postgres进程,来处理此客户端发送来的各种请求。客户端发来的任何请求,都需要postgres进程通过多次扫描系统表来获取此次请求所需的数据。为了加快处理请求的效率,在每个postgres进程初始化的时候,会创建一系列的cache来缓存catalog数据。下面就分析一下pg内部cache的内核实现(代码版本为10.4)。[搬迁博客~~]

第一节 cache种类

cache的相关代码在src/backend/utils/cache目录下

attribute cache代码:attoptcache.c

系统表cache代码:catcache.c

事件触发器cache代码:evtcache.c

消息机制代码:inval.c

计划cache代码:plancache.c

普通表描述符cache代码:relcache.c

系统表oid映射cache代码:relmapper.c

普通表oid映射cache代码:relfilenodemap.c

表空间cache代码:spccache.c

系统表cache代码:syscache.c

系统表检索函数:lsyscache.c

typecache代码:typcache.c

文本检索cache代码:ts_cache.c

此文主要对catcache和relcache进行说明。

第二节 catcache数据结构

1.syscache标识枚举

在syscache.h头文件中定义了枚举类型SysCacheIdentifier,在这里枚举了syscache的所有的元素.

每一个元素都称为一个catcache,下面会讲到。

enum SysCacheIdentifier{AGGFNOID = 0,
...
USERMAPPINGUSERSERVER
# define SysCacheSize (USERMAPPINGUSERSERVER + 1)};

2.cache描述符结构体

struct cachedesc{
Oid            reloid;
Oid            indoid;
int            nkeys;
int            key[4];
int            nbuckets;
};

在1中每一个枚举值对应一个上述结构体

3.cacheinfo数组

static const struct cachedesc cacheinfo[] = {{AggregateRelationId,AggregateFnoidIndexId,{
Anum_pg_aggregate_aggfnoid,
0,
0
},
16
},
…
{UserMappingRelationId, /* USERMAPPINGUSERSERVER */
UserMappingUserServerIndexId,
2,
{
Anum_pg_user_mapping_umuser,
Anum_pg_user_mapping_umserver,
0,
0
},
2
}
};

1中每一个枚举在cacheinfo中都有一个自己的空间,枚举值就是数组下标

此数组对每一个syscache进行初步描述,有用户连接时就会通过这些描述初始化cache。

4.catcache结构体

typedef struct catcache{
int id;
slist_node cc_next;const char *cc_relname;
Oid cc_reloid;
Oid cc_indexoid;
bool cc_relisshared;
TupleDesc cc_tupdesc;
int cc_ntup;
int cc_nbuckets;
int cc_nkeys;
int cc_key[CATCACHE_MAXKEYS];
PGFunction cc_hashfunc[CATCACHE_MAXKEYS];ScanKeyData cc_skey[CATCACHE_MAXKEYS];
bool cc_isname[CATCACHE_MAXKEYS];
dlist_head cc_lists;dlist_head *cc_bucket;
# ifdef CATCACHE_STATS
long cc_searches;
long cc_hits;
long cc_neg_hits;
long cc_newloads;
long cc_invals;
long cc_lsearches;
long cc_lhits;
# endif
} CatCache;

5. CatCTup结构体

typedef struct catctup
{
int ct_magic;
# define CT_MAGIC 0x57261502
CatCache *my_cache;dlist_node cache_elem;
struct catclist *c_list;
int refcount;
bool dead;
bool negative;
uint32 hash_value;
HeapTupleData tuple;
} CatCTup;

6.CatCList结构体

typedef struct catclist{int cl_magic;
# define CL_MAGIC 0x52765103
CatCache *my_cache;
dlist_node cache_elem;
int refcount;
bool dead;
bool ordered;
short nkeys;
uint32 hash_value;
HeapTupleData tuple;
int n_members;
CatCTup *members[FLEXIBLE_ARRAY_MEMBER];
} CatCList;

7.CatCacheHeader结构体

typedef struct catcacheheader
{
slist_head ch_caches;
int ch_ntup;
} CatCacheHeader;

8.catcache相关结构关系图(下称图1)

SysCacheIdentifier枚举了所有的catcache的类型,而cacheinfo则是穷举了每一个cache的结构。 CatCacheHeader是catcache链表的头,将每一个catcache初始化后,就将这个cacatche的结构链接到此链表。catcache结构体中描述了每一个cacatche的信息。缓存的系统表tuple数据就存储在cc_bucket中,在catche初始化时将cc_bucket初始化为一个hash桶数组。在每一个hash桶中存储着一个CatTup链表,每一个CatTup结构就对应着一个系统表的tuple。 在catcache结构中还有一个cc_lists链表,此链表是为了缓存一个多结果的系统表扫描。 比如使用表名作为条件扫描pg_class,可能返回多条记录。返回的记录还是存储在cc_bucket哈希桶 数组中,同时将在哈希桶中的位置存储在CatCList结构的members数组中。

第三节 relcache cache结构

此cache主要对数据库中的各种表的表结构进行缓存。

使用static HTAB *RelationIdCache;结构存储所有的relcache cache。

使用RelationData存储每一个relcache。

第四节 relcache初始化

RelationCacheInitialize()函数,创建RelationIdCache结构

RelationCacheInitializePhase2()函数,如果从globle/pg_internal.init文件加载cluster共享表relcache数据失败,则通过函数formrdesc加载必要relcache。

RelationCacheInitializePhase3()函数,如果从dbdir/pg_internal.init文件加载非共享表(包括索引、视图、序列等)relcache数据失败,则通过函数formrdesc加载必要relcache;调用InitCatalogCachePhase2()来循环一遍catcache,将所有的系统表的relcache添加到RelationIdCache中去。

第五节 relcache的行为

经过relcache的初始化后,所有系统表的relation结构都已经加载到relcache中去了。

普通表的relation表结构会在第一次打开这个表的时候,将普通表的relation结构加载到relcache中(举例:如下函数调用过程)。

relation_open()->RelationIdGetRelation()->RelationBuildDesc()->RelationCacheInsert()

已经加载到relcache中的数据一般会伴随着这个用户连接直到用户连接关闭。但是当一个表的结构发生了变化,需要同步这个变化到所有的保持连接的用户连接。如下是这个同步的实现过程描述:

假设当前有c1,c2这两个用户一直保持连接,c1对表t1做了表结构的修改,那么c1的postgres进程会通过RegisterRelcacheInvalidation(Oid dbId, Oid relId)函数注册t1表的relcache失效的消息message1。c2连接在c1改动后的第一个用户查询执行之前会先处理message1(参考函数LocalExecuteInvalidationMessage),最终使用RelationClearRelation函数完成c2的postgres进程的t1的relcache的更新。

第六节 catcache初始化

PostgresMain->InitPostgres->InitCatalogCache->InitCatCache

->RelationCacheInitializePhase3->InitCatalogCachePhase2

在InitCatalogCache函数中循环调用InitCatCache(cacheinfo中的项作为初始化的参数)来初始化每一个catcache.

在InitCatCache中为每一个catcache申请CatCache结构体空间,并通过参数为结构体空间赋值。

每种catcache的hash桶的个数是一定的,此时会为hash桶(cc_bucket)申请空间。而cc_lists则会保持空值。

第七节 catcache的行为(保存和读取缓存数据)

经过catcache的初始化后,已经为所有的catcache初始化了用来缓存扫描数据的hash桶。下面就说明hash桶如何存取数据的。

1.对于返回结果单一的系统表的扫描任务最终落到SearchCatCache()函数身上:

首先,通过如下代码获取此检索条件的缓存结果所在的hash桶。

hashValue = CatalogCacheComputeHashValue(cache, cache->cc_nkeys, cur_skey);
hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets);
bucket = &cache->cc_bucket[hashIndex];

依次检索此hash桶中的元组,是否满足条件如果满足则返回,如果没有满足条件则继续执行。

dlist_foreach(iter, bucket)
{bool        res;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 *//** see if the cached tuple matches our key.*/HeapKeyTest(&ct->tuple,cache->cc_tupdesc,cache->cc_nkeys,cur_skey,res);if (!res)continue;dlist_move_head(bucket, &ct->cache_elem);if (!ct->negative){//在hash同种检索到了结果,将此元组返回ResourceOwnerEnlargeCatCacheRefs(CurrentResourceOwner);ct->refcount++;ResourceOwnerRememberCatCacheRef(CurrentResourceOwner, &ct->tuple);CACHE3_elog(DEBUG2, "SearchCatCache(%s): found in bucket %d",cache->cc_relname, hashIndex);#ifdef CATCACHE_STATScache->cc_hits++;
#endifreturn &ct->tuple;}else{//此时检索到的是一个negative entry,意味着此检索条件没有任何结果CACHE3_elog(DEBUG2, "SearchCatCache(%s): found neg entry in bucket %d",cache->cc_relname, hashIndex);#ifdef CATCACHE_STATScache->cc_neg_hits++;
#endifreturn NULL;}}

在catcache中没有找到适合的结果,那么就去扫描表数据

relation = heap_open(cache->cc_reloid, AccessShareLock);scandesc = systable_beginscan(relation,cache->cc_indexoid,IndexScanOK(cache, cur_skey),NULL,cache->cc_nkeys,cur_skey);ct = NULL;while (HeapTupleIsValid(ntp = systable_getnext(scandesc))){//通过此处将扫描到的元组,插入到cache->cc_bucket[hashIndex]哈希桶中ct = CatalogCacheCreateEntry(cache, ntp,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);heap_close(relation, AccessShareLock);//如果没有扫描到任何结果,为此检索条件创建一个negative entryif (ct == NULL){if (IsBootstrapProcessingMode())return NULL;ntp = build_dummy_tuple(cache, cache->cc_nkeys, cur_skey);ct = CatalogCacheCreateEntry(cache, ntp,hashValue, hashIndex,true);heap_freetuple(ntp);CACHE4_elog(DEBUG2, "SearchCatCache(%s): Contains %d/%d tuples",cache->cc_relname, cache->cc_ntup, CacheHdr->ch_ntup);CACHE3_elog(DEBUG2, "SearchCatCache(%s): put neg entry in bucket %d",cache->cc_relname, hashIndex);/** We are not returning the negative entry to the caller, so leave its* refcount zero.*/return NULL;}CACHE4_elog(DEBUG2, "SearchCatCache(%s): Contains %d/%d tuples",cache->cc_relname, cache->cc_ntup, CacheHdr->ch_ntup);CACHE3_elog(DEBUG2, "SearchCatCache(%s): put in bucket %d",cache->cc_relname, hashIndex);#ifdef CATCACHE_STATScache->cc_newloads++;
#endifreturn &ct->tuple;

2.对于返回结果为多个的的系统表的扫描任务最终落到SearchCatCacheList()函数身上:

SearchCatCache函数是将检索到的元组直接放到hash桶中。

SearchCatCacheList函数也是将检索到的多个元组放到hash桶中,但是每个检索的组织形式是CatCList结构,他将他所需要的元组的hash桶地址放到CatCList.members[]数组成员中.

实现思路一致,这里不再赘述。

与relcache同样,当在一个用户连接发生了某个系统表的属性值的变化的时候,这个用户连接会通过RegisterCatalogInvalidation()函数注册一个消息,其他保持连接的用户连接在下一次执行查询时会先处理此消息,清楚自己进程对这个catcache的缓存。

结语

本文讲述了relcache和catcache的初始化和存取数据的过程。在这个过程中还有很多没有探究清楚的地方留待研究:

①其他cache的作用过程

②postgres进程间传递invalid消息的机制

怎么调用获取被创建的预制体_PostgreSQL为每一个backend创建的cache相关推荐

  1. 怎么调用获取被创建的预制体_Uber 开源 Plato:扩展性极强的开发测试会话 AI 平台,可实现多智能体并行训练...

    雷锋网 AI 科技评论按:在过去的几十年中,智能会话系统已经发生了显著的变化,从关键字识别交互式语音应答(IVR)系统到跨平台智能个人助理,都在慢慢成为日常生活中不可或缺的一部分.在这样的背景环境下, ...

  2. 怎么调用获取被创建的预制体_Go 语言 Web 编程系列—— 获取用户请求数据(上)...

    0.GET/POST 请求数据 在 PHP 中,可以直接通过全局变量 $_GET 和 $_POST 快速获取 GET/POST 请求数据,GET 请求数据主要是 URL 查询字符串中包含的参数,以前面 ...

  3. Unity的使用(四):预制体,创建地形和地形导航

    前面介绍了Unity游戏引擎的基础功能,现在终于要进入到游戏开发中了.那么,一款游戏开发要有资源,这个一般是由美术提供的,我们只需要负责程序方面的事.那么,怎么将获得的资源应用起来呢? 一. 导入资源 ...

  4. 【Unity】由预制体实例获取预制体资源及预制体资源路径

    [Unity]由预制体实例获取预制体资源及预制体资源路径 Unity中一个预制体对象可能处于3种状态: 位于Project中,是PrefabAsset: 位于Scene中,是PrefabInstanc ...

  5. Unity快速入门教程-详解预制体(Prefab)及其实例化Instantiate

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一.预制体(Prefab)是什么? 1.1预制体简介 1.2预制体是什么样子的? 1.3预制体作用与用途 二.制作一个 ...

  6. cocoscreater预制体prefab全攻略

    预制体prefab 怎么创建预制体 预制体的作用 1.批量创建相同类型的节点 2.提前制作一些特定时机才需要展示出来的节点 什么是预制体,字面意思,还未使用前预先制作好的节点资源,属性等同于普通节点, ...

  7. Unity 一键制作预制体,一键修改预制体

    生成 .修改预制体属性 所用到的API PrefabUtility.SaveAsPrefabAsset(GameObject instanceRoot, string assetPath); Pref ...

  8. Unity游戏教程初步(六):迷宫算法与预制体

    目录 前言 项目需求 Prim算法生成迷宫 1 算法思路介绍 2 代码示例 3 用于测试的代码 预制体(预制件) 1 定义 2 构建迷宫墙体预制体 完善工程 前言 本节我们来做迷宫生成.生成迷宫的算法 ...

  9. 【Unity3D日常开发】生成预制体,并且预制体自动销毁

    推荐阅读 CSDN主页 GitHub开源地址 Unity3D插件分享 简书地址 我的个人博客 QQ群:1040082875 一.前言 今天有粉丝问我一个很简单的问题,如何生成预制体,并且让预制体自动销 ...

  10. Unity -- 预制体与变体

    1.一个游戏物体的图标为蓝色的时候,表明这个游戏物体是通过预制体创建的 当我们在修改预制体的时候,通过预制体创建的游戏物体也会同步修改(给预制体添加组件的时候,通过预制体创建的游戏对象也会添加对应的组 ...

最新文章

  1. P3196 [HNOI2008]神奇的国度(弦图的最小染色问题)
  2. post请求参数格式
  3. TLS/SSL握手过程
  4. 3说明书_知行翻译:做化妆品说明书翻译时,这3点需要谨记
  5. 计算机英文版个人简历发文,计算机个人简历英文_英文简历.doc
  6. GNU make manual 翻译( 一百六十二)
  7. JSP隐含变量和Spring中Model在EL表达式中的读取顺序
  8. MVC3 Razor 语法检查 -(转)
  9. 张季跃 201771010139《面向对象程序设计(java)》第十三周学习总结
  10. C#多线程的用法2-线程的生命周期
  11. 深入学习Java中的字符串,代码点和代码单元
  12. 基于PHP+Web+Mysql的在线问卷调查系统
  13. centos7 ies4linux,Ubuntu 7.10中通过IEs4linux安装IE6
  14. 联想笔记本无法开启无线网卡的方法
  15. JS 实现网页截屏五种方法
  16. QQ邮箱今天大面积出现无法下载附件的问题
  17. C. Qualification Rounds(状压思维)
  18. Q50 数组中重复的数字
  19. 个人附加作业 201421123108 王坤彬 网络1414
  20. 【号外20191127】微软刚刚更新了snipping tool

热门文章

  1. Android系统--输入系统(三)必备Linux知识_双向通信(scoketpair)
  2. 使用sshfs挂载服务器文件系统,用curlftpfs挂载FTP服务器
  3. 用SQL语句可以取出中文汉字的首字母
  4. [转]计算机视觉、机器学习相关领域论文和源代码大集合--持续更新
  5. 自然辩证法 题目2
  6. java obervable_设计模式--观察者模式初探和java Observable模式
  7. (6)机器学习_支持向量机
  8. CentOS7下安装JDK1.8过程记录
  9. ImageDataGenerator生成器的flow,flow_from_directory用法
  10. python中处理WordNet