PostgreSQL中AutoVacLauncherMain函数的实现逻辑是怎样的

分类:编程技术 时间:2024-02-20 15:53 浏览:0 评论:0
0
本文介绍《PostgreSQL中AutoVacLauncherMain函数的实现逻辑是什么》的相关知识。在实际案例操作过程中,很多人都会遇到这样的困境。接下来就让小编带领大家学习一下如何处理吧。这些情况!我希望你能仔细阅读并学到一些东西!

1.数据结构

宏定义

#define GetProcessingMode() Mode#define SetProcessingMode(mode) \ do { AssertArg((mode) == BootstrapProcessing || \ (模式) == InitProcessing || \         (模式) == NormalProcessing); \模式=(模式); \ } while(0)

2、源码解读

AutoVacLauncherMain函数,autovacuum进程主循环。

/* * autovacuum启动器进程的主循环。 am_autovacuum_launcher = true; /* 通过 ps 识别自己 */ //Process ID init_ps_display(pgstat_get_backend_desc(B_AUTOVAC_LAUNCHER), "", "", ""); ereport(DEBUG1, (errmsg("自动真空启动器) 已启动"))); if (PostAuthDelay) pg_usleep(PostAuthDelay * 1000000L); //设置进程模式 SetProcessingMode(InitProcessing); /* * 设置信号处理程序。我们对数据库的操作与常规后端非常相似,因此我们使用相同的信号处理。请参阅 * tcop/postgres.c 中的等效代码。 * 设置信号控制器。 * autovacuum的执行与普通后台进程类似,因此采用相同的信号控制机制。 * 参考tcop/postgres.c中的代码。 */ pqsignal(SIGHUP, av_sighup_handler); pqsignal(SIGINT, StatementCancelHandler); pqsignal(SIGTERM, avl_sigterm_handler) ); pqsignal(SIGQUIT, Quickdie); //创建SIGALRM控制器InitializeTimeouts(); /* 建立 SIGALRM 处理程序 */ pqsignal(SIGPIPE, SIG_IGN);//忽略 SIGPIPE pqsignal(SIGUSR1, procsignal_sigusr1_handler); pqsignal(SIGUSR2, avl_sigusr2_handler); pq信号(SIGFPE、FloatExceptionHandler); pqsignal(SIGCHLD, SIG_DFL); /* 早期初始化 */ //基本初始化 BaseInit(); /* * 在共享内存中创建每个后端 PGPROC 结构,但 * EXEC_BACKEND 情况除外,这是在 SubPostmasterMain 中完成的。在使用 LWLock 之前,我们必须这样做(并且在 EXEC_BACKEND 情况下,我们已经必须使用 LWLock 做一些事情)。 */#ifndef EXEC_BACKEND InitProcess();#endif //初始化 InitPostgres(NULL, InvalidOid, NULL, InvalidOid, NULL, false); //设置处理模式 SetProcessingMode(NormalProcessing); /* * 创建一个我们将在其中完成所有工作的内存上下文。我们这样做是为了在错误恢复期间重置上下文,从而避免 * 可能的内存泄漏。 * 创建内存上下文。 * 这样做的原因是因为我们可以在错误恢复时重置上下文,避免内存泄漏。 */ AutovacMemCxt = AllocSetContextCreate(TopMemoryContext, * "Autovacuum Launcher", ALLOCSET_DEFAULT_SIZES);内存上下文tSwitchTo(AutovacMemCxt); /** 如果遇到异常,处理将在此处恢复。 * 如果发生异常,请从此处恢复。 * 此代码是 PostgresMain 错误恢复的精简版本。 */ if (sigsetjmp(local_sigjmp_buf, 1) != 0) { /* 由于不使用 PG_TRY,必须手动重置错误堆栈 */ //由于不使用 PG_TRY,因此必须在此处手动重置错误。 error_context_stack = N ULL; /* 清理时防止中断 */ // 清理期间禁用中断 HOLD_INTERRUPTS(); /* 忘记任何挂起的 QueryCancel 或超时请求 */ // 忽略所有 QueryCancel 或超时请求disable_all_timeouts(false);查询取消挂起 = false; /* 第二次避免竞争条件 */        /* 将错误报告到服务器日志 */ //将日志记录在服务器日志中。发出错误报告(); /* 中止当前事务以进行恢复 */   //中止当前事务以准备恢复 AbortCurrentTransaction(); /* * 释放任何其他资源,供 ca 使用我们没有处于*交易中。 */        LWLockReleaseAll(); pgstat_report_wait_end();中止缓冲区IO();解锁缓冲区(); /* 这可能是死代码,但我们要安全:*/      //这可能是死代码,但它可以确保安全 if (AuxProcessResourceOwner) ReleaseAuxProcessResources(false); AtEOXact_Buffers(假); AtEOXact_SMgr(); AtEOXact_Files(假); AtEOXact_HashTables(假); /* * 现在返回正常的顶级上下文并清除 ErrorContext 以供下次使用。 * 现在切换回正常的顶层上下文,并清除下次启动时的错误上下文 */sp; MemoryContextSwitchTo(AutovacMemCxt);刷新错误状态(); /* 刷新顶级上下文中所有泄漏的数据 */ //刷新顶级上下文中所有泄漏的数据 MemoryContextResetAndDeleteChildren(AutovacMemCxt); /* 不要留下指向已释放内存的悬空指针 */ //不要留下指向空闲内存的悬空指针 DatabaseListCxt = ​​NULL; dlist_init(&DatabaseList); /* * 麦当然 pgstat 也认为我们的统计数据已经消失。注意:我们*不能在这里使用autovac_refresh_stats。 * 确保 pgstat 也认为我们的统计数据已被丢弃。 * 注意:这里不能使用autovac_refresh_stats。 */ pgstat_clear_snapshot(); /* 现在我们可以再次允许中断 */ //可以允许中断 RESUME_INTERRUPTS(); /* 如果处于关闭模式,则无需进一步操作; just goaway */ //如果处于shutdown模式,则无需继续后续工作,跳转至shutdown   if (got_SIGTERM) goto shutdown; * 发生任何错误后至少休眠 1 秒。我们不想尽快填充错误日志。 */ pg_usleep(1000000L); } /* 我们现在可以处理 ereport(ERROR) */ // 现在可以处理 ereport(ERROR) PG_exception_stack = &local_sigjmp_buf; /* 调用rebuild_database_list之前必须解锁信号 */  //调用rebuild_database_list之前不能阻塞信号 PG_SETMASK(&UnBlockSig); /* * 设置始终安全的搜索路径。启动器无法连接到数据库,* 所以这没有效果。 * 设置始终安全的搜索路径。 * Launcher无法连接数据库,所以没有影响。 */ SetConfigOption("search_path", "", PGC_SUSET, PGC_S_OVERRIDE); /* * 在 autovac 进程中强制 Zero_damaging_pages 关闭,即使它是在 postgresql.conf 中设置的。我们真的不想要这样一个危险的选择; * 非交互式应用。 * 在autovacuum过程中,zero_damaging_pages被强制关闭,即使在配置文件中将该参数设置为ON。 * 我们真的不希望在没有交互的情况下应用如此危险的选项。 */ SetConfigOption("zero_damaging_pages", "false", PGC_SUSET, PGC_S_OVERRIDE); /* * 强制关闭可设置的超时,以避免这些设置阻止执行定期维护。实施。 */ SetConfigOption("statement_timeout", "0", PGC_SUSET, PGC_S_OVERRIDE); SetConfigOption("lock_timeout", "0", PGC_SUSET, PGC_S_OVERRIDE); SetConfigOption("idle_in_transaction_session_timeout", "0", PGC_SUSET, PGC_S_OVERRIDE); /* * 强制 default_transaction_isolation 为 READ COMMITTED。我们不想 * 支付可序列化模式的开销,也不想增加任何导致死锁或延迟其他事务的风险。 * 强制 default_transaction_isolation 为 READ COMMITTED . * 我们不想增加串行化模式的负担,也不想增加死锁或其他事务延迟的风险。 */ SetConfigOption("default_transaction_isolation", "read commited",              PGC_SUSET,    PGC_S_OVERRIDE);      /*    *在紧急模式下,只需启动一个工作线程(除非请求关闭)   * 然后离开。                           ) */ if (!AutovacuimingActive ()) {if (!Got_sigterm) do_start_Worker (); proc_exit (0);/*Done*/} Autovacuumshmem-> AV_LAUNCHERPID = MyProcPid; /* * 创建初始数据库列表。我们希望此列表保持的不变量是它按 next_time 递减排序。一旦条目更新到更高的时间,它就会将移到前面(这是*正确的,因为唯一的操作是将autovacuum_naptime添加到*条目中,并且时间总是增加)。 * 创建初始化数据库链表。 * 我们希望这个链表保持不变的是它是按照next_time递减排序的。 * 一旦条目更新到更高的时间,它将被移动到前面 * (这很好,因为唯一的操作是将 autovacuum_naptime 添加到条目中,并且时间将始终递增)。 */rebuild_database_list(InvalidOid); /* 循环直到关闭请求 */ //循环直到请求关闭 while (!got_SIGTERM) { struct timeval nap; TimestampTz 当前时间 = 0;布尔可以_启动; /* * 这个循环与 WaitLatch 的正常使用有点不同, * 因为我们想在第一次启动子进程之前休眠。所以它是 WaitLatch,然后是 ResetLatch,然后检查 * 唤醒条件。首先是waitlatch,然后是resetlatch,然后检查等待唤醒条件*/Launcher_define_sleep (! DLIST_IS_EMPTY (& Autovacuumshmem-> AV_FREEWORKERS), False, & Nap);/// *  * 等到午睡时间到期或者我们收到某种类型的信号(所有 * 信号处理程序将通过调用 SetLatch 唤醒我们)。处理器将通过调用 SetLatch 来唤醒进程 */ (void) WaitLatch(MyLatch,                                 (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L),                                            WAIT_EVENT_AUTOVACUUM_MAIN); sleep */ //在sleep过程中,进程会捕获相关中断。 ProcessCatchupInterrupt() ; /* 正常关机情况 */           //关机信号                                                                                                  /; {// 标志信号 GOT_SIGHUP = FALSE; ProcessConfigfile (PGC_SIGHUP);/*Shutdown Requested in Config File?*/// 在配置文件中,Shutdown? if (!Auto!Auto VacuumingActive ()) break;/*重新平衡,以防默认成本参数发生变化 */       //如果默认的cost参数改变,会自动平衡。 LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE); autovac_balance_cost(); LWLockRelease(AutovacuumLock); /* 重建链表以防午睡时间改变 */ //如果午睡时间改变,则重建链表rebuild_database_list(InvalidOid); } /* * 一个工作程序已完成,或 postmaster 发出启动失败信号 * 一个工作程序已完成,或 postmaster 信号异常且工作程序无法启动 */ if (got_SIGUSR2) { //SIGUSR2 信号 got_SIGUSR2 = false ; /* 如果需要,重新平衡成本限制 */ 平衡成本限制 if (AutoVacuumShmem->av_signal[AutoVacRebalance]) {bsp; lwlockacquire(AutovacuumLock,LW_EXClusive); Autovacuumshmem-> AV_SIGNAL [AutovacreBalance] = false; Autovac_cost(); lw LockRelease (AutoVacuumLock);} if (AutoVacuumshmem-> AV_SIGNAL [AutovacForkFaled]) {/**如果 postmaster 无法启动新的工作线程,我们会休眠 * 一会儿并重新发送信号。内wworker 的 * 状态仍然在内存中,所以这就足够了。之后,我们重新启动主循环。 * 如果postmaster无法启动新的worker,则休眠一段时间,然后重新发送信号。 * 新的worker的状态还在内存中,这样处理就可以了; * 之后,重新启动主循环。 * * XXX 我们应该限制重试的次数吗? * 我认为这没有多大意义,因为将来 * 一个工人的启动将继续以同样的方式失败。 * 是否需要增加重试次数限制? XXX* 我们不想太敏感,因为工作人员在未来启动时将继续以同样的方式失败。 /* 1s */ SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_WORKER);继续; p; * 在尝试启动工人之前,我们需要检查一些条件。首先,我们需要确保有可用的worker * 插槽。二、w我们需要确保启动时没有其他工作线程失败。 * 在尝试启动worker之前,需要检查一些条件。 * 首先需要确保有可用的worker slot;其次,需要保证worker启动时不会出现异常。 */ */ current_time = GetCurrentTimestamp(); LWLockAcquire(AutovacuumLock, LW_SHARED); can_launch = !dlist_is_empty(&AutoVacuumShmem->av_freeWorkers); if (AutoVacuumShmem->av_startingWorker != NULL) { int waittime; WorkerInfo工人 = AutoVacuumShmem->av_startingWorker; /* * 当另一个工作线程仍在启动时(或者在启动时失败),我们无法启动另一个工作线程,所以只需再睡一会儿;一旦准备好,那个工人就会再次叫醒我们。 * 然而,我们只会等待 autovacuum_naptime 秒(最多 60 秒)才能发生这种情况。请注意,连接到特定数据库的失败*在这里不是问题,*因为工作线程会自行删除在尝试连接之前从startingWorker * 指针开始。 * postmaster 检测到的问题(如 fork() 失败)也会以不同的方式报告和处理。唯一可能导致此代码触发的问题是 AutoVacWorkerMain 前面部分中的错误,* 在工作人员从 *startingWorker 指针中删除 WorkerInfo 之前。 * 当一个worker还在启动时,新的worker无法启动,因此会休眠一段时间; * 一旦准备好,另一个工作人员就会叫醒我们。 * 只需等待autovacuum_naptime参数设置时间(单位秒)(最大为60s)。*注意这里连接具体数据库没有问题,因为当worker尝试连接时,被StartingWorker指针销毁。如果fork()失败,将会报告并进行不同的处理。 * 这里唯一的问题是这里的处理逻辑可能会导致AutoVacWorkerMai失败。N的提前上升触发错误,*并且真正的worker要清除通过StartingWorker指针获取workerInfo。*/waittime = min(Autovacuum_naptime, 60)*1000; if (TimestampdifernceExceeeds (worker-> wi _launchTime, Current_time, Waittime) {lwlockRelease (AutovacuumLock);            LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE); ingWorker 在交换锁后仍然无效,*我们假设它与我们上面看到的相同(所以我们不' t * 重新检查启动时间)。 Worker;           worker->wi_dboid = InvalidOid;worker->wi_sharedrel = false;worker->wi_proc = NULL;worker->wi_launchtime = 0;dlist_push_head(&AutoVacuumShmem->av_freeWorkers,                  &工人->wi_links) ;bsp; elog (警告,“worker 启动时间太长;已取消”);}} Else Can_launch = False;} // 释放 lwlockRelease (AutoVacuumlock);/*Shav 或独占*//*如果可以的话' T 做任何事情,就回去睡觉 */  //什么也做不了,继续睡觉 if (!can_launch) continue; /* We're OK to start a new worker */ //你现在可以启动一个新的worker如果(dlist_is_empty(&DatabaseList))                                                                                                         列表为空时的特殊情况:启动一个工作线程马上。 * 这涵盖了初始情况,即 pgstats 中没有数据库(因此列表为空)。请注意,launcher_define_sleep 中的约束使我们不能太快地启动工作程序(当列表为空时,每个 autovacuum_naptime 最多启动一次)。 * 链表为空时的特殊情况:正确启动一个worker。 * 这涵盖了pgstats中没有数据库的初始情况(因此链表为空)。 * 请注意,launcher_define_sleep 中的约束会阻止我们过快地启动工作进程             *(当链表为空时,最多一次 autovacuum_naptime)。* 首先,远程adl_next_worker,我们从列表的尾部获取我们的数据库。 * 因为rebuild_database_list首先构造的是最远的adl_next_worker的链表,所以                                                                                                                                                                因为rebuild_database_list首先构造了与最远的adl_next_worker的链表,                                                                                                                                如果 next_worker 是现在或者是在*过去的bsp; * 启动worker,如果next_worker及时或者已经成为过去时。 current_time, 0)) launch_worker(current_time); /* 从 autovac 启动器正常退出在这里 */ //定期 exit.shutdown: ereport(DEBUG1, (errmsg("autovacuum 启动器正在关闭"))); AutoVacuumShmem->av_launcherpid = 0; proc_exit(0); /* did */}

《PostgreSQL中的AutoVacLauncherMain函数的实现逻辑是怎样的》内容介绍到这里。感谢您的阅读。如果您想了解更多行业资讯,可以关注网站,小编将为大家输出更多优质实用文章!

1. 本站所有资源来源于用户上传或网络,仅作为参考研究使用,如有侵权请邮件联系站长!
2. 本站积分货币获取途径以及用途的解读,想在本站混的好,请务必认真阅读!
3. 本站强烈打击盗版/破解等有损他人权益和违法作为,请各位会员支持正版!
4. 编程技术 > PostgreSQL中AutoVacLauncherMain函数的实现逻辑是怎样的

用户评论