Үндсэн агуулга руу алгасах
OpenAI

2026 оны нэгдүгээр сарын 22

Инженерчлэл

800 сая ChatGPT хэрэглэгчийг дэмжих PostgreSQL-ийн масштаблалт

Техникийн багийн гишүүн Bohan Zhang

Ачаалж байна…

Олон жилийн турш PostgreSQL нь ChatGPT болон OpenAI-ийн API зэрэг үндсэн бүтээгдэхүүнүүдийг тэтгэдэг, дотооддоо хамгийн чухал өгөгдлийн системүүдийн нэг байсаар ирсэн. Манай хэрэглэгчийн бааз хурдацтай өсөхийн хэрээр мэдээллийн сангууд дээрх ачаалал ч мөн огцом нэмэгдсэн. Өнгөрсөн нэг жилийн хугацаанд PostgreSQL дээрх ачаалал маань 10 дахинаас илүү өссөн бөгөөд одоо ч хурдтай өссөөр байна.

Энэ өсөлтийг даахын тулд продакшны дэд бүтцээ сайжруулах явцад бид нэг шинэ ойлголтод хүрсэн: PostgreSQL-ийг олон хүний өмнө нь боломжтой гэж бодож байснаас 훨 илүү том, уншилт давамгайлсан ачааллыг найдвартай дэмжихээр масштаблаж болдог. (Анх Калифорнийн Их Сургууль, Берклигийн эрдэмтдийн баг бүтээсэн) энэ систем нь Azure PostgreSQL flexible server instance(шинэ цонхонд нээгдэнэ)-ийн ганц primary болон дэлхийн олон бүсэд тархсан бараг 50 read replica-тайгаар асар их глобал урсгалыг дэмжих боломжийг бидэнд олгосон. Энэ бол OpenAI дээр бид PostgreSQL-ийг хатуу оновчлол, найдвартай инженерчлэлийн тусламжтайгаар 800 сая хэрэглэгчийн секундэд сая сая хүсэлтийг дэмжих хүртэл хэрхэн масштабласан түүх юм; мөн бид замдаа сурсан гол сургамжуудаа хуваалцана.

Анхны загварт гарсан цавууд

ChatGPT нээгдсэний дараа урсгал урьд өмнө байгаагүй хурдаар өссөн. Үүнийг дэмжихийн тулд бид application болон PostgreSQL мэдээллийн сангийн түвшинд өргөн хүрээний оновчлолуудыг хурдан хэрэгжүүлж, instance-ийн хэмжээг өсгөн босоогоор, илүү олон read replica нэмж хэвтээгээр масштабласан. Энэ архитектур бидэнд удаан хугацаанд сайн үйлчилсэн. Тасралтгүй сайжруулалтуудын ачаар ирээдүйн өсөлтөд ч хангалттай зай үлдээсээр байна.

Ганц primary-тэй архитектур OpenAI-ийн хэмжээний шаардлагыг хангаж чадна гэхээр гайхалтай сонсогдож магадгүй; гэхдээ үүнийг бодит байдал дээр ажиллуулах нь амаргүй. Postgres-ийн хэт ачааллаас үүдсэн хэд хэдэн SEV бидэнд тохиолдож байсан бөгөөд тэд ихэвчлэн ижил хэв маягтай: дээд давхаргын асуудал мэдээллийн сангийн ачааллыг гэнэт өсгөдөг, жишээлбэл кэшийн давхаргын алдаанаас үүдсэн өргөн хүрээний cache miss, CPU-г дүүргэх өндөр өртөгтэй олон талт join-уудын өсөлт, эсвэл шинэ функцийн нээлтээс үүдсэн бичилтийн шуурга. Нөөцийн ашиглалт өсөх тусам query-ийн саатал нэмэгдэж, хүсэлтүүд timeout болж эхэлдэг. Дараах дахин оролдлогууд нь ачааллыг улам өсгөж, ChatGPT болон API үйлчилгээг бүхэлд нь доройтуулах эрсдэлтэй хорт мөчлөгийг өдөөдөг.

Ачааллын масштаблалтын диаграм

PostgreSQL нь манай уншилт давамгайлсан ачаалалд сайн масштаблагддаг ч бичилт ихтэй үеүүдэд бидэнд сорилтууд тулгарсаар байна. Энэ нь ихэвчлэн PostgreSQL-ийн multiversion concurrency control (MVCC) хэрэгжилттэй холбоотой бөгөөд энэ нь бичилт давамгайлсан ачаалалд харьцангуй үр ашиг багатай болгодог. Жишээлбэл, query нь tuple эсвэл ганц талбарыг шинэчлэхэд ч бүх мөрийг хуулж шинэ хувилбар үүсгэдэг. Бичилт ихтэй ачааллын үед энэ нь бичилтийн өсгөлтийг мэдэгдэхүйц нэмэгдүүлдэг. Мөн query-ууд хамгийн сүүлийн хувилбарыг авахын тулд tuple-ийн олон хувилбар (dead tuple)-аар дамжин скан хийх шаардлагатай болдог тул уншилтын өсгөлт ч нэмэгддэг. MVCC нь хүснэгт ба индексийн дүүрэлт, индексийн арчилгааны нэмэлт зардал, autovacuum-ийн нарийн тааруулга зэрэг нэмэлт сорилтуудыг бий болгодог. (Эдгээр асуудлын гүнзгий задлан шинжилгээг би Carnegie Mellon University-ийн Prof. Andy Pavlo-той хамтран бичсэн The Part of PostgreSQL We Hate the Most(шинэ цонхонд нээгдэнэ) нэртэй блогт, PostgreSQL Wikipedia хуудсанд эш татагдсан(шинэ цонхонд нээгдэнэ) байдлаар олж болно.)

