Skip to content

Триггеры: от любви до ненависти

Пересказ статьи Ryan. Triggers: A LoveHate Relationship


Чтобы понять, почему триггеры могут вызывать такие различные реакции, я хочу привести краткую историю триггеров (с моей точки зрения) и то, как она повлияла на мой опыт работы с ними на раннем этапе.

Действие...реакция


Мы живем в мире действия/реакции - и не важно, как наши данные обрабатываются. По мере того, как системы реляционных баз данных развивались и совершенствовались на протяжении десятилетий, в декларативную систему добавлялись различные формы программных функций.
По замыслу простой ANSI SQL является декларативным ("Эй, база данных, вот данные, которые мне нужны, выясни, как это сделать"), а не процедурным ("Эй, база данных, мне нужны эти данные, я хочу получить их следующим образом"). В ранние времена не было стандарта на добавление функций процедурного типа, хотя позднее где-то в середине 90-х появилось определение SQL/PSM.

Тем не менее, в конце 80-х и 90-х годах поставщики баз данных пытались идти в ногу с быстро меняющимися требованиями и нуждами в мире баз данных. Несмотря на то, что триггеры не были официально включены в стандарт до SQL:99, базы данных типа Oracle уже реализовали свои собственные процедурные языки и функции. Триггеры могли быть отклонены в стандарте SQL-92, но команда по стандартизации не могла игнорировать их (или сложность, которую триггеры добавляли в транзакционную согласованность).

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

Другая важная причина состояла в том, что многое из того, что мы называем реляционной согласованностью, изначально была реализована как триггеры. Ограничения внешнего ключа? Триггеры. Проверочные ограничения? Триггеры.

И с этого начинается моя прекрасная история с триггерами. (Вы знали, что мы к этому когда-нибудь придем, правда?)

(Не)согласованные триггеры


Мое основательное знакомство с триггерами началось с SQL Server в большой (на сегодня) схеме приложения. Разработка схемы изначально велась в Sybase и окончательно в SQL Server. Хотя ограничения внешнего ключа и проверочные были добавлены уже в SQL Server, база данных все еще использовала триггеры для реляционной целостности, а не ограничения check или foreign key. Это было обусловлено в первую очередь тем, что команда использовала многие годы один и тот же инструмент ER, и он все еще применял для ограничений триггеры.

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

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

Проблемы с триггерами помимо ограничений


Помимо ограничений, триггеры сами по себе привносят другую сложность. Много лет назад, помнится, я читал эту статью Joe Celko и подумал, что имеется много вещей, связанных с триггерами, которые я не вполне понимал в то время. Они добавляют несколько слоев сложности и частично из-за продолжающегося использования их в качестве ограничений, триггеры часто используются не по назначению.

