Agrāk vai vēlāk gandrīz katra augoša PostgreSQL instalācija saskaras ar vienu un to pašu kļūdu: FATAL: sorry, too many clients already. Instinktīvs risinājums ir palielināt max_connections. Parasti tas situāciju pasliktina. Īstais risinājums ir PostgreSQL savienojumu pūls, un visplašāk izmantotais rīks tam ir PgBouncer — neliels, vienam uzdevumam paredzēts starpniekserveris, kas ļauj tūkstošiem lietotņu savienojumu koplietot dažus desmitus reālu datubāzes savienojumu.
Kāpēc PostgreSQL vajag savienojumu pūlu
PostgreSQL izmanto modeli "viens process uz savienojumu": katrs klienta savienojums serverī saņem savu aizmugursistēmas procesu. Šī arhitektūra ir noturīga, taču katrs process patērē atmiņu un CPU plānošanu, un jauna savienojuma atvēršana — autentifikācija, procesa palaišana, kataloga kešatmiņas — aizņem milisekundes, nevis mikrosekundes. Noklusējuma max_connections vērtība ir 100 ne bez iemesla.
Mūsdienu lietotnes situāciju pasliktina. Katra lietotnes instance, darba process, cron uzdevums un bezservera funkcija uztur savu savienojumu pūlu, tāpēc desmit konteineriem ar pūlu pa 20 jau vajag 200 savienojumu — lielākā daļa no tiem lielākoties dīkstāvē. Dīkstāves savienojumi nav bez maksas: tie aizņem atmiņu, un liels aizmugursistēmas procesu skaits padara dažas iekšējās darbības, piemēram, momentuzņēmumu iegūšanu, dārgākas visiem.
Pūls pārrauj saikni starp "klientu skaitu" un "datubāzes procesu skaitu". Lietotnes pieslēdzas PgBouncer tā, it kā tas būtu PostgreSQL; PgBouncer tām piešķir reālu servera savienojumu tikai tad, kad tas patiešām vajadzīgs.
Trīs pūla režīmi
PgBouncer dokumentācija apraksta trīs režīmus. Pareizā izvēle ir svarīgākais lēmums, ko pieņemsiet:
- Sesijas pūls. Klients patur vienu un to pašu servera savienojumu, līdz atvienojas. Darbojas visas PostgreSQL funkcijas, taču jūs ietaupāt tikai savienojumu atvēršanas izmaksas — izmantoto servera savienojumu skaits nesamazinās.
- Transakciju pūls. Klients tur servera savienojumu tikai transakcijas laikā; starp transakcijām savienojums atgriežas pūlā. Tieši šeit ir lielākie ieguvumi: simtiem klientu var koplietot nelielu pūlu. Cena — sesijas līmeņa stāvoklis vairs nepieder vienam klientam.
- Vaicājumu pūls. Transakciju pūls, kurā aizliegtas vairāku vaicājumu transakcijas. Noderīgs tikai īpašos gadījumos, piemēram, slodzēm tikai ar autocommit.
Vairumam tīmekļa lietotņu transakciju pūls ir pareizā noklusējuma izvēle — ja lietotne ar to ir saderīga.
Kas nedarbojas transakciju režīmā
Tā kā viena klienta secīgas transakcijas var izpildīties uz dažādiem servera savienojumiem, viss, kas balstās uz sesijas stāvokli, nav drošs. Saskaņā ar PgBouncer funkciju tabulu transakciju režīmā nedarbojas:
- Sesijas parametru
SET/RESET(tā vietā transakcijas iekšienē izmantojietSET LOCAL). LISTEN(NOTIFYsūtīšana darbojas).- Sesijas līmeņa ieteikuma bloķēšana (advisory locks) — izmantojiet transakcijas līmeņa
pg_advisory_xact_lock. - SQL līmeņa
PREPARE/DEALLOCATE,WITH HOLDkursori un pagaidu tabulas, kas pārdzīvo transakciju.
Protokola līmeņa sagatavotie vaicājumi — tie, ko vairums draiveru izveido automātiski — arī gadiem bija problēma. Kopš PgBouncer 1.21, kas iznāca 2023. gada oktobrī, tie transakciju režīmā tiek atbalstīti, ja max_prepared_statements ir iestatīts uz vērtību, kas nav nulle. Ja izmantojat vecāku PgBouncer, jaunināšana bieži ir vienkāršāka nekā katra draivera pārkonfigurēšana.
Praktiska pieeja: izmantojiet divus pūlus. Parastā lietotnes plūsma iet caur transakciju režīma pūlu; dažas komponentes, kurām patiešām vajag sesijas — uz LISTEN balstīts darba process, migrāciju rīks, ilgi administrēšanas uzdevumi —, pieslēdzas caur nelielu sesijas režīma pūlu vai tieši PostgreSQL.
Pūla izmēra noteikšana
Galveno PgBouncer iestatījumu nav daudz:
max_client_conn— cik klientu savienojumu PgBouncer pieņem. Tas var būt liels (tūkstoši); klientu savienojumi ar PgBouncer ir lēti.default_pool_size— cik servera savienojumu katram datubāzes un lietotāja pārim. Tieši šis skaitlis reāli noslogo PostgreSQL.reserve_pool_sizeunreserve_pool_timeout— daži papildu savienojumi īslaicīgiem slodzes pīķiem.max_db_connections— stingrs griests vienai datubāzei visiem lietotājiem kopā, lai viens serviss nevarētu izsmelt serveri.query_wait_timeout— cik ilgi klients drīkst gaidīt brīvu savienojumu, pirms saņem kļūdu, nevis karājas.
Datubāzes serveris paralēli var izpildīt tikai tik vaicājumu, cik ļauj tā CPU kodoli un I/O jauda. OLTP slodzēm saprātīgs sākumpunkts ir pūls, kas dažas reizes pārsniedz CPU kodolu skaitu; virs tā papildu savienojumi galvenokārt palielina konkurenci. Sāciet ar mazu pūlu, mēriet un palieliniet to tikai tik ilgi, kamēr caurlaidspēja turpina augt. Atcerieties, ka PostgreSQL max_connections jābūt lielākam par visu pūlu summu plus rezervi administratoriem, replikācijai un monitoringam.
PgBouncer monitorings
PgBouncer ir administrēšanas konsole: pieslēdzieties virtuālajai datubāzei pgbouncer un izpildiet SHOW POOLS; un SHOW STATS;. Īpaši sekojiet:
cl_waiting— klienti, kas gaida servera savienojumu. Reizēm neliels skaits ir normāli; pastāvīga rinda nozīmē, ka pūls ir par mazu vai vaicājumi par lēnu.maxwait— cik ilgi gaida vecākais gaidošais klients.sv_activepretsv_idle— cik noslogoti patiesībā ir servera savienojumi.- Vidējais vaicājumu un transakciju laiks no
SHOW STATS— ja tas pieaug, šaurā vieta ir pati datubāze, un lielāks pūls nepalīdzēs.
Ilgas vai "idle in transaction" sesijas ir klasisks veids, kā iztukšot transakciju pūlu: katra no tām aizņem servera savienojumu. PostgreSQL iestatījums idle_in_transaction_session_timeout pasargā pūlu no aizmirstām transakcijām. Ja lēni ir paši vaicājumi, sāciet ar mūsu PostgreSQL lēno vaicājumu optimizācijas kontrolsarakstu.
Kur darbināt PgBouncer
- Blakus datubāzei — viens centrāls pūls, visvieglāk pārvaldāms un pareizā izvēle vairumam risinājumu.
- Blakus lietotnei (piemēram, kā sidecar konteiners) — novērš vienu tīkla posmu, taču tad katram lietotnes mezglam ir savs pūls, tāpēc kopējais servera savienojumu skaits aug līdz ar mezglu skaitu.
- Ar augstu pieejamību — pašam PgBouncer nedrīkst kļūt par vienīgo kļūmes punktu. Darbiniet vismaz divas instances aiz slodzes balansētāja vai virtuālās IP adreses un pārliecinieties, ka pēc pārslēgšanās tās seko primārajam serverim. Datubāzes pusi aplūkojam ceļvedī par PostgreSQL augstu pieejamību ar Patroni.
Pārvaldītajiem pakalpojumiem ir savi ekvivalenti, piemēram, Amazon RDS Proxy, un pastāv arī alternatīvas, kā Pgpool-II, Odyssey un PgCat. PgBouncer joprojām ir noklusējuma izvēle, jo tas ir mazs, ātrs, labi izprotams un labi dara vienu darbu.
Galvenās atziņas
- Nerisiniet "too many connections", palielinot
max_connections; novietojiet pūlu PostgreSQL priekšā. - Transakciju pūls dod lielākos ieguvumus, taču sesijas stāvoklis (
SET,LISTEN, sesijas advisory locks) vairs nedarbojas. - PgBouncer 1.21+ transakciju režīmā atbalsta draiveru sagatavotos vaicājumus ar
max_prepared_statements. - Turiet servera pūlus nelielus, sekojiet
cl_waitingunmaxwaitun darbiniet PgBouncer ar rezervi.