PostgreSQL-ийг секундэд сая сая QPS-д хүргэн масштаблах нь

Эдгээр хязгаарлалтыг бууруулж, бичилтийн дарамтыг багасгахын тулд бид shard хийх боломжтой (өөрөөр хэлбэл хэвтээ хувааж болох) бичилт давамгайлсан ачааллуудыг Azure Cosmos DB зэрэг shard-тай системүүд рүү шилжүүлсэн бөгөөд шаардлагагүй бичилтийг багасгахын тулд application-ийн логикийг оновчилсоор байна. Мөн одоогийн PostgreSQL deployment дээр шинэ хүснэгт нэмэхийг бид цаашид зөвшөөрөхгүй. Шинэ ачааллууд анхнаасаа shard-тай системүүдийг ашигладаг.

Манай дэд бүтэц хувьсан өөрчлөгдсөн ч PostgreSQL нь shard хийгдээгүй хэвээр, бүх бичилтийг ганц primary instance хариуцсаар байна. Үндсэн шалтгаан нь одоо ажиллаж буй application-ийн ачааллуудыг shard хийх нь маш төвөгтэй, цаг их шаардсан ажил бөгөөд олон зуун application эцсийн цэгт өөрчлөлт оруулах хэрэгтэй болж, саруудаар, бүр жилээр ч үргэлжилж магадгүй. Манай ачаалал ихэвчлэн уншилт давамгайлсан бөгөөд бид өргөн хүрээний оновчлолуудыг хэрэгжүүлсэн тул одоогийн архитектур нь урсгалын цаашдын өсөлтийг дэмжих хангалттай нөөцтэй хэвээр байна. Ирээдүйд PostgreSQL-ийг shard хийх боломжийг бид үгүйсгэхгүй ч одоогийн болон ирээдүйн өсөлтөд хангалттай зай байгаа тул энэ нь ойрын хугацааны тэргүүлэх чиглэл биш байна.

Дараах хэсгүүдэд бид тулгарсан сорилтууд болон тэдгээрийг шийдэж, ирээдүйн доголдлоос сэргийлэхийн тулд хэрэгжүүлсэн өргөн хүрээний оновчлолуудыг гүнзгий авч үзнэ. Ингэснээр PostgreSQL-ийг хязгаарт нь тултал ашиглаж, секундэд сая сая query (QPS)-д хүргэн масштабласан.

Primary дээрх ачааллыг бууруулах

Сорилт: Зөвхөн нэг writer-тэй үед ганц primary тохиргоо бичилтийг масштаблаж чаддаггүй. Бичилтийн огцом өсөлт primary-г маш хурдан хэт ачааллуулж, ChatGPT болон манай API зэрэг үйлчилгээнд нөлөөлж чадна.

Шийдэл: Бид primary дээрх ачааллыг—уншилт, бичилт хоёуланг нь—аль болох багасгаж, бичилтийн огцом өсөлтийг даах хангалттай нөөцтэй байлгахыг зорьдог. Боломжтой бүх унших урсгалыг replica руу шилжүүлдэг. Гэхдээ зарим read query нь бичилтийн transaction-ийн нэг хэсэг учраас primary дээр үлдэх шаардлагатай. Тийм query-уудын хувьд бид үр ашигтай байлгаж, удаан query-ээс зайлсхийхэд анхаардаг. Бичилтийн урсгалын хувьд бид shard хийж болох, бичилт ихтэй ачааллуудыг Azure CosmosDB зэрэг shard-тай системүүд рүү шилжүүлсэн. Shard хийхэд илүү төвөгтэй боловч өндөр хэмжээний бичилт үүсгэдэг ачааллуудыг шилжүүлэхэд илүү хугацаа шаардлагатай бөгөөд энэ ажил одоо ч үргэлжилж байна. Мөн бид application-уудаа бичилтийн ачааллыг бууруулахын тулд эрчимтэй оновчилсон; жишээлбэл давхардсан бичилт үүсгэдэг алдаануудыг засаж, тохирох газарт lazy write нэвтрүүлж, урсгалын оргил үеийг тэгшилсэн. Үүнээс гадна хүснэгтийн талбаруудыг backfill хийх үед хэт их бичилтийн дарамтаас сэргийлэхийн тулд хатуу хурдны хязгаарлалт тавьдаг.

Query оновчлол

