PostgreSQL的失效消息

举报
宁谷花雨 发表于 2022/08/31 14:48:16 2022/08/31
【摘要】 一、当sess1对t1表做ddl时,会对t1表上access exclusive锁,也就是在释放锁前,其他session都是无法对t1表进行操作的,也就是其他session都阻塞在了获取t1表锁的等待中;疑问:如果不是对表不是上的互斥锁,万一发了失效消息了,会有什么问题或出现什么异常?Sess1本地的ddl执行完成后,会通过SI机制发送失效消息给其他session,调用RegisterRel...

一、

sess1t1表做ddl时,会对t1表上access exclusive锁,也就是在释放锁前,其他session都是无法对t1表进行操作的,也就是其他session都阻塞在了获取t1表锁的等待中;

疑问:如果不是对表不是上的互斥锁,万一发了失效消息了,会有什么问题或出现什么异常?

Sess1本地的ddl执行完成后,会通过SI机制发送失效消息给其他session,调用RegisterRelcacheInvalidation函数,然后在sess1的事务提交时才释放t1的表锁;

 

RegisterRelcacheInvalidation -> AddRelcacheInvalidationMessage -> AddInvalidationMessage函数:

1、把失效消息(dbidrelid)加入到session的本地缓存transInvalInfo->CurrentCmdInvalidMsgs中;需要判断是否已存在CurrentCmdInvalidMsgs中了,不重复加入;

2、从CurTransactionContext中申请内存

typedef struct InvalidationChunk

{

struct InvalidationChunk *next; /* list link */

int                        nitems;                        /* # items currently stored in chunk */

int                        maxitems;                /* size of allocated array in this chunk */

SharedInvalidationMessage msgs[FLEXIBLE_ARRAY_MEMBER];

} InvalidationChunk;

 

在事务提交时,调用CommitTransaction -> AtEOXact_Inval函数在主事务结束时来处理无效消息排队。

1如果是事务提交了,我们必须将PriorCmdInvalidMgs列表中的消息发送到共享无效消息队列。请注意,在下一个事务开始时(通过AcceptInvalizationMessages),这些数据不仅会被其他后端读取,还会被我们自己的后端读取。这意味着,我们可以跳过当前CmdInvalidMsgs中仍然存在的任何内容的即时本地处理,并将该列表发送出去。

ProcessInvalidationMessagesMulti -> SendSharedInvalidMessages -> SIInsertDataEntries

2如果是事务回滚了,我们将中止,并且必须在PriorcMDInvalidMgs中本地处理消息。不需要将消息发送到其他后端,因为它们无论如何都不会看到我们更改的元组。我们也可以忘记CurrentCmdInvalidMsgs,因为这些更改尚未触及缓存。

3在任何情况下,将各种列表重置为空。我们不需要在这里物理释放内存,因为TopTransactionContext无论如何都将被清空。

 

 

二、

index_update_stats函数:当create indexreindex时,更新pg_class系统表

1

hasindex:将relhasindex设置为该值;

reltuples:如果>=0,则将reltuples设置为此值;否则不修改;

如果reltuples>=0,则relpages和relallvisible也会更新(使用RelationGetNumberOfBlocks函数和visibilitymap_count函数);

 

注意:此操作的一个重要副作用是,SI无效消息被发送到所有后端(包括本session的进程),导致使用新的数据修改 或者 更新relcache。

即使我们发现pg_class行中不需要更改,也必须发生这种情况。在更新heap row时,这确保其他后端能够了解新索引。

在更新索引时,这一点很重要,因为一些索引AMs期望在reindexflush relcache

 

疑问:

1)为什么index AMs接口期望在reindexflush relcache

 

2、对pg_class row的修改,使用的是非事务性的,覆盖就地更新(overwrite-in-place update)。这有几个原因:

1)在bootstrap mode模式下,我们别无选择——更新无法工作。

2)我们可以重新索引pg_class本身,在这种情况下,我们不能移动其pg_class行,因为CatalogTupleInsert/CatalogTupleUpdate可能还不知道所有索引(请参见reindex_relation)。

3)由于我们在parent rel上执行CREATE INDEX时仅使用共享锁(以允许并发索引创建),因此普通更新可能会在大约同时提交另一个CREATE INDEX时遭遇元组并发更新失败。我们可以通过让它们都执行非事务更新来避免这种情况(我们假设它们都将尝试将pg_class行更改为相同的内容,所以先执行哪个并不重要)。

 

注:使用非事务更新是安全的,即使我们的事务在提交之前仍然可能失败。即使没有索引,将relhasindex设置为true也是安全的(VACUUM最终会修复它)。当然,不管怎样,新的relpages和reltuples计数都是正确的。然而,如果调用者没有提供更新的reltuples计数,我们不想更改relpages(或relallvisible),因为这会使reltuples/RelPagess比率变大,这才是真正重要的。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。