Что вы думаете об этом подходе для регистрации изменений в mysql и имеют какой-то аудиторский след

Теперь я читал несколько тем и провел некоторое исследование о внесении изменений в таблицу mysql. Сначала позвольте мне объяснить мою ситуацию:

У меня есть система билетов с таблицей : "билет"

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

Здесь мой триггер:

BEFORE UPDATE ON ticket FOR EACH ROW
BEGIN
INSERT INTO ticket_history
SET
    idticket = NEW.idticket,
    time_arrival = NEW.time_arrival,
    idticket_status = NEW.idticket_status,
    tmp_user = NEW.tmp_user,
    action = 'update',
    timestamp = NOW();
END

Мой новый подход, чтобы избежать наличия триггеров

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

Моя идея - создать новую таблицу:

    id   sql_fwd        sql_bwd      keys      values    user       timestamp
    -------------------------------------------------------------------------
    1    UPDATE...      UPDATE...    status    5         14          12345678
    2    UPDATE...      UPDATE...    status    4         7           12345678

Поток будет выглядеть так:

Сначала я бы выбрал что-то или более из БД:

SELECT keys FROM ticket;

Затем я выводю данные в 2 поля ввода:

<input name="key" value="value" /> <input type="hidden" name="key" value="value" />

Нажмите и отправьте его моей функции:

Я бы снова начал с SELECT: SELECT * FROM ticket; и убедитесь, что скрытое поле ввода == значение из последнего выбора. Если так, я могу продолжить и знать, что ни один другой пользователь не изменил что-то в то же время. Если скрытое поле не соответствует, я возвращаю пользователя в форму и выводит сообщение.

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

$sql_fwd = "UPDATE ticket 
            SET idticket_status = 1
            WHERE idticket = '".$c_get['id']."';";

$sql_bwd = "UPDATE ticket 
            SET idticket_status = 0
            WHERE idticket = '".$c_get['id']."';";

После этого я запустите UPDATE в билет и вставьте новую запись в свою новую таблицу для ведения журнала.

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

Мои вопросы к этому будут:

  • Было бы заметно делать дополнительный выбор каждый раз, когда я хочу что-то обновить?
  • Я могу потерять некоторые преимущества, которые у меня были бы с триггерами?
  • Есть ли большие недостатки
  • Есть ли какие-либо функции на моем сервере mysql или с php, которые уже делают что-то подобное?
  • Или может быть гораздо более простой способ сделать что-то вроде этого
  • Возможно, небольшое изменение в моем триггере уже достаточно?
  • Если я понимаю это право, MySQL выполняет обновление только в том случае, если значение изменилось, но триггер выполняется в любом случае правильно?
  • Если я могу изменить триггер, могу ли я все же предотвратить каким-либо образом перезапись данных, а 2 пользователя попытаются изменить билет одновременно на сервере mysql или я буду делать это в любом случае с PHP?

Спасибо за помощь уже

Ответ 1

Я уже ответил на аналогичный вопрос. Вы увидите некоторые хорошие альтернативы в этом вопросе.

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

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

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

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

Вместо этого вам может понадобиться построить "время" в вашей модели данных (это и было точкой моего ответа на другой вопрос).

С тех пор появился новый стиль архитектуры данных, известный как CQRS. Для этого требуется совсем другой способ взглянуть на дизайн приложения, но он явно разработан для reactive приложений; они предлагают гораздо более приятные способы решения проблемы "что произойдет, если кто-то отредактирует запись, когда текущий пользователь заполняет форму". Qaru - пример - мы можем видеть, в то время как набираем наши комментарии или ответы, был ли вопрос обновлен или отправлены другие ответы или комментарии. Там реактивная библиотека для PHP.

Ответ 2

Другой подход...

Когда рабочий начинает изменять...

  • Сохраните время и рабочий_ид в строке.
  • Продолжайте выполнять задачи.
  • Когда работник завершает работу, вытащите последний файл worker_id, который коснулся записи; если он сам, все хорошо. Очистите время и рабочий_ид.

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

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

Может быть, лучше иметь другую таблицу для времени и employee_ids (& ticket_id). Это позволило бы отметить, что несколько сотрудников в данный момент касаются одной записи.

Что касается History to Current, я (обычно) хотел бы иметь 2 таблицы:

  • История - пошаговый список того, какие изменения были сделаны, когда и кем. Это таблица только INSERTed в.
  • Текущий - текущий статус билета. Эта таблица в основном UPDATEd.

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

BEGIN; INSERT INTO History...; UPDATE Current...; COMMIT;

Ответ 3

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

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

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

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

Существует простое решение:

Добавьте столбец "version_number".

Когда вы выбираете с намерением изменить, вы захватываете этот номер версии.

Затем, когда пользователь отправляет новые данные, вы делаете:

UPDATE ... 
SET all modified columns,
    version_number=version_number+1 
WHERE ticket_id=...
    AND version_number = (the value you got)

Если кто-то вступил и изменил его, то они будут увеличивать номер версии, поэтому WHERE не найдет строку. Запрос вернет количество строк в 0. Таким образом, вы знаете, что оно было изменено. Затем вы можете выбрать его, сравнить значения и предложить варианты разрешения конфликтов для пользователя.

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

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

Теперь об истории:

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

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

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

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