Сорилт: Бид PostgreSQL дотор хэд хэдэн өндөр өртөгтэй query-г илрүүлсэн. Өмнө нь эдгээр query-ийн эзлэхүүн гэнэт өсөхөд CPU их хэмжээгээр хэрэглэж, ChatGPT болон API хүсэлтүүдийг удаашруулж байсан.

Шийдэл: Олон хүснэгтийг холбодог зарим өндөр өртөгтэй query нь бүх үйлчилгээг мэдэгдэхүйц доройтуулах, бүр унагах ч боломжтой. Тиймээс PostgreSQL query-уудыг үргэлж оновчилж, үр ашигтай байлгах, мөн Online Transaction Processing (OLTP)-ийн түгээмэл anti-pattern-уудаас зайлсхийх шаардлагатай. Жишээлбэл, бид нэг удаа 12 хүснэгт холбодог маш өндөр өртөгтэй query-г илрүүлсэн бөгөөд энэ query-ийн өсөлт нь өмнөх өндөр ноцтой SEV-үүдийн шалтгаан байсан. Боломжтой үед төвөгтэй олон хүснэгтийн join-оос зайлсхийх хэрэгтэй. Хэрэв join шаардлагатай бол query-г задалж, төвөгтэй join логикийг application-ийн давхарга руу шилжүүлэхийг бодолцох ёстойг бид сурсан. Эдгээр асуудалтай query-уудын олонхыг Object-Relational Mapping (ORM) framework-үүд үүсгэдэг тул тэдгээрийн гаргаж буй SQL-ийг нягтлан шалгаж, хүлээсэн шиг ажиллаж байгаа эсэхийг баталгаажуулах нь чухал. PostgreSQL-д удаан хугацаанд idle байдалтай query-ууд ч элбэг. idle_in_transaction_session_timeout зэрэг timeout-уудыг тохируулах нь autovacuum-ийг саатуулахаас сэргийлэхэд зайлшгүй хэрэгтэй.

Ганц эвдрэлийн цэгийг бууруулах

Сорилт: Read replica унтарсан ч урсгалыг бусад replica руу чиглүүлж болно. Гэвч ганц writer-д найдах нь ганц эвдрэлийн цэгтэй гэсэн үг—хэрэв тэр унтарвал бүх үйлчилгээ нөлөөлнө.

Шийдэл: Хамгийн чухал хүсэлтүүдийн дийлэнх нь зөвхөн read query-тай холбоотой. Primary дахь ганц эвдрэлийн цэгийн эрсдэлийг бууруулахын тулд бид эдгээр уншилтуудыг writer-оос replica руу шилжүүлсэн бөгөөд ингэснээр primary унтарсан ч тэдгээр хүсэлтүүд үргэлжлэн үйлчлэх боломжтой болсон. Бичилтийн үйлдлүүд алдаа өгөх хэвээр байх боловч нөлөө нь багасна; уншилт бэлэн хэвээр тул энэ нь artık SEV0 биш.

Primary-ийн алдааг бууруулахын тулд бид primary-г High-Availability (HA) горимд hot standby-тэй ажиллуулдаг. Энэ нь урсгалыг хариуцахад үргэлж бэлэн, тасралтгүй синк хийгддэг replica юм. Хэрэв primary унтарвал эсвэл засвар үйлчилгээний улмаас offline болгох шаардлагатай бол standby-г хурдан promotion хийж downtime-ийг багасгаж чадна. Azure PostgreSQL баг эдгээр failover-уудыг маш өндөр ачааллын үед ч аюулгүй, найдвартай байлгахын тулд ихээхэн ажил хийсэн. Read replica-ийн алдааг давахын тулд бид бүс бүрт хангалттай нөөцийн зайтай олон replica байршуулдаг бөгөөд ингэснээр ганц replica-ийн алдаа бүс нутгийн сааталд хүргэхгүй.

Ажлын ачааллын тусгаарлалт

Сорилт: Тодорхой хүсэлтүүд PostgreSQL instance-үүд дээр тэнцвэргүй их нөөц хэрэглэдэг нөхцөл бидэнд байнга тулгардаг. Энэ нь ижил instance дээр ажиллаж буй бусад ачааллын гүйцэтгэлийг доройтуулахад хүргэнэ. Жишээлбэл, шинэ функцийн нээлт нь PostgreSQL CPU-г ихээр хэрэглэдэг үр ашиггүй query-уудыг үүсгэж, бусад чухал функцуудын хүсэлтүүдийг удаашруулж болно.

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 прокси диаграм

Унших replica бүр олон PgBouncer pod ажиллуулдаг өөрийн Kubernetes deployment-тэй. Бид ижил 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 шаталсан replication диаграм

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

Талархал

Энэхүү нийтлэлд хувь нэмэр оруулсан Jon Lee, Sicheng Liu, Chaomin Yu, Chenglong Hao нарт, мөн PostgreSQL-ийг масштаблахад тусалсан бүх багт онцгой талархал илэрхийлье. Мөн хүчтэй хамтын ажиллагаа үзүүлсэн Azure PostgreSQL багт талархаж байна.