Пређите на главни садржај
OpenAI

22. јануар 2026.

Инжењеринг

Skaliranje PostgreSQL-a za 800 miliona korisnika ChatGPT‑a

Piše Bohan Zhang, član tehničkog osoblja

Учитавање…

Godinama je PostgreSQL jedan od najkritičnijih pozadinskih sistema podataka koji pokreću ključne proizvode kao što su ChatGPT i OpenAI API. Kako naša baza korisnika brzo raste, eksponencijalno rastu i zahtevi prema našim bazama podataka. Tokom protekle godine, naše PostgreSQL opterećenje poraslo je za više od 10 puta, i nastavlja brzo da raste.

Naši napori da unapredimo produkcionu infrastrukturu kako bismo podržali ovaj rast otkrili su novi uvid: PostgreSQL može da se skalira tako da pouzdano podrži mnogo veća opterećenja sa pretežno čitanjem nego što su mnogi ranije mislili da je moguće. Sistem (koji je prvobitno napravio tim naučnika sa Univerziteta Kalifornije u Berkliju) omogućio nam je da podržimo ogroman globalni saobraćaj sa jednom primarnom instancom Azure PostgreSQL flexible server(отвара се у новом прозору) i gotovo 50 replika za čitanje raspoređenih u više regiona širom sveta. Ovo je priča o tome kako smo u OpenAI skalirali PostgreSQL da podrži milione upita u sekundi za 800 miliona korisnika kroz rigorozne optimizacije i čvrst inženjerski rad; takođe ćemo pokriti ključne pouke koje smo usput naučili.

Pukotine u našem početnom dizajnu

Nakon lansiranja ChatGPT‑a, saobraćaj je rastao neviđenom brzinom. Da bismo ga podržali, brzo smo sproveli opsežne optimizacije i na sloju aplikacije i na sloju PostgreSQL baze podataka, skalirali naviše povećanjem veličine instance i skalirali u širinu dodavanjem više replika za čitanje. Ova arhitektura nam je dugo dobro služila. Uz stalna unapređenja, ona i dalje obezbeđuje dovoljno prostora za budući rast.

Možda zvuči iznenađujuće da arhitektura sa jednom primarnom instancom može da odgovori zahtevima razmere OpenAI-ja; međutim, postići da to funkcioniše u praksi nije jednostavno. Videli smo više SEV incidenta izazvanih preopterećenjem Postgresa, i oni često prate isti obrazac: problem uzvodno izazove nagli skok opterećenja baze podataka, kao što su široko rasprostranjeni promašaji keša zbog kvara sloja za keširanje, nalet skupih višestrukih joinova koji zasićuju CPU ili oluja upisa usled lansiranja nove funkcije. Kako iskorišćenost resursa raste, raste i kašnjenje upita, a zahtevi počinju da ističu. Pokušaji ponavljanja zatim dodatno pojačavaju opterećenje, pokrećući začarani krug koji može da degradira čitave usluge ChatGPT‑a i API-ja.

dijagram skaliranja opterećenja

Iako se PostgreSQL dobro skalira za naša opterećenja sa pretežno čitanjem, i dalje nailazimo na izazove tokom perioda intenzivnog upisnog saobraćaja. To je u velikoj meri posledica PostgreSQL implementacije multiverzione kontrole konkurentnosti (MVCC), koja ga čini manje efikasnim za opterećenja sa mnogo upisa. Na primer, kada upit ažurira torku ili čak samo jedno polje, ceo red se kopira da bi se napravila nova verzija. Pod velikim opterećenjem upisa, to dovodi do značajnog pojačanja upisa. Takođe povećava pojačanje čitanja, pošto upiti moraju da prolaze kroz više verzija torki (mrtve torke) da bi došli do najnovije. MVCC uvodi dodatne izazove kao što su naduvavanje tabela i indeksa, povećano opterećenje održavanja indeksa i složeno podešavanje autovacuum-a. (Detaljnu analizu ovih problema možete pronaći u blogu koji sam napisao sa prof. Andyjem Pavlom sa Univerziteta Carnegie Mellon pod nazivom Deo PostgreSQL-a koji najviše mrzimo(отвара се у новом прозору), koji je citiran(отвара се у новом прозору) na PostgreSQL stranici na Wikipediji.)

Skaliranje PostgreSQL-a na milione QPS-a

