გადადით მთავარ შინაარსზე
OpenAI

22 იანვარი, 2026

ინჟინერია

PostgreSQL-ის მასშტაბირება 800 მილიონი ChatGPT მომხმარებლისთვის

ავტორი: ბოჰან ჟანგი, ტექნიკური შტაბის წევრი

იტვირთება…

წლების განმავლობაში PostgreSQL იყო ერთ-ერთი ყველაზე კრიტიკული, ფონურად მოქმედი მონაცემთა სისტემა, რომელიც ამუშავებს ისეთ ძირითად პროდუქტებს, როგორიცაა ChatGPT და OpenAI-ის API. ჩვენი მომხმარებლების ბაზის სწრაფ ზრდასთან ერთად, ჩვენი მონაცემთა ბაზების მიმართ მოთხოვნაც ექსპონენციურად გაიზარდა. გასული წლის განმავლობაში PostgreSQL-ზე დატვირთვა 10-ჯერ მეტად გაიზარდა და კვლავაც სწრაფად მატულობს.

ჩვენი მცდელობებმა, რომ ეს ზრდა საწარმოო ინფრასტრუქტურის განვითარებით შეგვენარჩუნებინა, ახალი დასკვნა გვაჩვენა: PostgreSQL შეიძლება მასშტაბირდეს ისე, რომ საიმედოდ უზრუნველყოს ბევრად უფრო დიდი, კითხვებზე ორიენტირებული დატვირთვები, ვიდრე ადრე ბევრს შესაძლებლად მიაჩნდა. ამ სისტემამ (რომელიც თავდაპირველად კალიფორნიის უნივერსიტეტის, ბერკლის მეცნიერთა გუნდმა შექმნა) მოგვცა შესაძლებლობა, გლობალური მასშტაბის უზარმაზარი ტრაფიკი მოგვეხმარებინა ერთი ძირითადი Azure PostgreSQL flexible server instance(იხსნება ახალ ფანჯარაში)-ით და თითქმის 50 read replica-თი, რომლებიც მსოფლიოს სხვადასხვა რეგიონშია განაწილებული. ესაა ისტორია იმის შესახებ, თუ როგორ მოვახდინეთ OpenAI-ში PostgreSQL-ის მასშტაბირება, რათა 800 მილიონი მომხმარებლისთვის წამში მილიონობით მოთხოვნა გაგვეძლო მკაცრი ოპტიმიზაციებითა და ძლიერი ინჟინერიით; ასევე გავაზიარებთ მთავარ გაკვეთილებს, რომლებიც ამ გზაზე ვისწავლეთ.

ბზარები ჩვენს საწყის დიზაინში

ChatGPT‑ის გაშვების შემდეგ ტრაფიკი უპრეცედენტო ტემპით გაიზარდა. მის მხარდასაჭერად სწრაფად დავნერგეთ ფართო ოპტიმიზაციები როგორც აპლიკაციის, ისე PostgreSQL მონაცემთა ბაზის ფენებზე, გავზარდეთ მასშტაბი ინსტანციის ზომის გაზრდით და გავაფართოეთ ის მეტი read replica-ს დამატებით. ეს არქიტექტურა დიდი ხნის განმავლობაში კარგად გვემსახურებოდა. უწყვეტი გაუმჯობესებებით ის მომავალ ზრდასაც საკმარის სივრცეს უტოვებს.

შეიძლება გასაკვირი ჩანდეს, რომ ერთ-ძირითად-კვანძიანი არქიტექტურა OpenAI-ის მასშტაბის მოთხოვნებს აკმაყოფილებს; თუმცა პრაქტიკაში ამის ამუშავება მარტივი არ არის. რამდენიმე SEV გვინახავს, რომლებიც Postgres-ის გადატვირთვამ გამოიწვია, და ისინი ხშირად ერთსა და იმავე ნიმუშს მიჰყვება: ზედა ფენაში არსებული პრობლემა მონაცემთა ბაზაზე დატვირთვის უეცარ ზრდას იწვევს, მაგალითად, ქეშირების ფენის მარცხის გამო მასობრივი cache miss-ები, ძვირადღირებული მრავალმხრივი join-ების მოზღვავება, რომელიც CPU-ს აჯერებს, ან ახალი ფუნქციის გაშვებით გამოწვეული write storm. როცა რესურსების გამოყენება იზრდება, მოთხოვნების დაყოვნება იმატებს და მოთხოვნები timeout-ს აღწევს. ამის შემდეგ retry-ები დატვირთვას კიდევ უფრო აძლიერებს, რაც ქმნის მანკიერ ციკლს და შესაძლოა მთლიანად დააზიანოს ChatGPT‑ისა და API-ის სერვისები.

