Sari la conținutul principal
OpenAI

22 ianuarie 2026

Inginerie

Scalarea PostgreSQL pentru 800 de milioane de utilizatori ChatGPT

De Bohan Zhang, membru al personalului tehnic

Se încarcă…

De ani de zile, PostgreSQL este unul dintre cele mai importante sisteme de date care susțin produse esențiale precum ChatGPT și API-ul OpenAI. Odată cu creșterea rapidă a bazei de utilizatori, au crescut și cerințele privind bazele noastre de date. În ultimul an, sarcina PostgreSQL a crescut de peste 10 ori și continuă să crească rapid.

Eforturile noastre de a îmbunătăți infrastructura de producție pentru a susține această creștere au revelat o nouă perspectivă: PostgreSQL poate fi scalabil pentru a accepta sarcini de lucru mult mai mari, cu un volum mare de citiri, decât se credea că ar fi posibil până acum. Sistemul (creat inițial de o echipă de cercetători de la Universitatea din California, Berkeley) ne-a permis să sprijinim un trafic global masiv cu o singură instanță principală de server flexibilă Azure PostgreSQL(se deschide într-o fereastră nouă) și aproape 50 de replicări de citire răspândite în mai multe regiuni la nivel global. Aceasta este povestea modului în care am scalat PostgreSQL la OpenAI pentru a sprijini milioane de interogări pe secundă pentru 800 de milioane de utilizatori prin optimizări riguroase și inginerie solidă; de asemenea, vom prezenta concluziile cheie pe care le-am învățat pe parcurs.

Problemele din proiectul nostru inițial

După lansarea ChatGPT, traficul a crescut într-un ritm fără precedent. Pentru a-l susține, am implementat rapid optimizări extinse atât la nivelul aplicației, cât și la nivelul bazei de date PostgreSQL, am mărit capacitatea prin creșterea dimensiunii instanței și prin adăugarea mai multor replicări de citire. Această arhitectură ne-a fost utilă mult timp. Datorită îmbunătățirilor constante, continuă să ofere oportunități ample pentru o creștere viitoare.

Poate părea surprinzător că o arhitectură cu un singur element primar poate satisface cerințele de scalare ale OpenAI; cu toate acestea, implementarea acestui lucru în practică nu este simplă. Am observat mai multe SEV-uri cauzate de supraîncărcarea Postgres, care urmează adesea același tipar: o problemă în amonte provoacă o creștere bruscă a încărcării bazei de date, cum ar fi eșecuri generalizate ale cache-ului din cauza unei defecțiuni a stratului de cache, o creștere bruscă a îmbinărilor multidirecționale costisitoare care saturează CPU-ul sau o aglomerare de scrieri din cauza lansării unei noi funcții. Pe măsură ce utilizarea resurselor crește, latența interogărilor se mărește și cererile încep să expire. Reîncercările amplifică și mai mult încărcarea, declanșând un cerc vicios care poate afecta întregul serviciu ChatGPT și API.

Diagrama de scalare a sarcinii

Deși PostgreSQL se scalează bine pentru sarcinile noastre de lucru cu volum mare de citire, încă întâmpinăm provocări în perioadele cu trafic ridicat de scriere. Acest lucru se datorează în mare parte implementării controlului concurenței multiversiune (MVCC) al PostgreSQL, care îl face mai puțin eficient pentru sarcini de lucru cu volum mare de scriere. De exemplu, atunci când o interogare actualizează un tuplu sau chiar un singur câmp, întregul rând este copiat pentru a crea o nouă versiune. În condiții de sarcini mari de scriere, acest lucru duce la o amplificare semnificativă a scrierii. De asemenea, crește amplificarea citirii, deoarece interogările trebuie să scaneze mai multe versiuni de tupluri (tupluri moarte) pentru a o extrage pe cea mai recentă. MVCC impune provocări suplimentare, cum ar fi supraîncărcarea tabelelor și indexurilor, creșterea costurilor de întreținere a indexurilor și reglarea complexă a autovacuumului. (Poți găsi o analiză detaliată a acestor probleme într-un blog pe care l-am scris împreună cu prof. Andy Pavlo de la Carnegie Mellon University, intitulat The Part of PostgreSQL We Hate the Most(se deschide într-o fereastră nouă), menționat(se deschide într-o fereastră nouă) pe pagina Wikipedia a PostgreSQL.)

