23.7 Логирование двоичных файлов хранимых программ
Двоичный журнал содержит информацию о SQL-запросах, которые изменяют содержимое базы данных. Эта информация хранится в виде “событий”, описывающих эти изменения. (События двоичного журнала отличаются от хранимых объектов запланированных событий.) Двоичный журнал имеет две важные цели:
Для репликации двоичный журнал используется на серверах источника репликации в качестве записи запросов, которые должны быть отправлены на серверы репликации. Источник отправляет события, содержащиеся в его двоичном журнале, своим репликам, которые выполняют эти события, чтобы произвести те же изменения данных, что и на источнике. См. Раздел 16.2, «Реализация репликации».
Некоторые операции по восстановлению данных требуют использования двоичного журнала. После восстановления файла резервной копии выполняются события в двоичном журнале, которые были записаны после создания резервной копии. Эти события обновляют базы данных до момента создания резервной копии. См. Раздел 7.3.2, «Использование резервных копий для восстановления».
Однако, если ведется журнализация на уровне оператора, существуют определенные проблемы с ведением двоичного журнала в отношении хранимых программ (хранимых процедур и функций, триггеров и событий):
В некоторых случаях оператор может влиять на разные наборы строк на источнике и реплике.
Запросы, выполняемые на реплике, обрабатываются потоком SQL реплики, который имеет полные привилегии. Возможно, что процедура следует разным путям выполнения на серверах источника и реплики, поэтому пользователь может написать процедуру, содержащую опасный оператор, который выполняется только на реплике, где он обрабатывается потоком с полными привилегиями.
Если хранимая программа, изменяющая данные, является недетерминированной, она не повторяема. Это может привести к разным данным на источнике и реплике или к различиям восстановленных данных и исходных данных.
В этом разделе описывается, как MySQL обрабатывает двоичное журналирование для хранимых программ. Он указывает текущие условия, налагаемые реализацией на использование хранимых программ, и что можно сделать, чтобы избежать проблем с журналированием. Также предоставляется дополнительная информация о причинах этих условий.
Если не указано иное, замечания здесь предполагают, что двоичное журналирование включено на сервере (см. Раздел 5.4.4, «Двоичный журнал»). Если двоичный журнал не включен, репликация невозможна, и двоичный журнал недоступен для восстановления данных. В MySQL 5.7 двоичное журналирование по умолчанию не включено, и вы включаете его с помощью опции --log-bin.
В общем случае, описанные здесь проблемы возникают, когда двоичное журналирование происходит на уровне SQL-запроса (журналирование на основе операторов). Если вы используете журналирование на основе строк, журнал содержит изменения, внесенные в отдельные строки в результате выполнения SQL-операторов. При выполнении процедур или триггеров изменения строк регистрируются, а не запросы, которые их производят. Для хранимых процедур это означает, что оператор CALL не регистрируется. Для хранимых функций изменения строк, внесенные внутри функции, регистрируются, а не вызов функции. Для триггеров регистрируются изменения строк, вносимые триггером. На стороне реплики видны только изменения строк, а не вызов хранимой программы.
Журналирование в смешанном формате (binlog_format=MIXED) использует журналирование на основе операторов, за исключением случаев, когда гарантируется правильность результатов только при журналировании на основе строк. При смешанном формате, когда хранимая функция, хранимая процедура, триггер, событие или подготовленный запрос содержат что-либо, что не безопасно для журналирования на основе операторов, весь оператор помечается как небезопасный и регистрируется в формате на основе строк. Операторы, используемые для создания и удаления процедур, функций, триггеров и событий, всегда безопасны и регистрируются в формате операторов. Дополнительную информацию о журналировании на основе строк, в смешанном формате и на основе операторов, а также о том, как определяются безопасные и небезопасные операторы, см. в Разделе 16.2.1, «Форматы репликации».
Условия использования хранимых функций в MySQL можно обобщить следующим образом. Эти условия не применяются к хранимым процедурам или событиям планировщика событий, и они не применяются, если двоичное журналирование не включено.
Для создания или изменения хранимой функции вам необходимо право
SUPERв дополнение к правуCREATE ROUTINEилиALTER ROUTINE, которые обычно требуются. (В зависимости от значенияDEFINERв определении функции, может потребоваться правоSUPER, независимо от того, включено ли двоичное журналирование. См. Раздел 13.1.16, «Операторы CREATE PROCEDURE и CREATE FUNCTION».)-
При создании хранимой функции вы должны объявить, что она детерминированная или что она не изменяет данные. В противном случае она может быть небезопасной для восстановления данных или репликации.
По умолчанию, чтобы оператор
CREATE FUNCTIONбыл принят, должно быть явно указано по крайней мере одно изDETERMINISTIC,NO SQLилиREADS SQL DATA. В противном случае возникает ошибка:ERROR 1418 (HY000): This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable)
Эта функция детерминированная (и не изменяет данные), поэтому она безопасна:
CREATE FUNCTION f1(i INT) RETURNS INT DETERMINISTIC READS SQL DATA BEGIN RETURN i; END;
Эта функция использует
UUID(), которая не детерминированная, поэтому функция также не детерминированная и небезопасна:CREATE FUNCTION f2() RETURNS CHAR(36) CHARACTER SET utf8 BEGIN RETURN UUID(); END;
Эта функция изменяет данные, поэтому она может быть небезопасной:
CREATE FUNCTION f3(p_id INT) RETURNS INT BEGIN UPDATE t SET modtime = NOW() WHERE id = p_id; RETURN ROW_COUNT(); END;
Оценка характера функции основана на «честности» создателя. MySQL не проверяет, что функция, объявленная
DETERMINISTIC, свободна от операторов, которые дают недетерминированные результаты. При попытке выполнить хранимую функцию, если
binlog_format=STATEMENTустановлено, ключевое словоDETERMINISTICдолжно быть указано в определении функции. Если этого не происходит, генерируется ошибка, и функция не выполняется, если не заданоlog_bin_trust_function_creators=1для переопределения этой проверки (см. ниже). Для рекурсивных вызовов функции ключевое словоDETERMINISTICтребуется только для самого внешнего вызова. Если используется журналирование на основе строк или смешанное журналирование, оператор принимается и реплицируется даже если функция была определена без ключевого словаDETERMINISTIC.Поскольку MySQL не проверяет, действительно ли функция детерминированная во время создания, вызов хранимой функции с ключевым словом
DETERMINISTICможет выполнить действие, небезопасное для журналирования на основе операторов, или вызвать функцию или процедуру, содержащую небезопасные операторы. Если это происходит при установкеbinlog_format=STATEMENT, выводится сообщение об ошибке. Если используется журналирование на основе строк или смешанное журналирование, предупреждение не выдается, и оператор реплицируется в формате на основе строк.-
Чтобы ослабить предыдущие условия создания функций (что у вас должно быть право
SUPERи что функция должна быть детерминированной или не изменять данные), установите системную переменнуюlog_bin_trust_function_creatorsв 1. По умолчанию эта переменная имеет значение 0, но вы можете изменить ее так:mysql>
SET GLOBAL log_bin_trust_function_creators = 1;Вы также можете установить эту переменную при запуске сервера.
Если двоичное журналирование не включено,
log_bin_trust_function_creatorsне применяется.SUPERне требуется для создания функции, если, как описано ранее, значениеDEFINERв определении функции этого не требует. Дополнительную информацию о встроенных функциях, которые могут быть небезопасными для репликации (и, следовательно, заставляющих хранимые функции, которые их используют, быть небезопасными), см. в Разделе 16.4.1, «Функциональные возможности и проблемы репликации».
Триггеры аналогичны хранимым функциям, поэтому вышеизложенные замечания о функциях также применяются к триггерам с одним исключением: CREATE TRIGGER не имеет необязательного свойства DETERMINISTIC, поэтому триггеры предполагаются всегда детерминированными. Однако это предположение может быть неверным в некоторых случаях. Например, функция UUID() не детерминированная (и не реплицируется). Будьте осторожны при использовании таких функций в триггерах.
Триггеры могут обновлять таблицы, поэтому сообщения об ошибках, аналогичные сообщениям для хранимых функций, возникают с CREATE
TRIGGER, если у вас нет необходимых привилегий. На стороне репликации реплика использует атрибут триггера DEFINER для определения пользователя, который считается создателем триггера.
Остальная часть этого раздела содержит дополнительные подробности о реализации журналирования и ее последствиях. Вам не нужно ее читать, если вас не интересуют обоснования текущих условий журналирования в отношении использования хранимых программ. Это обсуждение относится только к журналированию на основе операторов, а не к журналированию на основе строк, за исключением первого пункта: операторы CREATE и DROP регистрируются как операторы независимо от режима журналирования.
Сервер записывает
CREATE EVENT,CREATE PROCEDURE,CREATE FUNCTION,ALTER EVENT,ALTER PROCEDURE,ALTER FUNCTION,DROP EVENT,DROP PROCEDUREиDROP FUNCTIONоператоры в двоичный журнал.-
Вызов сохранённой функции регистрируется как оператор
SELECT, если функция изменяет данные и происходит внутри оператора, который в противном случае не регистрировался бы. Это предотвращает нерепликацию изменений данных, которые возникают в результате использования сохранённых функций в нерегистрируемых операторах. Например, операторыSELECTне записываются в двоичный журнал, но операторSELECTможет вызывать сохранённую функцию, которая вносит изменения. Для обработки этого операторSELECTзаписывается в двоичный журнал, когда данная функция вносит изменение. Предположим, что на сервере источника выполнены следующие операторы:func_name()CREATE FUNCTION f1(a INT) RETURNS INT BEGIN IF (a < 3) THEN INSERT INTO t2 VALUES (a); END IF; RETURN 0; END; CREATE TABLE t1 (a INT); INSERT INTO t1 VALUES (1),(2),(3); SELECT f1(a) FROM t1;Когда выполняется оператор
SELECT, функцияf1()вызывается трижды. Два из этих вызовов вставляют строку, и MySQL регистрирует операторSELECTдля каждого из них. То есть MySQL записывает следующие операторы в двоичный журнал:SELECT f1(1); SELECT f1(2);
Сервер также регистрирует оператор
SELECTдля вызова сохранённой функции, когда функция вызывает сохранённую процедуру, которая вызывает ошибку. В этом случае сервер записывает операторSELECTв журнал вместе с ожидаемым кодом ошибки. На реплике, если произойдёт та же ошибка, это ожидаемый результат, и репликация продолжается. В противном случае репликация останавливается. -
Регистрация вызовов сохранённых функций вместо операторов, выполняемых функцией, имеет последствия для репликации, которые возникают из двух факторов:
Возможно, функция имеет разные пути выполнения на серверах источника и реплики.
Операторы, выполняемые на реплике, обрабатываются реплицирующей SQL-нитью, которая имеет полные привилегии.
Это означает, что хотя для создания функции пользователю необходимо иметь привилегию
CREATE ROUTINE, пользователь может написать функцию, содержащую опасный оператор, который выполняется только на реплике, где он обрабатывается нитью с полными привилегиями. Например, если значения идентификатора сервера источника и реплики соответственно равны 1 и 2, пользователь на сервере источника может создать и вызвать небезопасную функциюunsafe_func()следующим образом:mysql>
delimiter //mysql>CREATE FUNCTION unsafe_func () RETURNS INT->BEGIN->IF @@server_id=2 THEN->dangerous_statement; END IF;RETURN 1;->END;->//mysql>delimiter ;mysql>INSERT INTO t VALUES(unsafe_func());Операторы
CREATE FUNCTIONиINSERTзаписываются в двоичный журнал, поэтому реплика их выполняет. Поскольку реплицирующая SQL-нить имеет полные привилегии, она выполняет опасный оператор. Таким образом, вызов функции имеет разные эффекты на сервере источника и реплике и не является безопасным для репликации.Для защиты от этой опасности для серверов с включённой двоичной записью создатели сохранённых функций должны иметь привилегию
SUPERв дополнение к обычной привилегииCREATE ROUTINE, которая требуется. Аналогично, для использованияALTER FUNCTIONвам необходимо иметь привилегиюSUPERв дополнение к привилегииALTER ROUTINE. Без привилегииSUPERвозникает ошибка:ERROR 1419 (HY000): You do not have the SUPER privilege and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable)
Если вы не хотите требовать от создателей функций привилегии
SUPER(например, если все пользователи с привилегиейCREATE ROUTINEна вашей системе являются опытными разработчиками приложений), установите глобальную системную переменнуюlog_bin_trust_function_creatorsв 1. Вы также можете установить эту переменную при запуске сервера. Если двоичная запись не включена,log_bin_trust_function_creatorsне применяется.SUPERне требуется для создания функции, если, как описано ранее, значениеDEFINERв определении функции требует этого. -
Если функция, выполняющая обновления, является недетерминированной, она не повторяема. Это может иметь два нежелательных эффекта:
Это делает реплику отличной от источника.
Восстановленные данные отличаются от исходных данных.
Для решения этих проблем MySQL вводит следующее требование: на сервере источника создание и изменение функции отказывается, если вы не объявляете функцию детерминированной или не изменяющей данные. Здесь применяются два набора характеристик функций:
Характеристики
DETERMINISTICиNOT DETERMINISTICуказывают, всегда ли функция производит один и тот же результат для заданных входных данных. По умолчанию этоNOT DETERMINISTIC, если ни одна характеристика не задана. Чтобы объявить функцию детерминированной, вы должны явно указатьDETERMINISTIC.Характеристики
CONTAINS SQL,NO SQL,READS SQL DATAиMODIFIES SQL DATAпредоставляют информацию о том, считывает или записывает данные функция. Любое изNO SQLилиREADS SQL DATAуказывает, что функция не изменяет данные, но вы должны явно указать одно из них, потому что по умолчанию этоCONTAINS SQL, если ни одна характеристика не задана.
По умолчанию, чтобы оператор
CREATE FUNCTIONбыл принят, должно быть явно указано по крайней мере одно изDETERMINISTIC,NO SQLилиREADS SQL DATA. В противном случае возникает ошибка:ERROR 1418 (HY000): This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable)
Если вы установите
log_bin_trust_function_creatorsв 1, требование, чтобы функции были детерминированными или не изменяли данные, отпадает.
-
Вызовы хранимых процедур регистрируются на уровне операторов, а не на уровне
CALL. То есть, сервер не регистрирует операторCALL, а регистрирует те операторы внутри процедуры, которые фактически выполняются. В результате, те же изменения, которые происходят на исходном сервере, наблюдаются на репликах. Это предотвращает проблемы, которые могут возникнуть, если у процедуры есть разные пути выполнения на разных машинах.В общем случае, операторы, выполняемые внутри хранимой процедуры, записываются в двоичный журнал с теми же правилами, которые применялись бы при выполнении этих операторов автономно. При записи операторов процедур применяется особая обработка, поскольку выполнение операторов внутри процедур отличается от выполнения вне контекста процедуры:
-
Оператор, подлежащий регистрации, может содержать ссылки на локальные переменные процедуры. Эти переменные не существуют вне контекста хранимой процедуры, поэтому оператор, ссылающийся на такую переменную, не может быть зарегистрирован буквально. Вместо этого каждая ссылка на локальную переменную заменяется на этот конструкт для целей регистрации:
NAME_CONST(
var_name,var_value)var_name— имя локальной переменной, аvar_value— константа, обозначающая значение переменной в момент регистрации оператора.NAME_CONST()имеет значениеvar_valueи имяvar_name. Таким образом, если вы вызовете эту функцию напрямую, вы получите результат такого вида:mysql>
SELECT NAME_CONST('myname', 14);+--------+ | myname | +--------+ | 14 | +--------+NAME_CONST()позволяет выполнить автономный зарегистрированный оператор на реплике с тем же эффектом, что и исходный оператор, который был выполнен на источнике внутри хранимой процедуры.Использование
NAME_CONST()может привести к проблеме для операторовCREATE TABLE ... SELECT, когда выражения столбцов источника ссылаются на локальные переменные. Преобразование этих ссылок вNAME_CONST()-выражения может привести к именам столбцов, которые отличаются на источнике и реплике серверов, или к именам, которые слишком длинные, чтобы быть допустимыми идентификаторами столбцов. Способ решения проблемы — предоставить псевдонимы для столбцов, которые ссылаются на локальные переменные. Рассмотрим этот оператор, когдаmyvarимеет значение 1:CREATE TABLE t1 SELECT myvar;
Он переписывается следующим образом:
CREATE TABLE t1 SELECT NAME_CONST(myvar, 1);
Чтобы гарантировать, что имена столбцов в таблицах источника и реплики будут одинаковыми, запишите оператор так:
CREATE TABLE t1 SELECT myvar AS myvar;
Переписанный оператор становится:
CREATE TABLE t1 SELECT NAME_CONST(myvar, 1) AS myvar;
-
Оператор, подлежащий регистрации, может содержать ссылки на пользовательские переменные. Для обработки этого MySQL записывает оператор
SETв двоичный журнал, чтобы убедиться, что переменная существует на реплике с таким же значением, как и на источнике. Например, если оператор ссылается на переменную@my_var, этому оператору в двоичном журнале предшествует следующий оператор, гдеvalue— значение@my_varна источнике:SET @my_var =
value; -
Вызовы процедур могут происходить внутри подтверждённой или отменённой транзакции. Контекст транзакции учитывается, чтобы обеспечить корректную репликацию транзакционных аспектов выполнения процедуры. То есть, сервер регистрирует те операторы внутри процедуры, которые фактически выполняются и изменяют данные, а также регистрирует операторы
BEGIN,COMMITиROLLBACKпри необходимости. Например, если процедура обновляет только транзакционные таблицы и выполняется внутри транзакции, которая отменяется, эти обновления не регистрируются. Если процедура выполняется внутри подтверждённой транзакции, операторыBEGINиCOMMITрегистрируются вместе с обновлениями. Для процедуры, выполняемой внутри отменённой транзакции, её операторы регистрируются по тем же правилам, которые применялись бы, если бы операторы выполнялись автономно:Обновления транзакционных таблиц не регистрируются.
Обновления нетранзакционных таблиц регистрируются, потому что откат их не отменяет.
Обновления смешанных транзакционных и нетранзакционных таблиц регистрируются, окружённые операторами
BEGINиROLLBACK, чтобы реплики выполнили такие же изменения и откаты, как и на источнике.
-
Вызов хранимой процедуры не записывается в двоичный журнал на уровне операторов, если процедура вызывается изнутри хранимой функции. В этом случае единственное, что регистрируется, это оператор, вызывающий функцию (если он находится внутри регистрируемого оператора) или оператор
DO(если он находится внутри оператора, который не регистрируется). По этой причине следует проявлять осторожность при использовании хранимых функций, которые вызывают процедуру, даже если процедура сама по себе безопасна.
© 2025 Oracle
Licensed under the GPLv2 License.