Da bismo ublažili ova ograničenja i smanjili pritisak upisa, migrirali smo i nastavljamo da migriramo shardabilna (tj. opterećenja koja mogu horizontalno da se particionišu), upisno intenzivna opterećenja na shardovane sisteme kao što je Azure Cosmos DB, uz optimizaciju logike aplikacija da bi se nepotrebni upisi sveli na minimum. Takođe više ne dozvoljavamo dodavanje novih tabela u trenutno PostgreSQL okruženje. Nova opterećenja podrazumevano idu na shardovane sisteme.

Iako se naša infrastruktura razvijala, PostgreSQL je ostao neshardovan, sa jednom primarnom instancom koja opslužuje sve upise. Glavni razlog je to što bi shardovanje postojećih opterećenja aplikacija bilo veoma složeno i dugotrajno, zahtevalo bi izmene na stotinama aplikativnih krajnjih tačaka i potencijalno trajalo mesecima ili čak godinama. Pošto su naša opterećenja prvenstveno sa pretežno čitanjem i sproveli smo opsežne optimizacije, trenutna arhitektura i dalje pruža dovoljno rezerve za podršku daljem rastu saobraćaja. Iako ne isključujemo shardovanje PostgreSQL-a u budućnosti, to nije kratkoročni prioritet s obzirom na dovoljan prostor koji imamo za sadašnji i budući rast.

U narednim odeljcima dublje ćemo ući u izazove sa kojima smo se suočili i opsežne optimizacije koje smo sproveli da bismo ih rešili i sprečili buduće prekide, gurajući PostgreSQL do njegovih granica i skalirajući ga na milione upita u sekundi (QPS).

Smanjenje opterećenja na primarnoj instanci

Izazov: Sa samo jednim pisačem, postavka sa jednom primarnom instancom ne može da skalira upise. Jaki skokovi upisa mogu brzo da preopterete primarnu instancu i utiču na usluge kao što su ChatGPT i naš API.

Rešenje: Opterećenje na primarnoj instanci svodimo na minimum koliko god je moguće — i čitanja i upise — kako bismo obezbedili da ima dovoljan kapacitet da podnese skokove upisa. Saobraćaj čitanja se gde god je moguće prebacuje na replike. Međutim, neki upiti za čitanje moraju da ostanu na primarnoj instanci zato što su deo transakcija upisa. Za njih se fokusiramo na to da budu efikasni i da izbegavaju spore upite. Kada je reč o upisnom saobraćaju, migrirali smo shardabilna, upisno intenzivna opterećenja na shardovane sisteme kao što je Azure CosmosDB. Opterećenja koja je teže shardovati, ali i dalje generišu veliku količinu upisa, duže se migriraju i taj proces je još u toku. Takođe smo agresivno optimizovali naše aplikacije kako bismo smanjili opterećenje upisa; na primer, ispravili smo bagove u aplikacijama koji su izazivali redundantne upise i uveli odložene upise, gde je to prikladno, da bismo izravnali skokove saobraćaja. Pored toga, kada naknadno popunjavamo polja u tabelama, sprovodimo stroga ograničenja protoka da bismo sprečili prekomeran pritisak upisa.

Optimizacija upita

Izazov: Identifikovali smo nekoliko skupih upita u PostgreSQL-u. Ranije su iznenadni skokovi obima tih upita trošili velike količine CPU resursa, usporavajući i ChatGPT i API zahteve.

Rešenje: Nekoliko skupih upita, kao što su oni koji spajaju mnogo tabela, mogu značajno da degradiraju ili čak obore celu uslugu. Moramo neprekidno da optimizujemo PostgreSQL upite kako bismo obezbedili da budu efikasni i da izbegavaju uobičajene antiobrasce online obrade transakcija (OLTP). Na primer, jednom smo identifikovali izuzetno skup upit koji je spajao 12 tabela, a skokovi u tom upitu bili su odgovorni za ranije SEV incidente visoke ozbiljnosti. Treba izbegavati složene joinove nad više tabela kad god je to moguće. Ako su joinovi neophodni, naučili smo da razmotrimo razbijanje upita i premeštanje složene logike joinova na sloj aplikacije. Mnoge od ovih problematičnih upita generišu Object-Relational Mapping okviri (ORM-ovi), pa je važno pažljivo pregledati SQL koji proizvode i obezbediti da se ponaša kako se očekuje. Takođe je uobičajeno pronaći dugotrajne neaktivne upite u PostgreSQL-u. Podešavanje vremenskih ograničenja poput idle_in_transaction_session_timeout ključno je kako bi se sprečilo da blokiraju autovacuum.

