Масштабирование БД
Как хранилище данных масштабируется и распределяется — репликация, шардирование и выбор ключа шардирования, партиционирование против шардирования, реплики для чтения, денормализация, пул соединений и CAP для баз данных.
9 вопросов
JuniorТеорияОчень частоВ чём разница между вертикальным и горизонтальным масштабированием и где вертикальное упирается в предел?
В чём разница между вертикальным и горизонтальным масштабированием и где вертикальное упирается в предел?
Вертикальное масштабирование (scale up) добавляет CPU, RAM или диск одной машине; горизонтальное (scale out) добавляет машины и размазывает нагрузку по ним. Вертикальное проще всего, но упирается в самый большой сервер, дорожает и оставляет единую точку отказа. За потолком — репликами или шардами.
Типичные ошибки
- ✗Менять местами определения scale up и scale out
- ✗Верить, что у вертикального масштабирования нет потолка железа
- ✗Забывать, что один прокачанный сервер — по-прежнему одна точка отказа
Уточняющие вопросы
- →Почему масштабировать вширь запись труднее, чем чтение?
- →Как единая точка отказа подталкивает к масштабированию вширь?
JuniorТеорияЧастоЧто такое пул соединений базы данных и что происходит при его исчерпании?
Что такое пул соединений базы данных и что происходит при его исчерпании?
Пул соединений держит открытые соединения и выдаёт их запросам, избегая TCP- и auth-рукопожатия. Когда все розданы, запросы встают в очередь; если за таймаут ничто не освободится, они падают с pool-timeout. Разгружают, поднимая размер пула в пределах лимита, сокращая удержание или ставя пулер вроде PgBouncer.
Типичные ошибки
- ✗Думать, что пул открывает свежее соединение на запрос, а не переиспользует
- ✗Ждать, что исчерпание молча отбросит запросы, а не даст таймаут
- ✗Считать, что пул может расти без лимита соединений на стороне базы
Уточняющие вопросы
- →Как подобрать размер пула под лимит max-connections базы?
- →Как долгая удерживаемая транзакция истощает пул соединений?
JuniorТеорияЧастоЧем партиционирование отличается от шардирования и когда партиционирования одного достаточно?
Чем партиционирование отличается от шардирования и когда партиционирования одного достаточно?
Партиционирование делит одну таблицу на части внутри одного экземпляра; шардирование разносит части по узлам. Партиционирование помогает отсечению, но делит CPU, память и диск одного сервера. Достаточно, пока у узла есть ёмкость и нужно лишь отсекать сканы или удалять партиции, — но не чтобы масштабировать запись за одну машину.
Типичные ошибки
- ✗Считать партиционирование и шардирование одинаковыми, а не «в одном узле» против «между узлами»
- ✗Думать, что партиционирование добавляет CPU или память, а не делит один сервер
- ✗Ждать, что партиционирование масштабирует запись за пределы одной машины
Уточняющие вопросы
- →Как отсечение партиций ускоряет запрос по диапазону?
- →Какой сигнал говорит, что пора переходить от партиционирования к шардированию?
JuniorТеорияЧастоЧто такое репликация базы данных и чем отличаются master-slave и master-master?
Что такое репликация базы данных и чем отличаются master-slave и master-master?
Репликация держит копии тех же данных на нескольких узлах. В master-slave один master принимает все записи и передаёт их на read-only slaves для чтений. В master-master несколько узлов принимают записи и синхронизируются — выше доступность записи, но риск конфликтов write-write.
Типичные ошибки
- ✗Путать репликацию (полные копии) с шардированием (непересекающиеся куски)
- ✗Инвертировать роли — думать, что в master-slave записи принимают slaves
- ✗Игнорировать конфликты write-write, присущие master-master
Уточняющие вопросы
- →Как read-реплика помогает масштабировать нагрузку на чтение?
- →Как разрешаются конфликты write-write в master-master?
JuniorТеорияЧастоЧто такое шардирование и что делает ключ шардирования хорошим?
Что такое шардирование и что делает ключ шардирования хорошим?
Шардирование делит набор данных горизонтально между узлами (шардами); каждый шард держит непересекающееся подмножество строк. Хороший ключ ровно распределяет строки и трафик, высокой кардинальности, совпадает с типичным фильтром, чтобы большинство запросов били в один шард, и редко меняется.
Типичные ошибки
- ✗Путать шардирование (непересекающиеся горизонтальные куски) с репликацией (полные копии)
- ✗Брать ключ низкой кардинальности, набивающий строки в пару шардов
- ✗Брать монотонный id, создающий хотспот на новом шарде при вставках
Уточняющие вопросы
- →Почему монотонно растущий ключ шардирования создаёт хотспот записи?
- →Как межшардовые запросы усложняют JOIN и агрегацию?
MiddleТеорияЧастоКак теорема CAP применяется к реплицированной базе — чем жертвуют во время сетевого раздела (partition)?
Как теорема CAP применяется к реплицированной базе — чем жертвуют во время сетевого раздела (partition)?
Во время раздела CAP позволяет реплицированному хранилищу держать только согласованность или доступность, но не обе. Согласованная (CP) схема блокирует или отклоняет записи на стороне меньшинства, поэтому никто не читает разошедшиеся данные; доступная (AP) — обслуживает, но может вернуть устаревшие, конфликтующие данные. Выбор — C против A.
Типичные ошибки
- ✗Думать, что все три свойства CAP достижимы разом во время раздела
- ✗Считать устойчивость к разделу необязательной, которую можно инженерно убрать
- ✗Менять местами поведение CP и AP во время раскола
Уточняющие вопросы
- →Как AP-хранилище сливает конфликтующие записи после схождения раздела?
- →Где модель согласованности-задержки
PACELCрасширяет компромисс CAP?
MiddleТеорияЧастоЧто такое денормализация, чего она стоит и как поддерживать дублированные данные согласованными?
Что такое денормализация, чего она стоит и как поддерживать дублированные данные согласованными?
Денормализация хранит избыточные, заранее соединённые данные ради чтений без JOIN ценой записи. Стоит памяти и согласованности: каждый дубликат надо обновлять при записи, иначе копии разъезжаются. Согласованность держат, обновляя все копии в одной транзакции, триггерами или асинхронно через события.
Типичные ошибки
- ✗Путать денормализацию с нормализацией (убирать, а не добавлять избыточность)
- ✗Верить, что база держит дублированные поля в синхроне бесплатно
- ✗Забывать, что каждый дубликат надо обновлять при каждой записи
Уточняющие вопросы
- →Когда асинхронное распространение предпочтительнее единой транзакции записи?
- →Как обнаруживать и чинить расхождение между денормализованными копиями?
SeniorТеорияИногдаЧтение с реплики возвращает устаревшие данные сразу после записи в master. Почему так и как этого избежать?
Чтение с реплики возвращает устаревшие данные сразу после записи в master. Почему так и как этого избежать?
Репликация асинхронна: master отвечает раньше, чем изменение доходит до реплики, поэтому чтение там видит старую версию — лаг репликации. Решения: читать свои же записи с master, применять синхронную репликацию, закреплять сессию за узлом или ждать, пока реплика дойдёт до позиции записи в логе.
Типичные ошибки
- ✗Винить порядок чтения клиента вместо асинхронного лага репликации
- ✗Думать, что реплика никогда не сходится, а не догоняет после задержки
- ✗Верить, что более сильная изоляция master убирает лаг репликации
Уточняющие вопросы
- →Чем read-your-writes согласованность отличается от полной сильной согласованности?
- →Какую цену по пропускной способности синхронная репликация добавляет записи?
SeniorДизайнИногдаКрупный multi-tenant сервис шардирует главную таблицу по client_id, и назначение шарда — простой хэш этого ключа. Аналитика показывает сильный перекос нагрузки: около 5% клиентов дают примерно 80% всех чтений и записей. Самые нагруженные клиенты постоянно ложатся на горстку шардов, пока большинство шардов почти простаивает, а простое добавление шардов перестало помогать. Объясните, что именно ломается при таком перекосе, почему добавление шардов это не чинит, и какие изменения схемы шардирования вы бы заложили, чтобы размазать нагрузку, сохранив быстрый одношардовый поиск для обычных клиентов.
Крупный multi-tenant сервис шардирует главную таблицу по client_id, и назначение шарда — простой хэш этого ключа. Аналитика показывает сильный перекос нагрузки: около 5% клиентов дают примерно 80% всех чтений и записей. Самые нагруженные клиенты постоянно ложатся на горстку шардов, пока большинство шардов почти простаивает, а простое добавление шардов перестало помогать. Объясните, что именно ломается при таком перекосе, почему добавление шардов это не чинит, и какие изменения схемы шардирования вы бы заложили, чтобы размазать нагрузку, сохранив быстрый одношардовый поиск для обычных клиентов.
Тяжёлые клиенты ложатся на пару шардов-хотспотов: те упираются в CPU и IO и режут пропускную способность. Добавление шардов почти не помогает: клиент всё в одном шарде. Решения: составной ключ (client_id плюс второе измерение) или суб-шардирование крупных арендаторов, выделенные шарды для крупнейших клиентов и кэш горячих чтений.
Типичные ошибки
- ✗Считать, что хэш-ключ исключает перекос, хотя перекошен сам трафик арендаторов
- ✗Ждать пользы от лишних шардов, когда весь арендатор ложится в один шард
- ✗Игнорировать выделенные шарды или кэш для пары клиентов-китов
Уточняющие вопросы
- →Как составной ключ шардирования размажет одного тяжёлого арендатора?
- →Какова эксплуатационная цена изоляции китов на выделенных шардах?