PostgreSQL的失效消息
一、
当sess1对t1表做ddl时,会对t1表上access exclusive锁,也就是在释放锁前,其他session都是无法对t1表进行操作的,也就是其他session都阻塞在了获取t1表锁的等待中;
疑问:如果不是对表不是上的互斥锁,万一发了失效消息了,会有什么问题或出现什么异常?
Sess1本地的ddl执行完成后,会通过SI机制发送失效消息给其他session,调用RegisterRelcacheInvalidation函数,然后在sess1的事务提交时才释放t1的表锁;
RegisterRelcacheInvalidation -> AddRelcacheInvalidationMessage -> AddInvalidationMessage函数:
1、把失效消息(dbid、relid)加入到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 index或reindex时,更新pg_class系统表
1、
hasindex:将relhasindex设置为该值;
reltuples:如果>=0,则将reltuples设置为此值;否则不修改;
如果reltuples>=0,则relpages和relallvisible也会更新(使用RelationGetNumberOfBlocks函数和visibilitymap_count函数);
注意:此操作的一个重要副作用是,SI无效消息被发送到所有后端(包括本session的进程),导致使用新的数据修改 或者 更新relcache。
即使我们发现pg_class行中不需要更改,也必须发生这种情况。在更新heap row时,这确保其他后端能够了解新索引。
在更新索引时,这一点很重要,因为一些索引AMs期望在reindex后flush relcache。
疑问:
1)为什么index AMs接口期望在reindex后flush 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比率变大,这才是真正重要的。
- 点赞
- 收藏
- 关注作者
评论(0)