3 виміри масштабування систем: чому автомасштабування — це лише частина картини
Мене звати Віталій Жгута, і я cтарший cистемний архітектор в EPAM Україна. Хочу поділитися з вами власними спостереженнями щодо підходів до масштабування систем.
Проводячи технічні співбесіди на позиції DevOps-інженерів та System Architects, я помітив: коли ми переходимо до питань про масштабування системи, у більшості випадків розмова одразу зводиться до інфраструктури.
«Налаштуємо Auto Scaling Group», «Додамо ще подів у Kubernetes», «Піднімемо більший інстанс бази даних».
Кандидати можуть годинами захоплено розповідати про метрики горизонтального автомасштабування (HPA), конфігурації Terraform та стратегії розгортання без збереження стану (stateless). Але майже ніколи розмова не зачіпає фундаментальних вимірів масштабування: роботи зі станом, жорстких компромісів розподілених систем та бізнес-вимог. Ми почали плутати просте нарощування інфраструктурних ресурсів зі справжнім архітектурним масштабуванням.
Навіщо ми взагалі масштабуємо системи?
Перш ніж говорити про те, як масштабувати, варто згадати, чому ми це робимо. Все починається не з метрик використання процесора, а з бізнесу. Точніше — з SLA (Service Level Agreement), угоди про рівень послуг між нами та нашими клієнтами.
SLA визначає, що клієнт очікує від нашої системи. З цих бізнес-домовленостей виростають конкретні інженерні вимоги:
- Доступність (Availability) — частка часу, коли система готова обробляти запити (ті самі «дев’ятки»).
- Надійність (Reliability) — здатність системи виконувати свої функції без збоїв протягом заданого проміжку часу.
- Цілісність даних (Data Integrity) — гарантія того, що дані не будуть втрачені або спотворені.
- Продуктивність (Performance / Latency) — швидкість відгуку системи на дії користувача. Навіть доступна система стає «неробочою» для клієнта, якщо сторінка вантажиться 10 секунд.
- Масштабованість (Scalability) — здатність системи зберігати продуктивність при зростанні навантаження (без деградації сервісу).
- Безпека (Security) — захист даних та інфраструктури від несанкціонованого доступу, що також часто прописується в Enterprise-контрактах.
Коли кількість користувачів зростає, підтримувати ці вимоги (особливо масштабованість та доступність) в межах одного сервера стає фізично неможливо. Щоб виконати умови SLA під навантаженням, ми змушені розподіляти систему на кілька вузлів.
CAP-теорема: фундаментальні обмеження
Щойно ми переходимо від одного сервера до розподіленої системи (мережі серверів), масштабування перестає бути просто «додаванням заліза». У гру вступають фундаментальні закони обчислювальної техніки. Найвідоміший із них — CAP-теорема. Вона стверджує, що в будь-якій розподіленій системі неможливо одночасно гарантувати більше двох із трьох властивостей:
- Узгодженість (Consistency) — кожен клієнт бачить однакові, найновіші дані (або отримує помилку).
- Доступність (Availability) — успішний запит завжди отримує відповідь (без гарантії, що дані найновіші, наприклад, система може віддати застарілий кеш).
- Стійкість до розділення (Partition tolerance) — система продовжує працювати навіть у разі втрати зв’язку між її вузлами.
Ключовий момент, який часто втрачається: у реальному світі мережі завжди можуть розірватися або працювати із затримками. Тому Partition tolerance — це не опція, а даність. Оскільки уникнути проблем із мережею неможливо, інженерам завжди доводиться робити важкий вибір: коли вузли втрачають зв’язок один з одним, ми маємо зупинити систему, щоб не віддати старі дані (обрати Consistency), або продовжувати відповідати на запити, ризикуючи віддати неактуальну інформацію (обрати Availability)?
Саме цей вибір, зумовлений нашим SLA (що для бізнесу важливіше саме зараз: гарантувати точність даних чи залишатися доступним для користувачів?), диктує всю подальшу архітектуру. І ось тут ми підходимо до головного питання: якими інструментами ми можемо досягти цих цілей?
Три осі масштабування: AKF Scale Cube
Щоб розібратися, як саме ми приймаємо ці архітектурні рішення, зручно використовувати ментальну модель, відому як AKF Scale Cube. Давайте спробуємо поглянути на неї через класичну задачу з проєктування систем: сервіс коротких посилань на кшталт Bitly. Я свідомо беру банальний і знайомий усім приклад, щоб ми не відволікалися на складну бізнес-логіку, а сфокусувалися виключно на осях масштабування.
Функціонально все просто:
- Користувач надсилає довгий URL.
- Система генерує короткий ідентифікатор (ID).
- Під час переходу за коротким посиланням відбувається перенаправлення.
- Додатково — збирається аналітика.
Коли наша система опрацьовує
Архітектура:
- API без збереження стану (Stateless)
- Балансувальник навантаження
- Кілька екземплярів застосунку
- Одна реляційна база даних
- Кеш (наприклад, Redis) для популярних посилань
Вісь X: Клонування (Горизонтальне масштабування)
Коли зростає трафік, ми просто йдемо в конфігурацію і змінюємо кількість реплік із 3 на 30, або налаштовуємо автомасштабування. Це працює, доки вузьким місцем є процесор (CPU), оперативна пам’ять (RAM) чи мережа на рівні застосунку.
Збільшуючи кількість екземплярів застосунку за балансувальником, ми застосовуємо горизонтальне масштабування (рух по осі X). Це наше перше рішення для підтримки Доступності та Продуктивності з нашого SLA. Якщо один вузол падає — трафік йде на інші. Якщо користувачів стає більше — ми просто додаємо вузли, і затримка (latency) залишається низькою.
Цей підхід дає швидкий ефект і вимагає мінімальних змін у коді. Це найдешевший спосіб виграти час. Але згодом, коли клонів стає занадто багато, система впирається в нове обмеження — усі екземпляри застосунку починають жорстко конкурувати за ресурси єдиної бази даних (запис нових посилань, оновлення лічильників, транзакції). Масштабування самої бази (наприклад, створення read-реплік) рятує лише тимчасово.
Вісь Y: Функціональна декомпозиція
Коли навантаження збільшується до сотень тисяч запитів на секунду і додається важка аналітика, моноліт з єдиною базою більше не виживає. Щоб виконати SLA, система логічно розділяється на незалежні домени:
- Сервіс перенаправлень (Redirect Service) — максимально легкий, оптимізований тільки для читання.
- Сервіс створення посилань (Shortening Service) — важчий компонент, який відповідає за генерацію та перевірку нових ID.
- Сервіс аналітики (Analytics Service) — оброблення подій переходів (через асинхронний конвеєр або чергу).
Тепер ми ізолюємо різні типи навантаження (великий обсяг запису від великого обсягу читання) і можемо масштабувати ці компоненти незалежно по осі X. З погляду SLA ми захищаємо масштабованість та надійність. Наприклад, якщо сервіс аналітики «впаде» або почне гальмувати, базовий функціонал перенаправлень (Redirect Service) продовжить працювати швидко і без збоїв.
Декомпозиція сервісу на менші частини — це рух по осі Y. Вона змінює структуру самого застосунку та зону відповідальності команд. Але є критичний нюанс: часто після такої декомпозиції сервіси продовжують використовувати спільну базу даних. Вузьке місце не зникає, а стає очевиднішим.
Вісь Z: Сегментування даних (sharding)
Наш сервіс набирає популярності, аудиторія стає глобальною, навантаження — мільйони RPS. На цьому рівні одна база фізично не справляється.
З’являється потреба у:
- Сегментуванні (sharding) за хешем ідентифікатора (
hash(short_id)). - Регіональному розподілі (Європа / США / Азія).
- Ізоляції «гарячих» корпоративних клієнтів (Enterprise) на окремих кластерах.
- Окремому сховищі даних (Data Warehouse) для аналітики.
Щоб опрацювати запит, система повинна: визначити, у якому саме сегменті (shard) лежать потрібні дані; маршрутизувати запит до правильного кластера; врахувати різні профілі затримок клієнтів у різних регіонах (вимога продуктивності/latency з SLA). З’являються абсолютно нові інженерні виклики: перебалансування даних, складна аналітика між сегментами, забезпечення узгодженості та відновлення після збоїв на рівні окремих сегментів.
Це вже рух по осі Z. Він докорінно змінює модель даних, і саме тут ми зіштовхуємося з CAP-теоремою у повному обсязі. Наприклад, під час регіонального сегментування (sharding) виникає проблема глобальної унікальності ID. Якщо між дата-центрами в США та Європі зникає зв’язок (Partition), ми маємо обрати:
- Consistency (Узгодженість): заборонити створення нових посилань, доки зв’язок не відновиться (порушуємо доступність, але гарантуємо відсутність конфліктів).
- Availability (Доступність): дозволити створювати посилання локально. Ми виконуємо SLA щодо доступності, але змушені вводити регіональні префікси або складні алгоритми узгодження (eventual consistency), шукаючи баланс між вимогами щодо затримки та точністю.
Це вже не просто інфраструктурне масштабування. Це фундаментальна зміна самої топології системи.
Ціна масштабування: інфраструктура проти складності
Архітектура — це завжди про компроміси. Перехід від осі X до осей Y та Z — це не просто логічна інженерна еволюція продукту. Це фундаментальна зміна фокусу: від збільшення кількості серверів до стрімкого зростання інженерної складності, координації команд та операційного навантаження.
Рух по осі X (нарощування клонів) порівняно простий: він здебільшого вимагає лише додаткових обчислювальних ресурсів, які легко виділити або автоматизувати. Натомість рух по осях Y (мікросервіси) та Z (сегментування) привносить експоненційну складність. Це болючий рефакторинг моноліту чи бази даних, розв’язання фундаментальних проблем узгодженості (згадаймо CAP-теорему), складніший деплой та моніторинг. Зрештою, розподілена система може суттєво уповільнити час випуску нових функцій (time-to-market).
Саме тому бездумно впроваджувати мікросервіси або сегментування (sharding) лише тому, що «всі так роблять», — хибний шлях. Зріла високонавантажена система майже завжди використовує всі три осі одночасно, але робить це обґрунтовано: масштабуючи ту чи іншу площину лише еволюційно, коли вичерпано простіші варіанти, а підтримувати SLA іншими інструментами вже фізично неможливо.
Підсумки
Коли на співбесіді, під час перегляду архітектури або гасіння «пожежі» в промисловому середовищі ми одразу пропонуємо «додати ще подів» або «підняти більший БД-інстанс» — ми діємо рефлекторно, використовуючи лише частину інженерного інструментарію.
Концепцію трьох осей, або AKF Scale Cube, Мартін Ебботт та Майкл Фішер запропонували ще у 2009 році. Відтоді з’явилися Kubernetes, безсерверні (serverless) платформи та потужні керовані бази даних. Інфраструктура стала значно зручнішою, але фундаментальні обмеження розподілених систем нікуди не зникли.
Саме тому автомасштабування не вирішує проблему конкуренції за дані; мікросервіси самі по собі не гарантують відсутності вузьких місць; а керовані хмарні сервіси не скасовують законів фізики, мережевих затримок та жорстких компромісів CAP-теореми.
Щоб приймати справді зрілі архітектурні рішення, варто спиратися на кілька базових принципів:
- Починайте з бізнесу (SLA): Перш ніж масштабувати, зрозумійте, що саме болить. Ми рятуємо доступність? Нам не вистачає продуктивності (latency)? Чи система не може перетравити новий обсяг даних (масштабованість)?
- Визнайте обмеження (CAP): Змиріться з тим, що мережа буде падати (partition tolerance). Визначте разом із бізнесом, чим ви пожертвуєте в цей момент: свіжістю даних (consistency) чи можливістю обслуговувати клієнтів (availability).
- Застосовуйте AKF Scale Cube для вибору стратегії: Запитайте себе: ми зараз вперлися в обчислювальні ресурси (вісь X), у конкуренцію за спільну базу даних через монолітну логіку (вісь Y) чи в межі єдиного сховища на глобальному рівні (вісь Z)?
- Не ускладнюйте передчасно: Для 80% проєктів грамотного руху по осі X вистачить на роки. Рухайтеся по осях Y та Z лише тоді, коли біль від їх впровадження менший за біль від неможливості виконати SLA поточними засобами.
Для більшості продуктів руху по осі X буде цілком достатньо протягом усього їхнього життєвого циклу. І це нормально. Але коли система починає рости, розуміння вимог SLA, обмежень CAP-теореми та інших вимірів масштабування дає змогу приймати інженерні рішення усвідомлено, а не реактивно.
Можливо, саме в цьому і полягає ключова різниця між інфраструктурним мисленням та архітектурним.
4 коментарі
Додати коментар Підписатись на коментаріВідписатись від коментарівДякую за статтю.
Перепрошую за наївне питання- але хіба окрема OLAP база для DWH не є вже де-факто стандартом індустрії?
Стосовно sharding вцілому- мені здається ця проблема уже достатньо відома і є цілий спектр інструментів які її вирішують/абстрагують від розробників . Чи не більш доцільним/цінним буде одразу збирати систему з подібних інструментів і знову переходити до умовного IaС налаштування regions=[ EU, US, APAC ... etc]
Дякую за коментар! Ви підняли надзвичайно цікаву тему. Дійсно, сучасний ринок пропонує розкішні інструменти (NewSQL бази типу CockroachDB, Spanner, або Citus), які обіцяють «шардінг з коробки». Здається, що можна просто налаштувати інфраструктуру — і забути.
Але на практиці ми стикаємося з концепцією «дірявої абстракції» (leaky abstraction).
1. Про ілюзію повної абстракції шардінгу:
Ці інструменти геніально абстрагують інфраструктурний біль (Ops), але вони ніколи не знімають відповідальності з розробника (Dev). Чому?
Проблема Distributed JOINs: Якщо розробник пише код, думаючи, що працює зі звичайною локальною базою, і робить класичний JOIN, розподілена база виконає його. Але якщо дані лежать на різних шардaх (наприклад, у EU та US), база почне ганяти гігабайти даних через океан. Продуктивність впаде до нуля. Розробник мусить знати про шардінг, щоб правильно обрати Shard Key і ко-локувати пов’язані дані на одному вузлі.
Фізика і транзакції: regions=[EU, US] в Terraform означає, що під капотом працюватимуть розподілені транзакції (Two-Phase Commit або консенсус). Швидкість світла не обдуриш — кожна транзакція матиме затримку в десятки мілісекунд. Розробник має адаптувати бізнес-логіку (асинхронність, таймаути), інакше система не виконає SLA.
2. Щодо DWH і «збирати одразу з таких інструментів»:
Тут усе впирається в ROI (Return on Investment). Збирати систему одразу з розподілених баз та DWH — це свідомо обрати найвищий рівень складності розробки з першого дня. Розробникам доведеться враховувати ці «діряві абстракції», писати складніший код і обходити обмеження мережі. Time-to-market різко впаде, а вартість розробки та хмарних ресурсів виросте.
Тому, хоча інструменти існують і вони прекрасні, архітектурне рішення про їх використання (рух по осях Y та Z) має диктуватися реальними проблемами масштабу, а не просто їхньою наявністю.
Але виходить що рух по вісям Y та Z можливий тільки зі змінами в продукт. Виходить для Operations доступна тільки вісь X і може read репліка для бази даних?
Дуже гарне зауваження! Дійсно, глибока декомпозиція вимагає зміни коду. Але Operations (DevOps) мають у своєму арсеналі способи рухатися по осях Y та Z суто інфраструктурними методами.
Вісь Y (Декомпозиція):
Навіть якщо ми маємо моноліт, ми можемо імітувати мікросервісний підхід через L7-маршрутизацію (API Gateway / Ingress). Ми розгортаємо кілька пулів нашої аплікації і кажемо маршрутизатору: «Всі запити на /catalog відправляй на пул А, а всі запити на /checkout — на пул Б». Бізнес-логіка єдина, але ресурси під різні функції ізольовані.
Вісь Z (Шардінг):
Як приклад: робота з топологією мережі та сегментацією клієнтів.
Географічна ізоляція: розгортаємо ідентичні кластери в US та EU, а користувачів маршрутизуємо через Geo-DNS.
Ізоляція за SLA (Tenant isolation): виділяємо окремі кластери під різні тарифи (Free, General, Enterprise). Запити від преміум-клієнтів летять на виділене, потужне «залізо». Продукт той самий, але дані та клієнти жорстко сегментовані.
Тому Operations не обмежені лише нарощуванням інстансів/ресурсів (Вісь X). Завдяки грамотній маршрутизації та топології вони можуть виграти для розробників купу часу перед тим, як доведеться реально переписувати код