Kalo te përmbajtja kryesore
OpenAI

22 janar 2026

Inxhinieria

Shkallëzimi i PostgreSQL për 800 milionë përdorues të ChatGPT

Nga Bohan Zhang, anëtar i stafit teknik

Duke ngarkuar…

Prej vitesh, PostgreSQL ka qenë një nga sistemet më kritike të të dhënave që punojnë në prapaskenë, duke fuqizuar produkte thelbësore si ChatGPT dhe API e OpenAI. Ndërsa baza jonë e përdoruesve po rritet me shpejtësi, edhe kërkesat ndaj bazave tona të të dhënave janë rritur në mënyrë eksponenciale. Gjatë vitit të kaluar, ngarkesa jonë e PostgreSQL është rritur më shumë se 10 herë, dhe vazhdon të rritet shpejt.

Përpjekjet tona për të avancuar infrastrukturën tonë të prodhimit për të mbështetur këtë rritje zbuluan një njohuri të re: PostgreSQL mund të shkallëzohet për të mbështetur në mënyrë të besueshme ngarkesa pune shumë më të mëdha të orientuara nga leximet sesa shumëkush më parë mendonte se ishte e mundur. Sistemi (fillimisht i krijuar nga një ekip shkencëtarësh në Universitetin e Kalifornisë, Berkeley) na ka mundësuar të mbështesim trafik masiv global me një instancë të vetme primare Azure PostgreSQL flexible server instance(hapet në një dritare të re) dhe pothuajse 50 replika leximi të shpërndara në shumë rajone në mbarë botën. Kjo është historia se si e kemi shkallëzuar PostgreSQL në OpenAI për të mbështetur miliona pyetje për sekondë për 800 milionë përdorues përmes optimizimeve rigoroze dhe inxhinierisë solide; gjithashtu do të mbulojmë pikat kryesore që mësuam gjatë rrugës.

Çarje në dizajnin tonë të parë

Pas qarkullimit të ChatGPT, trafiku u rrit me një ritëm të paparë. Për ta mbështetur, ne zbatuam me shpejtësi optimizime të gjera si në shtresën e aplikacionit ashtu edhe në shtresën e bazës së të dhënave PostgreSQL, e shkallëzuam duke rritur madhësinë e instancës dhe e zgjeruam duke shtuar më shumë replika leximi. Kjo arkitekturë na ka shërbyer mirë për një kohë të gjatë. Me përmirësime të vazhdueshme, vazhdon të ofrojë mundësi të mjaftueshme për rritje në të ardhmen.

Mund të duket e habitshme që një arkitekturë me një primar të vetëm mund të përmbushë kërkesat e shkallës së OpenAI; megjithatë, ta bësh këtë të funksionojë në praktikë nuk është e lehtë. Kemi parë disa SEV të shkaktuara nga mbingarkesa e Postgres, dhe ato shpesh ndjekin të njëjtin model: një problem në rrjedhën lart shkakton një rritje të papritur të ngarkesës së bazës së të dhënave, si p.sh. cache misses të përhapura nga një dështim i shtresës së cache, një rritje e bashkimeve të kushtueshme me shumë drejtime që ngop procesorin, ose një stuhi shkrimesh nga qarkullimi i një veçorie të re. Ndërsa shfrytëzimi i burimeve rritet, vonesa e kërkesave rritet dhe kërkesat fillojnë të skadojnë. Riprovimet pastaj amplifikojnë më tej ngarkesën, duke shkaktuar një cikël vicioz me potencialin për të degraduar të gjitha shërbimet e ChatGPT dhe API.

Diagrami i shkallëzimit të ngarkesës