Ublažavanje jedinstvene tačke otkaza

Izazov: Ako replika za čitanje otkaže, saobraćaj i dalje može da se usmeri na druge replike. Međutim, oslanjanje na jednog pisača znači i postojanje jedinstvene tačke otkaza — ako ona otkaže, pogođena je cela usluga.

Rešenje: Većina kritičnih zahteva uključuje samo upite za čitanje. Da bismo ublažili jedinstvenu tačku otkaza na primarnoj instanci, ta čitanja smo prebacili sa pisača na replike, obezbeđujući da ti zahtevi mogu da nastave da se opslužuju čak i ako primarna instanca otkaže. Iako bi operacije upisa i dalje otkazivale, uticaj je smanjen; to više nije SEV0 jer su čitanja i dalje dostupna.

Da bismo ublažili otkaze primarne instance, pokrećemo je u režimu visoke dostupnosti (HA) sa toplom rezervom, kontinuirano sinhronizovanom replikom koja je uvek spremna da preuzme opsluživanje saobraćaja. Ako primarna instanca otkaže ili treba da bude isključena radi održavanja, možemo brzo da promovišemo rezervu kako bismo smanjili zastoj. Tim Azure PostgreSQL-a uložio je značajan rad da obezbedi da ova prebacivanja ostanu bezbedna i pouzdana čak i pod veoma velikim opterećenjem. Da bismo se nosili sa otkazima replika za čitanje, raspoređujemo više replika u svakom regionu sa dovoljnim rezervama kapaciteta, obezbeđujući da otkaz jedne replike ne dovede do regionalnog prekida rada.

Izolacija opterećenja

Izazov: Često nailazimo na situacije u kojima određeni zahtevi troše nesrazmerno veliku količinu resursa na PostgreSQL instancama. To može dovesti do degradacije performansi drugih opterećenja koja rade na istim instancama. Na primer, lansiranje nove funkcije može uvesti neefikasne upite koji intenzivno troše PostgreSQL CPU, usporavajući zahteve za druge kritične funkcije.

Solution: To mitigate the “noisy neighbor” problem, we isolate workloads onto dedicated instances to ensure that sudden spikes in resource-intensive requests don’t impact other traffic. Specifically, we split requests into low-priority and high-priority tiers and route them to separate instances. This way, even if a low-priority workload becomes resource-intensive, it won’t degrade the performance of high-priority requests. We apply the same strategy across different products and services as well, so that activity from one product does not affect the performance or reliability of another.

Connection pooling

Challenge: Each instance has a maximum connection limit (5,000 in Azure PostgreSQL). It’s easy to run out of connections or accumulate too many idle ones. We’ve previously had incidents caused by connection storms that exhausted all available connections.

Solution: We deployed PgBouncer as a proxy layer to pool database connections. Running it in statement or transaction pooling mode allows us to efficiently reuse connections, greatly reducing the number of active client connections. This also cuts connection setup latency: in our benchmarks, the average connection time dropped from 50 milliseconds (ms) to 5 ms. Inter-region connections and requests can be expensive, so we co-locate the proxy, clients, and replicas in the same region to minimize network overhead and connection use time. Moreover, PgBouncer must be configured carefully. Settings like idle timeouts are critical to prevent connection exhaustion.

dijagram PostgreSQL proksija

Svaka replika za čitanje ima sopstveni Kubernetes deployment sa više PgBouncer podova. Pokrećemo više Kubernetes deploymenta iza iste Kubernetes Service, koja raspodeljuje saobraćaj između podova.

Caching

Challenge: A sudden spike in cache misses can trigger a surge of reads on the PostgreSQL database, saturating CPU and slowing user requests.

Solution: To reduce read pressure on PostgreSQL, we use a caching layer to serve most of the read traffic. However, when cache hit rates drop unexpectedly, the burst of cache misses can push a large volume of requests directly to PostgreSQL. This sudden increase in database reads consumes significant resources, slowing down the service. To prevent overload during cache-miss storms, we implement a cache locking (and leasing) mechanism so that only a single reader that misses on a particular key fetches the data from PostgreSQL. When multiple requests miss on the same cache key, only one request acquires the lock and proceeds to retrieve the data and repopulate the cache. All other requests wait for the cache to be updated rather than all hitting PostgreSQL at once. This significantly reduces redundant database reads and protects the system from cascading load spikes.

