27.7 Логирование двоичных данных хранимых программ
Двоичный журнал содержит информацию о SQL-запросах, изменяющих содержимое базы данных. Эта информация хранится в виде «событий», описывающих эти изменения. (События двоичного журнала отличаются от хранимых объектов запланированных событий.) Двоичный журнал выполняет две важные функции:
Для репликации, двоичный журнал используется на серверах источника репликации в качестве записи запросов, которые будут отправлены на серверы реплики. Источник отправляет события, содержащиеся в его двоичном журнале, своим репликам, которые выполняют эти события, чтобы произвести те же изменения данных, которые были произведены на источнике. См. Раздел 19.2, «Реализация репликации».
Некоторые операции восстановления данных требуют использования двоичного журнала. После восстановления файла резервной копии, события в двоичном журнале, которые были записаны после создания резервной копии, повторно выполняются. Эти события обновляют базу данных до момента создания резервной копии. См. Раздел 9.3.2, «Использование резервных копий для восстановления».
Однако, если протоколирование происходит на уровне запроса, существуют определенные проблемы с двоичным протоколированием в отношении хранимых программ (хранимых процедур и функций, триггеров и событий):
В некоторых случаях запрос может затрагивать разные наборы строк на источнике и реплике.
Запросы, реплицированные на реплике, обрабатываются потоком приложения реплики. Если вы не реализовали проверки привилегий репликации (см. Раздел 19.3.3, «Проверка привилегий репликации»), поток приложения имеет полные привилегии. В этом случае процедура может следовать разным путям выполнения на серверах источника и реплики, поэтому пользователь может написать процедуру, содержащую опасный запрос, который выполняется только на реплике.
Если хранимая программа, изменяющая данные, является недетерминированной, она не повторяема. Это может привести к разным данным на источнике и реплике или к тому, что восстановленные данные будут отличаться от исходных данных.
Этот раздел описывает, как MySQL обрабатывает двоичное протоколирование хранимых программ. Он указывает текущие условия, которые накладывает реализация на использование хранимых программ, и что можно сделать, чтобы избежать проблем с протоколированием. Он также предоставляет дополнительную информацию о причинах этих условий.
Если не указано иное, замечания в данном разделе предполагают, что двоичное протоколирование включено на сервере (см. Раздел 7.4.4, «Двоичный журнал».) Если двоичный журнал не включен, репликация невозможна, а также двоичный журнал недоступен для восстановления данных. Двоичное протоколирование включено по умолчанию и отключено только в том случае, если вы запустили сервер с --skip-log-bin или --disable-log-bin при запуске.
В общем случае, описанные здесь проблемы возникают, когда двоичное протоколирование происходит на уровне SQL-запроса (протоколирование на основе запросов). Если вы используете протоколирование на основе строк, журнал содержит изменения, внесенные в отдельные строки в результате выполнения SQL-запросов. При выполнении процедур или триггеров изменения строк протоколируются, а не запросы, которые эти изменения производят. Для хранимых процедур это означает, что запрос CALL не протоколируется. Для хранимых функций изменения строк внутри функции протоколируются, а не вызов функции. Для триггеров протоколируются изменения строк, внесенные триггером. На стороне реплики видны только изменения строк, а не вызов хранимой программы.
Двоичное протоколирование смешанного формата (binlog_format=MIXED) использует протоколирование на основе запросов, за исключением случаев, когда только протоколирование на основе строк гарантирует правильные результаты. В формате смешанного протоколирования, когда хранимая функция, хранимая процедура, триггер, событие или подготовленный запрос содержат что-либо, что небезопасно для протоколирования на основе запросов, весь запрос помечается как небезопасный и протоколируется в формате на основе строк. Запросы для создания и удаления процедур, функций, триггеров и событий всегда безопасны и протоколируются в формате запросов. Более подробную информацию о протоколировании на основе строк, смешанном и на основе запросов, а также о том, как определяются безопасные и небезопасные запросы, см. в Разделе 19.2.1, «Форматы репликации».
Условия использования хранимых функций в MySQL можно сформулировать следующим образом. Эти условия не применяются к хранимым процедурам или событиям планировщика событий, и они не применяются, если двоичное протоколирование не включено.
Для создания или изменения хранимой функции вам необходимо разрешение
SET_ANY_DEFINERв дополнение к разрешениюCREATE ROUTINEилиALTER ROUTINE, которое обычно требуется. (В зависимости от значенияDEFINERв определении функции, может потребоваться разрешениеSET_ANY_DEFINER, независимо от того, включено ли двоичное протоколирование. См. Раздел 15.1.17, «Запросы 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 utf8mb4 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в определении функции этого не требует. Информацию о встроенных функциях, которые могут быть небезопасными для репликации (и, следовательно, также делают небезопасными хранимые функции, которые их используют), см. в Разделе 19.5.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вместе с ожидаемым кодом ошибки. На реплике, если возникнет та же ошибка, это ожидаемый результат, и репликация продолжается. В противном случае репликация останавливается. -
Ведение журнала вызовов хранимых функций вместо операторов, выполняемых функцией, имеет последствия для репликации, которые возникают из-за двух факторов:
Возможно, что функция будет следовать различным путям выполнения на серверах источника и реплики.
Операторы, выполняемые на реплике, обрабатываются потоком приложения репликации. Если вы не реализуете проверки привилегий репликации (см. Раздел 19.3.3, «Проверка привилегий репликации»), поток приложения обладает полными привилегиями.
Последствие заключается в том, что хотя пользователю необходимо иметь привилегию
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записываются в двоичный журнал, поэтому реплика выполняет их. Поскольку поток приложения реплики обладает полными привилегиями, он выполняет опасный оператор. Таким образом, вызов функции оказывает различное воздействие на источник и реплику и не является безопасным для репликации.Чтобы защититься от этой опасности для серверов, у которых включено двоичное ведение журнала, создатели хранимых функций должны обладать привилегией
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в определении функции не требует этого. Использование проверок привилегий репликации рекомендуется независимо от принятых решений о привилегиях для создателей функций. Проверки привилегий репликации можно настроить, чтобы гарантировать, что для канала репликации авторизуются только ожидаемые и соответствующие операции. Инструкции по этому поводу см. в Разделе 19.3.3, «Проверка привилегий репликации».
-
Если функция, выполняющая обновления, является недетерминированной, она не повторяема. Это может иметь два нежелательных последствия:
Это приводит к различиям между репликой и источником.
Восстановленные данные не соответствуют исходным данным.
Для решения этих проблем 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.