Scalarea PostgreSQL la milioane de QPS

Pentru a atenua aceste limitări și a reduce presiunea de scriere, am migrat și continuăm să migrăm fișiere fragmentabile (adică sarcini de lucru care pot fi partiționate orizontal), sarcini de lucru cu volum mare de scriere către sisteme partiționate, cum ar fi Azure Cosmos DB, optimizând logica aplicației pentru a minimiza scrierile inutile. De asemenea, nu mai permitem adăugarea de noi tabele la implementarea PostgreSQL curentă. În mod implicit, noile sarcini de lucru sunt direcționate către sistemele fragmentate.

Chiar dacă infrastructura noastră a evoluat, PostgreSQL a rămas nefragmentat, cu o singură instanță primară care deservește toate scrierile. Motivul principal este că fragmentarea sarcinilor de lucru ale aplicațiilor existente ar fi extrem de complexă și ar necesita mult timp, impunând modificări la sute de puncte finale ale aplicațiilor și putând dura luni sau chiar ani. Deoarece sarcinile noastre de lucru sunt în principal de citire intensivă și am implementat optimizări extinse, arhitectura actuală încă oferă suficient spațiu pentru a sprijini creștere continuă a traficului. Deși nu excludem posibilitatea fragmentării PostgreSQL în viitor, aceasta nu reprezintă o prioritate pe termen scurt, având în vedere că dispunem de suficiente resurse pentru creșterea actuală și viitoare.

În secțiunile următoare, vom analiza provocările cu care ne-am confruntat și optimizările extinse pe care le-am implementat pentru a le rezolva și pentru a preveni întreruperile viitoare, forțând limitele PostgreSQL și scalându-l la milioane de interogări pe secundă (QPS).

Reducerea sarcinii asupra sistemului primar

Provocare: având un singur scriitor, o configurație cu un singur element primar nu poate scala operațiunile de scriere. Creșterile bruște ale volumului de scriere pot supraîncărca rapid sistemul primar și pot afecta servicii precum ChatGPT și API-ul nostru.

Soluție: reducem la minimum sarcina asupra sistemului primar, atât în timpul operațiunilor de citire, cât și al celor de scriere, pentru a ne asigura că are o capacitate suficientă pentru a gestiona creșterile bruște ale volumului de scriere. Traficul de citire este descărcat către replicări ori de câte ori este posibil. Totuși, unele interogări de citire trebuie să rămână pe serverul principal, deoarece fac parte din tranzacții de scriere. Pentru acestea, ne concentrăm pe asigurarea eficienței lor și evitarea interogărilor lente. Pentru traficul de scriere, am migrat sarcini de lucru fragmentabile și cu utilizare intensă a scrierii către sisteme fragmentate, cum ar fi Azure CosmosDB. Migrarea sarcinilor de lucru care sunt mai greu de fragmentat, dar care generează totuși un volum mare de scriere, durează mai mult, iar acest proces este încă în desfășurare. De asemenea, am optimizat agresiv aplicațiile noastre pentru a reduce sarcina de scriere; de exemplu, am remediat bugurile aplicațiilor care cauzau scrieri redundante și am introdus scrieri lente, acolo unde a fost cazul, pentru a atenua creșterile bruște ale traficului. În plus, atunci când completăm retroactiv câmpurile tabelului, impunem limite stricte de rată pentru a preveni o presiune excesivă de scriere.

Optimizarea interogărilor

Provocare: am identificat mai multe interogări costisitoare în PostgreSQL. În trecut, creșterile bruște de volum din aceste interogări consumau cantități mari de CPU, încetinind atât cererile ChatGPT, cât și cele API.

