Sep 21, 2020 · 3 min read
Шардирование — разделение базы данных на две, три, пять частей, и раскладывание их по разным машинам. В какой-то момент это может быть выгоднее (дешевле), чем покупать машину помощнее.
У этого, разумеется, есть свои минусы. Коротко обсудим в этой статье.
В прошлый раз говорили про кеширование и балансеры.
Как шардировать? #
Рассотрим какие есть схемы шардирования.
В статье «System Design для самых маленьких» я уже писал про шардирование.
Range based #
Делим условный список сущностей, которые имеют смысл для нашего бизнес-домена, на несколько частей. Скажем, если это пользователи — пусть первый миллион пользователей находится на машине А, второй миллион на машине Б, и т.д.
Минус такого подхода в том, что мы предполагаем, что все сущности равнозначны (в смысле нагрузки), но это не так. Какие-то шарды будут перегружены, а какие-то стоять без дела.
Vertical #
Поделим все данные по принадлежности к определённым фичам. Скажем, если мы делаем инстаграм, то у нас будут: пользователи, картинки, фоловеры. Разложим их по разным базам.
Обычно различные фичи имеют различный профиль нагрузки, поэтому с разделением довольно удобно куда-то дать больше мощности, а куда-то меньше, в зависимости от профиля нагрузки.
Внутри «вертикали» можно дополнительно использовать range based, если она перегружена.
Hash-based #
При вычислении куда положить (и откуда достать) данные используем хеш-функция. В самом простом варианте — остаток от деления. Скажем, у нас 100500 пользователей и 10 серверов: остаток от деления айдишника пользователя на 10 скажем на каком сервере находятся данные этого пользователя.
Возникает проблема при добавлении новых серверов – весь мапинг собьётся. Поможет Consistent Hashing, чтобы снизить количество данных, которые придётся переналить. Ну и переналивать данные на новые шарды все-таки придётся, но делать это можно без простоя всей системы, прямо на «горячую».
Directory Service #
Чтобы понимать куда отправлять запросы за конкретным пользователем или его картинками, нужно держать мапинг между данными и серверами, в зависимости от того какая схема шардирования используется.
Удобно для этой цели поднять отдельный сервис, который знает где искать данные — сервер директорий.
Какие есть проблемы? #
Joins #
Теряем возможность джойнить. Если надо собрать данные из разных таблиц, которые лежат на разных машинах — такое себе мероприятия в смысле производительности.
Приходится денормализовывать данные, т.е. появляется дублирование данных, и как следствие — неконсистентность. С этим приходится бороться вручную.
Referential Integrity #
С помощью foreign key поддерживается консистентность связей между различными таблицами, всё работает само.
Пример, есть у нас две таблицы: parent
и child
. В дочерней соответственно есть foreign_key
, который указывает на родительские записи.
mysql> SELECT * FROM parent;
+--------+
| par_id |
+--------+
| 1 |
| 2 |
| 3 |
+--------+
mysql> SELECT * FROM child;
+--------+----------+
| par_id | child_id |
+--------+----------+
| 1 | 1 |
| 1 | 2 |
| 2 | 3 |
| 2 | 4 |
| 2 | 5 |
| 3 | 6 |
+--------+----------+
(primary key — child_id, а foreign key — par_id)
Что будет если удалить первую запись из родительской таблицы?
mysql> DELETE FROM parent where par_id = 1;
Правильно. Каскадом за родительской таблицей будут удалены невалидные записи из дочерней.
mysql> SELECT * FROM parent;
+--------+
| par_id |
+--------+
| 2 |
| 3 |
+--------+
mysql> SELECT * FROM child;
+--------+----------+
| par_id | child_id |
+--------+----------+
| 2 | 3 |
| 2 | 4 |
| 2 | 5 |
| 3 | 6 |
+--------+----------+
Однако, если мы разделили данные фактически по разным базам — нет такой магии, которая будет работать сквозным образом.
Про консистеность приходится думать на уровне приложения.
Материалы #
- https://www.educative.io/courses/grokking-the-system-design-interview
- https://www.informit.com/articles/article.aspx?p=30875&seqNum=8
PS. Обсудить можно в телеграм-чате любознательных программистов. Welcome! 🤗
Подписывайтесь на мой твитер или канал в телеграме, чтобы узнавать о новых разборах задач.