Megjithëse PostgreSQL shkallëzohet mirë për ngarkesat tona të punës me shumë lexime, ne ende përballemi me sfida gjatë periudhave të trafikut të lartë të shkrimeve. Kjo është kryesisht për shkak të implementimit të kontrollit të njëkohshmërisë me shumë versione (MVCC) të PostgreSQL, që e bën atë më pak efikas për ngarkesa pune të rënda me shkrime. Për shembull, kur një kërkesë përditëson një tuple ose edhe një fushë të vetme, i gjithë rreshti kopjohet për të krijuar një version të ri. Nën ngarkesa të mëdha shkrimi, kjo çon në një amplifikim të konsiderueshëm të shkrimit. Gjithashtu rrit amplifikimin e leximit, pasi pyetjet duhet të skanojnë nëpër versione të shumta të tuple-ve (dead tuples) për të marrë versionin më të fundit. MVCC sjell sfida shtesë si fryrja e tabelave dhe indekseve, rritja e kostos së mirëmbajtjes së indekseve dhe rregullimi kompleks i vetëspastrimit. (Mund të gjesh një analizë të thelluar mbi këto çështje në një blog që e shkrova me Prof. Andy Pavlo në Universitetin Carnegie Mellon, të titulluar The Part of PostgreSQL We Hate the Most(hapet në një dritare të re), cituar(hapet në një dritare të re) në faqen e Wikipedia-s për PostgreSQL.)

Shkallëzimi i PostgreSQL në miliona QPS

Për të zbutur këto kufizime dhe për të reduktuar presionin e shkrimit, kemi migruar, dhe vazhdojmë të migrojmë, të dhëna të ndashme (d.m.th. ngarkesa pune që mund të ndahen horizontalisht), ngarkesa pune me shumë shkrime në sisteme të ndara si Azure Cosmos DB, duke optimizuar logjikën e aplikacionit për të minimizuar shkrimet e panevojshme. Ne gjithashtu nuk lejojmë më shtimin e tabelave të reja në implementimin aktual të PostgreSQL. Ngarkesat e reja të punës parazgjedhin sistemet e fragmentuara.

Edhe pse infrastruktura jonë ka evoluar, PostgreSQL ka mbetur i pafragmentuar, me një instancë primare të vetme që shërben për të gjitha shkrimet. Arsyeja kryesore është se ndarja e ngarkesave ekzistuese të punës së aplikacionit në pjesë do të ishte shumë komplekse dhe kërkon shumë kohë, duke kërkuar ndryshime në qindra pika fundore të aplikacionit dhe potencialisht mund të zgjasë muaj ose edhe vite. Meqenëse ngarkesat tona të punës janë kryesisht të orientuara drejt leximit dhe kemi zbatuar optimizime të gjera, arkitektura aktuale ende ofron hapësirë të mjaftueshme për të mbështetur rritjen e vazhdueshme të trafikut. Ndërsa nuk po e përjashtojmë fragmentimin e PostgreSQL në të ardhmen, nuk është një prioritet i afërt, duke pasur parasysh hapësirën e mjaftueshme që kemi për rritjen aktuale dhe të ardhshme.

Në seksionet në vijim, do të thellohemi në sfidat me të cilat u përballëm dhe optimizimet e gjera që zbatuam për t’i adresuar ato dhe për të parandaluar ndërprerje të ardhshme, duke e çuar PostgreSQL në kufijtë e tij dhe duke e shkallëzuar atë në miliona pyetje për sekondë (QPS).

Reduktimi i ngarkesës në primarin

Sfidë: Me vetëm një shkrues, një konfigurim me një primar të vetëm nuk mund të rrisë kapacitetin e shkrimeve. Rritjet e mëdha të shkrimeve mund të mbingarkojnë shpejt primarin dhe të ndikojnë në shërbime si ChatGPT dhe API ynë.