Soluție: câteva interogări costisitoare, cum ar fi cele care unesc mai multe tabele, pot degrada semnificativ sau chiar pot întrerupe întregul serviciu. Trebuie să optimizăm continuu interogările PostgreSQL pentru a ne asigura că sunt eficiente și pentru a evita anti-modelele comune de procesare a tranzacțiilor online (OLTP). De exemplu, am identificat o interogare extrem de costisitoare care unea 12 tabele, în care creșterile bruște ale acestei interogări erau responsabile pentru SEV-uri cu severitate ridicată din trecut. Ar trebui să evităm îmbinările complexe cu mai multe tabele ori de câte ori este posibil. Dacă sunt necesare îmbinări, trebuie să luăm în considerare divizarea interogării și mutarea logicii complexe de îmbinare în stratul aplicației. Multe dintre aceste interogări problematice sunt generate de cadrele de mapare obiect-relațional (ORM), deci este important să verifici cu atenție SQL-ul pe care îl produc și să te asiguri că se comportă conform așteptărilor. De asemenea, în PostgreSQL întâlnim frecvent interogări inactive de lungă durată. Configurarea timpilor de expirare, cum ar fi idle_in_transaction_session_timeout, este esențială pentru a împiedica blocarea autovacuumului.

Atenuarea unui punct de eșec unic

Provocare: dacă o replicare de citire se defectează, traficul poate fi în continuare direcționat către alte replicări. Totuși, a te baza pe un singur scriitor înseamnă a avea un singur punct de eșec — dacă acesta se defectează, întregul serviciu este afectat.

Soluție: majoritatea cererilor critice implică doar interogări de citire. Pentru a atenua punctul de eșec unic din sistemul primar, am descărcat acele citiri de la scriitor către replicări, asigurându-ne că acele cereri pot continua să funcționeze chiar și în cazul în care sistemul primar se defectează. Deși operațiunile de scriere vor continua să eșueze, impactul este redus; nu mai este vorba de un SEV0, deoarece operațiunile de citire rămân disponibile.

Pentru a atenua defecțiunile sistemului primar, îl rulăm în modul de înaltă disponibilitate (HA) cu un sistem de rezervă activ, o replicare sincronizată continuu, care este întotdeauna gata să preia traficul. Dacă sistemul principal se defectează sau trebuie să fie deconectat pentru mentenanță, putem trece rapid la sistemul de rezervă pentru a reduce la minimum timpul de nefuncționare. Echipa Azure PostgreSQL a depus eforturi semnificative pentru a se asigura că aceste comutări la rezervă rămân securizate și fiabile chiar și în condiții de sarcină foarte ridicată. Pentru a gestiona eșecurile replicărilor de citire, implementăm mai multe replicări în fiecare regiune cu o capacitate suficientă, asigurându-ne că un singur eșec al replicării nu va duce la o într-o întrerupere regională.

Izolarea sarcinii de lucru

Provocare: întâlnim adesea situații în care anumite cereri consumă o cantitate disproporționată de resurse în instanțele PostgreSQL. Acest lucru poate duce la o performanță degradată pentru alte sarcini de lucru care rulează în aceleași instanțe. De exemplu, lansarea unei noi funcționalități poate introduce interogări ineficiente care consumă mult CPU PostgreSQL, încetinind cererile pentru alte funcționalități critice.

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.

diagramă proxy postgreSQL

Fiecare replicare de citire are propria implementare Kubernetes care rulează mai multe poduri PgBouncer. Rulăm mai multe implementări Kubernetes în spatele aceluiași serviciu Kubernetes, care echilibrează traficul între poduri.

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(se deschide într-o fereastră nouă), 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.

diagramă de replicare în cascadă PostgreSQL

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(se deschide într-o fereastră nouă). 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(se deschide într-o fereastră nouă) 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.

Autor

Bohan Zhang

Mulțumiri

Am dori să mulțumim în mod special lui Jon Lee, Sicheng Liu, Chaomin Yu și Chenglong Hao, care au contribuit la realizarea acestui articol, precum și întregii echipe care a ajutat la scalarea PostgreSQL. De asemenea, dorim să-i mulțumim echipei Azure PostgreSQL pentru parteneriatul solid.