Spec-Zone.ru › MySQL 9.2

27.8 Ведение журнала двоичных файлов хранимых программ

Двоичный журнал содержит информацию о 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.18, «Операторы 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, требование, чтобы функции были детерминированными или не изменяли данные, отменяется.

  • Вызовы хранимых процедур регистрируются на уровне оператора, а не на уровне Вызова. То есть, сервер не регистрирует оператор вызова, а регистрирует операторы внутри процедуры, которые фактически выполняются. В результате, те же изменения, которые происходят на исходном сервере, также происходят на репликах. Это предотвращает проблемы, которые могут возникнуть, если у процедуры разные пути выполнения на разных машинах.

    В общем случае, операторы, выполняемые внутри хранимой процедуры, записываются в двоичный журнал с теми же правилами, которые применялись бы при выполнении операторов автономно. При регистрации операторов процедур соблюдается особая осторожность, потому что выполнение операторов внутри процедур немного отличается от выполнения в контексте вне процедуры:

    • Оператор, подлежащий регистрации, может содержать ссылки на локальные переменные процедуры. Эти переменные не существуют за пределами контекста хранимой процедуры, поэтому оператор, ссылающийся на такую переменную, не может быть зарегистрирован буквально. Вместо этого каждая ссылка на локальную переменную заменяется этим конструктом для целей регистрации:

      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() может привести к проблеме для операторов создания таблицы, когда выражения столбцов источника ссылаются на локальные переменные. Преобразование этих ссылок в выражения 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 записывает оператор назначения переменной в двоичный журнал, чтобы убедиться, что переменная существует на реплике с таким же значением, как и на источнике. Например, если оператор ссылается на переменную @my_var, этому оператору предшествует следующий оператор в двоичном журнале, где value — значение @my_var на источнике:

      SET @my_var = value;
      
    • Вызовы процедур могут происходить внутри транзакции, подтверждённой или отменённой. Учёт контекста транзакции гарантирует, что транзакционные аспекты выполнения процедуры реплицируются правильно. То есть, сервер регистрирует те операторы внутри процедуры, которые фактически выполняются и изменяют данные, а также регистрирует операторы подтверждения, отката и начало транзакции при необходимости. Например, если процедура обновляет только транзакционные таблицы и выполняется внутри транзакции, которая отменена, эти обновления не регистрируются. Если процедура выполняется внутри подтверждённой транзакции, операторы подтверждения и отката регистрируются вместе с обновлениями. Для процедуры, выполняющейся внутри отменённой транзакции, её операторы регистрируются по тем же правилам, которые применялись бы, если бы эти операторы выполнялись автономно:

      • Обновления транзакционных таблиц не регистрируются.

      • Обновления не транзакционных таблиц регистрируются, так как откат не отменяет их.

      • Обновления смешанных транзакционных и не транзакционных таблиц регистрируются, окруженные операторами подтверждения и отката, чтобы реплики выполняли те же изменения и откаты, что и на источнике.

  • Вызов хранимой процедуры не записывается в двоичный журнал на уровне оператора, если процедура вызывается изнутри хранимой функции. В этом случае регистрируется только оператор, вызывающий функцию (если он находится внутри регистрируемого оператора) или оператор DO (если он находится внутри оператора, который не регистрируется). По этой причине следует соблюдать осторожность при использовании хранимых функций, которые вызывают процедуру, даже если сама процедура в целом безопасна.

© 2025 Oracle
Licensed under the GPLv2 License.
https://docs.oracle.com/cd/E17952_01/mysql-9.2-en/stored-programs-logging.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API