Как стать автором
Обновить

Масштабирование базы данных через шардирование и партиционирование

Время на прочтение 11 мин
Количество просмотров 153K
Всего голосов 37: ↑34 и ↓3 +31
Комментарии 17

Комментарии 17

Как там обстоят дела с партицированием и INSERT ON CONFLICT DO UPDATE, допилили ли поддержку?
Мы решили попробовать citusdb для создания шардирования. В принципе, намного удобнее, нежели самому пытаться настроить и сконфигурировать postgresql, плюсом идет cstore_fdw для того чтобы сделать колоночную базу. В 2gis не смотрели на подобные citus-у решения?
а какие есть ещё подобные решения?
Я пробовал разбивать таблицу с несколькими миллионами записей на партиции (ну, подготовиться на вырост), причем запросы идут в 99% случаев только к одной-двум партициям, но нашел, что разницы в производительности нет, а с работой по двум даже проседает из-за объединения и поиска результатов. Идея была разбить данные по годам, так как старые данные нужны редко, но работа на стыке двух лет портит всю малину.
Видимо запросы фильтровали по чему-то другому, а не по дате, поэтому нужно было смотреть все партиции :)
Ну так ежу понятно… Я-ж о том, что надо либо дофига данных, чтобы разбиение начало давать эффект, либо чтобы подавляющее большинство запросов гарантированно работали только с одним разделом.

А в моем случае, при разбивке по календарным годам, всегда есть период, в который практически ВСЕ запросы будут попадать в границу и захватывать два раздела.

Я думал, что может сделать первый раздел на 2 года, но как плавающее окно, а архивные уже по годам.
Но тогда нужно периодически (раз в месяц или несколько) мигрировать данные в архив — и это будет тяжелая операция, включающая перестроение разделов и их CONSTRAINTS — и мне это не сильно нравится.
Ну так ежу понятно…

Сорри, не заметил:
но работа на стыке двух лет портит всю малину.

Но если бОльшая часть запросов к последнему периоду, то должны поиметь профит :)

У Вас только 2 партиции: архив и текущая?
Тогда можете и не заметить ускорения.

Можно не мигрировать, а чтобы создавались новые партиции автоматически для каждого года.
Но если бОльшая часть запросов к последнему периоду, то должны поиметь профит :)


Много запросов типа — дай последний месяц (два, три) или последний год. Если у нас сейчас январь — все такие запросы захватят два раздела, и будет медленней. То есть тогда эффект от разбиения данных на разделы будет исключительно когда индексы перестанут в память влезать.

У Вас только 2 партиции: архив и текущая?
Тогда можете и не заметить ускорения.


Почему? Если их побить так, чтобы все 99% запросов попадали в текущий раздел, и только редкие исторические — в архив, в этом есть смысл.
Но по-видимому, в любом случае нужно плавающее окно для текущего раздела, а не YEAR == 2000.
В mysql партиционирование нативное без наследования, хз есть ли наследование.
Но наследовние, наверное, более гибко.

Партицировать стоит по одному признаку, по какому-то одному полю — это очень важно.

Можно, но для этого нужно создать больше наследников. Но вряд ли это удобно.

А как будет работать шардинг с группировками по данным из нескольких шард? :)

В mysql вроде вообще нельзя ходить к шардам через одно место.

Кто-то делал шардинг на слейвах по другому полю?
Как делать шардинг/партицирование по UUID полю? :)
Как именно в постгресе это делать — не знаю, но могу предположить теоритически. Возьмите хэш-функцию, которая на входе берет UUID, а на выходе будет давать цифру шарда. Для этих целей неплохо подходит murmur hash.
Подумал. :)

Если использовать хеш, то это скрывает от нас UUID, и будут сложности при добавлении партиций/шард.
Партиции еще полбеды, а в случае с шардами данные придется гонять между серверами и синхронизировать их.
Придется переганять данные минимум с одной партиции/шарды (если используем диапазоны хешей и просто делим какой-то диапазон на 2), максимум со всех шард (если делим по хешу на количество шард/партиций автоматически).

Для партиционирования, в принципе, не так страшна необходимость перераспределения данных при добавлении партиции.

Использование хэша будет смешивать старые и новые данные. Это может быть и плюсом и минусом:
+ Равномерная нагрузка нагрузка на шарды/партиции
— Нельзя вынести шарды со старыми данными, к которым вряд ли будут обращаться, на слабый сервер.
— При фильтрации по диапазонам нужно проверять все партиции/шарды, но вряд ли по UUID фильтруют по диапазонам.
— При фильтре по нескольким последним UUID запросы уедут скорее всего на разные партиции/шарды.
Вместо хеша можно использовать младшие значения UUID, они примерно равномерно равномерно распределены.

Если нужно, чтобы запросы (особенно с новыми UUID) падали на одну партицию/шарду:
Можно использовать RANGE-партиционирование (mysql).
При этом теряем автоматическую равномерность.
Но для старых, редко используемых данных, размер партиции/шарды можно подобрать, или сбросить все старые данные в одну партицию и начать с чистого листа :)
А потом просто следить, сколько данных в новой партиции/шарде и изменять RANGE при необходимости. Можно повесить на крон. При добавлении (для нового диапазона) партиции/шарды данные не перегоняются, так как их еще нет.
:)

То есть:
Можно к UUID относиться как к числам, учитывая их сильную неравномерность (собственно почему я изначально не относился к UUID как к числу).
При этом нужно следить за количеством данных, когда нужно добавить новую шарду/партицию (хотя вон в статье описано, что это можно положить на саму БД).
При необходимости разделения старой шарды на 2 новые в какой-то пропорции по количеству диапазоны нужно подбирать эмпирически (используем бинарный поиск и радуемся :) ).
Еще одно замечание:
Часто при использовании UUID все равно нужен порядковый номер, можно для условия использовать его. :)
Но не всегда это возможно (собственно почему я и задал вопрос :) ):
уже все API/запросы на UUID
все связи между таблицами по UUID, а связанные данные лучше хранить на одной шарде (Если бы запросы приходили в int, а номер шарды вычислять на приложении, то можно жить, но нужно самим обрабатывать запросы на 2 шарды, если данных лежат в разных шардах. Также придется узнать UUID у главной таблицы, на который ссылается этот int, а это +1 запрос :) То есть проще добавить int в зависимой таблице: не нужно вычислять номер шарды на приложении, делать доп. запрос, обрабатывать 2 шарды :) ).
Вместо view «news» с union'ами, можно создать на мастер-ноде таблицу «news» и отнаследовать от нее FDW таблицы, тогда постгрес сам будет эти данные склеивать.

CREATE FOREIGN TABLE news_1_fdw (
)
INHERITS (news)
SERVER news_1_server
OPTIONS (
    table_name 'news_1'
);
Получается, в случае использования внешних таблиц, каждый запрос к основной будет грузить все внешние узлы и дожидаться ответа от каждого?
В случае, если нужные данные будут лежать на разных серверах (или сервер не сможет определить по запросу, что на одном: запрос не по условию шардирования).

Например в mysql такой проблемы скорее всего нету, так как нету возможности положить логику шардирования на БД. Приложение должно само решить, к какой шарде оно хочет обратиться. Поэтому возможности будут слегка обрезаны. :)
а если нужна репликация шардов?
Зарегистрируйтесь на Хабре , чтобы оставить комментарий