Перейти к содержанию

Улучшения бинарного журнала и репликации

Благодаря постоянному развитию в MyDB Сервер для MySQL вошли ряд улучшений, связанные с репликацией и обработкой бинарных журналов. Это привело к особенностям репликации, отличающим ее от MySQL.

Безопасность операторов с указанием LIMIT

MySQL рассматривает все команды UPDATE/DELETE/INSERT ... SELECT с указанием LIMIT как небезопасные. Эти команды переключают режим бинарного журнала с режима statement-based на row-based, если binlog_format установлен в MIXED.

Вот почему:

  • Указание LIMIT без ORDER BY делает результат недетерминированным

  • Одна и та же команда может затрагивать разные строки даннных на первичном сервере и на репликах

UPDATE table1 LIMIT 10 SET col1 = 'value';
DELETE FROM table1 LIMIT 5;
INSERT INTO table2 SELECT * FROM table1 LIMIT 3;

Чтобы сделать эти команды безопасными для statement-based репликации, нужно сделать одно из следующих действий:

  • Убрать указание LIMIT

  • Добавить указание ORDER BY, чтобы сделать результаты детерминированными

UPDATE table1 SET col1 = 'value' ORDER BY id LIMIT 10;
DELETE FROM table1 ORDER BY id LIMIT 5;
INSERT INTO table2 SELECT * FROM table1 ORDER BY id LIMIT 3;

Исключение составляет случай, когда указание LIMIT используется с ORDER BY по уникальному ключу — в этом случае команда становится детерминированной и безопасной для statement-based репликации.

MyDB Сервер для MySQL более точен, он рассматривает такие команды как безопасные, если они содержат укахание ORDER BY PK или условие WHERE.

Улучшение производительности при обновлении положения журнала ретрансляции

MySQL всегда обновляет позицию журнала ретрансляции в настройках репликации с несколькими источниками независимо от того, была ли уже выполнены зафиксированная транзакция или нет. MyDB Сервер пропускает обновления позиции журнала ретрансляции для уже зарегистрированных GTID.

Детали позиции журнала ретрансляции

В частности, такие безусловные обновления позиции журнала ретрансляции вызывали дополнительные операции fsync в случае relay-log-info-repository=TABLE и с большим количеством каналов, передающих такие дублирующие (уже выполненные) транзакции, ситуация становилась хуже пропорционально. Исправлена ошибка #1786 (MySQL #85141).

Улучшение производительности при обновлении источника и статуса соединения

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

Конфигурация с master_info_repository=TABLE и sync_master_info=0 заставляет реплику обновлять информацию о состоянии источника и соединения в специальной таблице только при ротации файлов журнала, а не после каждых sync_master_info событий. Но эта функциональность не работала в конфигурациях репликации с несколькими источниками. Сигналы Heartbeat, отправляемые на реплику, чтобы пропустить события GTID, которые она уже выполнила, ранее оценивались как события ротации журнала ретрансляции и приводили к синхронизации таблицы mysql.slave_master_info. Эта неточность могла привести к огромному (вплоть до 5 раз при некоторых настройках) увеличению нагрузки на запись на реплике до того, как проблема была исправлена в Percona Server for MySQL. Исправлена ошибка #1812 (MySQL #85158).

Запись команд FLUSH в бинарный журнал

Команды FLUSH, такие как FLUSH SLOW LOGS, не записываются в бинарный журнал, если системная переменная binlog_skip_flush_commands установлена в ON.

Кроме того, были реализованы следующие изменения в поведении режимов read_only и super_read_only:

  • Когда для read_only установлено значение ON, любая команда FLUSH ..., выполняемая обычным пользователем (без привилегии SUPER), не записывается в бинарный журнал независимо от значения переменной binlog_skip_flush_commands.

  • Когда для параметра super_read_only установлено значение ON, любая команда FLUSH ..., выполняемая любым пользователем (даже обладающим привилегией SUPER), не записывается в бинарный журнал независимо от значения переменной binlog_skip_flush_commands.

Попытка запустить команду FLUSH без привилегий SUPER или RELOAD приводит к ошибке ER_SPECIFIC_ACCESS_DENIED_ERROR независимо от значения переменной binlog_skip_flush_commands.

binlog_skip_flush_commands

Свойство Значение
Командная строка Да
Конфигурационный файл Да
Область видимости Глобальная
Динамическая Да
По умолчанию OFF