Zgjidhje: Ne e ulim ngarkesën në primar sa më shumë që të jetë e mundur — si për leximet ashtu edhe për shkrimet — për të siguruar që të ketë kapacitet të mjaftueshëm për të përballuar rritjet e papritura të shkrimeve. Trafiku i leximit dërgohet te replikat sa herë që është e mundur. Megjithatë, disa pyetje leximi duhet të mbeten në primare sepse janë pjesë e transaksioneve të shkrimit. Për ato, përqendrohemi në sigurimin që të jenë efikase dhe të shmangin pyetjet e ngadalta. Për trafikun e shkrimit, kemi migruar ngarkesa pune të ndashme dhe me shkrim intensiv në sisteme të ndara si Azure CosmosDB. Ngarkesat e punës që janë më të vështira për t'u ndarë, por që ende gjenerojnë volum të lartë shkrimesh, kërkojnë më shumë kohë për t'u migruar, dhe ai proces është ende në vazhdim. Ne gjithashtu optimizuam në mënyrë agresive aplikacionet tona për të reduktuar ngarkesën e shkrimeve; për shembull, kemi rregulluar gabime të aplikacionit që shkaktonin shkrime të tepërta dhe kemi prezantuar shkrime të vonuara, aty ku është e përshtatshme, për të zbutur pikat e rritjes së trafikut. Përveç kësaj, kur plotësojmë fushat e tabelës, vendosim kufizime të rrepta të normës për të parandaluar presionin e tepërt të shkrimit.

Optimizimi i kërkesave

Sfidë: Ne identifikuam disa kërkesa të kushtueshme në PostgreSQL. Në të kaluarën, rritjet e papritura të volumit në këto kërkesa do të konsumonin sasi të mëdha të CPU-së, duke ngadalësuar si ChatGPT ashtu edhe kërkesat e API-së.

Zgjidhje: Disa pyetje të kushtueshme, si ato që bashkojnë shumë tabela, mund të degradojnë ndjeshëm ose madje të rrëzojnë të gjithë shërbimin. Duhet të optimizosh vazhdimisht pyetjet e PostgreSQL për të siguruar që ato të jenë efikase dhe të shmangësh antimodelet e zakonshme të Përpunimit të Transaksioneve Online (OLTP). Për shembull, ne dikur identifikuam një pyetje jashtëzakonisht të kushtueshme që bashkonte 12 tabela, ku rritjet në këtë pyetje ishin përgjegjëse për SEV me ashpërsi të lartë në të kaluarën. Duhet të shmangim bashkimet komplekse të shumë tabelave sa herë që është e mundur. Nëse bashkimet janë të nevojshme, mësuam të marrim parasysh ndarjen e pyetjes dhe të zhvendosim logjikën komplekse të bashkimeve në shtresën e aplikacionit. Shumë nga këto pyetje problematike gjenerohen nga kornizat e Hartimit Objekt-Relacional (ORM), prandaj është e rëndësishme të rishikosh me kujdes SQL-in që ato prodhojnë dhe të sigurohesh që sillet siç pritet. Është gjithashtu e zakonshme të gjenden kërkesa të papërdorura afatgjata në PostgreSQL. Konfigurimi i timeouts si idle_in_transaction_session_timeout është thelbësor për të parandaluar bllokimin e autovacuum.

Masat zbutëse për pikën e vetme të dështimit

Sfidë: Nëse një replikat leximi bie, trafiku mund të ridrejtohet te replikat e tjera. Megjithatë, mbështetja te një shkrues i vetëm do të thotë të kesh një pikë të vetme dështimi—nëse ai dështon, preket i gjithë shërbimi.

Zgjidhje: Shumica e kërkesave më kritike përfshijnë vetëm kërkesa leximi. Për të zbutur pikën e vetme të dështimit te kryesori, i zhvendosëm ato lexime nga shkruesi te replikat, duke siguruar që ato kërkesa të vazhdojnë të shërbehen edhe nëse kryesori bie. Ndërsa operacionet e shkrimit do të vazhdonin të dështonin, ndikimi është i zvogëluar; nuk është më një SEV0, pasi leximet mbeten të disponueshme.

Për të zbutur dështimet parësore, ne e ekzekutojmë parësorin në modalitetin me Disponueshmëri të Lartë (HA) me një rezervë aktive, një replikë të sinkronizuar vazhdimisht që është gjithmonë gati të marrë përsipër shërbimin e trafikut. Nëse primari bie ose duhet të nxirret jashtë linje për mirëmbajtje, mund ta promovojmë shpejt rezervën për të minimizuar kohën e ndërprerjes. Ekipi i Azure PostgreSQL ka bërë një punë të rëndësishme për të siguruar që këto failover të mbeten të sigurta dhe të besueshme edhe nën ngarkesë shumë të lartë. Për të trajtuar dështimet e replikave të leximit, ne vendosim disa replika në secilin rajon me kapacitet të mjaftueshëm, duke siguruar që dështimi i një replike të vetme të mos çojë në një ndërprerje rajonale.

