PostgreSQL "terminated by signal 9: Killed" servera žurnālā gandrīz vienmēr nozīmē, ka Linux kodolam beidzās atmiņa un tā OOM killer izvēlējās kādu postgres procesu. PostgreSQL pats nenokrita; kaut kas ārpus tā nosūtīja SIGKILL. Šo incidentu sāpīgu padara tas, kas notiek pēc tam: viena miruša backend dēļ postmaster izmet visas pārējās sesijas un palaiž avārijas atjaunošanu.
Kā izskatās žurnāli
Servera pusē redzēsiet šādu secību. PostgreSQL 17 un vecākās versijās pirmajā rindā ir server process; PostgreSQL 18 tā vietā nosauc procesa tipu, piemēram, client backend (PID ...).
LOG: server process (PID 48213) was terminated by signal 9: Killed
DETAIL: Failed process was running: SELECT c.region, count(DISTINCT o.customer_id) ...
LOG: terminating any other active server processes
LOG: all server processes terminated; reinitializing
LOG: database system was interrupted; last known up at ...
LOG: database system was not properly shut down; automatic recovery in progress
LOG: redo starts at ...
LOG: database system is ready to accept connections
Rinda DETAIL: Failed process was running ir visa incidenta noderīgākā: tā parāda vaicājumu, kas izpildījās nogalinātajā procesā. Lietotnes katrā tajā brīdī atvērtajā sesijā redz citu ziņojumu:
FATAL: terminating connection because of crash of another server process
DETAIL: The postmaster has commanded this server process to roll back the current
transaction and exit, because another server process exited abnormally and
possibly corrupted shared memory.
HINT: In a moment you should be able to reconnect to the database and repeat your command.
Nākamās sekundes vai minūtes jauni savienojumi tiek noraidīti ar FATAL: the database system is in recovery mode. Ja vēlaties detalizētāk izprast starta fāzi un "redo in progress" rindas, par to rakstījām rakstā PostgreSQL "Connection refused".
Kāpēc viens nogalināts vaicājums apstādina visu
PostgreSQL backend procesi koplieto atmiņu: shared_buffers, slēdzeņu tabulas, WAL buferus. Ar signālu 9 nogalinātam procesam nav iespējas atbrīvot slēdzenes vai pabeigt rakstīšanu šajā koplietotajā apgabalā, tāpēc postmaster tam vairs nevar uzticēties. Tas liek visiem pārējiem procesiem beigt darbu, no jauna inicializē koplietoto atmiņu un atskaņo WAL no pēdējā kontrolpunkta. Tā ir apzināta rīcība. Tā ir cena par to, lai neriskētu ar bojātiem datiem.
Vai serveris restartējas automātiski, nosaka restart_after_crash, kas pēc noklusējuma ir on. Dokumentācijā norādīts, ka to var būt lietderīgi izslēgt, ja PostgreSQL darbojas klasterprogrammatūras pārvaldībā, lai tālāko rīcību izlemtu HA rīks.
Ja OOM killer izvēlas pašu postmaster, automātiskas atjaunošanas nav: instance paliek izslēgta, līdz to palaiž no jauna. Tāpēc dokumentācija iesaka aizsargāt postmaster (skatīt zemāk).
1. solis: pierādiet, ka tas bija OOM killer
Signālu 9 var nosūtīt arī cilvēks ar kill -9, watchdog vai orķestrators. Pirms maināt iestatījumus, pārbaudiet kodola žurnālu:
# kas nosūtīja signālu 9? kodola žurnāls parāda, vai tas bija OOM killer
journalctl -k --since "2 hours ago" | grep -i -E "out of memory|oom"
dmesg -T | grep -i -E "killed process|oom"
# vai postgres darbojas cgroup ar atmiņas limitu (systemd, docker, k8s)?
systemctl show postgresql -p MemoryMax -p MemoryHigh
cat /sys/fs/cgroup/$(cut -d: -f3 /proc/$(pgrep -o postgres)/cgroup)/memory.max
# pašreizējais overcommit režīms un postmaster aizsardzība pret OOM
sysctl vm.overcommit_memory vm.overcommit_ratio
cat /proc/$(pgrep -o postgres)/oom_score_adj
PostgreSQL dokumentācija kodola ziņojumu parāda kā Out of Memory: Killed process 12345 (postgres); precīzs formulējums atkarīgs no kodola versijas. Ja nogalināšana notika cgroup ietvaros (systemd MemoryMax, Docker vai Kubernetes atmiņas limits), kodola žurnālā būs minēta atmiņas cgroup. Tas maina risinājumu: hostā var būt daudz brīvas RAM, bet jūs atduraties pret konteinera limitu.
2. solis: atrodiet, kas izmantoja atmiņu
- Vaicājums no
Failed process was running. Palaidiet tamEXPLAINun meklējiet vairākas kārtošanas un hash operācijas vienā plānā, īpaši ar paralēlajiem worker procesiem. work_mem, sareizināts. Dokumentācija to saka skaidri:work_mem(noklusējums4MB) ir limits vienai kārtošanas vai hash operācijai, nevis vaicājumam. Sarežģīts vaicājums var izpildīt vairākas vienlaikus, daudzas sesijas var to darīt paralēli, un katram paralēlajam worker ir savs limits. Hash operācijas drīkst izmantotwork_mem×hash_mem_multiplier, un reizinātāja noklusējums ir2.0.- Savienojumi. Katram backend papildus koplietotajai atmiņai ir sava privātā atmiņa. Simtiem aktīvu backend kopā ar dāsnu
work_memir klasiska recepte. - Uzturēšanas darbi. Indeksu veidošana un citas uzturēšanas operācijas katrā procesā izmanto
maintenance_work_mem. Vairākas vienlaikus papildus parastajai slodzei var serveri pārslogot. - Citi procesi hostā. Rezerves kopiju aģenti, ETL darbi vai JVM uz tās pašas mašīnas bieži izraisa OOM killer, un kodols tik un tā nogalina postgres procesu.
Kamēr aizdomīgā sesija vēl darbojas, superlietotājs var izsaukt pg_log_backend_memory_contexts(pid) (PostgreSQL 14+). Tā ieraksta šī backend atmiņas kontekstus servera žurnālā, un var redzēt, kura izpildītāja daļa aug. Savas sesijas kontekstus var redzēt skatā pg_backend_memory_contexts.
3. solis: novērsiet, lai neatkārtotos
- Godīgi saplānojiet atmiņu. Saskaitiet
shared_buffers, reālo vienlaicīgumu × operācijas vaicājumā ×work_mem, uzturēšanas darbus, OS un visu pārējo hostā. Ja summa neietilpst, samazinietwork_memglobāli un palieliniet to tikai tām lomām vai sesijām, kurām tas vajadzīgs (SET work_mematskaišu darbā). - Samaziniet backend skaitu. Dokumentācija pati iesaka samazināt
max_connectionsun tā vietā izmantot savienojumu pūlu. Transakciju pūls PostgreSQL priekšā ierobežo, cik backend pastāv vienlaikus; skatiet mūsu PgBouncer ceļvedi. - Pārejiet uz stingru overcommit. Ar
vm.overcommit_memory=2kodols atsaka piešķīrumus virs commit limita, tāpēc PostgreSQL vienā vaicājumā saņem parastuERROR: out of memory, nevisSIGKILL, kas atiestata visas sesijas. Dokumentācijā teikts, ka tas pilnībā nenovērš OOM killer, bet ievērojami samazina iespējamību. Uzmanieties ar limitu: saskaņā ar kodola dokumentāciju 2. režīmā commit limits ir swap plusvm.overcommit_rationo fiziskās RAM, un šī attiecība pēc noklusējuma ir 50%. Lielā serverī ar mazu swap tas var būt daudz mazāk, nekā gaidāt, tāpēc iestatietvm.overcommit_ratio(vaivm.overcommit_kbytes) apzināti un pārbaudiet zem slodzes, pirms uz to paļaujaties. - Aizsargājiet postmaster. Dokumentētā pieeja ir palaist postmaster ar OOM score korekciju
-1000un ļaut bērnu procesiem atgriezties pie0:
Tas neaptur backend nogalināšanu, bet uztur dzīvu uzraugošo procesu, tāpēc instance atjaunojas pati, nevis paliek izslēgta. Dažas distribūciju pakotnes to jau iestata savos systemd unit failos; pārbaudiet, pirms pievienojat.# root lietotāja palaišanas skripts / systemd unit, pirms postgres starta echo -1000 > /proc/self/oom_score_adj export PG_OOM_ADJUST_FILE=/proc/self/oom_score_adj export PG_OOM_ADJUST_VALUE=0 - Labojiet konteinera limitu, ne tikai PostgreSQL. Ja tika sasniegts cgroup limits, vai nu palieliniet to, vai dimensionējiet
shared_buffersunwork_mempēc limita, nevis pēc hosta RAM.
Lielās datubāzēs tas sāp vairāk
- Atjaunošanas laiks aug līdz ar WAL. Pēc atiestatīšanas PostgreSQL atskaņo visu, kas rakstīts kopš pēdējā kontrolpunkta. Serveriem, kas noskaņoti intensīvai rakstīšanai, bieži ir liels
max_wal_sizeun garšcheckpoint_timeout, tāpēc atskaņošana var ilgt minūtes, nevis sekundes. Izmēriet to pilna izmēra kopijā, lai failover un veselības pārbaužu taimauti atbilstu realitātei. - Kešatmiņa sākas auksta. Koplietotā atmiņa tiek izveidota no jauna, tāpēc pēc restarta
shared_buffersir tukšs. Ja darba kopa ir simtiem GB, pirmais posms pēc atjaunošanas ir lēnāks nekā parasti, un lietotņu atkārtotu mēģinājumu vētra šajā brīdī var to pasliktināt. - Lieli analītiskie vaicājumi ir tipisks izraisītājs. Hash join un agregācijas pār miljardiem rindu, katra ar vairākiem paralēlajiem worker, ātri sareizina
work_mem.work_mem, kas bija drošs, kamēr tabulas bija mazas, datiem augot var kļūt bīstams, pat bez jebkādām konfigurācijas izmaiņām. - HA jāsaskaņo ar restarta politiku. Ar Patroni vai līdzīgiem rīkiem izlemiet, vai vēlamais iznākums ir lokāls restarts pēc avārijas vai failover, un attiecīgi iestatiet
restart_after_crashun rīka taimautus. Atkārtoti OOM kill primārajā serverī ir iemesls labot atmiņu, nevis veikt failover atkal un atkal.
Galvenās atziņas
- "terminated by signal 9: Killed" gandrīz vienmēr ir Linux OOM killer; apstipriniet to kodola žurnālā un pārbaudiet cgroup limitu.
- Viens nogalināts backend izmet visas sesijas un palaiž avārijas atjaunošanu; tā PostgreSQL sargā koplietoto atmiņu, tā nav kļūda.
work_memattiecas uz katru operāciju un katru paralēlo worker, tāpēc rēķiniet to pēc reālā vienlaicīguma un turiet backend skaitu zemu ar pūlu.- Stingrs overcommit pārvērš nogalināšanu parastās "out of memory" kļūdās, bet iestatiet
vm.overcommit_ratioapzināti; aizsargājiet postmaster aroom_score_adj. - Lielās datubāzēs pēc katra OOM kill rēķinieties ar ilgu WAL atskaņošanu, aukstu kešatmiņu un iztukšotām unlogged tabulām.