დატვირთვის მასშტაბირების დიაგრამა

მიუხედავად იმისა, რომ PostgreSQL ჩვენს კითხვებზე ორიენტირებულ დატვირთვებში კარგად მასშტაბირდება, მაღალი write ტრაფიკის პერიოდებში მაინც ვაწყდებით სირთულეებს. ეს ძირითადად PostgreSQL-ის multiversion concurrency control-ის (MVCC) განხორციელებით არის განპირობებული, რაც მას write-heavy დატვირთვებისთვის ნაკლებად ეფექტიანს ხდის. მაგალითად, როცა მოთხოვნა tuple-ს ან თუნდაც ერთ ველს აახლებს, ახალი ვერსიის შესაქმნელად მთელი მწკრივი კოპირდება. მძიმე write დატვირთვისას ეს მნიშვნელოვან write amplification-ს იწვევს. ასევე იზრდება read amplification-იც, რადგან მოთხოვნებმა უახლესი ვერსიის მისაღებად tuple-ის მრავალ ვერსიას (dead tuple-ებს) შორის უნდა გაიაროს. MVCC დამატებით სირთულეებსაც ქმნის, როგორიცაა ცხრილების და ინდექსების ბლოუტი, ინდექსების მოვლის გაზრდილი ზედნადები და autovacuum-ის კომპლექსური გამართვა. (ამ საკითხებზე ღრმა ანალიზი შეგიძლიათ ნახოთ ბლოგში, რომელიც კარნეგი მელონის უნივერსიტეტის პროფ. ენდი პავლოსთან ერთად დავწერე და ჰქვია PostgreSQL-ის ის ნაწილი, რომელიც ყველაზე მეტად გვძულს(იხსნება ახალ ფანჯარაში), და რომელიც PostgreSQL-ის Wikipedia გვერდზეც არის ციტირებული(იხსნება ახალ ფანჯარაში).)

PostgreSQL-ის მასშტაბირება მილიონობით QPS-მდე

ამ შეზღუდვების შესამსუბუქებლად და write წნეხის შესამცირებლად, shardable (ანუ ჰორიზონტალურად დაყოფადი) write-heavy დატვირთვები გადავიტანეთ და კვლავაც გადაგვაქვს shard-ულ სისტემებში, როგორიცაა Azure Cosmos DB, თან აპლიკაციის ლოგიკას ისე ვაუმჯობესებთ, რომ ზედმეტი ჩაწერები შევამციროთ. ასევე, მიმდინარე PostgreSQL deployment-ში ახალი ცხრილების დამატება აღარ არის ნებადართული. ახალი დატვირთვები ნაგულისხმევად shard-ულ სისტემებზე მიდის.

მიუხედავად იმისა, რომ ჩვენი ინფრასტრუქტურა განვითარდა, PostgreSQL shard-ების გარეშე დარჩა და ყველა ჩაწერას ერთი primary instance ემსახურება. ამის მთავარი მიზეზი ისაა, რომ არსებული აპლიკაციური დატვირთვების დაშარდვა ძალიან რთული და შრომატევადი იქნებოდა, მოითხოვდა ცვლილებებს ასობით application endpoint-ში და შესაძლოა თვეები ან წლებიც კი დაეჭირა. რადგან ჩვენი დატვირთვები უმეტესად კითხვებზეა ორიენტირებული და ფართო ოპტიმიზაციებიც დავნერგეთ, არსებული არქიტექტურა ჯერ კიდევ საკმარის მარაგს გვაძლევს ტრაფიკის შემდგომი ზრდის მხარდასაჭერად. მიუხედავად იმისა, რომ მომავალში PostgreSQL-ის დაშარდვას არ გამოვრიცხავთ, ახლო ვადაში ეს პრიორიტეტი არ არის, რადგან მიმდინარე და მომავალი ზრდისთვის საკმარისი სივრცე გვაქვს.