Когда для binlog_skip_flush_commands установлено значение ON, команды FLUSH ... не записываются в бинарный журнал.

Установка binlog_skip_flush_commands не влияет на следующие команды, потому что они не записываются в бинарный журналЖ

•   `FLUSH LOGS`

•   `FLUSH BINARY LOGS`

•   `FLUSH TABLES WITH READ LOCK`

•   `FLUSH TABLES ... FOR EXPORT`

Команда FLUSH не записывается в бинарный журнал и игнорирует значение binlog_skip_flush_commands, когда она запускается с ключевым словом NO_WRITE_TO_BINLOG (или его синонимом LOCAL).

Сохранение комментариев в командах DDL

Когда вы выполняете команды DDL, например DROP TABLE, сервер выполняет следующие действия в отношении бинарного журнала:

Действие Описание
Удаляет комментарии Сервер удаляет любые комментарии в изначальной команде. Например, если вы используете DROP TABLE my_table /* This is a comment */;, в бинарном журнале не сохраняется комментарий.
Добавляет кавычки Сервер добавляет кавычки вокруг имени таблицы. То есть, если вы запускаете DROP TABLE my_table;, он записывает в журнал DROP TABLE "my_table";.

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

binlog_ddl_skip_rewrite

Свойство Значение
Командная строка Да
Конфигурационный файл Да
Область видимости Глобальная
Динамическая Да
По умолчанию OFF

Если переменная выключена (по умолчанию), сервер удаляет комментарии и добавляет кавычки.

Если переменная включена, все DDL команды DROP TABLE, затрагивающие одну таблицу, записываются в бинарный журнал в следующем виде:

• Комментарии сохраняются, то есть, любые заметки, которые вы добавляете в команду, остаются в бинарном журнале.

• Добавляются кавычки.

Вы можете включать binlog_ddl_skip_rewrite на лету:

-- Проверяем текущее значение
SHOW VARIABLES LIKE 'binlog_ddl_skip_rewrite';

-- Включить переменную
SET GLOBAL binlog_ddl_skip_rewrite = ON;

-- Выключить переменную
SET GLOBAL binlog_ddl_skip_rewrite = OFF;

Вы можете включить её на постоянной основе, добавив её в конфигурационный файл

[mysqld]
binlog_ddl_skip_rewrite = ON

После добавления в конфигурационный файл перезапустите сервис MySQL.

DDL команды DROP TABLE с несколькими таблицами не поддерживаются и возвращают ошибку.

SET binlog_ddl_skip_rewrite = ON;
/*comment at start*/DROP TABLE t /*comment at end*/;

Пользовательские функции бинарного журнала

Чтобы реализовать восстановление на определенный момент времени (point in time recovery), мы добавили binlog_utils_udf. Включены следующие определяемые пользователем функции:

Имя Возвращает Описание
get_binlog_by_gtid() Имя файла бинлога в виде строки Возвращает имя файла бинлога, содержащего указанный GTID
get_last_gtid_from_binlog() GTID в виде строки Возвращает последний GTID в указанном бинлоге
get_gtid_set_by_binlog() Набор GTID в виде строки Возвращает все GTID в указанном бинлоге
get_binlog_by_gtid_set() Имя файла бинлога в виде строки Возвращает имя файла бинлога, который содержит хотя бы один GTID из указанного набора
get_first_record_timestamp_by_binlog() Временную метку в виде целого числа Возвращает временную метку первого события в указанном бинлоге
get_last_record_timestamp_by_binlog() Временную метку в виде целого числа Возвращает временную метку последнего события в указанном бинлоге

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

Все функции, принимающие имя бинарного журнала в качестве параметра, принимают только короткие имена без компонента пути. Если во входных данных обнаружен разделитель пути (/), возвращается ошибка. Это требуется для ограничения путей, из которых можно читать бинарные журналы. Они всегда считываются из текущего каталога для бинарных журналов (системная переменная @@log_bin_basename).

Все функции, возвращающие имена файлов бинарного журнала, возвращают имя в краткой форме, без компонента пути.

Основной синтаксис get_binlog_by_gtid() следующий:

* get_binlog_by_gtid(string) [AS] alias

Использование: SELECT get_binlog_by_gtid(string) [AS] alias

Пример использования команды get_binlog_gtid:

CREATE FUNCTION get_binlog_by_gtid RETURNS STRING SONAME 'binlog_utils_udf.so';
SELECT get_binlog_by_gtid("F6F54186-8495-47B3-8D9F-011DDB1B65B3:1") AS result;
Ожидаемый результат
+--------------+
| result       |
+==============+
| binlog.00001 |
+--------------+
DROP FUNCTION get_binlog_by_gtid;

Основной синтаксис get_last_gtid_from_binlog() следующий:

* get_last_gtid_from_binlog(string) [AS] alias

Использование: SELECT get_last_gtid_from_binlog(string) [AS] alias

Например использования команды get_last_gtid_from_binlog:

CREATE FUNCTION get_last_gtid_from_binlog RETURNS STRING SONAME 'binlog_utils_udf.so';
SELECT get_last_gtid_from_binlog("binlog.00001") AS result;
Ожидаемый результат
+-----------------------------------------+
| result                                  |
+=========================================+
| F6F54186-8495-47B3-8D9F-011DDB1B65B3:10 |
+-----------------------------------------+
DROP FUNCTION get_last_gtid_from_binlog;

Основной синтаксис get_gtid_set_by_binlog() следующий:

* get_gtid_set_by_binlog(string) [AS] alias

Использование: SELECT get_gtid_set_by_binlog(string) [AS] alias

Например использование команды get_gtid_set_by_binlog:

CREATE FUNCTION get_gtid_set_by_binlog RETURNS STRING SONAME 'binlog_utils_udf.so';
SELECT get_gtid_set_by_binlog("binlog.00001") AS result;
Ожидаемый результат
+-------------------------+
| result                  |
+=========================+
| 11ea-b9a7:7,11ea-b9a7:8 |
+-------------------------+
DROP FUNCTION get_gtid_set_by_binlog;

Основной синтаксис get_binlog_by_gtid_set() следующий:

* get_binlog_by_gtid_set(string) [AS] alias

Использование: SELECT get_binlog_by_gtid_set(string) [AS] alias

Пример использования команды get_binlog_by_gtid_set:

CREATE FUNCTION get_binlog_by_gtid_set RETURNS STRING SONAME 'binlog_utils_udf.so';
SELECT get_binlog_by_gtid_set("11ea-b9a7:7,11ea-b9a7:8") AS result;
Ожидаемый результат
+---------------------------------------------------------------+
| result                                                        |
+===============================================================+
| bin.000003                                                    |
+---------------------------------------------------------------+
DROP FUNCTION get_binlog_by_gtid_set;

Основной синтаксис get_first_record_timestamp_by_binlog() следующий:

* get_first_record_timestamp_by_binlog(TIMESTAMP) [AS] alias

Использование: SELECT get_first_record_timestamp_by_binlog(TIMESTAMP) [AS] alias

Пример использования команды get_first_record_timestamp_by_binlog:

CREATE FUNCTION get_first_record_timestamp_by_binlog RETURNS INTEGER SONAME 'binlog_utils_udf.so';
SELECT FROM_UNIXTIME(get_first_record_timestamp_by_binlog("bin.00003") DIV 1000000) AS result;
Ожидаемый результат
+---------------------+
| result              |
+=====================+
| 2020-12-03 09:10:40 |
+---------------------+
DROP FUNCTION get_first_record_timestamp_by_binlog;

Основной синтаксис get_last_record_timestamp_by_binlog() следующий:

* get_last_record_timestamp_by_binlog(TIMESTAMP) [AS] alias

Использование: SELECT get_last_record_timestamp_by_binlog(TIMESTAMP) [AS] alias

Пример использования команды get_last_record_timestamp_by_binlog:

CREATE FUNCTION get_last_record_timestamp_by_binlog RETURNS INTEGER SONAME 'binlog_utils_udf.so';
SELECT FROM_UNIXTIME(get_last_record_timestamp_by_binlog("bin.00003") DIV 1000000) AS result;
Ожидаемый результат
+---------------------+
| result              |
+=====================+
| 2020-12-04 04:18:56 |
+---------------------+
DROP FUNCTION get_last_record_timestamp_by_binlog;

Ограничения

Для следующих переменных не присваивайте значения с одним или несколькими символами точки (.):

Значение с использованием точки (.) обрабатывается по-разному в MySQL и Percona XtraBackup и может привести к непредсказуемому поведению.


Последнее обновление: 2025-01-28