Есть имхо, что просто мастер-мастер схема, даже с galeradb и подобными (не знаю про vitess), или недостаточно надежна, или слишком сложна (нужны всякие скрипты-костыли для проверки целостности данных).
Нет гарантии того, что запись прошла прям на все узлы нашего мастер-мастер.
Я б добавил какой-то буфер (типа Кафки), в котором данные хранятся, пока не придет подтверждение завершенной транзакции от всех узлов ДБ кластера.
И я тут кидал ссылку на митап Mysql@Scale
, там чуваки из Баду и Авито говорили про их опыт:
- В основном master-slave схемы, а не мастер-мастер. Это просто проще.
- ProxySQL на каждом узле, который обращается к базе
- Запросы чз ProxySQL балансятся по нодам мускула, и proxysql выкидывает сдохшие узлы из списков балансировки. Тут вопрос про балансировку, раз они в основном используют мастер-слейв, видимо, мастеров все-таки обычно много.
- И тут деталь, которую я или не досмотрел, или не понял - как мы убеждаемся, что транзакции успешно прошли на всех узлах
Я несколько лет назад перевозил всю инфраструктуру нашу из одного ЦОДа в другой и соответственно надо было заново развернуть её в другом ЦОДе.
Т.к. до этого всё было поднято спонтанно, руками и местами отказоустойчивость была конкретно ручная, то мы решили всю инфру раскатывать через ansible и попутно решали вопросы повышения отказоустойчивости.
И тоже думали про кластеры и прочую автоматическую хрень (master-master). Связывать с этим не хотелось, т.к. было понятно что на поддержку и в случае факапа софта потребуется очень много времени.
В итоге решили делать по схоже схеме с деградационными схемами. Т.е. в случае выхода из строя мастера мы готовы пожить в RO до тех пор пока руками не примем решение что с этим делать. Ибо мастера могут потеряться по разным причинам (ошибка в сети, ошибка в конфигурации, выход из строя железки).
1. С mysql вообще схема была простая: master -> slave -> [slave N] -> slave for backup. На всех слейвах read_only=1. Все обращения в mysql идут через именам, имена смотрят на haproxy, инстансов haproxy 2 (1 мастер, 2 слейв). У слейва настроен пул из всех слейвов и мастера. У мастера настроен master и в качестве бэкапа следующий за ним слейв.
В итоге если слейв какой-то подыхает - всё продолжит штатно работать - мы его восстановим и подоткнем в нужное место обратно.
Если подохнет мастер - мы получил об этом нотификацию и пойдем смотреть причину и по факту диагностики примем решение запускать мастера на следующем слейве или восстанавливать мастера исходного. А всё это время сервисы работают в RO. Да, сервисы должны быть готовы к этому - в этом нет большой проблемы.
2. С Редисом тоже похожая схема - глядя на статec sentinel, который рядом с инстансом крутится - мы понимаем кто есть мастер и туда haproxy льет трафик. Несколько раз это проверялось в бою.
3. memcache с помощью mcrouter без проблем масштабируется и HA тоже настроить можно.
4. кассандра из коробки HA, но надо понимать что там изначально eventual consistency и сравнивать ее с мускулем неразумно.
Вообщем мысль в том, что иногда или почти всегда правильнее построить архитектуру проще и понимать, что в случае сбоя вы просто немного деградируете и понимать как с этой деградацией работать, чем обкладывать себя кластерами, которые живут своей жизнью и хер поймешь как его потом собрать обратно, если на каком-то коммутатор пакет дропнулся или уборщица патч-корд зацепила.