Какова наилучшая архитектура для отслеживания изменений поля на объектах?

У нас есть веб-приложение, построенное поверх базы данных SQL. К объектам нескольких различных типов можно добавлять комментарии, и некоторые из этих объектов требуют отслеживания на уровне полей, аналогично тому, как изменения полей отслеживаются в большинстве систем отслеживания проблем (например, статус, назначение, приоритет). Мы хотели бы показать, кем произведено изменение, каким было предыдущее значение и какое новое значение.

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

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

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


person Nicole    schedule 25.01.2010    source источник


Ответы (7)


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

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

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

С третьим вариантом вы можете либо иметь «текущие» данные в базовой таблице, либо вы можете иметь каждый столбец, который исторически хранится только в исторической таблице. Затем вам нужно будет просмотреть последнюю строку, чтобы получить текущее состояние объекта. Я предпочитаю это, потому что это позволяет избежать проблемы дублирования данных в вашей базе данных или необходимости просматривать несколько таблиц для одних и тех же данных.

Итак, у вас может быть:

Проблема_Заявки (идентификатор_заявки, имя_заявки) Проблема_Заявки_История (идентификатор_заявки, дата_время изменения, описание, комментарий, имя пользователя)

В качестве альтернативы вы можете использовать:

Проблема_Заявки (идентификатор_заявки, имя_заявки) Проблема_Заявки_Комментарии (идентификатор_заявки, дата_время изменения, комментарий, имя пользователя) Проблема_Заявки_Статусы (идентификатор_заявки, дата-время_изменения, идентификатор_состояния, имя пользователя)

person Tom H    schedule 25.01.2010
comment
Или вы можете использовать таблицы аудита для сбора большей части информации (таким образом обеспечивая способ отслеживания изменений, сделанных из любого места) и связанную таблицу change_comments для сбора комментариев к изменениям из графического интерфейса. Просто зависит от того, насколько критичен аудит записей. - person HLGEM; 25.01.2010
comment
Определенно. Если бы аудит был дополнительным требованием, вы могли бы объединить их. - person Tom H; 25.01.2010
comment
@HLGEM — все интерактивные действия, включая взаимодействие с графическим интерфейсом, проходят через единый API, поэтому для нас триггеры приложений так же надежны, как и триггеры базы данных. Однако в целом ваш дизайн звучит как элегантное решение. @ Том Х. - Как вы справляетесь с масштабом в предложенном вами дизайне? Я также, вероятно, должен был упомянуть, что эти объекты могут иметь добавленные пользователем свойства, которые мы также хотим отслеживать. У нас уже будет много данных, поэтому для нас очень важен масштаб. - person Nicole; 26.01.2010
comment
Это решение известно Renesis. Основная проблема заключается в том, как быстро эти данные могут расти. Поэтому решением было бы найти способ ограничить объем хранимых данных. - person Gladwin Burboz; 18.02.2010


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

person Cade Roux    schedule 25.01.2010
comment
Я не знаю, я бы сказал, что интерфейс и интерфейс не имеют значения. Если внешний интерфейс использует одну учетную запись пользователя для подключения к базе данных, вам нужно будет что-то встроить, чтобы точно фиксировать пользователя, фактически вносящего изменения, а не только учетную запись приложения. Я не пользовался AutoAudit, но не могу представить, чтобы он обошёл эту проблему без каких-либо изменений во внешнем интерфейсе. - person Tom H; 25.01.2010
comment
Это правильно - вы можете использовать только информацию, доступную для соединения, в сгенерированных триггерах. Если его нет в строке подключения, или в сеансе, или в новых строках, поступающих в таблицу, у вас может не быть всей необходимой информации, но это справедливо для всех проектов. Вы можете обойти это, используя SET CONTEXT_INFO в начале каждого соединения. - person Cade Roux; 26.01.2010
comment
В нашем случае у нас есть однопользовательское подключение к базе данных. Однако меня интересуют общие рекомендации, поэтому спасибо за ваше предложение... +1 - person Nicole; 26.01.2010

Вот решение, которое я бы порекомендовал для достижения вашей цели.

Разработайте свою модель аудита, как показано ниже.



  ----------------  1      *  ------------                       
 | AuditEventType |----------| AuditEvent |                      
  ----------------            ------------                       
                                | 1    | 1                       
                                |      |                         
               -----------------        -------------            
              | 0,1                                  | +         
    -------------------                       ----------------   
   | AuditEventComment |                     | AuditDataTable |  
    -------------------                       ----------------   
                                                     | 1         
                                                     |           
                                                     |           
                                                     | +         
          -----------------  +             1  --------------     
         | AuditDataColumn |------------------| AuditDataRow |   
          -----------------                   --------------     



.

Тип события аудита

Содержит список всех возможных типов событий в системе и их общее описание.

.

Событие аудита

Содержит информацию о конкретных событиях, вызвавших это действие.

.

Комментарий к событию аудита

Содержит необязательный пользовательский комментарий о событии аудита. Комментарии могут быть очень большими, поэтому лучше хранить их в CLOB.

.

Таблица данных аудита

Содержит список одной или нескольких таблиц, на которые повлияло соответствующее событие AuditEvent.

.

Строка данных аудита

Содержит список одной или нескольких идентифицирующих строк в соответствующей таблице AuditDataTable, на которые повлияло соответствующее событие AuditEvent.

.

Колонка данных аудита

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

.

Конструктор аудита

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

Убедитесь, что все ваши действия для события находятся в одной транзакции БД вместе с сохранением данных аудита.

person Gladwin Burboz    schedule 18.02.2010

Я не понимаю реальных сценариев использования проверенных данных, однако... вам нужно просто отслеживать изменения? Нужно ли будет «откатывать» некоторые изменения? Насколько частым (и гибким) должен быть отчет/поиск в журнале аудита?

Лично я бы исследовал что-то вроде этого:

Создайте таблицу аудита. У него есть идентификатор, номер версии, идентификатор пользователя и поле clob.

  • Когда объект #768795 будет создан, добавьте строку в AuditTable со значениями: Id=#768795 Версия:0 Пользователь: (идентификатор пользователя, создавшего новый объект) clob: xml-представление всего объекта. (если пространство является проблемой и доступ к этой таблице нечастый, вы можете использовать большой двоичный объект и «архивировать» представление xml на лету).

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

Если вы хотите отслеживать только подмножество полей либо потому, что остальные неизменяемы, несущественны, либо вы отчаянно нуждаетесь в скорости/пространстве, просто сериализуйте подмножество, которое вам нужно.

person p.marino    schedule 19.02.2010
comment
Хорошие вопросы. Нет, мы не хотим откатывать изменения. Однако мы хотим иметь возможность выводить историю объекта, как это делают обычные системы отслеживания проблем. Мы хотим, чтобы эта история выглядела примерно так: Пользователь X изменил поле Y со значения A на значение Z. - person Nicole; 19.02.2010

Я знаю, что этот вопрос очень старый, но еще одна возможность, встроенная в sql, - это ИЗМЕНЕНИЯ ОТСЛЕЖИВАНИЯ:

вы можете найти дополнительную информацию по этой ссылке: Введение в сбор данных об изменениях (CDC) в SQL Server 2008 http://www.simple-talk.com/sql/learn-sql-server/introduction-to-захват-изменения-данных-(cdc)-in-sql-server-2008/

person Doug Lubey of Louisiana    schedule 15.07.2012

Я думаю, что Observer — идеальный шаблон в этом сценарии.

person Sujay Ghosh    schedule 10.02.2011