您如何看待这种在 mysql 中记录更改并具有某种审计跟踪的方法
What do you think of this approach for logging changes in mysql and have some kind of audit trail
我现在已经通读了几个主题并做了一些关于记录更改到 mysql table 的研究。先说明一下我的情况:
我有一个 table 的票务系统:'ticket'
截至目前,我已经创建了 触发器,它将在我的 table 中输入重复条目:'ticket_history' 有 "action" "user" 和 "timestamp"作为附加列。经过几周的测试后,我对该构建有些不满意,因为每次更改都会在历史 table 中创建我的行的完整副本。我知道磁盘 space 很便宜,我不应该担心它,但为了检索某种日志或用户好看的历史记录是痛苦的,至少对我来说是这样。此外,使用我编写的触发器,即使没有任何变化,我也会在历史记录中得到一个新行。但这只是我触发器的设计缺陷!
这是我的触发器:
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
我为了避免触发的新方法
在这个话题上花了一些时间后,我想出了一个方法,我想讨论和实施。但首先我会对此提出一些问题:
我的想法是创建一个新的table:
id sql_fwd sql_bwd keys values user timestamp
-------------------------------------------------------------------------
1 UPDATE... UPDATE... status 5 14 12345678
2 UPDATE... UPDATE... status 4 7 12345678
流程在我看来是这样的:
起初我会 select 来自 DB 的东西或更多:
SELECT keys FROM ticket;
然后我在 2 个输入字段中显示数据:
<input name="key" value="value" />
<input type="hidden" name="key" value="value" />
点击提交并将其交给我的函数:
我会再次从 SELECT 开始:SELECT * FROM ticket;
并确保隐藏的输入字段 == 来自最新 select 的值。如果是这样,我可以继续并知道在此期间没有其他用户更改过某些内容。如果隐藏字段不匹配,我将用户带回表单并显示一条消息。
接下来,我将为该操作构建 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']."';";
我 运行 更新了 ticket 并在我的新 table 中插入一个新条目用于记录。
有了它,我可以尝试在两个用户同时编辑同一张票时捕获可能的覆盖,对于我的历史记录,我可以简单地查找键和值并生成某种列表。还有 SQL_BWD 我可以简单地撤消更改。
我的问题是:
- 每次我想更新一些东西时额外 select 会不会很明显?
- 我会失去一些触发器带来的好处吗?
- 有什么大的缺点吗
- 我的 mysql 服务器或 php 服务器上是否有任何功能已经在做类似的事情?
- 或者是否有更简单的方法来做类似的事情
- 我的触发器可能略有变化我现在已经足够了吗?
- 如果我没看错,MySQL 只是在值发生变化时才执行更新,但无论如何都会执行触发器,对吧?
- 如果我能够更改触发器,当 2 个用户尝试在 mysql 服务器上同时编辑票证时,我是否仍然可以以某种方式防止数据被覆盖,或者我会这样做吗? PHP?
谢谢你的帮助
另一种方法...
当工作人员开始做出改变时...
- 将时间和 worker_id 存储在行中。
- 继续完成任务。
- 当 worker 完成时,获取最后一个 worker_id 触及记录;如果是他自己,那一切都很好。清除时间和worker_id.
另一方面,如果另一个工人溜进来,则需要一些解决方案。这进入了您的概念,即某些事情可以并行进行。
- 可以将评论添加到不同的 table,因此没有冲突。
- 更改优先级本身可能不是问题。
- 其他的东西可能更乱。
时间&worker_id秒(&ticket_id)再有一个table可能会更好。这将允许标记多个工作人员当前正在接触单个记录。
至于历史与当前,我(通常)喜欢有 2 tables:
- 历史 -- 更改内容、更改时间和更改人的详细列表。这table只是
INSERTed
成。
- 当前 -- 工单的当前状态。这个 table 大部分是
UPDATEd
.
此外,我更喜欢直接从应用程序的 "database layer" 中编写历史记录,而不是通过触发器。这使我可以更好地控制每个 table 的内容和时间的细节。加上 'transactions' 很清楚。这让我有信心保持两个 table 同步:
BEGIN; INSERT INTO History...; UPDATE Current...; COMMIT;
I do understand that disk space is cheap and I should not worry about it but in order to retrieve some kind of log or nice looking history for the user is painful, at least for me.
大历史table不一定是问题。巨大的 tables 只使用磁盘 space,这很便宜。他们只有在对它们进行查询时才会减慢速度。幸运的是,历史记录不是您一直使用的东西,它很可能仅用于解决问题或审计。
将历史分区 table 很有用,例如按月或周。这使您可以简单地删除非常旧的记录,更重要的是,由于已经备份了前几个月的历史记录,因此您的每日备份计划只需要备份当月。这意味着庞大的历史记录 table 不会减慢您的备份速度。
With that I can try to catch possible overwrites while two users are editing the same ticket in the same time
有一个简单的解决方案:
添加一列"version_number"。
当你select有意修改时,你抓住这个version_number。
然后,当用户提交新数据时,你做:
UPDATE ...
SET all modified columns,
version_number=version_number+1
WHERE ticket_id=...
AND version_number = (the value you got)
如果有人介入并修改了它,那么他们将增加版本号,因此 WHERE 将找不到该行。该查询将 return 行计数为 0。因此您知道它已被修改。然后您可以 SELECT 它,比较值,并向用户提供冲突解决选项。
您还可以添加上次修改者、修改时间等列,并将此信息呈现给用户。
如果你想让打开修改页面的用户锁定其他用户,也可以这样做,但这需要超时(以防他们离开 window 打开并回家,例如).所以这个比较复杂。
现在,关于历史:
例如,您不希望有一个名为 "comments" 的大型 TEXT 列,每个人都可以在其中输入内容,因为每次有人添加一个字母时都需要将其复制到历史记录中。
最好把它看成一个论坛:每张票就像一个主题,可以有一串评论(如帖子),存储在另一个 table 中,并附有关于谁写的信息它,什么时候等等。您也可以将其历史化。
使用触发器的缺点是触发器不知道登录的用户,只知道 MySQL 用户。因此,如果您想记录谁做了什么,您将必须按照我上面的建议添加一个带有 user_id 的列。您也可以使用 Rick James 的解决方案。两者都可以。
请记住,MySQL 触发器不会在外键级联删除时触发...因此,如果以这种方式删除行,它将不起作用。在这种情况下,在应用程序中执行会更好。
我之前回答过 similar 个问题。您会在该问题中看到一些不错的选择。
对于您的情况,我认为您合并了几个问题 - 一个是 "storing an audit trail",另一个是 "managing the case where many clients may want to update a single row"。
首先,我不喜欢触发器。它们是某些其他操作的副作用,对于非平凡的情况,它们会使调试变得更加困难。设计不当的触发器或审计 table 确实会减慢您的应用程序,您必须确保您的触发器逻辑在许多开发人员之间得到协调。我意识到这是个人喜好和偏见。
其次,根据我的经验,这种要求很少 "show the status of this one table over time" - 几乎总是 "allow me to see what happened to the system over time",如果存在这种要求,它通常具有相当高的优先级。例如,对于票务系统,您可能需要创建和更改票务状态的用户的姓名和电子邮件地址; category/classification 的名称,也许是项目的名称等。所有这些属性都可能是其他 table 的外键。当确实发生需要审计的事情时,这种要求很可能是 "let me see immediately",而不是“让数据库开发人员花费数小时试图拼凑来自 8 个不同历史 table 的图片。在票务系统中,工单详细信息屏幕可能需要显示此信息。
如果这一切都是真的,那么我认为由触发器填充的历史记录 table 不是一个好主意 - 您必须将所有业务逻辑构建到两组代码中,一组用于显示"regular" 应用程序,一个显示 "audit trail"。
相反,您可能希望将 "time" 构建到您的数据模型中(这是我对另一个问题的回答)。
从那时起,出现了一种新型的数据架构,称为 CQRS. This requires a very different way of looking at application design, but it is explicitly designed for reactive applications; these offer much nicer ways of dealing with the "what happens if someone edits the record while the current user is completing the form" question. Stack Overflow is an example - we can see, whilst typing our comments or answers, whether the question was updated, or other answers or comments are posted. There's a reactive library for PHP。
我现在已经通读了几个主题并做了一些关于记录更改到 mysql table 的研究。先说明一下我的情况:
我有一个 table 的票务系统:'ticket'
截至目前,我已经创建了 触发器,它将在我的 table 中输入重复条目:'ticket_history' 有 "action" "user" 和 "timestamp"作为附加列。经过几周的测试后,我对该构建有些不满意,因为每次更改都会在历史 table 中创建我的行的完整副本。我知道磁盘 space 很便宜,我不应该担心它,但为了检索某种日志或用户好看的历史记录是痛苦的,至少对我来说是这样。此外,使用我编写的触发器,即使没有任何变化,我也会在历史记录中得到一个新行。但这只是我触发器的设计缺陷!
这是我的触发器:
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
我为了避免触发的新方法
在这个话题上花了一些时间后,我想出了一个方法,我想讨论和实施。但首先我会对此提出一些问题:
我的想法是创建一个新的table:
id sql_fwd sql_bwd keys values user timestamp
-------------------------------------------------------------------------
1 UPDATE... UPDATE... status 5 14 12345678
2 UPDATE... UPDATE... status 4 7 12345678
流程在我看来是这样的:
起初我会 select 来自 DB 的东西或更多:
SELECT keys FROM ticket;
然后我在 2 个输入字段中显示数据:
<input name="key" value="value" />
<input type="hidden" name="key" value="value" />
点击提交并将其交给我的函数:
我会再次从 SELECT 开始:SELECT * FROM ticket;
并确保隐藏的输入字段 == 来自最新 select 的值。如果是这样,我可以继续并知道在此期间没有其他用户更改过某些内容。如果隐藏字段不匹配,我将用户带回表单并显示一条消息。
接下来,我将为该操作构建 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']."';";
我 运行 更新了 ticket 并在我的新 table 中插入一个新条目用于记录。
有了它,我可以尝试在两个用户同时编辑同一张票时捕获可能的覆盖,对于我的历史记录,我可以简单地查找键和值并生成某种列表。还有 SQL_BWD 我可以简单地撤消更改。
我的问题是:
- 每次我想更新一些东西时额外 select 会不会很明显?
- 我会失去一些触发器带来的好处吗?
- 有什么大的缺点吗
- 我的 mysql 服务器或 php 服务器上是否有任何功能已经在做类似的事情?
- 或者是否有更简单的方法来做类似的事情
- 我的触发器可能略有变化我现在已经足够了吗?
- 如果我没看错,MySQL 只是在值发生变化时才执行更新,但无论如何都会执行触发器,对吧?
- 如果我能够更改触发器,当 2 个用户尝试在 mysql 服务器上同时编辑票证时,我是否仍然可以以某种方式防止数据被覆盖,或者我会这样做吗? PHP?
谢谢你的帮助
另一种方法...
当工作人员开始做出改变时...
- 将时间和 worker_id 存储在行中。
- 继续完成任务。
- 当 worker 完成时,获取最后一个 worker_id 触及记录;如果是他自己,那一切都很好。清除时间和worker_id.
另一方面,如果另一个工人溜进来,则需要一些解决方案。这进入了您的概念,即某些事情可以并行进行。
- 可以将评论添加到不同的 table,因此没有冲突。
- 更改优先级本身可能不是问题。
- 其他的东西可能更乱。
时间&worker_id秒(&ticket_id)再有一个table可能会更好。这将允许标记多个工作人员当前正在接触单个记录。
至于历史与当前,我(通常)喜欢有 2 tables:
- 历史 -- 更改内容、更改时间和更改人的详细列表。这table只是
INSERTed
成。 - 当前 -- 工单的当前状态。这个 table 大部分是
UPDATEd
.
此外,我更喜欢直接从应用程序的 "database layer" 中编写历史记录,而不是通过触发器。这使我可以更好地控制每个 table 的内容和时间的细节。加上 'transactions' 很清楚。这让我有信心保持两个 table 同步:
BEGIN; INSERT INTO History...; UPDATE Current...; COMMIT;
I do understand that disk space is cheap and I should not worry about it but in order to retrieve some kind of log or nice looking history for the user is painful, at least for me.
大历史table不一定是问题。巨大的 tables 只使用磁盘 space,这很便宜。他们只有在对它们进行查询时才会减慢速度。幸运的是,历史记录不是您一直使用的东西,它很可能仅用于解决问题或审计。
将历史分区 table 很有用,例如按月或周。这使您可以简单地删除非常旧的记录,更重要的是,由于已经备份了前几个月的历史记录,因此您的每日备份计划只需要备份当月。这意味着庞大的历史记录 table 不会减慢您的备份速度。
With that I can try to catch possible overwrites while two users are editing the same ticket in the same time
有一个简单的解决方案:
添加一列"version_number"。
当你select有意修改时,你抓住这个version_number。
然后,当用户提交新数据时,你做:
UPDATE ...
SET all modified columns,
version_number=version_number+1
WHERE ticket_id=...
AND version_number = (the value you got)
如果有人介入并修改了它,那么他们将增加版本号,因此 WHERE 将找不到该行。该查询将 return 行计数为 0。因此您知道它已被修改。然后您可以 SELECT 它,比较值,并向用户提供冲突解决选项。
您还可以添加上次修改者、修改时间等列,并将此信息呈现给用户。
如果你想让打开修改页面的用户锁定其他用户,也可以这样做,但这需要超时(以防他们离开 window 打开并回家,例如).所以这个比较复杂。
现在,关于历史:
例如,您不希望有一个名为 "comments" 的大型 TEXT 列,每个人都可以在其中输入内容,因为每次有人添加一个字母时都需要将其复制到历史记录中。
最好把它看成一个论坛:每张票就像一个主题,可以有一串评论(如帖子),存储在另一个 table 中,并附有关于谁写的信息它,什么时候等等。您也可以将其历史化。
使用触发器的缺点是触发器不知道登录的用户,只知道 MySQL 用户。因此,如果您想记录谁做了什么,您将必须按照我上面的建议添加一个带有 user_id 的列。您也可以使用 Rick James 的解决方案。两者都可以。
请记住,MySQL 触发器不会在外键级联删除时触发...因此,如果以这种方式删除行,它将不起作用。在这种情况下,在应用程序中执行会更好。
我之前回答过 similar 个问题。您会在该问题中看到一些不错的选择。
对于您的情况,我认为您合并了几个问题 - 一个是 "storing an audit trail",另一个是 "managing the case where many clients may want to update a single row"。
首先,我不喜欢触发器。它们是某些其他操作的副作用,对于非平凡的情况,它们会使调试变得更加困难。设计不当的触发器或审计 table 确实会减慢您的应用程序,您必须确保您的触发器逻辑在许多开发人员之间得到协调。我意识到这是个人喜好和偏见。
其次,根据我的经验,这种要求很少 "show the status of this one table over time" - 几乎总是 "allow me to see what happened to the system over time",如果存在这种要求,它通常具有相当高的优先级。例如,对于票务系统,您可能需要创建和更改票务状态的用户的姓名和电子邮件地址; category/classification 的名称,也许是项目的名称等。所有这些属性都可能是其他 table 的外键。当确实发生需要审计的事情时,这种要求很可能是 "let me see immediately",而不是“让数据库开发人员花费数小时试图拼凑来自 8 个不同历史 table 的图片。在票务系统中,工单详细信息屏幕可能需要显示此信息。
如果这一切都是真的,那么我认为由触发器填充的历史记录 table 不是一个好主意 - 您必须将所有业务逻辑构建到两组代码中,一组用于显示"regular" 应用程序,一个显示 "audit trail"。
相反,您可能希望将 "time" 构建到您的数据模型中(这是我对另一个问题的回答)。
从那时起,出现了一种新型的数据架构,称为 CQRS. This requires a very different way of looking at application design, but it is explicitly designed for reactive applications; these offer much nicer ways of dealing with the "what happens if someone edits the record while the current user is completing the form" question. Stack Overflow is an example - we can see, whilst typing our comments or answers, whether the question was updated, or other answers or comments are posted. There's a reactive library for PHP。