MySQL: казнить нельзя помиловать



    Сайт и интернет-магазин «Эльдорадо» — это около 40 тысяч покупок ежедневно. Объяснять, что это значит для бизнеса компании, наверное, не надо.

    Исторически магазин работает на движке Bitrix с огромным количеством кастомного кода и дополнений. В качестве хранилища выступает кластер MySQL с четырьмя мастер-серверами.

    Монолитные приложения есть у значительного числа компаний, и работать с ними приходится очень многим. Способов борьбы с монолитом предостаточно, вот только про удачные, к сожалению, пишут мало. Надеюсь, что рассказ о том, как мы подпираем наш монолит (пока его не распилили) будет вам интересен.

    Мы прекрасно понимаем, что массивная архитектура может приносить много проблем. Но разрушить ее легко, простым волевым решением, невозможно: продажи идут, сайт должен работать, изменения для пользователей не должны быть радикальными. Поэтому переход от монолита к набору микросервисов занимает время, которое нам нужно продержаться: обеспечить работоспособность системы и ее устойчивость к нагрузкам.

    В чем состояла проблема


    Кластер БД на сайте eldorado.ru очень долгое время был построен вот по такой схеме:


    Все мастера в этой схеме работают одновременно и все в активном режиме, последовательно проигрывая репликационный поток ...M1->M2->M3->M4->M1->M2->M3->M4->M1->M2…

    В нашем сетапе такая конфигурация давала единственный плюс — она позволяла системе работать и держать его нагрузку. Дело в том, что балансировщик запросов приложения после любого апдейта для обеспечения консистентности переключает весь read поток на этот мастер, а одного мастера когда-то не хватало, чтобы держать весь поток чтения.

    Но такая схема не могла обеспечить ни надежности, ни быстродействия работы. Хотя она выглядела просто, но у нее был целый ряд недостатков. Она очень медленно обновляла данные в кластере: в худшем случае было до пяти плечей репликации (в зависимости от того, на каком мастере изначально инициируются изменения). Из-за этих задержек множество проблем возникало и в работе сайта, и при оформлении заказов в интернет-магазине.

    Минусы такой схемы:

    • Слейвы самой отдаленной от активного мастера зоны получают обновление данных в худшем случае только через 4х время выполнения транзакции, иногда возникали бешеные задержки репликации;
    • Любая авария на любом из мастеров приводит к неконсистентности данных на всем кластере до ее устранения;
    • В некоторых случаях аппаратных аварий восстановление кольца без потери данных близко к невозможному (снятие-разворачивание из бэкапа — многие часы);
    • Даже плановое отключение любого мастера автоматически требует либо снятия с нагрузки слейвов либо их переключения на другой мастер;
    • Нет инструментов, которые могут нормально работать в такой топологии (и хорошо, что нет, не используйте ее);
    • Любой тяжелый UPDATE/DELETE и даже SELECT при определенных обстоятельствах блокирует репликацию во всем кольце на время своего выполнения;
    • Слейв до завершения выполнения транзакции еще не знает, что он отстает в slave_status по seconds_behind_master.

    Вы можете в своих тестовых окружениях эмулировать такое поведение включив искусственную задержку репликации на 1-2 секунды на слейвах (что мы и делали), это отличный способ проверить ваше приложение на готовность к таким распределенным архитектурам через опцию MASTER_DELAY = N.

    И, наконец, переход на другую БД в нашем случае — не вариант, потому что система слишком масштабна и многое в ней завязано на использование особенностей MySQL и даже на нюансы работы его внутреннего оптимизатора запросов.

    Как мы ее решали


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

    Часть компаний нам просто не ответили (и это нормально), а другие написали, что взяться за такую задачу они не готовы. При этом вопрос о возможной стоимости проекта даже не вставал.
    Если большие интеграторы не хотят связываться с задачей, то самим решить ее стало вдвойне интересно. В нашем распоряжении было хорошее оборудование, проблемы со стабильностью и отказоустойчивостью стояли во втором приоритете, и в самом начале мы хотели хоть как-то обеспечить ускорение передачи данных. Для этого хорошо подходили несколько опций, которые появились в MySQL версий 5.6 и 5.7.

    Правда, документация прозрачно намекала на то, что просто включить их не получиться, т.к. в кольце обязательно будет слейв с меньшей версией, а тут вот так:
    The 5.7 master is able to read the old binary logs written prior to the upgrade and to send them to the 5.7 slaves. The slaves recognize the old format and handle it properly.

    Binary logs created by the master subsequent to the upgrade are in 5.7 format. These too are recognized by the 5.7 slaves.

    In other words, when upgrading to MySQL 5.7, the slaves must be MySQL 5.7 before you can upgrade the master to 5.7.

    Для тестов нам было достаточно поднять тестовое кольцо, например через mysqld_multi, и погонять на нем типовые запросы (можно даже на одном хосте, 4 инстанса на разных портах с разными оффсетами), примерно так:

    mysql -h127.0.0.1 -P 3302 -e "RESET SLAVE; RESET MASTER; SHOW MASTER STATUS\G;"
    mysql -h127.0.0.1 -P 3302 -e "CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT=3301, MASTER_USER='root', MASTER_PASSWORD='', MASTER_LOG_FILE='master1-binlog.000001', MASTER_LOG_POS = 107;START SLAVE;"
    mysql -h127.0.0.1 -P 3303 -e "RESET SLAVE; RESET MASTER; SHOW MASTER STATUS\G;"
    mysql -h127.0.0.1 -P 3303 -e "CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT=3302, MASTER_USER='root', MASTER_PASSWORD='', MASTER_LOG_FILE='master2-binlog.000001', MASTER_LOG_POS = 107;START SLAVE;"
    mysql -h127.0.0.1 -P 3304 -e "RESET SLAVE; RESET MASTER; SHOW MASTER STATUS\G;"
    mysql -h127.0.0.1 -P 3304 -e "CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT=3303, MASTER_USER='root', MASTER_PASSWORD='', MASTER_LOG_FILE='master3-binlog.000001', MASTER_LOG_POS = 107;START SLAVE;"
    mysql -h127.0.0.1 -P 3301 -e "RESET SLAVE; RESET MASTER; SHOW MASTER STATUS\G;"
    mysql -h127.0.0.1 -P 3301 -e "CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT=3304, MASTER_USER='root', MASTER_PASSWORD='', MASTER_LOG_FILE='master4-binlog.000001', MASTER_LOG_POS = 107;START SLAVE;"

    После этого можно менять версию любого инстанса запуская конфиг с нужным портом другим бинарником, который положили рядом, и производя mysql_upgrade для данных/системных таблиц.

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

    Еще немного полезной для нашего случая документации:
    To avoid incompatibilities, set the following variables on the MySQL 5.6 master:

    binlog_checksum=NONE
    binlog_row_image=FULL
    binlog_rows_query_log_events=OFF
    log_bin_use_v1_row_events=1 (NDB Cluster only) 

    Этот флаг оказался для нас обязательным, хотя никакого NDB не используем, без него репликация заканчивалась между серверами 5.6 — 5.5, а mysqlbinlog читает лог без этой опции с ошибкой ERROR: Error in Log_event::read_log_event(): 'Sanity check failed', data_len: 8203, event_type: 30, если включить, то все даже заводится и работает.
    GTID мы не стали включать, т.к. помимо требования к совместимости со всеми старыми инструментами, объективно не видим достаточного количества плюсов для перехода.

    gtid_mode=OFF

    Самый простой тест, чтобы проверить корректность работы репликации — залить дамп по очереди и на сервер с 5.5, и на сервер с 5.6 и посмотреть, все ли будет ок.

    К сожалению, хотя вполне ожидаемо, тесты были неудачными.

    Last_Error: Column 18 of table 'eldorado2.b_adv_banner' cannot be converted from type '<unknown type>' to type 'datetime'

    datetime в 5.6 особенный, в него добавлены микросекунды, поэтому в 5.6 у него новый datetime, неизвестный в 5.5

    версия 5.6 — может работать в кластере в кольце параллельно с 5.5, если при этом ни в одной из таблиц, которые бегают по репликации не создается полей с новыми типами полей. (datetime 5.6 != datetime 5.5, аналогично time, timestamp, всего в нашей бд таких полей больше 240).

    Полностью гарантировать отсутствие DDL с этими полями мы не могли, а ставить под угрозу работоспособность всего кластера не хотелось. Но у нас был более безопасный «План Б».

    Он подразумевал наличие дополнительного железа для маневров и поднятию полной копии кластера рядом, к нашему счастью, такое железо у нас было. А раз уж есть такая возможность, то надо было сделать сразу «нормальный» кластер.

    Но, при этом надо обеспечить сохранение работоспособности всех текущих инструментов мониторинга, отладки, анализа бинлогов и максимально устранить все имеющиеся недостатки текущей архитектуры.

    Многоканальная репликация


    Нужна серебряная пуля, чтобы и сайты были целы, и админы сыты. Это – многоканальная репликация. Мы априори не доверяли новым возможностям и не были уверены в технологии, нигде такой информации или кейсов мы найти не смогли, публичного опыта в крупных production мало.

    Поэтому все продумали сами, план был такой:

    • От одного из слейвов мы начинаем разворачивать еще одно плечо: с нуля разворачиваем новый кластер сразу на версии 5.7, выстраиваем всю новую топологию уже с новыми опциями;
    • Проводим все необходимые нам нагрузочные тесты и проверяем стабильность;
    • Тюним конфигурацию, и если все хорошо — заново разворачиваем весь кластер, но уже не модифицируя никак данные, чтобы он был готов для последующего переключения продуктивной нагрузки.




    — Каждый ублажает впереди стоящего, в то время, как стоящий позади, ублажает его самого. Вопрос заключается в том, кто из них счастлив в большей степени, нежели остальные? Я ошеломленно уставился на диковинную тварь с лицом усталой женщины. Так вот она, знаменитая «загадка Сфинкса»! На мой вкус, все это было как-то чересчур!
    (Макс Фрай «Мой рагнарек»)

    В целевой схеме 4 мастера — это 4 независимых друг от друга потока записи на каждый слейв, которые обрабатываются независимо.

    На всех мастерах теперь можно было выключить log_slave_updates – им просто не надо теперь ничего никуда не нужно ретранслировать, они отдают все изменения в основном потоке (=>еще ниже нагрузка на мастера).

    А заодно можно еще по пути включить минимальный формат бинлогов и параллельную обработку транзакций (достаточно условно, надо правильно ее понимать):

    slave_parallel_workers=5
    slave_parallel_type=LOGICAL_CLOCK
    binlog_row_image=minimal

    При таком сетапе мы в любой момент могли переключить нагрузку на новый кластер, но этот маршрут односторонний и отката не предусматривает.

    На время жизни подключений к старому кластеру опция log_slave_updates на одном мастере-точке входа в новый кластер еще есть и поэтому все изменения из коннектов к «старому» кластеру отлично доезжают в новый, а сразу же после их отмирания эту опцию отключили, приложение же в этот момент смотрело на 3 других мастера и потоки данных никак не пересекались.

    В итоге мы получили такой набор достоинств:

    • Если долгий запрос и блокирует что-то, то это влияет только на один поток репликации из 4х и никак не влияет на другие;
    • Новый формат бинлогов который раньше был невозможен просто из-за версии MySQL теперь занимает в разы меньше места и соответственно трафика, за счет этого намного больше изменений может проходить по всему кластеру;
    • Теперь можно любой мастер выключить абсолютно безболезненно не влияя на все остальное;
    • Аварии мастеров теперь не так страшны, теперь можно в любой непонятной ситуации за минуту склонировать любой сервер, перегенерить server_id, создать креды для доступа слейвов и — новый мастер готов.

    Есть и «минус»:

    • У каждого мастера стало намного больше слейвов и можно легче упереться в канал (на самом деле это не увеличение трафика, а перераспределение во времени и пространстве).

    Что дала новая схема


    Переезд на новую схему оказался успешным, мы провели его за один день, 28 августа 2020 года. Опыт использования новой архитектуры показал, что в три-четыре раза сократилось число проблем с репликацией (совсем избавиться от них невозможно). Повысилась стабильность системы. А главным результатом стало повышение максимальной пропускной способности системы. Если раньше разработчики любые непонятные проблемы могли списывать на репликацию, то теперь у них это не прокатывает.

    Число клиентских проблем, порожденных задержками репликации, сократилось в несколько раз, а это значит, что клиенты стали испытывать хотя бы немного меньше боли. Теперь мы можем выключать когда угодно любой мастер-сервер, чтобы провести на нем работы. Это не оказывает влияния на весь кластер и не останавливает процесс репликации.

    Кластер обслуживает основной сайт «Эльдорадо» – пока еще в большой части старое монолитное приложение с карточками товаров, личным кабинетом, корзиной, обработкой заказов, колл-центром и т.п. На момент написания статьи общая нагрузка на кластер на чтение (только на слейвы) составляет 40k rps, примерно по 5к rps на каждый сервер БД без учета технической нагрузки на отдельные технические слейвы, которая в пиковые моменты существенно выше. Может показаться, что это не очень много, но надо учитывать характер и сложность этих запросов.

    Очень надеемся, что кому-нибудь пригодится наш опыт. Кроме многоканальной репликации мы также используем много интересных вещей, таких как blackhole и federated таблицы, они также позволяют снять очень много головной боли (и немного добавить тем, кто не понимает зачем они нужны), если кому-то интересны нюансы и любые другие вопросы по нашему MySQL – велкам в комментарии.

    За пол года промышленной эксплуатации мы пока не столкнулись с какими-либо проблемами, связанными с многоканальностью и однозначно можем рекомендовать такой сетап, как достаточно отказоустойчивый и надежный.

    Сам же монолит сейчас находится в процессе распила на некоторое количество отдельных и независимых сервисов, часть из которых мы будем шардировать и рассказывать вам о полученном опыте — следите за обновлениями.

    Отдельное спасибо моей отличной команде, без нее мы бы это не сделали!

    P.S. Кстати, нам по-прежнему очень нужны талантливые программисты. Если вы такой, приходите, будет интересно.

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

      +5

      Спасибо, бро! Наконец-то нормальный хардкорный текст.

        +5
        Возникли вопросы:
        1. Приложение работает только с одним мастером или с несколькими сразу?
        2. Кто определяет с каким мастером должно взаимодействовать приложение? Есть ли балансировка?

        P.S. Было бы интересно почитать про federated таблицы.
          +3
          Одна нода приложения работает с одним мастером, но все ноды поделены на 4 «зоны», это просто 4 разных окружения, с немного отличающимися параметрами и своими инстансами кешей, например. Каждая зона работает со своим мастером, поэтому можно сказать что приложение в целом работает со всеми 4мя одновременно, т.к. все активные и работают параллельно. Выбор мастера — вопрос балансировки по этим зонам, тут конечно мы имеем возможность распределять веса и отправлять на любую из них нужную долю пользователей. и со стороны приложения, конечно можем в любой момент сказать в какой зоне какой мастер использовать.

          Про federated — часть некритичных для логики данных (логи заказов, например) — вынесены в отдельный кластер mysql и под них делается отдельный коннект. чтобы иметь возможность делать джоины тех же данных заказов между разными БД, например, основную таблицу заказов сджоинить с логами, можно извращаться, а можно использовать federated таблицы (хотя это тоже извращение :)). Этот federated надо использовать очень осторожно, потому что любой запрос требующий поиска по таблице без первичного ключа приведет к тому, что вся таблица будет полностью копироваться по сети. точно также и в обратную сторону на отдельной реплике для аналитиков мы сделали и обратный проброс таблицы с логами в основную БД.
            +3

            И для некоторых критичных данных, типа данных заказа, приложение может по оффсету сходить в любой "соседний" нужный мастер :)

          +2
          Фасеты/фильтрация тоже только Mysql или есть elasticsearch?

          Тот же вопрос про поиск.
            +3

            Фасеты на моей памяти никогда не были на mysql, раньше в монолите фасеты лежали в редисе, индексы для полнотекста в сфинксе, сейчас и для фасетов и для поиска — это отдельный сервис на джаве и под джавой уже лежит несколько индексов в эластике.

              +1
              Как вы решаете проблему пересчёта эластика, когда приходит обновление наличия/цен? Он же пожиратель всего в такие моменты.

              Или такой проблемы нет?..

              И вдогонку про поиск: вот такие запросы (без пробелов в середине) — это что за магия? Это не полнотекст (из-за отсутствия пробела).
                +1
                (вангую, что перед полнотекстом стоит какой-нибудь яндексовский спеллер на основе Catboost или поднятый, или дёргается по API)
                  +1
                  Про МВ не скажу, там своя кухня на фронте. Про эльдорадо — нет никакой магии, есть список алиасов и автоматических преобразований слов. вся история поисков и статистика по словам естественно ведется. +еще во многих регионах работает внешний сервис anyquery, про его магию не в курсе, но думаю что примерно в эту же сторону работает, т.к. судя по отзывам тех кто работал в админке там много как раз такой ручной настройки. сам руками внутренности не щупал.
                    +1
                    Про пересчет — есть несколько моментов. реалтайм обновление остатков и цен для нас не сильно больно сейчас (раньше на древнем железе с шпиндельными дисками было хуже), намного больнее для нас — полный пересчет индексов (и он архитектурно пока тоже нужен, т.к. далеко не все данные уходят в реалтайме).
                +3

                И Вам даже ребята из Percona не помогли? Или не писали им? Или денег не хватило на их услуги? Только честно.


                Почему сидите на древнем mysql? Я так понял что 5.6 и местами 5.5, по тексту про 5.7 вроде не было упоминаний.

                  +2

                  Им тоже писали и они входят в список тех кто либо не ответил, либо отказался, но мы специально в статье не указываем имен :)
                  Сейчас везде 5.7. До этого сначала было везде 5.5, потом слейвы 5.7+мастера 5.5, и самая боль про обновление мастеров (хотели сначала до 5.6 мягко, а получилось в итоге сразу на 5.7) — как раз в статье.

                    +1
                    5.7 это хорошо, просто из статьи немного не очевидно, что Вы убили 2-х зайцев.

                    А почему не перешли на 8.0?
                      +1
                      Мы посчитали такой переход менее рискованным, чтобы не наткнуться на новые грабли 8й версии, и про переход на 8ку можно будет еще статью написать. Просто чтобы некоторые баги поймать надо неделями гонять нагрузочные тесты :)
                  +3
                  Почему вы не используете gtid?
                  gtid-mode = ON
                  binlog-transaction-dependency-tracking = WRITESET

                  Позволяет накатывать binlog реплики в несколько потоков и значительно уменьшить отставание (при должной настройке)?
                    +2

                    Тут не претендуем на объективность, он нам не понравился в ситуациях потери части транзакций или скипа части бинлога. проигрывание и восстановление может превратиться в боль, списки проигранных сетов тоже. В идеальном мире наверное да, но при нашем потоке изменений — пока не стали менять. Может быть когда-нибудь в будущем ещё потестим и включим, но пока не так уж хочется.

                      +4
                      У нас на 40 000 QPS жило достаточно долго и не было таких проблем, потом нагрузку снизили изменением архитектуры. Как понимаю тут боль совсем не в базе и не в монолите как таковом, а в архитектуре монолита, которая не позволяет шардировать данные. Чтобы не быть голословным:

                        +3
                        У нас тоже нет таких проблем, но мы всегда рассчитываем на худшее в действиях пользователей и готовимся заранее ) Можно несколькими костылями пошардить часть данных, но лучше и проще сделать нормальное решение рядом и вынести. Основная боль — это как раз из сильносвязанного монолита вычленить то, что можно будет сделать слабосвязанным. Сервис новый написать это где-то 5% трудозатрат имхо)

                        Статейки неплохие, спасибо! можем вам рассказать как делать большие альтеры без боли и влияния на пользователей в любое время :)
                          +1
                          Очень интересно! Особенно если это не про pt-online-schema-change, не сливает всю таблицу в репликацию, не аффектит триггеры и совместимо с foreign keys.
                            0
                            с такими вводными наверное не будет нормальных решений, кроме как не использовать триггеры и внешние ключи :) а насчет инструмента — мы юзаем gh-ost. всю таблицу он, конечно, сливает, но делает это умно — меедленно с контролем отставания по другим слейвам перетаскивает данные из основной таблицы. при этом он цепляется как еще один слейв к мастеру и читает поток изменений из бинлога, и эти изменения проигрываются и накатываются на таблицу-копию. Когда данные синхронизированы, вешает небольшую блокировку на несколько секунд и в этот момент ренеймит таблицы. Внешние ключи конечно не дадут такие махинации делать. по принципу очень похоже на pt-schema-change, но чуток поумнее.
                              +1
                              Спасибо, интересное решение. У нас есть триггеры на огромных таблицах и foreign keys на некоторых. Триггеры не позволяют использовать pt-schema-change. И отказаться от них нельзя.
                      +4
                      Вы пишите в статье о binlog_transaction_dependency_tracking, но совершенно не говорите о transaction_write_set_extraction, а эти 2 параметра очень сильно связаны друг с другом и изменить binlog_transaction_dependency_tracking с COMMIT_ORDER на WRITESET как указано в статье без изменения transaction_write_set_extraction невозможно!

                      И второе это баг в MySQL связанный с binlog_transaction_dependency_tracking который исправили только в 5.7.33, у нас например при попытке включить WRITESET реплика через некоторое время просто развалилась и перестала работать. Вот такой милый параметр.

                      P.S. У нас 45 kQPS, местами 50 kQPS и в принципе все гуд.
                        +1
                        Спасибо, полезное замечание, подправил. Естественно привел не все настройки, только те которые показались интересными. У нас transaction_write_set_extraction = XXHASH64
                          +1
                          40к rps мы привели просто как текущее значение, для общего понимания масштаба.
                          в высокий сезон это 1.5-2х от этих значений, но это скорее повод задуматься об оптимизации кода, чем об оптимизации кластера.
                          +1

                          а расскажите пожалуйста подробнее про несколько потоков репликации, я о такой фиче не знаю. Пробовал slave_parallel_workers, но оно плохо параллелит и по сути никак не ускоряет репликацию в моем кейсе. Потоки это что-то другое, или вы про эту настройку?

                            +2
                            mysqlhighavailability.com/improving-the-parallel-applier-with-writeset-based-dependency-tracking Percona/MySQL 5.7.x (точно не помню в какую версию сделали бэкпорт) и 8.x
                              0

                              О, спасибо. Я похоже не внимательно читал доку. Не знал что эта опция может улучшить параллельность выполнения.
                              Получается 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 скапливаются.

                              +2
                              получилось в статье совсем неочевидно, хотя картинку старался делать максимально детальной :)
                              вот тут подробнее. в нашем кейсе потоки данных физически идут от разных мастеров, каждый поток стартует как обычная репликация, но работает с добавлением ко всем командам FOR CHANNEL='masterN'. На каждом слейве у нас сразу 4 мастера. И если транзакции с них при этом не имеют общих блокировок, то выполняются параллельно в рамках одного таймстемпа. ключевое для повышения параллельности обработки — slave_parallel_type и binlog_transaction_dependency_tracking, но как уже тут писали — не совсем безопасно и не стоит просто так крутить из дефолтов особенно в некоторых минорных версиях 5.7.
                                0

                                У меня один мастер. Я пробовал slave_parallel_type=LOGICAL_CLOCK и разное количество воркеров. Это никак не помогает. Параллельность выполнения проверял как в этой статье. Но теперь я знаю что эту ситуацию можно улучшить с помощью binlog_transaction_dependency_tracking.


                                Я кажется понял зачем в вашей схеме несколько мастеров. Видимо для того чтобы ускорить репликацию, так как вместо одного канала, появляется несколько каналов репликации. Так как сами мастера это же не разгружает, всё равно все запросы на запись попадают на каждый мастер.

                                  0

                                  В первую очередь это нужно чтобы любой мастер можно было выключить в любой момент и легко пережить аварию/обслуживание. Но это точно далеко не самый типичный сетап. Я про крайней мере не знаю пока никого, кто делал бы так же. а так да, у каждого треда свой поток applier'a. И немного это разгружает, т.к. часть запросов по бинлогу приходит как раз в row, но конечно не по общему объему записи.

                                    0
                                    В первую очередь это нужно чтобы любой мастер можно было выключить в любой момент и легко пережить аварию/обслуживание

                                    Тогда мне непонятно как вы этого достигли. Ранее вы писали:


                                    Одна нода приложения работает с одним мастером, но все ноды поделены на 4 «зоны», это просто 4 разных окружения

                                    У вас бэкенды поделены на 4 типа, и каждый пишет в свой мастер. В такой конфигурации непонятно, как можно в любой момент отключить хоть один мастер. Тут только наверное если использовать какой-то proxy, например proxysql, который видит что мастер упал/отключен и в этом случае направляет трафик бэкенда на другой мастер.

                                      0

                                      Приложению всегда можно сказать "иди в другой". Это можно и как фоллбэк при невозможности выполнения запроса юзать, и просто переключая настройки енва по срабатыванию мониторинга.

                            0
                            Сам же монолит сейчас находится в процессе распила на некоторое количество отдельных и независимых сервисов, часть из которых мы будем шардировать и рассказывать вам о полученном опыте — следите за обновлениями

                            Можно приоткрыть завесу тайны — что останется от кода битрикса после распила?
                              +4
                              у нас тайн нет. Стратегически — ничего не останется, уже существенная часть работает без него, да и последние апдейты php только повышают вероятность такого сценария. Остались самые тяжелые места для переноса. но на их распил до конца легко может уйти еще года 2-3.
                              0
                              Напишу, вдруг кому полезно будет.
                              Уже достаточно давно использую решение такой проблемы (на 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
                                0
                                выглядит немного костыльно и, конечно, есть вероятность блокировать весь кластер при неудачном тяжелом запросе на мастере.
                                  +1
                                  Тут больше о том, что таким евентом можно точно знать отставание. А что делать с отставанием — другой вопрос.

                                    0
                                    в этой части — да, сами примерно также когда-то мониторили. но сейчас от этого ушли, больше нет такой острой необходимости. сейчас мы мониторим все блокирующие запросы на мастерах (также как и долгие селекты), это получилось полезнее и позволяет еще до попадания в бинлог все найти и отреагировать.
                                  0
                                  Я помню facebook писал об этой проблеме давно-давно (не знаю что у них с базой сейчас). У них было 2 ДЦ, основной и вторичный. Вторичный брал трафик, который к нему ближе, но обновления посылал в основной. Проблему они решили тем, что при изменениях ставили браузеру временную cookie, которая load-balancer-у говорила: «проксируй все запросы в основной ДЦ». Я подумал, что интересное решение.
                                  0
                                  а проблема с отказом одного и мастеров и потерей данных не решилась никак я понимаю?
                                  с MySQL очень давно работал с 5.1 еще. Потом все на MS SQL.
                                  потом пришлось переехать на версию MySQL повыше и таких детских болячек (раньше null в поле not null вставлялось, а в новой версии перестало) я в MS SQL не встречал.
                                  Эльдорадо не может себе позволить СУБД с поддержкой?
                                    0

                                    Такой проблемы с потерей данных, к счастью не было никогда. Худшее что происходило — меняли материнскую плату на тогда ещё железном сервере с мастером. К нашему счастью отказ материнки позволял нормально работать до поставки вендором.
                                    А насчёт БД с поддержкой, у нас полно оракла и есть несколько саповских монстров типа ханы (не конкретно в ИМ eldorado, а в целом в мвидео-эльдорадо), но наличие поддержки не гарантирует отсутствие проблем ни в одной из них.

                                    0
                                    Почему стали переделывать — понял. Но не понял, почему не посмотрели в сторону альтернатив, типа галеры. А если смотрели — что помешало?
                                      0

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

                                      0
                                      Не думали перейти на облачные технологии? Типа Amazon Web Services.

                                      Вот как сделано у нас:
                                      Для отказоустойчивости hot-cold кластер на RHEL. Подключен SAN storage к активному ноду а так же VIP (virtual IP адрес указывает на активный нод). Между нодами есть хардбит. И если один нод упал(чего небыло никогда) то SAN storage и VIP адрес подключаются ко второму ноду. Все, что увидит клиент это то, что нет ответа от сервера пока mysql не поднимется на втором холодном ноде.

                                      Для того чтоб в базу можно было быстро писать и так же быстро читать и не делать master-slave кластеры то сделано что-то типа materialized view. Это две базы. Основная, куда можно быстро и эфективно записывать и вторая View в которую посредством триггеров переносятся изменения из основной базы. Это дает возможность сделать разные способы чтения и записи(к примеру индексы сделаны по разному в таблицах куда будем писать и откуда будем читать).
                                        0

                                        Регулярно думаем об облаках, но есть нюансы и ограничения: облака только российские (яндекс/mail/крок), а ещё лучше собственное где твои ресурсы точно не будут конкурировать с кем-нибудь ещё. С зарубежными намного больше рисков, какими бы удобными они ни были, хотя и их тоже рассматривали.
                                        Насчёт вашего примера — одна большая нода с пассивным стендбаем — это скорее про оракл, для мускуля (даже если она будет очень толстая) — это плохо по целому ряду причин. Такую толстую ноду мускуля легко уронить в сегфолт простым сбросом кверикеша, или как она будет долго висеть в блокировках при большом рпс?.. Главный вопрос — как ее будут дальше скейлить на чтение при росте нагрузки x10? Из вашей схемы пока мне неочевиден 100% выигрыш, т.к. при больших объемах записи вам надо строить больше индексов для нескольких представлений, т.е. это некий tradeoff для случаев большого чтения, ценой удорожания записи. Но в целом интересный подход, для каких-то кейсов точно подойдёт.

                                          0
                                          Если возрастет нагрузка на чтение\запись в х10 то это не проблема. И блокировки тоже не проблема. Потому как есть два сервиса. Один в базу пишет а второй читает(раз в день из базы читает все данные и держит в памяти). И когда происходит запись то сервисы посылают друг другу сообщения, что данные в базе изменились. Сервис который из базы читает рассылает подписчикам изменившиеся данные(дельты) через low latency протокол работающий по UDP. Все сервисы работают в режиме hot-hot кластера и тоже общаются по low latency протоколу. Единственная неприятность в hot-hot схеме это возможный Split-brain но это крайне редкие события.
                                            0

                                            Это уже не совсем бд) это сервис, который ещё и в память может вместить все. При таких вводных тут совсем другой подход. (Для подписчиков видимо интеграция через grpc?)
                                            Но чем толще данные, тем больнее падение такой системы и время восстановления будет.

                                              0
                                              grpc как я понимаю сделан на основе google protobuf. Я говорил о протоколе для быстрой доставки данных IBM WebSphere Low Latency Messaging.
                                            0
                                            Такую толстую ноду мускуля легко уронить в сегфолт простым сбросом кверикеша

                                            ЕМНИП кверикеш давно не рекомендуется к использованию, он блокирующийся внутри и плохо масштабируется с ростом числа ядер.

                                              0
                                              Мы проводили очень много тестов на эту тему, и основной вывод был — конкретно нам — пока лучше с маленьким кверикешем и с тюнингом размера попадающих в него элементов, чем без него. Разница по cpu в разы, несмотря на фрагментацию, блокировки и т.п. вещи.
                                                0

                                                мне привычнее видеть кэширование вынесенным в memcached/redis. конечно, больше ручной работы, но результат более стабильный.

                                          0
                                          Интересно, сколько еще компаний не проапгрейдились на более новые mysql/mariadb? Уж 5.7 вышел давным-давно, и вот спустя все эти годы — такие героические решения.
                                            0

                                            Я думаю многие. Кто-то ещё на фортране пишет до сих пор :) и в нашем зоопарке софт на delphi вполне активно используется.
                                            Конкретно в нашем случае, не будь кольца, обновление бы прошло, конечно, намного раньше.

                                          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                          Самое читаемое