შემდეგ სექციებში განვიხილავთ იმ გამოწვევებს, რომლებსაც წავაწყდით, და იმ ფართო ოპტიმიზაციებს, რომლებიც მათ გადასაჭრელად და მომავალი გათიშვების თავიდან ასაცილებლად დავნერგეთ, რათა PostgreSQL მისი შესაძლებლობების ზღვრამდე მიგვეყვანა და წამში მილიონობით მოთხოვნამდე (QPS) მოგვემასშტაბირებინა.

Primary-ზე დატვირთვის შემცირება

გამოწვევა: მხოლოდ ერთი ჩამწერით, single-primary კონფიგურაცია ჩაწერების მასშტაბირებას ვერ ახერხებს. write-ის ძლიერი პიკები სწრაფად ტვირთავს primary-ს და გავლენას ახდენს ისეთ სერვისებზე, როგორიცაა ChatGPT და ჩვენი API.

გადაწყვეტა: ვცდილობთ, primary-ზე დატვირთვა მაქსიმალურად შევამციროთ — როგორც კითხვა, ისე ჩაწერა — რათა მას საკმარისი რესურსი დარჩეს write პიკების სამართავად. read ტრაფიკი შეძლებისდაგვარად replica-ებზე გადაგვაქვს. თუმცა ზოგიერთი read მოთხოვნა primary-ზე უნდა დარჩეს, რადგან ისინი write ტრანზაქციების ნაწილია. ამ შემთხვევებში ვამახვილებთ ყურადღებას იმაზე, რომ ისინი ეფექტიანი იყოს და slow query-ებს ავარიდოთ თავი. write ტრაფიკისთვის shardable, write-heavy დატვირთვები გადავიტანეთ shard-ულ სისტემებში, როგორიცაა Azure CosmosDB. დატვირთვები, რომელთა დაშარდვაც უფრო რთულია, მაგრამ მაინც მაღალი მოცულობის ჩაწერებს წარმოქმნის, გადატანას მეტ დროს მოითხოვს და ეს პროცესი ჯერ კიდევ მიმდინარეობს. ასევე აგრესიულად მოვახდინეთ ჩვენი აპლიკაციების ოპტიმიზაცია write დატვირთვის შესამცირებლად; მაგალითად, გამოვასწორეთ აპლიკაციური შეცდომები, რომლებიც ზედმეტ ჩაწერებს იწვევდა, და საჭიროების შემთხვევაში დავნერგეთ lazy write-ები, რათა ტრაფიკის პიკები გაგვეთანაბრებინა. გარდა ამისა, ცხრილის ველების backfill-ისას მკაცრ rate limit-ებს ვიყენებთ, რათა ზედმეტი write წნეხი ავიცილოთ.

მოთხოვნების ოპტიმიზაცია

გამოწვევა: PostgreSQL-ში რამდენიმე ძვირადღირებული მოთხოვნა გამოვავლინეთ. წარსულში ამ მოთხოვნების მოცულობის უეცარი პიკები CPU-ს დიდ ნაწილს ხარჯავდა, რაც ანელებდა როგორც ChatGPT‑ს, ისე API-ის მოთხოვნებს.

გადაწყვეტა: რამდენიმე ძვირადღირებულ მოთხოვნას, მაგალითად ისეთს, რომელიც ბევრ ცხრილს აერთიანებს, შეუძლია მთელი სერვისი მნიშვნელოვნად დააზიანოს ან საერთოდაც გათიშოს. საჭიროა PostgreSQL მოთხოვნების უწყვეტი ოპტიმიზაცია, რათა ისინი ეფექტიანი იყოს და თავიდან ავიცილოთ Online Transaction Processing-ის (OLTP) გავრცელებული ანტიშაბლონები. მაგალითად, ერთხელ გამოვავლინეთ უკიდურესად ძვირადღირებული მოთხოვნა, რომელიც 12 ცხრილს აერთებდა, და ამ მოთხოვნის პიკები წარსულში მაღალი სიმძიმის SEV-ების მიზეზი იყო. რთულ, მრავალცხრილოვან join-ებს შეძლებისდაგვარად უნდა ავერიდოთ. თუ join-ები აუცილებელია, ვისწავლეთ, რომ უნდა ვიფიქროთ მოთხოვნის დაშლაზე და რთული join ლოგიკის აპლიკაციის ფენაში გადატანაზე. ამ პრობლემური მოთხოვნების დიდი ნაწილი Object-Relational Mapping-ის (ORM) ფრეიმვორკების მიერ გენერირდება, ამიტომ მნიშვნელოვანია, მათ მიერ შექმნილი SQL ყურადღებით გადავამოწმოთ და დავრწმუნდეთ, რომ იგი მოსალოდნელად იქცევა. PostgreSQL-ში ასევე ხშირად გვხვდება ხანგრძლივად გაშვებული უმოქმედო მოთხოვნები. ისეთი timeout-ების კონფიგურაცია, როგორიცაა idle_in_transaction_session_timeout, აუცილებელია იმისთვის, რომ მათ autovacuum არ დაბლოკონ.