Среди проблем, на которые указал Joe, есть несколько, которые, я считаю, выделяются среди прочих в смысле причин не использовать триггеры в большинстве случаев. Добавлю и ту, которую он специально не упомянул (но о которой я слышу чаще всего).

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

  2. Триггеры легко испортить: думаю, что часто это справедливо, и иногда я испытывал это на себе. Особенно, когда одни триггеры вызывают срабатывание других триггеров, и очень сложно (иногда почти невозможно) отладить то, что происходит во всем стеке.

  3. Триггеры оказывают влияние на производительность: тут нет путей обхода. Если требуется выполнить некоторые действия как часть каждой транзакции или модификации, это повлияет на общую производительность в какой-то мере. "Хорошей" новостью здесь является то, что общие улучшения баз данных сделали, вероятно, этот эффект незначительным в большинстве жизненных случаев использования, но это по-прежнему неизбежная плата.

  4. (Бонус) Триггеры прячут логику: имеются бесчисленные истории о том, как программный баг был спрятан триггерами. Когда разработчики или администраторы баз данных не знают, как просмотреть всю цепочку измененных данных, может быть сложно понять, что происходит (смотрите пункт #2 выше). В частности, в области разработки, изобилующей ORM, триггеры могут сделать фактически невозможным точно проследить, что произошло при вставке или модификации данных. Тщательно оцените выгоды, которые вы получаете, внедрив триггер, по отношению к способности вашей команды действительно понимать всю систему.

Добавьте к этому числу характер изменений от базы к базе (уровень строки по сравнению с уровнем таблицы? только триггеры AFTER или база данных поддерживает также триггеры BEFORE?), и все становится еще запутанней.

И все же...

Мне нравятся триггеры...обычно


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

Здесь четыре приходящих на ум случая использования, которые, я думаю, все еще являются действенными вариантами для триггеров, по крайней мере, в PostgreSQL. Это может зависеть от выбранного вами сервера баз данных, но если у вас установлен жесткий контроль над изменениями в вашей модели, и есть команда, которая понимает, почему вы сделали такой архитектурный выбор, то, думаю, триггеры могут быть прекрасным выбором.

Таблица аудита


Многие люди не согласятся с этим, и это нормально. Некоторые базы данных реализовали другие функции, чтобы сделать (возможно) лучшее отслеживание модификации данных. И все же таблицы аудита являются классическим случаем использования триггеров. Что еще лучше, PostgreSQL обеспечивает такую возможность как триггерные фильтры, поэтому триггер не будет срабатывать, если условие не выполняется.

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

Распространение изменений


Иногда при изменении одной таблицы или столбца необходимо распространить изменения на другие части базы данных. Многие приложения, с которым я ранее работал, имели в архитектуре большие древообразные родительско-дочерние структуры.

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

Построение очереди


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

PostgreSQL обладает функциональностью LISTEN/NOTIFY, которая сама по себе не является очередью, но может служить для похожей цели. И подобно Service Broker в SQL Server вы можете публиковать сообщения вручную или с помощью триггера. Это придает (в основном) ощущение асинхронности вашей работе.

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

Я думаю, что это одна из наиболее полезных вещей, для которых приложения могли бы использовать триггеры, но многие из них этого не делают. ИМХО, стоит рассмотреть эту возможность.

Автоматическое обновление столбца


Это тоже может быть спорным. Извините.

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

Классический пример - темпоральный столбец "последнее изменение". Не имеется естественной функции, которая установит значение столбца, когда происходит обновление строки. Триггеры делают это возможным. Еще лучше, если вы используете один и тот же столбец (имя и тип) в каждой таблице, которую вы хотите отслеживать. Тогда вы можете использовать одну триггерную функцию для всех таблиц!
Следует ли это делать в коде приложения? Возможно. Это вам решать. Но тут легко установить соглашения об именовании и схеме, так что эта функциональность легко реализуется и поддерживается.

Когда у есть есть только молоток…


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

  • Как вы будете это документировать?

  • Насколько легко будет "увидеть" модель вашей схемы (включая функции типа триггеров)?

  • Если возможно (и ваша база данных поддерживает это), ограничиваете ли вы область действия триггеров с помощью фильтров?

  • Есть ли у вас план тестирования функциональности, чтобы гарантировать ожидаемую работу при каждой миграции?

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

Ссылки по теме


  1. Пример триггера в SQL Server

  2. Триггеры в PostgreSQL: часть 1

  3. Руководство по триггерам в SQL: настройка отслеживания базы данных в PostgreSQL

Обратные ссылки

Нет обратных ссылок

Комментарии

Показывать комментарии Как список | Древовидной структурой

Нет комментариев.

Автор не разрешил комментировать эту запись

Добавить комментарий

Enclosing asterisks marks text as bold (*word*), underscore are made via _word_.
Standard emoticons like :-) and ;-) are converted to images.

To prevent automated Bots from commentspamming, please enter the string you see in the image below in the appropriate input box. Your comment will only be submitted if the strings match. Please ensure that your browser supports and accepts cookies, or your comment cannot be verified correctly.
CAPTCHA

Form options

Добавленные комментарии должны будут пройти модерацию прежде, чем будут показаны.