Izolimi i ngarkesave të punës

Sfida: Shpesh hasim situata ku kërkesa të caktuara konsumojnë një sasi joproporcionale të burimeve në instancat PostgreSQL. Kjo mund të çojë në përkeqësim të performancës për ngarkesa të tjera pune që ekzekutohen në të njëjtat instanca. Për shembull, një veçori e re mund të sjellë pyetje joefikase që konsumojnë shumë procesorin e PostgreSQL, duke ngadalësuar kërkesat për veçori të tjera kritike.

Zgjidhje: Për të zbutur problemin e “fqinjit të zhurmshëm”, izolojmë ngarkesat e punës në instanca të dedikuara për të siguruar që rritjet e papritura të kërkesave me konsum të lartë burimesh të mos ndikojnë në trafikun tjetër. Në mënyrë specifike, ne i ndajmë kërkesat në kategori me prioritet të ulët dhe të lartë dhe i drejtojmë ato në instanca të veçanta. Në këtë mënyrë, edhe nëse një ngarkesë pune me prioritet të ulët bëhet e rëndë për burimet, ajo nuk do të ulë performancën e kërkesave me prioritet të lartë. Ne përdorim të njëjtën strategji në produkte dhe shërbime të ndryshme, në mënyrë që aktiviteti i një produkti të mos ndikojë në performancën ose besueshmërinë e një tjetri.

Grupimi i lidhjeve

Sfida: Çdo instancë ka një kufi maksimal lidhjesh (5000 në Azure PostgreSQL). Është e lehtë të të mbarojnë lidhjet ose të grumbullosh shumë lidhje të papërdorura. Më parë kemi pasur incidente të shkaktuara nga stuhi të lidhjeve që shteruan të gjitha lidhjet e disponueshme.

Zgjidhje: Ne vendosëm PgBouncer si një shtresë proxy për të menaxhuar lidhjet e bazës së të dhënave. Ekzekutimi i tij në modalitetin e grumbullimit të deklaratave ose transaksioneve na lejon të ripërdorim në mënyrë efikase lidhjet, duke reduktuar ndjeshëm numrin e lidhjeve aktive të klientëve. Kjo gjithashtu ul vonesën e konfigurimit të lidhjes: në testet tona, koha mesatare e lidhjes ra nga 50 milisekonda (ms) në 5 ms. Lidhjet dhe kërkesat ndërrajonale mund të jenë të kushtueshme, prandaj ne vendosim proxy-n, klientët dhe replikat në të njëjtin rajon për të minimizuar mbingarkesën e rrjetit dhe kohën e përdorimit të lidhjes. Për më tepër, PgBouncer duhet të konfigurohet me kujdes. Cilësimet si përfundimet e kohës së papunësisë janë kritike për të parandaluar shterimin e lidhjeve.

diagrami i deleguesit të postgreSQL

Çdo replikat leximi ka implementimin e vet të Kubernetes që ekzekuton disa pod-e të PgBouncer. Ne ekzekutojmë disa vendosje të Kubernetes pas të njëjtit Shërbim Kubernetes, i cili balancon trafikun nëpër pod-e.

Ruajtja në memorien specifike

Sfida: Një rritje e papritur e cache misses mund të shkaktojë një rritje të leximeve në bazën e të dhënave PostgreSQL, duke ngopur procesorin dhe duke ngadalësuar kërkesat e përdoruesve.