Scaling read replicas

Challenge: The primary streams Write Ahead Log (WAL) data to every read replica. As the number of replicas increases, the primary must ship WAL to more instances, increasing pressure on both network bandwidth and CPU. This causes higher and more unstable replica lag, which makes the system harder to scale reliably.

Solution: We operate nearly 50 read replicas across multiple geographic regions to minimize latency. However, with the current architecture, the primary must stream WAL to every replica. Although it currently scales well with very large instance types and high-network bandwidth, we can’t keep adding replicas indefinitely without eventually overloading the primary. To address this, we’re collaborating with the Azure PostgreSQL team on cascading replication(отвара се у новом прозору), where intermediate replicas relay WAL to downstream replicas. This approach allows us to scale to potentially over a hundred replicas without overwhelming the primary. However, it also introduces additional operational complexity, particularly around failover management. The feature is still in testing; we’ll ensure it’s robust and can fail over safely before rolling it out to production.

dijagram kaskadne replikacije PostgreSQL-a

Rate limit

Challenge: A sudden traffic spike on specific endpoints, a surge of expensive queries, or a retry storm can quickly exhaust critical resources such as CPU, I/O, and connections, which causes widespread service degradation.

Solution: We implemented rate-limiting across multiple layers—application, connection pooler, proxy, and query—to prevent sudden traffic spikes from overwhelming database instances and triggering cascading failures. It’s also crucial to avoid overly short retry intervals, which can trigger retry storms. We also enhanced the ORM layer to support rate limiting and when necessary, fully block specific query digests. This targeted form of load shedding enables rapid recovery from sudden surges of expensive queries.

Schema Management

Challenge: Even a small schema change, such as altering a column type, can trigger a full table rewrite(отвара се у новом прозору). We therefore apply schema changes cautiously—limiting them to lightweight operations and avoiding any that rewrite entire tables.

Solution: Only lightweight schema changes are permitted, such as adding or removing certain columns that do not trigger a full table rewrite. We enforce a strict 5-second timeout on schema changes. Creating and dropping indexes concurrently is allowed. Schema changes are restricted to existing tables. If a new feature requires additional tables, they must be in alternative sharded systems such as Azure CosmosDB rather than PostgreSQL. When backfilling a table field, we apply strict rate limits to prevent write spikes. Although this process can sometimes take over a week, it ensures stability and avoids any production impact.

Results and the road ahead

This effort demonstrates that with the right design and optimizations, Azure PostgreSQL can be scaled to handle the largest production workloads. PostgreSQL handles millions of QPS for read-heavy workloads, powering OpenAI’s most critical products like ChatGPT and the API platform. We added nearly 50 read replicas, while keeping replication lag near zero, maintained low-latency reads across geo-distributed regions, and built sufficient capacity headroom to support future growth.

This scaling works while still minimizing latency and improving reliability. We consistently deliver low double-digit millisecond p99 client-side latency and five-nines availability in production. And over the past 12 months, we’ve had only one SEV-0 PostgreSQL incident (it occurred during the viral launch(отвара се у новом прозору) of ChatGPT ImageGen, when write traffic suddenly surged by more than 10x as over 100 million new users signed up within a week.)

While we’re happy with how far PostgreSQL has taken us, we continue to push its limits to ensure we have sufficient runway for future growth. We’ve already migrated the shardable write-heavy workloads to our sharded systems like CosmosDB. The remaining write-heavy workloads are more challenging to shard—we’re actively migrating those as well to further offload writes from the PostgreSQL primary. We’re also working with Azure to enable cascading replication so we can safely scale to significantly more read replicas.

Looking ahead, we’ll continue to explore additional approaches to further scale, including sharded PostgreSQL or alternative distributed systems, as our infrastructure demands continue to grow.

Аутор

Bohan Zhang

Zahvalnice

Posebno se zahvaljujemo Jonu Leeju, Sichengu Liuu, Chaominu Yuu i Chenglongu Hau, koji su doprineli ovom tekstu, kao i celom timu koji je pomogao u skaliranju PostgreSQL-a. Takođe bismo želeli da zahvalimo Azure PostgreSQL timu na snažnom partnerstvu.