Spec-Zone.ru › MySQL 5.7

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.
https://docs.oracle.com/cd/E17952_01/mysql-5.7-en/stored-programs-logging.html

Spec-Zone.ru

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