Zgjidhje: Për të reduktuar presionin e leximit në PostgreSQL, përdorim një shtresë cache për të shërbyer shumicën e trafikut të leximit. Megjithatë, kur normat e goditjeve të memories specifike bien papritur, një shpërthim i dështimeve të memories specifike mund të çojë një numër të madh kërkesash drejtpërdrejt te PostgreSQL. Kjo rritje e papritur e leximeve të bazës së të dhënave konsumon burime të konsiderueshme, duke ngadalësuar shërbimin. Për të parandaluar mbingarkesën gjatë stuhive të cache-miss, ne zbatojmë një mekanizëm kyçjeje (dhe lirimi) të memories specifike, në mënyrë që vetëm një lexues që dështon për një çelës të caktuar të marrë të dhënat nga PostgreSQL. Kur disa kërkesa dështojnë në të njëjtin çelës memorieje specifike, vetëm një kërkesë e merr bllokimin dhe vazhdon të marrë të dhënat dhe të ripopullojë memorien specifike. Të gjitha kërkesat e tjera presin që cache të përditësohet, në vend që të gjitha të godasin PostgreSQL njëherësh. Kjo ul ndjeshëm leximet e tepërta të bazës së të dhënave dhe mbron sistemin nga rritjet e njëpasnjëshme të ngarkesës.

Shkallëzimi i replikateve të leximit

Sfida: Primari transmeton të dhënat e Write Ahead Log (WAL) te çdo replikat leximi. Ndërsa numri i replikateve rritet, primari duhet të dërgojë WAL te më shumë instanca, duke rritur presionin si mbi gjerësinë e brezit të rrjetit ashtu edhe mbi CPU. Kjo shkakton vonesë më të madhe dhe më të paqëndrueshme të replikës, gjë që e bën sistemin më të vështirë për t'u shkallëzuar në mënyrë të besueshme.

Zgjidhje: Ne operojmë pothuajse 50 replika leximi në disa rajone gjeografike për të minimizuar vonesën. Megjithatë, me arkitekturën aktuale, primari duhet të transmetojë WAL te çdo replikë. Megjithëse aktualisht shkallëzohet mirë me lloje instancash shumë të mëdha dhe gjerësi të lartë të bandës së rrjetit, nuk mund të vazhdojmë të shtojmë replika pafundësisht pa mbingarkuar përfundimisht primarin. Për ta adresuar këtë, po bashkëpunojmë me ekipin e Azure PostgreSQL për replikimin kaskadues(hapet në një dritare të re), ku replikat e ndërmjetme përcjellin WAL te replikat pasuese. Kjo qasje na lejon të shkallëzojmë në mbi njëqind replika pa e mbingarkuar primarin. Megjithatë, kjo gjithashtu sjell kompleksitet shtesë operacional, veçanërisht në menaxhimin e failover-it. Veçoria është ende në testim; do të sigurohemi që të jetë e qëndrueshme dhe të mund të kalojë në mënyrë të sigurt në rast dështimi përpara se ta prezantojmë në prodhim.

diagrami i replikimit kaskadë postgreSQL

Kufizimi i kuotës

Sfida: Një rritje e papritur e trafikut në pika fundore specifike, një rritje e pyetjeve të kushtueshme, ose një stuhi riprovimesh mund të shterojë shpejt burime kritike si CPU, I/O dhe lidhjet, duke shkaktuar degradim të gjerë të shërbimit.

Zgjidhje: Ne kemi zbatuar kufizimin e shpejtësisë në disa shtresa — aplikacion, grupues i lidhjeve, delegues dhe kërkesë — për të parandaluar që rritjet e papritura të trafikut të mbingarkojnë instancat e bazës së të dhënave dhe të shkaktojnë dështime kaskadë. Është gjithashtu e rëndësishme të shmangësh intervalet tepër të shkurtra të riprovimit, të cilat mund të shkaktojnë stuhi riprovimesh. Ne gjithashtu përmirësuam shtresën ORM për të mbështetur kufizimin e shpejtësisë dhe, kur është e nevojshme, për të bllokuar plotësisht përmbledhjet e pyetjeve specifike. Kjo formë e synuar e ndërprerjes së ngarkesës mundëson rikuperim të shpejtë nga rritjet e papritura të kërkesave të kushtueshme.

Menaxhimi i skemës

Sfidë: Edhe një ndryshim i vogël i skemës, si ndryshimi i tipit të një kolone, mund të shkaktojë një rishkrim të plotë të tabelës(hapet në një dritare të re). Prandaj, ne i zbatojmë ndryshimet e skemës me kujdes—duke i kufizuar ato në operacione të lehta dhe duke shmangur çdo ndryshim që rishkruan tabela të tëra.

