Производительность триггера против хранимой процедуры в MySQL

11

Сообщение здесь на DBA.StackExchange ( Каковы лучшие практики для триггеров для поддержания номера ревизии в записях? ) Породило интересный (по крайней мере, интересный для меня) вопрос относительно производительности в MySQL.

Контекст заключается в том, что мы хотим вставить запись в таблицу для каждой обновляемой строки. Перед обновлением строки мы хотим сохранить предыдущее значение, а затем увеличить один из столбцов (столбец «версия»).

Если мы сделаем это внутри триггера, это сработает хорошо. Для MySQL триггеры строка за строкой , так что это было бы простым решением. Выберите данные, которые в данный момент находятся в таблице, вставьте их в таблицу журналов и обновите столбец «версия» в новых данных.

Однако возможно переместить эту логику в хранимую процедуру. Если вы сделаете это, вы выполняете вставку, а затем увеличиваете столбец «версия» в таблице. Вся вещь будет установлена ​​на основе.

Итак, когда дело доходит до выполнения этой вставки, будет ли более целесообразным использовать подход хранимых процедур на основе множеств или подход, основанный на триггерах?

Этот вопрос касается MySQL (поскольку он имеет построчные триггеры), хотя он может применяться к другим построчным триггерным СУБД.

Ричард
источник
1
Следует помнить одну вещь, касающуюся внедрения логики управления версиями в хранимую процедуру - насколько вы будете ограничены, когда кто-то каким-то образом напрямую пишет в таблицу, минуя ваш механизм аудита?
billinkc
Я согласен. Но на другом конце шкалы, возможно, вы хотите преднамеренно обойти эту регистрацию в определенных обстоятельствах. Конечно, это совершенно другой вопрос . Мне просто любопытно узнать о последствиях для производительности.
Ричард

Ответы:

7

Для простоты триггеры - это способ реализации любого вида отслеживания изменений в базе данных. Тем не менее, вы должны знать, что происходит под капотом при использовании триггеров.

Согласно программированию хранимых процедур MySQL , страница 256 под заголовком «Служебная нагрузка триггера» говорит следующее:

Важно помнить, что по необходимости триггеры увеличивают накладные расходы в операторе DML, к которому они применяются. фактическое количество служебных данных будет зависеть от природы триггера, но, поскольку все триггеры MySQL выполняются FOR EACH ROW, накладные расходы могут быстро накапливаться для операторов, обрабатывающих большое количество строк. Поэтому вам следует избегать размещения каких-либо дорогостоящих операторов SQL или процедурного кода в триггерах.

Подробное объяснение затрат на запуск приведено на страницах 529-531. Заключительный вывод из этого раздела гласит следующее:

Урок здесь следующий: так как код триггера будет выполняться один раз для каждой строки, на которую воздействует инструкция DML, триггер может легко стать наиболее значимым фактором производительности DML. Код внутри тела триггера должен быть как можно более легким, и, в частности, любые операторы SQL в триггере должны поддерживаться индексами, когда это возможно.

Не упоминается в книге еще один фактор при использовании триггеров: когда дело доходит до ведения журнала аудита, имейте в виду, что вы вводите данные. Я говорю это потому, что если вы решите войти в таблицу MyISAM, каждый INSERT в таблицу MyISAM производит полную блокировку таблицы во время INSERT. Это может стать серьезным узким местом в среде с большим трафиком и высокой транзакцией. Кроме того, если триггер работает с таблицей InnoDB, и вы регистрируете изменения в MyISAM изнутри триггера, это тайно отключит соответствие ACID (т. Е. Уменьшит количество транзакций блока до поведения автоматической фиксации), которое нельзя откатить.

При использовании триггеров на таблицах InnoDB и регистрации изменений

  • Таблица, в которую вы входите, также InnoDB
  • Вы отключили автокоммит
  • Вы тщательно настраиваете блоки START TRANSACTION ... COMMIT / ROLLBACK

