Шардирование и репликация
Зачем шардировать, выбор ключа шарда, горячие ключи, маршрутизация и ребалансировка шардов, кросс-шард операции, репликация и выбор SQL против NoSQL.
10 вопросов
JuniorТеорияОчень частоЧто делает репликация базы данных и как primary и replica делят работу?
Что делает репликация базы данных и как primary и replica делят работу?
primary принимает все записи; узлы replica копируют его данные и обслуживают чтения для масштабирования чтения. Если primary падает, failover повышает один replica. Асинхронная репликация быстра, но реплики отстают и отдают устаревшие данные; синхронная согласована, но медленнее.
Типичные ошибки
- ✗Путать репликацию с шардированием — она копирует весь набор данных на каждый узел, а разбиение это отдельная задача.
- ✗Считать, что чтение с
replicaвсегда свежее — из-за асинхронного отставания read-after-write вернёт устаревшие данные. - ✗Забыть про failover — без повышения
replicaпотеряprimaryполностью останавливает записи.
Уточняющие вопросы
- →Когда ты примешь отставание асинхронной репликации вместо цены синхронной?
- →Как требование read-your-own-writes меняет то, с какого узла ты читаешь?
JuniorТеорияОчень частоЗачем шардировать базу по нескольким серверам вместо одного большого инстанса?
Зачем шардировать базу по нескольким серверам вместо одного большого инстанса?
Шардируешь, чтобы поднять пропускную способность на запись, распределить нагрузку CPU по машинам, разместить данные ближе к пользователям и хранить больше объёма, чем влезает в один сервер. Каждый шард всегда работает с репликами для надёжности и масштабирования чтения.
Типичные ошибки
- ✗Считать, что шардирование только про объём хранилища, игнорируя запись, CPU и гео-близость как равноценные причины.
- ✗Думать, что шард — единая точка отказа, забывая, что каждый шард сам по себе работает с репликами для надёжности.
- ✗Шардировать слишком рано на данных, которые ещё потянул бы один узел Postgres, забирая сложность cross-shard без выгоды.
Уточняющие вопросы
- →Чем добавление реплик к шарду отличается от самого шардирования?
- →Какие сигналы говорят, что пора именно шардировать, а не масштабировать вертикально?
MiddleТеорияЧастоЧто делает ключ шардирования хорошим и что ломается при плохом?
Что делает ключ шардирования хорошим и что ломается при плохом?
У хорошего ключа шардирования высокая кардинальность (user_id подходит, gender нет), чтобы строки расходились по многим шардам, нагрузка распределялась ровно и ни один шард не грелся, и он совпадает с типичными паттернами доступа, чтобы большинство запросов попадало в один шард. Плохой ключ создаёт горячие шарды или вынуждает дорогой кросс-шард запрос почти на каждый запрос.
Типичные ошибки
- ✗Брать колонку с низкой кардинальностью, например булев флаг или статус, как ключ шардирования
- ✗Считать, что любой уникальный ключ ровно распределяет нагрузку независимо от паттернов доступа
- ✗Игнорировать паттерны запросов, из-за чего частые чтения веерно идут по всем шардам
Уточняющие вопросы
- →Как обнаружить, что выбранный ключ шардирования породил горячий шард?
- →Когда запрос не может использовать ключ шардирования, почему он так дорог?
MiddleТеорияЧастоКогда и как ребалансируют шарды и что делает ребалансировку дешёвой?
Когда и как ребалансируют шарды и что делает ребалансировку дешёвой?
Ребалансируй, когда шард слишком разросся, перегрет, кластер растёт или сжимается либо меняется ключ шардирования. Переноси диапазоны на другие узлы с минимальным простоем и применяй consistent hashing, чтобы добавленный узел двигал лишь долю данных.
Типичные ошибки
- ✗Ребалансируешь все шарды разом глобальной перетасовкой, выводя весь набор данных в офлайн вместо переноса по одному диапазону.
- ✗Хэшируешь через modulo от числа узлов, и добавление одного узла перемещает почти все ключи вместо доли.
- ✗Считаешь горячий шард проблемой ёмкости и только добавляешь ресурсы, не разбивая горячий диапазон и не соля ключ.
Уточняющие вопросы
- →Как consistent hashing сокращает объём перемещаемых данных против modulo?
- →Как держать чтения корректными, пока диапазон мигрирует между двумя шардами?
MiddleТеорияЧастоКакую проблему согласованности чтения создаёт асинхронная репликация и как её устранить?
Какую проблему согласованности чтения создаёт асинхронная репликация и как её устранить?
Асинхронная репликация подтверждает запись на primary до того, как реплики её применят: быстро, но возникает лаг репликации, поэтому чтение сразу после записи может попасть на устаревшую реплику (аномалия read-after-write). Чините чтением read-your-writes с primary или синхронной репликацией.
Типичные ошибки
- ✗Считать, что асинхронная репликация всегда строго согласована и реплики мгновенно отражают каждую закоммиченную запись.
- ✗Думать, что больше реплик уменьшает лаг репликации, хотя лишние реплики дают ёмкость чтения, но не делают одну реплику свежее.
- ✗Путать лаг репликации с failover и считать, что продвижение реплики устраняет промах read-after-write.
Уточняющие вопросы
- →Как реализовать приём согласованности read-your-writes для пользователя, который только что оставил комментарий?
- →Когда допустима модель согласованности bounded staleness на репликах вместо чтения с primary?
MiddleТеорияЧастоКак шардированная система направляет запрос к шарду, где лежат его данные?
Как шардированная система направляет запрос к шарду, где лежат его данные?
Ключ шардирования преобразуется в адрес через DSN-строку, через прокси с картой шардов или через координатор, который планирует и пересылает запрос, например компонент маршрутизации Citus для Postgres. Компромиссы — лишняя латентность на переход и маршрутизатор как узкое место и точка отказа.
Типичные ошибки
- ✗Считать, что каждый запрос несёт ключ шардирования, и кросс-шард поиск по не-ключевой колонке не требует fan-out.
- ✗Считать координатор бесплатным, игнорируя лишнюю латентность перехода и то, что он становится узким местом и SPOF.
- ✗Верить, что умный драйвер клиента убирает всю стоимость маршрутизации, хотя карта всё равно где-то живёт и синхронизируется.
Уточняющие вопросы
- →Как устаревшая карта шардов на прокси или драйвере приводит к попаданию запроса не на тот шард?
- →Когда координатор вроде Citus предпочтительнее маршрутизации в драйвере клиента?
JuniorТеорияИногдаКак выбрать между SQL и NoSQL базой данных для сервиса?
Как выбрать между SQL и NoSQL базой данных для сервиса?
Выбирай по паттернам доступа и согласованности, а не по хайпу. SQL реляционна, с ACID, joins, жёсткой схемой и строгой согласованностью; NoSQL (document, key-value, wide-column) даёт гибкую схему и лёгкое масштабирование, но часто итоговую согласованность. По умолчанию бери Postgres; NoSQL — под реальную нужду.
Типичные ошибки
- ✗Считать NoSQL строго более быстрой заменой SQL, а не другой моделью данных
- ✗Полагать, что любое NoSQL-хранилище даёт строгую согласованность, как реляционная база
- ✗Выбирать базу по хайпу, а не по паттернам доступа и требованиям к согласованности
Уточняющие вопросы
- →Когда реальная задача оправдывает переход на NoSQL вместо Postgres?
- →Какое семейство NoSQL подходит для key-value поиска против document-запросов?
JuniorТеорияИногдаВ чём разница между партиционированием и шардированием?
В чём разница между партиционированием и шардированием?
Партиционирование делит ОДНУ таблицу на куски — по range, list или hash — внутри ОДНОГО инстанса базы, так что планировщик пропускает нерелевантные партиции и сканирует меньше. Шардирование делит данные между НЕСКОЛЬКИМИ инстансами, чтобы вырасти за один сервер.
Типичные ошибки
- ✗Говорить, что партиционирование и шардирование — одно и то же, хотя одно живёт на одном инстансе
- ✗Утверждать, что партиционирование разносит данные по многим серверам — это шардирование
- ✗Забывать, что партиционированная таблица всё ещё в одной базе и делит её лимиты ресурсов
Уточняющие вопросы
- →Когда range-партиционирование выгоднее hash для временных рядов?
- →На каком масштабе один партиционированный инстанс перестаёт справляться и нужен шард?
MiddleДизайнИногдаВаш продукт работает на Postgres, шардированном по tenant_id. Аналитические запросы — кросс-шард агрегации и роллапы для дашбордов — сканируют много шардов и теперь просаживают латентность клиентских OLTP-транзакций, особенно на пике. Команда аналитики хочет near-real-time дашборды (отставание секунды-минута), а не вчерашний батч. Спроектируйте разделение так, чтобы тяжёлая аналитическая нагрузка никогда не касалась транзакционного пути. Ограничения:
- Никакой регрессии OLTP-латентности на шардированном Postgres под аналитической нагрузкой.
- Дашборды должны быть near-real-time, а не раз в сутки батчем.
- Сырые исторические данные должны дёшево храниться для ad-hoc переобработки.
Укажите, как изменения покидают OLTP-хранилище, где реально выполняются аналитические запросы и как встраиваются слои сырых данных и BI.
Ваш продукт работает на Postgres, шардированном по tenant_id. Аналитические запросы — кросс-шард агрегации и роллапы для дашбордов — сканируют много шардов и теперь просаживают латентность клиентских OLTP-транзакций, особенно на пике. Команда аналитики хочет near-real-time дашборды (отставание секунды-минута), а не вчерашний батч. Спроектируйте разделение так, чтобы тяжёлая аналитическая нагрузка никогда не касалась транзакционного пути. Ограничения:
- Никакой регрессии OLTP-латентности на шардированном Postgres под аналитической нагрузкой.
- Дашборды должны быть near-real-time, а не раз в сутки батчем.
- Сырые исторические данные должны дёшево храниться для ad-hoc переобработки.
Укажите, как изменения покидают OLTP-хранилище, где реально выполняются аналитические запросы и как встраиваются слои сырых данных и BI.
Держите OLTP и OLAP раздельно. Оставьте шардированный Postgres под транзакции и снимайте его изменения через CDC или доменные события в Kafka к стрим-процессору, который складывает их в ClickHouse под аналитику, datalake на S3/parquet и хранилище Snowflake, питающее BI. Кросс-шард агрегация уходит с транзакционного пути, поэтому OLTP-латентность держится, а дашборды остаются near-real-time.
Типичные ошибки
- ✗Направлять аналитику прямо в OLTP-шарды, из-за чего тяжёлые агрегации просаживают латентность транзакций под нагрузкой
- ✗Считать read-реплики OLAP-хранилищем — они повторяют ту же строковую OLTP-схему, а не колоночный аналитический движок
- ✗Пропускать слой стриминга и выгружать шарды ночным батчем, что убивает требование near-real-time дашбордов
Уточняющие вопросы
- →Как change data capture (CDC) снимает изменения с Postgres, не добавляя нагрузку на транзакционный путь?
- →Почему колоночное OLAP-хранилище ClickHouse подходит для кросс-шард агрегации лучше, чем OLTP-шарды?
SeniorДизайнИногдаТаблица заказов маркетплейса переросла один инстанс Postgres; спроектируй её шардирование, учитывая, что нагрузка пишущая, и покупатели, и продавцы запрашивают свои заказы, дневные отчёты по выручке идут по всем данным, а несколько вирусных продавцов дают непропорциональный трафик.
Таблица заказов маркетплейса переросла один инстанс Postgres; спроектируй её шардирование, учитывая, что нагрузка пишущая, и покупатели, и продавцы запрашивают свои заказы, дневные отчёты по выручке идут по всем данным, а несколько вирусных продавцов дают непропорциональный трафик.
Шардируй по ключу высокой кардинальности под доминирующий путь (seller_id), направляй записи через координатор или прокси вроде Citus и гаси горячих продавцов солью или выделенным шардом. Чтения покупателя и выручку отдавай из денормализованной read-модели или fan-out с merge.
Типичные ошибки
- ✗Выбор ключа низкой кардинальности вроде order_status, из-за чего весь трафик ложится на пару горячих шардов.
- ✗Живые кросс-шард join для запросов покупателя и отчётов по выручке вместо денормализованной read-модели.
- ✗Забывают про горячих продавцов, и один вирусный продавец насыщает один шард, пока остальные простаивают.
Уточняющие вопросы
- →Как ребалансировать, когда шард одного продавца перерастает остальные?
- →Где в этой схеме появляется репликация, когда шарды уже на месте?