Zgjidhje: Lejohen vetëm ndryshime të lehta të skemës, siç është shtimi ose heqja e disa kolonave që nuk shkaktojnë një rishkrim të plotë të tabelës. Ne zbatojmë një afat të rreptë prej 5 sekondash për ndryshimet në skemë. Krijimi dhe heqja e indekseve në të njëjtën kohë është e lejuar. Ndryshimet e skemës janë të kufizuara vetëm në tabelat ekzistuese. Nëse një veçori e re kërkon tabela shtesë, ato duhet të jenë në sisteme alternative të fragmentuara si Azure CosmosDB sesa PostgreSQL. Kur plotësohet një fushë tabele, zbatojmë kufizime të rrepta të shpejtësisë për të parandaluar rritje të papritura të shkrimeve. Megjithëse ky proces ndonjëherë mund të zgjasë më shumë se një javë, ai siguron stabilitet dhe shmang çdo ndikim në prodhim.

Rezultatet dhe rruga përpara

Kjo përpjekje tregon se me dizajnin dhe optimizimet e duhura, Azure PostgreSQL mund të shkallëzohet për të përballuar ngarkesat më të mëdha të punës në prodhim. PostgreSQL përpunon miliona QPS për ngarkesa pune të fokusuara në lexim, duke fuqizuar produktet më kritike të OpenAI si ChatGPT dhe platformën API. Shtuam pothuajse 50 replika leximi, duke mbajtur vonesën e replikimit pothuajse zero, ruajtëm lexime me vonesë të ulët në rajone të shpërndara gjeografikisht dhe krijuam hapësirë të mjaftueshme kapaciteti për të mbështetur rritjen e ardhshme.

Ky shkallëzim funksionon duke minimizuar vonesën dhe përmirësuar besueshmërinë. Ne ofrojmë vazhdimisht vonesë p99 në anën e klientit në milisekonda të ulëta dyshifrore dhe disponueshmëri pesë-nëntëshe në prodhim. Dhe gjatë 12 muajve të fundit, kemi pasur vetëm një incident SEV-0 të PostgreSQL (ndodhi gjatë nisjes virale(hapet në një dritare të re) të ChatGPT ImageGen, kur trafiku i shkrimit u rrit papritur me më shumë se 10 herë, ndërsa mbi 100 milionë përdorues të rinj u regjistruan brenda një jave.)

Ndërsa jemi të kënaqur me sa larg na ka çuar PostgreSQL, ne vazhdojmë të shtyjmë kufijtë e tij për të siguruar që kemi hapësirë të mjaftueshme për rritjen e ardhshme. Ne kemi migruar tashmë ngarkesat e punës me shkrime të rënda që mund të ndahen në sistemet tona të ndara si CosmosDB. Ngarkesat e mbetura të punës që kërkojnë shumë shkrime janë më sfiduese për t'u ndarë—po i migrojmë aktivisht edhe ato për të lehtësuar më tej shkrimet nga primari i PostgreSQL. Ne po punojmë gjithashtu me Azure për të mundësuar replikimin kaskadë që të mund të shkallëzojmë në mënyrë të sigurt në shumë më tepër replika leximi.

Në të ardhmen, do të vazhdojmë të eksplorojmë qasje shtesë për të shkallëzuar më tej, duke përfshirë PostgreSQL të ndarë në fragmentime ose sisteme të tjera të shpërndara alternative, ndërsa kërkesat tona për infrastrukturë vazhdojnë të rriten.

Autor

Bohan Zhang

Falënderime

Falënderime të veçanta për Jon Lee, Sicheng Liu, Chaomin Yu dhe Chenglong Hao, të cilët kontribuuan në këtë postim, si dhe për të gjithë ekipin që ndihmoi në shkallëzimin e PostgreSQL. Ne gjithashtu dëshirojmë të falënderojmë ekipin e Azure PostgreSQL për partneritetin e tyre të fortë.