聊聊Postgres中的IPC之SI Message Queue (2)

SI message 队列的初始化。这个是由postmaster在启动服务器时做的,作为共享内存的一部分,由postmaster初始化。此时,SI message为空,因为此时还没有Invalid Message产生。

每个backend初始化(我们知道这些Invalid Message是由于我执行了SQL文对数据库进行了修改才产生的,那么很显然我们执行SQL文的途径是前端发送SQL文,后端启动一个backend进程去处理)时,需要初始化自己的共享内存并且向SI message注册自己。注册的目的有两个,一个是声明自己作为Invalid Message的生产者的身份,另一个表示自己也需要接受其他backend的Invalid Message。

每个backend执行SQL文,产生Invalid Message,其他backend接收该Invalid Message,当然,这个过程复杂点,会在后面细说。那么每个backend接收和发送Invalid Message的时机是什么呢?

当然啦,你每次执行SQL的时候,是一个好时机,在执行SQL文的开头和结尾,backend都会去check SI message队列中的无效消息。以下是调用栈:

exec_simple_query ->start_xact_command ->StartTransactionCommand /* 事务开始 ->StartTransaction ->AtStart_Cache ->AcceptInvalidationMessages ->ReceiveSharedInvalidMessages /* consume SI message ->SIGetDataEntries -> do query ->finish_xact_command ->CommitTransactionCommand /* 事务结束 ->CommitTransaction ->AtEOXact_Inval ->SendSharedInvalidMessages /* send SI message ->SIInsertDataEntries ->SICleanupQueue

那么,难道我不执行SQL文,我的backend就不刷新无效消息么?

我们看一段注释:

/* * Because backends sitting idle will not be reading sinval events, we * need a way to give an idle backend a swift kick in the rear and make * it catch up before the sinval queue overflows and forces it to go * through a cache reset exercise. This is done by sending * PROCSIG_CATCHUP_INTERRUPT to any backend that gets too far behind. * * The signal handler will set an interrupt pending flag and will set the * processes latch. Whenever starting to read from the client, or when * interrupted while doing so, ProcessClientReadInterrupt() will call * ProcessCatchupEvent(). */

没有错,要是某个backend长时间不读取SI Message或者backend落后太多,超过了SI Message队列可以接受的最大长度,那么就向该backend发送SIGUSR1,唤醒该backend让其做适当的操作。

3.实现细节

为了实现SI Message的这一功能,PostgreSQL在共享内存中开辟了shmInvalBuffer记录系统中所发出的所有Invalid Message以及所有进程处理无消息的进度。shmInvalBuffer是一个全局变量,其数据结构如下:

typedef struct SISeg { /* * General state information */ int minMsgNum; /* oldest message still needed */ int maxMsgNum; /* next message number to be assigned */ int nextThreshold; /* # of messages to call SICleanupQueue */ int lastBackend; /* index of last active procState entry, +1 */ int maxBackends; /* size of procState array */ slock_t msgnumLock; /* spinlock protecting maxMsgNum */ /* * Circular buffer holding shared-inval messages */ SharedInvalidationMessage buffer[MAXNUMMESSAGES]; /* * Per-backend invalidation state info (has MaxBackends entries). */ ProcState procState[FLEXIBLE_ARRAY_MEMBER]; } SISeg;

在shmInvalBuffer中,Invalid Message存储在由Buffer字段指定的定长数组中(其长度MAXNUMMESSAGES预定义为4096),该数组中每一个元素存储一个Invalid Message,也可以称该数组为无效消息队列。无效消息队列实际是一个环状结构,最初数组为空时,新来的无效消息从前向后依次存放在数组中,当数组被放满之后,新的无效消息将回到Buffer数组的头部开始插人。minMsgNum字段记录Buffer中还未被所有进程处理的无效消息编号中的最小值,maxMsgNum字段记录下一个可以用于存放新无效消息的数组元素下标。实际上,minMsgNum指出了Buffer中还没有被所有进程处理的无效消息的下界,而maxMsgNum则指出了上界,即编号比minMsgNmn小的无效消息是已经被所有进程处理完的,而编号大于等于maxMsgNum的无效消息是还没有产生的,而两者之间的无效消息则是至少还有一个进程没有对其进行处理。因此在无效消息队列构成的环中,除了 minMsgNum和maxMsgNum之间的位置之外,其他位置都可以用来存放新增加的无效消息。

PostgreSQL在shmInvalBuffer中用一个ProcState数组(procState字段)来存储正在读取无效消息的进程的读取进度,该数组的大小与系统允许的最大进程数MaxBackends有关,在默认情况下这个
数组的大小为100 (系统的默认最大进程数为100,可在postgresql.conf中修改)。ProcState的结构如数据结构如下所示。

/* Per-backend state in shared invalidation structure */ typedef struct ProcState { /* procPid is zero in an inactive ProcState array entry. */ pid_t procPid; /* PID of backend, for signaling */ PGPROC *proc; /* PGPROC of backend */ /* nextMsgNum is meaningless if procPid == 0 or resetState is true. */ int nextMsgNum; /* next message number to read */ bool resetState; /* backend needs to reset its state */ bool signaled; /* backend has been sent catchup signal */ bool hasMessages; /* backend has unread messages */ /* * Backend only sends invalidations, never receives them. This only makes * sense for Startup process during recovery because it doesn't maintain a * relcache, yet it fires inval messages to allow query backends to see * schema changes. */ bool sendOnly; /* backend only sends, never receives */ /* * Next LocalTransactionId to use for each idle backend slot. We keep * this here because it is indexed by BackendId and it is convenient to * copy the value to and from local memory when MyBackendId is set. It's * meaningless in an active ProcState entry. */ LocalTransactionId nextLXID; } ProcState;

在ProcSlate结构中记录了PID为procPid的进程读取无效消息的状态,其中nextMsgNum的值介于 shrolnvalBuffer 的 minMsgNum 值和 maxMsgNum 值之间。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpjgxx.html