Incidentu reaģēšana

PostgreSQL "could not resize shared memory segment": No Space Left on Device

Liels vaicājums beidzas ar "No space left on device", bet datu disks ir pustukšs. Runa ir par /dev/shm, un Docker vidē tas pēc noklusējuma ir tikai 64 MB. Lūk, kāpēc paralēlajiem vaicājumiem tas vajadzīgs, kā apstiprināt cēloni un kā to pareizi iestatīt Docker un Kubernetes vidē.

· 13 min lasīšanai · AG Data komanda

PostgreSQL kļūda "could not resize shared memory segment ... No space left on device" mulsina, jo datu sējumā parasti ir daudz vietas. Runa nav par šo disku. PostgreSQL mēģina piešķirt dinamisko koplietojamo atmiņu paralēlam vaicājumam, šī atmiņa atrodas /dev/shm, un /dev/shm ir pilns. Docker vidē tas ir tikai 64 MB, ja vien nenorādāt citādi, tāpēc šī kļūda ir tik bieža konteineros un tik reta uz fiziskiem serveriem.

Pilns ziņojums izskatās šādi (segmenta nosaukums un izmērs atšķirsies):

ERROR:  could not resize shared memory segment "/PostgreSQL.2051486325" to 67145056 bytes: No space left on device

Tipisks scenārijs: izstrādes vidē datubāze strādā labi, bet produkcijā viena atskaite, analītikas vaicājums vai liels join beidzas ar šo kļūdu, dažreiz tikai tad, kad vienlaikus darbojas vairāki šādi vaicājumi.

Kas īsti tiek palielināts

Kad PostgreSQL sāk paralēlu operāciju, process, kas to uzsāk, vispirms izveido dinamiskās koplietojamās atmiņas segmentu, kas pastāv visas operācijas laikā. Darba procesi (workers) tam pievienojas, lai apmainītos ar stāvokli, kļūdām un datiem. Tas aprakstīts pirmkoda failā README.parallel. Paralēls hash join iet vēl tālāk: tā vietā, lai katrs process būvētu savu kopiju, paralēlo plānu dokumentācija skaidro, ka procesi kopā būvē vienu koplietojamu hash tabulu, un arī tā atrodas koplietojamajā atmiņā.

To, kā šī atmiņa tiek iegūta, nosaka dynamic_shared_memory_type. Resursu konfigurācijas dokumentācija uzskaita posix (shm_open), sysv, windows un mmap un saka, ka pirmā atbalstītā iespēja parasti ir platformas noklusējums, tāpēc Linux vidē parasti redzēsiet posix. Linux vidē POSIX koplietojamās atmiņas objekti ir faili tmpfs failu sistēmā, kas parasti piemontēta /dev/shm.

Kļūdas teksts nāk no dsm_impl.c. Linux vidē PostgreSQL apzināti izsauc posix_fallocate() jaunajam segmentam. Komentārs kodā paskaidro, kāpēc: ja tas vienkārši paplašinātu failu, tmpfs piešķirtu lapas tikai tad, kad tām piekļūst, un vietas trūkums tad nogalinātu procesu ar SIGBUS. Piešķirot vietu jau iepriekš, PostgreSQL saņem tīru ENOSPC kļūdu un atceļ tikai vaicājumu. Tātad ziņojums ir PostgreSQL pašaizsardzība, nevis bojājuma pazīme.

Kāpēc konteineri to sasniedz pirmie

Oficiālā postgres Docker attēla dokumentācija to saka tieši: noklusējuma /dev/shm izmērs konteineriem ir 64 MB, un, kad koplietojamā atmiņa ir izsmelta, jūs saņemat tieši šo kļūdu. Tā iesaka docker run komandai nodot --shm-size (piemērā 256MB) vai līdzvērtīgu shm_size docker compose failā.

Aptuvena sajūta, kāpēc 64 MB ir tik maz: work_mem pēc noklusējuma ir 4 MB, un hash operācijas drīkst izmantot work_mem reiz hash_mem_multiplier (pēc noklusējuma 2.0). Dokumentācija arī brīdina, ka tādi limiti kā work_mem attiecas uz katru paralēlo darba procesu atsevišķi un ka sarežģīts vaicājums var vienlaikus veikt vairākas kārtošanas un hash operācijas. Palieliniet work_mem līdz atskaitēm reālistiskai vērtībai, ļaujiet vienlaikus darboties dažiem paralēliem vaicājumiem, un 64 MB tmpfs ir beigusies. Ārpus konteineriem /dev/shm parasti ir tmpfs bez tieši norādīta izmēra, un kodola tmpfs noklusējums ir puse no fiziskās RAM, tāpēc tas pats vaicājums tur reti sūdzas.

Apstipriniet cēloni divās minūtēs

Pārbaudiet /dev/shm izmēru un aizpildījumu tur, kur PostgreSQL patiešām darbojas:

# konteinerā vai uz hosta
df -h /dev/shm
ls -la /dev/shm | head        # PostgreSQL.NNNN faili ir aktīvi DSM segmenti

docker inspect --format '{{.HostConfig.ShmSize}}' my-postgres

Tad apskatiet iestatījumus, kas nosaka, cik daudz dinamiskās koplietojamās atmiņas vaicājumi pieprasa:

SHOW dynamic_shared_memory_type;
SHOW min_dynamic_shared_memory;
SHOW max_parallel_workers_per_gather;
SHOW work_mem;
SHOW hash_mem_multiplier;

