Answers:
优点是您可以保留历史记录(便于审核),而不必担心通过引用数据库中要删除的行的其他表来级联删除操作。缺点是您必须对任何报告/显示方法进行编码以考虑该标记。
至于这是一种常见的做法-我会说是的,但是对于任何情况,是否使用它取决于您的业务需求。
编辑:想到另一个缺点-如果表上有唯一索引,则删除的记录仍将占据“一个”记录,因此您也必须围绕这种可能性进行编码(例如,具有唯一索引的User表用户名;删除的记录仍会阻止新记录的已删除用户的用户名。要解决此问题,您可以在GUID上添加已删除的用户名列,但这是我不推荐的非常棘手的解决方法。最好有一个规则,一旦使用了用户名,就永远不能替换它。)
CREATE UNIQUE INDEX ... WHERE DELETED_AT is null
在PostgreSQL中),然后不删除具有删除日期的所有行的索引。(它们可以改为包含在非唯一索引中。)
逻辑删除是惯例吗?是的,我在很多地方都看到了这一点。他们安全吗?这真的取决于它们是否比数据在删除之前安全性低?
当我担任技术主管时,我要求我们的团队保留所有数据,当时我知道我们将使用所有数据来构建各种BI应用程序,尽管当时我们不知道需求会是什么。是。从审核,故障排除和报告的角度来看,这很好(这是一个用于B2B交易的电子商务/工具网站,并且如果有人使用了该工具,即使后来他们的帐户被关闭,我们也要记录下来),它确实有几个缺点。
不利之处包括(不包括已经提到的其他问题):
在决定使用逻辑,物理删除或归档时,我会问自己以下问题:
Activated
表和Deactivated
表模式- Id,Name,etc..
排在Activated
- 1001,Smith007,etc...
当他被停用,那么我们就可以清除所有,但ID列史密斯Activated
和他添加到Deactivated
。
可能有点晚了,但我建议大家查看Pinal Dave的有关逻辑/软删除的博客文章:
我就是根本不喜欢这种设计[soft delete]。我坚信该体系结构,在该体系结构中,仅必要的数据应位于单个表中,而无用的数据应移至存档表中。我建议不要使用isDeleted列,而是建议使用两个不同的表:一个带有订单,另一个带有删除的订单。在那种情况下,您将必须同时维护两个表,但是实际上,这非常容易维护。当将UPDATE语句写入isDeleted列时,将INSERT INTO写入另一个表并从原始表中删除它。如果情况是回滚,则以相反的顺序写另一个INSERT INTO和DELETE。如果您担心事务失败,请将此代码包装在TRANSACTION中。
在上述情况下,较小的表格相对于较大的表格有什么优势?
- 较小的桌子易于维护
- 索引重建操作更快
- 将存档数据移动到另一个文件组将减少主文件组的负载(考虑到所有文件组都在不同的系统上)–这也将加快备份速度。
- 由于尺寸较小,统计信息将经常更新,这将减少资源消耗。
- 索引的大小会更小
- 该表的性能将随着较小的表大小而提高。
我是一名NoSQL开发人员,在上一份工作中,我使用了对某人始终至关重要的数据,如果在创建的同一天意外删除了该数据,则无法在上次备份中找到它从昨天!在这种情况下,软删除始终可以节省一天的时间。
我使用时间戳进行了软删除,并注册了删除文档的日期:
IsDeleted = 20150310 //yyyyMMdd
每个星期天,都有一个进程在数据库中浏览并检查该IsDeleted
字段。如果当前日期和时间戳之间的差异大于N天,则该文档被硬删除。考虑到该文档在某些备份上仍然可用,因此这样做是安全的。
编辑:此NoSQL用例是关于在数据库中创建的大型文档,每天数十或数百个,而不是数千或数百万。通常,它们是包含工作流程过程的状态,数据和附件的文档。这就是为什么用户有可能删除重要文档的原因。该用户可以是具有Admin特权的人员,也可以是文档的所有者,仅举几例。
TL; DR我的用例不是大数据。在这种情况下,您将需要其他方法。
我使用的一种模式是创建镜像表并将触发器附加到主表上,因此所有删除(如果需要,也可以进行更新)都记录在镜像表中。
这使您可以“重建”已删除/更改的记录,并且仍然可以在主表中进行硬删除并将其保持“干净”-还可以创建“撤消”功能,还可以记录日期,时间,以及在镜像表中执行该操作的用户(在狩猎女巫的情况下非常有价值)。
另一个优点是,查询主数据库时,没有机会意外地包含已删除的记录,除非您故意从镜像表中包含记录(您可能希望显示实时和已删除的记录)。
另一个优点是,镜像表可以独立清除,因为它不应该具有任何实际的外键引用,与从使用软删除但仍具有到其他表的引用连接的主表进行清除相比,这是一个相对简单的操作。
还有什么其他优点?-非常棒,如果您有很多编码员在项目上工作,并且混合技巧和对细节级别的关注在数据库上进行读取,则不必熬夜,希望其中一个不会忘记不包括删除的信息记录(大声笑,不包括已删除的记录= True),这会导致诸如高估客户可用现金头寸之类的事情,然后当您在交易系统上工作时,他们去与客户购买一些股票(例如,在交易系统中)即使它们可能有更多的初始“开销”,它也会很快发现健壮解决方案的价值。
例外:
-作为指导,对“引用”数据(例如用户,类别等)使用软删除,对“事实”类型的数据(即交易历史)使用硬删除到镜像表。
我几乎总是进行软删除,原因如下:
isdeleted
任何地方都不是问题,userid
无论如何都必须检查(如果数据库包含来自多个用户的数据)。您可以通过将这两项检查放在单独的函数中(或使用视图)来通过代码执行检查回复:“这安全吗?” -这取决于您的意思。
如果您是说要进行物理删除,那么您将阻止任何人找到已删除的数据,是的,这或多或少是正确的;您可以安全地物理删除需要删除的敏感数据,因为这意味着它们已从数据库中永久删除。(但是,请注意,可能还有其他相关数据副本,例如备份,事务日志或传输中的记录版本,例如数据包嗅探器-只是因为您从数据库中删除并没有保证它没有保存在其他地方。)
如果您是说进行逻辑删除,那么您的数据将更加安全,因为您将永远不会丢失任何数据,这也是事实。这对于审核方案很有用;我倾向于,因为它承认这一基本事实:一旦生成的数据,它永远不会设计这种方式真的走(尤其是如果它曾经被,比如说,通过互联网搜索引擎缓存的能力)。当然,真正的审核方案不仅要求删除逻辑合理,而且还必须记录更新以及更改时间和更改者。
如果您的意思是说数据不会落入任何不应该看到的人的手中,那么这完全取决于您的应用程序及其安全性结构。在这方面,逻辑删除的安全性与数据库中的其他安全性差不多。
我强烈不同意逻辑删除,因为您会遇到许多错误。
首先,每个查询必须注意IsDeleted字段,并且复杂查询出错的可能性更高。
其次是性能:假设一个表具有100000个recs,而其中只有3个处于活动状态,现在将该数字乘以数据库表即可;另一个性能问题是与旧记录(已删除记录)的新记录可能发生冲突。
我看到的唯一的好处是记录历史,但也有其他方法来实现这一结果,例如,你可以创建一个记录表,你可以保存信息:TableName,OldValues,NewValues,Date,User,[..]
哪里*Values
可以varchar
写这种形式的细节fieldname : value
; [..]或将信息存储为xml
。
所有这些都可以通过代码或触发器来实现,但是您只有一个表,拥有所有的历史记录。另一个选择是查看指定的数据库引擎是否本机支持跟踪更改,例如在SQL Server数据库上有SQL跟踪数据更改。
我曾经做过软删除,只是为了保留旧记录。我意识到,用户不必像我想的那样经常查看旧记录。如果用户要查看旧记录,则只能从存档或审计表中查看,对吗?那么,软删除有什么优势?它只会导致更复杂的查询语句等。
在我决定不再软删除之前,以下是我已经实现的事情:
实施审核,以记录所有活动(添加,编辑,删除)。确保没有链接到审计的外键,并确保此表是安全的,除了管理员,其他任何人都不能删除。
确定哪些表被视为“事务表”,哪些表很可能会保留很长时间,并且用户很有可能希望查看过去的记录或报告。例如; 购买交易。该表不仅应保留主表的ID(例如dept-id),还应保留其他信息(例如,引用名称(例如dept-name))或任何其他必要的报告字段。
实现主表的“活动/非活动”或“启用/禁用”或“隐藏/显示”记录。因此,用户可以禁用/禁用主记录而不是删除记录。这样更安全。
只是我的两分钱的意见。
为了回复Tohid的评论,我们遇到了同样的问题,我们想要保留记录的历史记录,并且不确定是否需要is_deleted
专栏。
我在谈论我们的python实现以及我们遇到的类似用例。
我们遇到了https://github.com/kvesteri/sqlalchemy-continuum,这是一种获取对应表的版本表的简便方法。最少的代码行,并捕获添加,删除和更新的历史记录。
这不仅限于is_deleted
专栏。您始终可以使用backref版本表来检查此条目所发生的情况。条目是删除,更新还是添加。
这样,我们根本就不需要is_deleted
列,并且删除功能非常简单。这样,我们也无需记住is_deleted=False
在任何api中进行标记。
大多数情况下使用软删除,因为您不想公开某些数据,但是由于历史原因必须保留它(产品可能会停产,因此您不希望使用它进行任何新的交易,但仍需要使用销售交易的历史)。顺便说一句,有些人将产品信息值复制到销售交易数据中,而不是引用产品来处理该信息。
实际上,它看起来更像是对可见/隐藏或活动/非活动功能的重新措词。因为这就是商业世界中“删除”的含义。我想说终结者可以删除人,但老板只是解雇他们。
这种做法是非常常见的模式,由于许多原因,很多应用程序都在使用这种做法。因为这不是实现此目标的唯一方法,所以您将有成千上万的人说这很棒或胡说八道,而且两者都有很好的论据。
从安全的角度来看,SoftDelete不会取代Audit的工作,也不会取代备份的工作。如果您担心“在两个备份案例之间进行插入/删除”,则应阅读有关完整或批量恢复模型的信息。我承认SoftDelete可以使恢复过程变得更简单。
由您决定您的要求。
作为替代方案,我们让用户使用通过MobiLink更新的远程设备。如果我们删除服务器数据库中的记录,则这些记录永远不会在客户端数据库中被标记为已删除。
所以我们都做。我们与客户一起确定他们希望能够恢复数据的时间。例如,在我们的客户说应该删除它们之前,通常客户和产品一直处于活动状态,但是销售历史仅保留13个月,然后自动删除。客户可能希望将已删除的客户和产品保留两个月,但将历史记录保留六个月。
因此,我们在一夜之间运行了一个脚本,该脚本根据这些参数将逻辑删除的内容标记为逻辑删除,然后在两个/六个月后,将对今天标记为逻辑删除的所有内容进行硬删除。
我们关心的不是数据安全,而是关于在内存有限的客户端设备(例如智能手机)上拥有庞大的数据库。四年内每周两次订购200种产品的客户将拥有超过81,000行的历史记录,其中75%的客户不在乎是否看到。
这完全取决于系统及其数据的用例。
例如,如果您谈论的是政府监管的系统(例如,制药公司的系统被认为是质量系统的一部分,并且必须遵循FDA电子记录的准则),那么您最好不敢进行硬删除!FDA的审核员可以要求系统中与产品编号ABC-123相关的所有记录,并且可以更好地获取所有数据。如果您的业务流程所有者表示系统不允许任何人在以后的新记录上使用产品编号ABC-123,请改用软删除方法,使其在系统内“不活动”,同时仍保留历史数据。
但是,也许您的系统及其数据有一个用例,例如“跟踪北极的天气”。也许您每小时获取一次温度读数,并且一天结束时汇总每天的平均值。汇总后,也许不再使用每小时数据,而您在创建汇总后便会硬删除每小时的读数。(这是一个虚构的小例子。)
关键是,这完全取决于系统的用例及其数据,而不是纯粹从技术角度做出决定。
好!众所周知,这取决于情况。
如果您在诸如UserName或EmailID的列上有索引-并且您永远不会希望再次使用相同的UserName或EmailID;您可以进行软删除。
也就是说,请始终检查SELECT操作是否使用主键。如果SELECT语句使用主键,则在WHERE子句中添加标志不会有多大区别。让我们举个例子(伪):
表用户(用户ID [主键],电子邮件ID,已删除)
SELECT * FROM Users where UserID = 123456 and IsDeleted = 0
由于UserID列具有主键,因此该查询在性能方面不会有任何区别。最初,它将基于PK扫描表,然后执行下一个条件。
软删除根本无法工作的情况:
在大多数网站上注册时,都会使用EmailID作为您的唯一标识。我们非常了解,一旦在Facebook,G +等网站上使用了EmailID,其他任何人都无法使用它。
有一天,用户希望从网站上删除其个人资料。现在,如果您进行逻辑删除,则该用户将无法再次注册。另外,使用相同的EmailID再次注册并不意味着还原整个历史记录。众所周知,删除意味着删除。在这种情况下,我们必须进行物理删除。但是为了维护帐户的整个历史记录,我们应该始终将此类记录归档在归档表或已删除表中。
是的,在我们有很多外国表的情况下,处理非常麻烦。
还请记住,软/逻辑删除会增加表的大小,因此会增加索引的大小。
我已经在另一篇文章中回答了。但是,我认为我的答案更适合这里的问题。
我对软删除实际的解决方案是通过创建具有以下各列的新表归档:
original_id
,table_name
,payload
,(和一个可选的主键`ID)。其中,
original_id
是已删除记录的原始ID,table_name
是已删除记录的表名("user"
在您的情况下),payload
是已删除记录的所有列中的JSON字符串化字符串。我还建议在列上建立索引以
original_id
供以后的数据检索。通过这种方式存档数据。您将拥有这些优势
- 跟踪历史记录中的所有数据
- 无论删除的记录的表结构如何,只有一个地方可以归档任何表中的记录
- 不用担心原始表中的唯一索引
- 不用担心在原始表中检查外来索引
WHERE
每个查询中没有更多子句可检查是否删除