MySQL, ROW/STATEMENT/MIXED-репликация и триггеры
Описанная особенность MySQL попалась мне на глаза слишком поздно, пишу, чтобы кто-то не напоролся на те же грабли. Начнем с начала. Итак, необходимо было отслеживать изменения MySQL-базы данных и складывать эти изменения в очередь (не в БД) для дальнейшей обработки внешней системой. Для отслеживания изменений подходят триггеры, но они активируются в процессе выполнения запросов транзакции и в случае последующего "rollback" не будут откатываться (что совершенно нормально для триггеров, влияющих только на состояние БД, т.к. состояние БД будет корректно откатываться). Поэтому необходимо выполнять триггеры только для успешных транзакций: проще всего это достигнуть с помощью репликации - на слейв передаются только запросы зафиксированных транзакций. Таким образом, мастер-БД не содержит триггеров, после репликации данные попадают на слейв, таблицы на котором обвешаны триггерами, те активируются и данные попадают в очередь. Казалось бы, все замечательно?
Однако MySQL не был бы MySQL, если бы не какие-нибудь приколы на пути. Читаем раздел 16.3.1.29 документации: триггеры на слейве выполняются только при использовании STATEMENT-based репликации, при использовании ROW-based репликации они выполняются на мастере. ROW-based репликация - одно из нововведений 5.1, при котором на слейв передаются не запросы (текст запроса), измененный в БД записи (подробнее см. раздел 16.1.2). Но это было бы еще полбеды - попробовал, увидел, что триггеры не выполняются, и пошел разбираться. Однако в MySQL придумали еще режим репликации MIXED: при этом сервер сам переключается между STATEMENT и ROW-based репликацией (я не нашел, по какому условию). Далее, в версиях MySQL от 5.1.12 до 5.1.29 режимом репликации по умолчанию выбирается как раз MIXED. Как вы уже догадываетесь, в MIXED-режиме триггеры на слейве то выполняются, то нет, при этом внешне это совершенно незаметно. Так прекрасно работавший слейв в один прекрасный день вдруг перестает поставлять обновления в очередь, хотя сам по себе слейв не отстал и содержит все данные.
Итого, несколько часов на поиск проблемы, работа по пересинхронизации данных во внешней системе... Спасибо, MySQL!
Кратко: чтобы триггеры на слейве всегда выполнялись, надо добавить в my.cnf (на мастере):
[mysqld]
binlog-format=STATEMENT
Комментарии
Comments powered by Disqus