代码位置:
src/backend/postmaster/pgstat.c
所有的统计信息收集器的东西放在一个大的丑陋的文件。
TODO:
- Separate collector, postmaster and backend stuff into different files. 单独收集,postmaster和backend内容到不同的文件。
- Add some automatic call for pgstat vacuuming. 添加一些自动调用 pgstat vacuuming
- Add a pgstat config column to pg_database, so this entire thing can be enabled/disabled on a per db basis. 将pgstat config 列添加到pg_database,因此可以在每个DB的基础上启用/禁用整个事件。
从 postmaster 启动时或存在的 collector 进程死掉是调用。试图启动一个新的statistics collector。
int
pgstat_start(void)
{
time_t curtime;
pid_t pgStatPid;
/* 检查套接字是否存在,否则 pgstat_init 失败,而且无能为力。 */
if (pgStatSock == PGINVALID_SOCKET)
return 0;
/* 如果上次 collector 启动后不久,就什么都不做。这是一个安全阀,为了防止collector 在启动的时候死掉,连续重复尝试,注意,因为我们将从 postmaster main loop 重新调用,我们稍后会得到另一个机会启动。 */
curtime = time(NULL);
if ((unsigned int) (curtime - last_pgstat_start_time) <
(unsigned int) PGSTAT_RESTART_INTERVAL)
return 0;
last_pgstat_start_time = curtime;
/* collector 的岔口 */
#ifdef EXEC_BACKEND
switch ((pgStatPid = pgstat_forkexec()))
#else
switch ((pgStatPid = fork_process()))
#endif
{
case -1:
ereport(LOG,
(errmsg("could not fork statistics collector: %m")));
return 0;
#ifndef EXEC_BACKEND
case 0:
/* postmaster子进程 ... */
InitPostmasterChild();
/* 关闭 postmaster's 套接字 */
ClosePostmasterPorts(false);
/* 把我们连接到 postmaster 的共享内存 */
dsm_detach_all();
PGSharedMemoryDetach();
/* pgstat collector 主函数 */
PgstatCollectorMain(0, NULL);
break;
#endif
default:
return (int) pgStatPid;
}
/* shouldn't get here */
return 0;
}
****************************************************************************************************************************
/* 启动统计收集器过程。这是postmaster child process 的代码。
* The argc/argv parameters are valid only in EXEC_BACKEND case.
*/
NON_EXEC_STATIC void
PgstatCollectorMain(int argc, char *argv[])
{
int len;
PgStat_Msg msg;
int wr;
/* 忽略所有信号,通常绑定到postmaster 的某些行动,除了SIGHUP 和 SIGQUIT 。注意,我们不需要 SIGUSR1 处理器来支持闩锁操作,因为我们只使用本地锁存器。 */
pqsignal(SIGHUP, pgstat_sighup_handler);
pqsignal(SIGINT, SIG_IGN);
pqsignal(SIGTERM, SIG_IGN);
pqsignal(SIGQUIT, pgstat_exit);
pqsignal(SIGALRM, SIG_IGN);
pqsignal(SIGPIPE, SIG_IGN);
pqsignal(SIGUSR1, SIG_IGN);
pqsignal(SIGUSR2, SIG_IGN);
pqsignal(SIGCHLD, SIG_DFL);
pqsignal(SIGTTIN, SIG_DFL);
pqsignal(SIGTTOU, SIG_DFL);
pqsignal(SIGCONT, SIG_DFL);
pqsignal(SIGWINCH, SIG_DFL);
PG_SETMASK(&UnBlockSig);
/* 通过ps 确定自己 */
init_ps_display("stats collector", "", "", "");
/* 读取现有的统计文件或初始化统计数据为零。 */
pgStatRunningInCollector = true;
pgStatDBHash = pgstat_read_statsfiles(InvalidOid, true, true);
/* 循环处理消息,直到我们得到 SIGQUIT 或检测到我们的父进程 postmaster 死掉。出于性能原因,我们不希望在每个消息之后都执行ResetLatch/WaitLatch ;相反,只有在 recv() 无法获得消息之后才这样做。这实际上意味着,如果后端疯狂的发送给我们的东西,我们将不会注意到postmaster 进程死掉,直到事情有点松懈,这似乎是好的。为了做到这一点,我们有一个内部循环,只要 recv() 成功就可以迭代。我们确实在内部循环中识别 got_SIGHUP ,这意味着此类中断将得到处理,但是锁存器直到下一次动作中断时才会被清除。*/
for (;;)
{
/* 清除任何已挂起的唤醒 */
ResetLatch(MyLatch);
/* 如果我们从postmaster得到 SIGQUIT,退出 */
if (need_exit)
break;
/* 只要我们继续获取消息,没有收到 need_exit 设置,就会一直loop。 */
while (!need_exit)
{
/* 如果我们从 postmaster 收到 SIGHUP ,重新加载配置 */
if (got_SIGHUP)
{
got_SIGHUP = false;
ProcessConfigFile(PGC_SIGHUP);
}
/* 如果一个新的请求已被现有文件不满足,则写入stats 文件。 */
if (pgstat_write_statsfile_needed())
pgstat_write_statsfiles(false, false);
/* 尝试接收和处理消息。由于套接字被设置为非阻塞模式,所以这不会阻塞。在Windows上,我们不得不强制与 pgwin32_recv 协作,尽管以前在套接字上使用pg_set_noblock()。这是非常不好的的,应该有一天被修复。 */
#ifdef WIN32
pgwin32_noblock = 1;
#endif
len = recv(pgStatSock, (char *) &msg,
sizeof(PgStat_Msg), 0);
#ifdef WIN32
pgwin32_noblock = 0;
#endif
if (len < 0)
{
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
break; /* out of inner loop */
ereport(ERROR,
(errcode_for_socket_access(),
errmsg("could not read statistics message: %m")));
}
/* 我们忽略比我们的普通的报头小的消息。 */
if (len < sizeof(PgStat_MsgHdr))
continue;
/* 接收的长度必须与标题中的长度相匹配。 */
if (msg.msg_hdr.m_size != len)
continue;
/* 我们接受这个消息。处理它。 */
switch (msg.msg_hdr.m_type)
{
case PGSTAT_MTYPE_DUMMY:
break;
case PGSTAT_MTYPE_INQUIRY:
pgstat_recv_inquiry((PgStat_MsgInquiry *) &msg, len);
break;
case PGSTAT_MTYPE_TABSTAT:
pgstat_recv_tabstat((PgStat_MsgTabstat *) &msg, len);
break;
case PGSTAT_MTYPE_TABPURGE:
pgstat_recv_tabpurge((PgStat_MsgTabpurge *) &msg, len);
break;
case PGSTAT_MTYPE_DROPDB:
pgstat_recv_dropdb((PgStat_MsgDropdb *) &msg, len);
break;
case PGSTAT_MTYPE_RESETCOUNTER:
pgstat_recv_resetcounter((PgStat_MsgResetcounter *) &msg,
len);
break;
case PGSTAT_MTYPE_RESETSHAREDCOUNTER:
pgstat_recv_resetsharedcounter(
(PgStat_MsgResetsharedcounter *) &msg,
len);
break;
case PGSTAT_MTYPE_RESETSINGLECOUNTER:
pgstat_recv_resetsinglecounter(
(PgStat_MsgResetsinglecounter *) &msg,
len);
break;
case PGSTAT_MTYPE_AUTOVAC_START:
pgstat_recv_autovac((PgStat_MsgAutovacStart *) &msg, len);
break;
case PGSTAT_MTYPE_VACUUM:
pgstat_recv_vacuum((PgStat_MsgVacuum *) &msg, len);
break;
case PGSTAT_MTYPE_ANALYZE:
pgstat_recv_analyze((PgStat_MsgAnalyze *) &msg, len);
break;
case PGSTAT_MTYPE_ARCHIVER:
pgstat_recv_archiver((PgStat_MsgArchiver *) &msg, len);
break;
case PGSTAT_MTYPE_BGWRITER:
pgstat_recv_bgwriter((PgStat_MsgBgWriter *) &msg, len);
break;
case PGSTAT_MTYPE_FUNCSTAT:
pgstat_recv_funcstat((PgStat_MsgFuncstat *) &msg, len);
break;
case PGSTAT_MTYPE_FUNCPURGE:
pgstat_recv_funcpurge((PgStat_MsgFuncpurge *) &msg, len);
break;
case PGSTAT_MTYPE_RECOVERYCONFLICT:
pgstat_recv_recoveryconflict((PgStat_MsgRecoveryConflict *) &msg, len);
break;
case PGSTAT_MTYPE_DEADLOCK:
pgstat_recv_deadlock((PgStat_MsgDeadlock *) &msg, len);
break;
case PGSTAT_MTYPE_TEMPFILE:
pgstat_recv_tempfile((PgStat_MsgTempFile *) &msg, len);
break;
default:
break;
}
} /* 内部消息处理循环结束 */
/* Sleep 直到有事情要做 */
#ifndef WIN32
wr = WaitLatchOrSocket(MyLatch,
WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_SOCKET_READABLE,
pgStatSock, -1L,
WAIT_EVENT_PGSTAT_MAIN);
#else
/* Windows,至少在Windows Server 2003 R2体现,有时会丢失 FD_READ 事件。唤醒并重试 recv() 修复,所以不要无限期sleep。这只是开头,但是除非有人想调试那里到底发生了什么,否则这是我们所能做的最好的事情。2秒的超时时间与我们在9.2之前的行为相匹配,并且需要足够短,以免引发 backend_read_statsfile 的“使用陈旧的统计数据”投诉。 */
wr = WaitLatchOrSocket(MyLatch,
WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_SOCKET_READABLE | WL_TIMEOUT,
pgStatSock,
2 * 1000L /* msec */ ,
WAIT_EVENT_PGSTAT_MAIN);
#endif
/* 如果 postmaster 死了,将紧急救助。这是为了避免对所有 postmaster 的子进程进行手工清理。 */
if (wr & WL_POSTMASTER_DEATH)
break;
} /* end of outer loop */
/* 在下一次启动时保存最终统计数据以重用。 */
pgstat_write_statsfiles(true, true);
exit(0);
}