Single point of failure-ის შერბილება

გამოწვევა: თუ read replica მწყობრიდან გამოდის, ტრაფიკი მაინც შეიძლება სხვა replica-ებზე გადამისამართდეს. თუმცა ერთ writer-ზე დაყრდნობა ნიშნავს single point of failure-ის არსებობას — თუ ის გაითიშა, მთელი სერვისი ზიანდება.

გადაწყვეტა: ყველაზე კრიტიკული მოთხოვნების უმეტესობა მხოლოდ read მოთხოვნებს მოიცავს. იმისათვის, რომ primary-ში არსებული single point of failure შევამსუბუქოთ, ეს read-ები writer-იდან replica-ებზე გადავტვირთეთ, რათა primary-ის გათიშვის შემთხვევაშიც ამ მოთხოვნების მომსახურება გაგრძელდეს. მიუხედავად იმისა, რომ write ოპერაციები მაინც ჩაიშლება, გავლენა მცირდება; ეს უკვე SEV0 აღარ არის, რადგან read-ები ხელმისაწვდომი რჩება.

Primary-ის მარცხის შესამსუბუქებლად primary-ს High-Availability (HA) რეჟიმში ვამუშავებთ hot standby-სთან ერთად — მუდმივად სინქრონიზებული replica-თი, რომელიც ყოველთვის მზადაა ტრაფიკის მომსახურება გადაიბაროს. თუ primary გაითიშა ან მისი maintenance-ისთვის ოფლაინში გადაყვანაა საჭირო, standby-ს სწრაფად ვაპრომოუტებთ, რათა downtime მინიმუმამდე დავიყვანოთ. Azure PostgreSQL-ის გუნდმა მნიშვნელოვანი სამუშაო ჩაატარა იმის უზრუნველსაყოფად, რომ ასეთი failover-ები ძალიან მაღალი დატვირთვის პირობებშიც უსაფრთხო და საიმედო დარჩეს. read replica-ების მარცხის სამართავად თითოეულ რეგიონში რამდენიმე replica-ს ვუშვებთ საკმარისი სიმძლავრის მარაგით, რათა ერთი replica-ს მარცხმა რეგიონული გათიშვა არ გამოიწვიოს.

დატვირთვების იზოლაცია

გამოწვევა: ხშირად ვაწყდებით სიტუაციებს, როცა გარკვეული მოთხოვნები PostgreSQL ინსტანციებზე რესურსების არაპროპორციულად დიდ ნაწილს მოიხმარს. ეს შეიძლება იმავე ინსტანციებზე გაშვებული სხვა დატვირთვების მუშაობის გაუარესებას იწვევდეს. მაგალითად, ახალი ფუნქციის გაშვებამ შეიძლება არაეფექტიანი მოთხოვნები მოიტანოს, რომლებიც PostgreSQL-ის CPU-ს ძლიერ იტვირთავს და სხვა კრიტიკული ფუნქციების მოთხოვნებს ანელებს.

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.

PostgreSQL პროქსის დიაგრამა

თითოეულ read replica-ს აქვს საკუთარი Kubernetes deployment, სადაც გაშვებულია რამდენიმე PgBouncer pod. ჩვენ ერთსა და იმავე Kubernetes Service-ის უკან ვუშვებთ რამდენიმე Kubernetes deployment-ს, რომელიც ტრაფიკს pod-ებს შორის ანაწილებს.

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.

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(იხსნება ახალ ფანჯარაში). 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

მადლობები

განსაკუთრებული მადლობა ჯონ ლის, სიჩენგ ლიუს, ჩაომინ იუს და ჩენგლონგ ჰაოს, რომლებმაც წვლილი შეიტანეს ამ პოსტში, და მთელ გუნდს, რომელიც PostgreSQL-ის მასშტაბირებაში დაგვეხმარა. ასევე გვინდა მადლობა გადავუხადოთ Azure PostgreSQL-ის გუნდს ძლიერი პარტნიორობისთვის.