Таким образом, журналы аудита могут извлечь выгоду из COMMIT / ROLLBACK, как и основные таблицы.

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

ПРЕДОСТЕРЕЖЕНИЕ

В зависимости от того, насколько сложным является триггер, он все еще может быть узким местом. Если вы хотите уменьшить количество узких мест в журнале аудита, есть кое-что, что вы можете сделать. Однако это потребует небольшого изменения инфраструктуры.

Используя аппаратное оборудование, создайте еще два Сервера БД

Это позволит серверу сократить количество операций ввода-вывода при записи в основную базу данных (MD) благодаря ведению журнала аудита. Вот как вы можете это сделать:

Шаг 01) Включите бинарное ведение журнала в основной базе данных.

Шаг 02) Используя недорогой сервер, настройте MySQL (той же версии, что и MD) с включенным двоичным журналированием. Это будет марка. Настройка репликации с MD на DM.

Шаг 03) Используя второй недорогой сервер, настройте MySQL (ту же версию, что и MD) с отключенным бинарным ведением журнала. Настройте каждую таблицу аудита для использования --replicate-do-table . Это будет АС. Настройка репликации от DM до AU.

Шаг 04) mysqldump структуры таблицы из MD и загрузки ее в DM и AU.

Шаг 05) Преобразуйте все таблицы аудита в MD, чтобы использовать механизм хранения BLACKHOLE

Шаг 06) Конвертируйте все таблицы в DM и AU, чтобы использовать механизм хранения BLACKHOLE

Шаг 07) Преобразуйте все таблицы аудита в AU, чтобы использовать механизм хранения MyISAM

Когда сделано

  • DM будет копировать с MD и записывать материал только в свой двоичный журнал
  • С фильтром --replicate-do-table во всех таблицах аудита AU будет реплицироваться с DM

Это позволяет хранить информацию аудита на отдельном сервере БД, а также уменьшает любое ухудшение операций ввода-вывода при записи, которое обычно имеет MD.

RolandoMySQLDBA
источник
Потрясающий ответ +++ 1
b_dubb
1

Вот подход, чтобы выполнить это обновление оптом.

Для этого примера

  • table_A имеет идентификатор ПЕРВИЧНОГО КЛЮЧА
  • Вы создаете таблицу с именем table_A_Keys2Update с просто идентификатором PRIMARY KEY
  • Вы заполняете table_A_Keys2Update идентификаторами из table_A, которые, как вы знаете, должны быть обновлены

Чтобы создать table_A_Keys2Update, сделайте следующее:

CREATE TABLE table_A_Keys2Update SELECT id FROM table_A;
ALTER TABLE table_A_Keys2Update ADD PRIMARY KEY (id);

После заполнения table_A_Keys2Update идентификаторами, номера ревизий которых необходимо увеличить, выполните следующее ОБНОВЛЕНИЕ ОБНОВЛЕНИЯ, чтобы увеличить номер ревизии всех строк, идентификатор которых содержится в table_A и table_A_Keys2Update:

UPDATE table_A A INNER JOIN table_A_Keys2Update B USING (id)
SET A.revision = A.revision + 1;

Этот однострочный запрос может заменить триггер и хранимую процедуру.

При желании вы можете поместить этот запрос в хранимую процедуру и вызывать его, если хотите.

RolandoMySQLDBA
источник
Это действительно вставка, о которой мне интересно. Если вы вставляете INTO аудит SELECT <what> FROM <primary_table> WHERE <параметры из хранимой процедуры>, вы можете выполнить массовую вставку. В триггере вы просто вставили бы в ЗНАЧЕНИЯ аудита <данные из обновленной строки> . Итак, будет ли однострочная построчная вставка быстрее, чем массовая вставка?
Ричард
Для простоты триггер был бы намного лучше: 1) при условии, что primary_table никогда не испытывает массовых вставок в середине любого пикового времени, 2) информация аудита должна читаться по требованию в любой данный момент, и 3) ваш сайт с низким оборотом.
RolandoMySQLDBA