Ja /dev/shm ir 64M, dynamic_shared_memory_type ir posix un neizdevušā vaicājuma EXPLAIN plānā redzams Gather vai Parallel Hash, atbilde ir rokā. Ja /dev/shm ir liels un tomēr pildās, paskatieties, kas vēl tur raksta: citi procesi tajā pašā konteinerā vai pod to koplieto.

Tas nav tas pats, kas pilns datu disks. Šī kļūda nenozīmē, ka tabulām vai WAL trūkst vietas, un failu dzēšana datu direktorijā nepalīdzēs. Ja pilns ir pats datu sējums, simptomi un risinājums ir citi; skatiet mūsu ceļvedi par pilnu PostgreSQL disku un pg_wal pieaugumu.

1. risinājums: piešķiriet /dev/shm saprātīgu izmēru

Šis ir īstais risinājums. Docker un docker compose vidē:

docker run -d --name my-postgres --shm-size=1g postgres:18

# docker compose
services:
  db:
    image: postgres:18
    shm_size: 1gb

Kubernetes nav tieša --shm-size ekvivalenta; /dev/shm izmēru nosaka konteineru izpildvide, tāpēc pārbaudiet to ar df pod iekšienē. Ierastā pieeja ir piemontēt tam pāri atmiņā balstītu emptyDir:

spec:
  containers:
    - name: postgres
      volumeMounts:
        - name: dshm
          mountPath: /dev/shm
  volumes:
    - name: dshm
      emptyDir:
        medium: Memory
        sizeLimit: 1Gi

Divas detaļas no Kubernetes sējumu dokumentācijas šeit ir svarīgas. Ar medium: Memory sējums ir tmpfs, un tajā ierakstītie faili tiek ieskaitīti tā konteinera atmiņas limitā, kas tos ierakstīja. Un, ja izmērs nav norādīts, atmiņā balstīts sējums tiek izmērots pēc mezgla pieejamās (allocatable) atmiņas. Tāpēc iestatiet sizeLimit un iekļaujiet sagaidāmo koplietojamo atmiņu pod atmiņas limitā, citādi šīs kļūdas risinājums pārvērtīsies OOM kill. Ja izmantojat operatoru, vispirms pārbaudiet tā dokumentāciju: daži PostgreSQL operatori jau montē koplietojamo atmiņu šādi vai piedāvā tam iestatījumu.

Cik lielu? Dokumentācijā formulas nav. Saprātīga pieeja ir sākt ar dažiem simtiem megabaitu līdz gigabaitam instancei, kurā darbojas analītika, vērot df -h /dev/shm smagāko atskaišu laikā un vajadzības gadījumā palielināt. Tā ir RAM, tāpēc tai jāietilpst blakus shared_buffers un visam pārējam.

2. risinājums: samaziniet to, ko vaicājumi pieprasa

Ja konteineru tieši tagad mainīt nevarat, varat panākt, lai vaicājumi pieprasa mazāk. Paralēlie vaicājumi sesijā ir izslēgti, ja max_parallel_workers_per_gather ir 0:

-- ātrs atvieglojums vienai atskaitei vai sesijai
SET max_parallel_workers_per_gather = 0;

-- vai konkrētai atskaišu lomai
ALTER ROLE reporting SET max_parallel_workers_per_gather = 1;

Tas šiem vaicājumiem novērš kļūdu uz paralēlisma rēķina, tāpēc tas ir pagaidu līdzeklis, nevis konfigurācija, ko paturēt. Agresīva work_mem vai hash_mem_multiplier samazināšana atskaišu lomām arī samazina koplietojamo hash tabulu izmēru. Un bieži īstā problēma ir pats vaicājums: hash join pār desmitiem miljonu rindu, kur varēja izmantot indeksu. Mūsu lēno vaicājumu optimizācijas kontrolsaraksts apraksta, kā tādus atrast un salabot.

Un kā ar min_dynamic_shared_memory?

min_dynamic_shared_memory servera startā rezervē atmiņu paralēlajiem vaicājumiem; noklusējums ir 0. Kad šis apgabals ir nepietiekams vai izlietots, jaunie paralēlie vaicājumi atgriežas pie atmiņas piešķiršanas no operētājsistēmas ar dynamic_shared_memory_type. Tā kā rezervētais apgabals ir daļa no galvenās koplietojamās atmiņas, kas piešķirta startā, nevis fails /dev/shm, tas var palīdzēt, un dokumentācija norāda, ka tam var nākt par labu huge pages. Taču tā ir fiksēta rezervācija, kas tiek atņemta no RAM neatkarīgi no tā, vai tiek izmantota, un viss, kas pārsniedz to, joprojām iet uz /dev/shm. Uztveriet to kā papildu regulēšanu pareizi izmērotam /dev/shm, nevis kā aizstājēju. Tā maiņai vajag restartu.

Kā novērst nākamo reizi

Galvenās atziņas

PostgreSQL Docker Kubernetes Problēmu novēršana
Sadarbība ar mums

PostgreSQL konteineros uzvedas dīvaini?

Mēs darbinām PostgreSQL klientiem Docker un Kubernetes vidē un zinām, kur noklusējumi sāpina. Pastāstiet, kas neizdodas, un palīdzēsim to novērst pareizi.

Sazināties Vairāk no emuāra