Comments 53
Спасибо, бро! Наконец-то нормальный хардкорный текст.
1. Приложение работает только с одним мастером или с несколькими сразу?
2. Кто определяет с каким мастером должно взаимодействовать приложение? Есть ли балансировка?
P.S. Было бы интересно почитать про federated таблицы.
Про federated — часть некритичных для логики данных (логи заказов, например) — вынесены в отдельный кластер mysql и под них делается отдельный коннект. чтобы иметь возможность делать джоины тех же данных заказов между разными БД, например, основную таблицу заказов сджоинить с логами, можно извращаться, а можно использовать federated таблицы (хотя это тоже извращение :)). Этот federated надо использовать очень осторожно, потому что любой запрос требующий поиска по таблице без первичного ключа приведет к тому, что вся таблица будет полностью копироваться по сети. точно также и в обратную сторону на отдельной реплике для аналитиков мы сделали и обратный проброс таблицы с логами в основную БД.
Тот же вопрос про поиск.
Фасеты на моей памяти никогда не были на mysql, раньше в монолите фасеты лежали в редисе, индексы для полнотекста в сфинксе, сейчас и для фасетов и для поиска — это отдельный сервис на джаве и под джавой уже лежит несколько индексов в эластике.
И Вам даже ребята из Percona не помогли? Или не писали им? Или денег не хватило на их услуги? Только честно.
Почему сидите на древнем mysql? Я так понял что 5.6 и местами 5.5, по тексту про 5.7 вроде не было упоминаний.
Им тоже писали и они входят в список тех кто либо не ответил, либо отказался, но мы специально в статье не указываем имен :)
Сейчас везде 5.7. До этого сначала было везде 5.5, потом слейвы 5.7+мастера 5.5, и самая боль про обновление мастеров (хотели сначала до 5.6 мягко, а получилось в итоге сразу на 5.7) — как раз в статье.
А почему не перешли на 8.0?
gtid-mode = ON
binlog-transaction-dependency-tracking = WRITESET
Позволяет накатывать binlog реплики в несколько потоков и значительно уменьшить отставание (при должной настройке)?
Тут не претендуем на объективность, он нам не понравился в ситуациях потери части транзакций или скипа части бинлога. проигрывание и восстановление может превратиться в боль, списки проигранных сетов тоже. В идеальном мире наверное да, но при нашем потоке изменений — пока не стали менять. Может быть когда-нибудь в будущем ещё потестим и включим, но пока не так уж хочется.
Статейки неплохие, спасибо! можем вам рассказать как делать большие альтеры без боли и влияния на пользователей в любое время :)
И второе это баг в MySQL связанный с binlog_transaction_dependency_tracking который исправили только в 5.7.33, у нас например при попытке включить WRITESET реплика через некоторое время просто развалилась и перестала работать. Вот такой милый параметр.
P.S. У нас 45 kQPS, местами 50 kQPS и в принципе все гуд.
в высокий сезон это 1.5-2х от этих значений, но это скорее повод задуматься об оптимизации кода, чем об оптимизации кластера.
а расскажите пожалуйста подробнее про несколько потоков репликации, я о такой фиче не знаю. Пробовал slave_parallel_workers, но оно плохо параллелит и по сути никак не ускоряет репликацию в моем кейсе. Потоки это что-то другое, или вы про эту настройку?
О, спасибо. Я похоже не внимательно читал доку. Не знал что эта опция может улучшить параллельность выполнения.
Получается binlog_transaction_dependency_tracking=WRITESET
, требует transaction_write_set_extraction=XXHASH64
(или MURMUR32
). А transaction_write_set_extraction в свою очередь требует binlog_format=ROW
.
Итого:
binlog_transaction_dependency_tracking = WRITESET
transaction_write_set_extraction = XXHASH64
binlog_format = ROW
Эх, не хотел на ROW формат переходить. Уж очень много места сьедает, сейчас итак за ден, бинлоги на 250GiB скапливаются.
вот тут подробнее. в нашем кейсе потоки данных физически идут от разных мастеров, каждый поток стартует как обычная репликация, но работает с добавлением ко всем командам FOR CHANNEL='masterN'. На каждом слейве у нас сразу 4 мастера. И если транзакции с них при этом не имеют общих блокировок, то выполняются параллельно в рамках одного таймстемпа. ключевое для повышения параллельности обработки — slave_parallel_type и binlog_transaction_dependency_tracking, но как уже тут писали — не совсем безопасно и не стоит просто так крутить из дефолтов особенно в некоторых минорных версиях 5.7.
У меня один мастер. Я пробовал slave_parallel_type=LOGICAL_CLOCK
и разное количество воркеров. Это никак не помогает. Параллельность выполнения проверял как в этой статье. Но теперь я знаю что эту ситуацию можно улучшить с помощью binlog_transaction_dependency_tracking.
Я кажется понял зачем в вашей схеме несколько мастеров. Видимо для того чтобы ускорить репликацию, так как вместо одного канала, появляется несколько каналов репликации. Так как сами мастера это же не разгружает, всё равно все запросы на запись попадают на каждый мастер.
В первую очередь это нужно чтобы любой мастер можно было выключить в любой момент и легко пережить аварию/обслуживание. Но это точно далеко не самый типичный сетап. Я про крайней мере не знаю пока никого, кто делал бы так же. а так да, у каждого треда свой поток applier'a. И немного это разгружает, т.к. часть запросов по бинлогу приходит как раз в row, но конечно не по общему объему записи.
В первую очередь это нужно чтобы любой мастер можно было выключить в любой момент и легко пережить аварию/обслуживание
Тогда мне непонятно как вы этого достигли. Ранее вы писали:
Одна нода приложения работает с одним мастером, но все ноды поделены на 4 «зоны», это просто 4 разных окружения
У вас бэкенды поделены на 4 типа, и каждый пишет в свой мастер. В такой конфигурации непонятно, как можно в любой момент отключить хоть один мастер. Тут только наверное если использовать какой-то proxy, например proxysql, который видит что мастер упал/отключен и в этом случае направляет трафик бэкенда на другой мастер.
Сам же монолит сейчас находится в процессе распила на некоторое количество отдельных и независимых сервисов, часть из которых мы будем шардировать и рассказывать вам о полученном опыте — следите за обновлениями
Можно приоткрыть завесу тайны — что останется от кода битрикса после распила?
Уже достаточно давно использую решение такой проблемы (на Mysql5.5, Percona5.6, Percona5.7) в случаях, когда мастер и слейв заранее известны и автоматически меняться местами не будут.
Минусы такой схемы:
…
Слейв до завершения выполнения транзакции еще не знает, что он отстает в slave_status по seconds_behind_master.
Делается таблица max_delay_sec int, date_last_update datetime и 2 евента: для реплики и для мастера.
На мастере поле date_last_update обновляется евентом раз в 2 секунды.
На слейве сравнивается date_last_update с now() и выясняется приемлимость задержки.
После того, как в бд есть реальная задержка репликации можно уже что угодно делать.
Например, у меня закрывается возможность подключения для хапрокси:
Update mysql.user
set Host= (select if(abs(timestampdiff(second, date_last,now()))<=Setup.max_delay_sec ,'%','127.0.0.2')
from Setup
Where id_Setup=1
)
Where user='robot_haproxy';
If row_Count() is not null and row_count()>0 Then
flush privileges;
Как бы коряво этот костыль не выглядел, но он успешно живет 6 лет и перенаправляет/разделяет поток чтения с базы в условиях, когда железо виртуальное, исключительно нестабильное.
Сейчас изменение пользователя заменено на скрипт типа такого github.com/olafz/percona-clustercheck
с MySQL очень давно работал с 5.1 еще. Потом все на MS SQL.
потом пришлось переехать на версию MySQL повыше и таких детских болячек (раньше null в поле not null вставлялось, а в новой версии перестало) я в MS SQL не встречал.
Эльдорадо не может себе позволить СУБД с поддержкой?
Такой проблемы с потерей данных, к счастью не было никогда. Худшее что происходило — меняли материнскую плату на тогда ещё железном сервере с мастером. К нашему счастью отказ материнки позволял нормально работать до поставки вендором.
А насчёт БД с поддержкой, у нас полно оракла и есть несколько саповских монстров типа ханы (не конкретно в ИМ eldorado, а в целом в мвидео-эльдорадо), но наличие поддержки не гарантирует отсутствие проблем ни в одной из них.
Альтернативы смотрели, но далеко не все проходит требование по минимальным изменениям и совместимости со всеми уже имеющимися инструментами обслуживания. В случае с галерой — не понравилось ограничение по возможным движкам и ограничения скорости связанные с количеством мастеров (упирание в самый медленный). Видимо основная боль там — решение проблемы с консистентностью и эта проблема конечно у нас есть, но она для нас менее критична, чем скорость. для нового проекта использовать вполне можно, а вот можно ли вообще провернуть такую бесшовную миграцию — пока не уверен.
Вот как сделано у нас:
Для отказоустойчивости hot-cold кластер на RHEL. Подключен SAN storage к активному ноду а так же VIP (virtual IP адрес указывает на активный нод). Между нодами есть хардбит. И если один нод упал(чего небыло никогда) то SAN storage и VIP адрес подключаются ко второму ноду. Все, что увидит клиент это то, что нет ответа от сервера пока mysql не поднимется на втором холодном ноде.
Для того чтоб в базу можно было быстро писать и так же быстро читать и не делать master-slave кластеры то сделано что-то типа materialized view. Это две базы. Основная, куда можно быстро и эфективно записывать и вторая View в которую посредством триггеров переносятся изменения из основной базы. Это дает возможность сделать разные способы чтения и записи(к примеру индексы сделаны по разному в таблицах куда будем писать и откуда будем читать).
Регулярно думаем об облаках, но есть нюансы и ограничения: облака только российские (яндекс/mail/крок), а ещё лучше собственное где твои ресурсы точно не будут конкурировать с кем-нибудь ещё. С зарубежными намного больше рисков, какими бы удобными они ни были, хотя и их тоже рассматривали.
Насчёт вашего примера — одна большая нода с пассивным стендбаем — это скорее про оракл, для мускуля (даже если она будет очень толстая) — это плохо по целому ряду причин. Такую толстую ноду мускуля легко уронить в сегфолт простым сбросом кверикеша, или как она будет долго висеть в блокировках при большом рпс?.. Главный вопрос — как ее будут дальше скейлить на чтение при росте нагрузки x10? Из вашей схемы пока мне неочевиден 100% выигрыш, т.к. при больших объемах записи вам надо строить больше индексов для нескольких представлений, т.е. это некий tradeoff для случаев большого чтения, ценой удорожания записи. Но в целом интересный подход, для каких-то кейсов точно подойдёт.
Это уже не совсем бд) это сервис, который ещё и в память может вместить все. При таких вводных тут совсем другой подход. (Для подписчиков видимо интеграция через grpc?)
Но чем толще данные, тем больнее падение такой системы и время восстановления будет.
Такую толстую ноду мускуля легко уронить в сегфолт простым сбросом кверикеша
ЕМНИП кверикеш давно не рекомендуется к использованию, он блокирующийся внутри и плохо масштабируется с ростом числа ядер.
MySQL: казнить нельзя помиловать