物理记录与逻辑/软删除数据库记录?


116

与实际或物理删除记录相比,对记录进行逻辑/软删除(即设置一个标志,说明已删除记录)有什么好处?

这是惯例吗?

这样安全吗?


22
使用删除时间戳,而不是标志。
Dave Jarvis 2013年

@DaveJarvis,您能解释一下为什么使用时间戳是一种更好的标记方法吗?
C亨利

4
标志不提供有关何时删除该行的任何信息。时间信息有很多用途,包括系统调试。
戴夫·贾维斯

Answers:


70

优点是您可以保留历史记录(便于审核),而不必担心通过引用数据库中要删除的行的其他表来级联删除操作。缺点是您必须对任何报告/显示方法进行编码以考虑该标记。

至于这是一种常见的做法-我会说是的,但是对于任何情况,是否使用它取决于您的业务需求。

编辑:想到另一个缺点-如果表上有唯一索引,则删除的记录仍将占据“一个”记录,因此您也必须围绕这种可能性进行编码(例如,具有唯一索引的User表用户名;删除的记录仍会阻止新记录的已删除用户的用户名。要解决此问题,您可以在GUID上添加已删除的用户名列,但这是我不推荐的非常棘手的解决方法。最好有一个规则,一旦使用了用户名,就永远不能替换它。)


显示为活动/停用的用户=)另外,如果它是唯一索引(假设您是数据库在控制唯一索引),这意味着什么-它仍将阻止已删除用户的新记录用户名?
Coops

@CodeBlend-如上所述,如果您的User表在Username列上具有唯一索引,则如果对名为“ Chris Shaffer”的用户进行软/逻辑删除,则该用户名将无法用于新用户创建一个新帐户,而如果您进行了硬/物理删除,则用户名将再次可用。
克里斯·谢弗

嗯,我是在考虑行而不是用户名(用户名)。如果要保留完整的历史记录,那么如果有一个“订单”或与该用户链接的内容,则必须进行软/逻辑删除。
Coops 2013年

11
@ChrisShaffer或者,可以选择仅对未删除的行编制索引,而不使用GUID。例如:(CREATE UNIQUE INDEX ... WHERE DELETED_AT is null在PostgreSQL中),然后不删除具有删除日期的所有行的索引。(它们可以改为包含在非唯一索引中。)
KajMagnus

6
@克里斯·谢弗(Chris Shaffer):引用“您不必担心通过其他各个表级联删除操作”。并非如此,您必须手动转发软删除,这是一个很大的麻烦,并且会导致不一致。实际上,这是一个缺点,因为不再需要执行外键关系。您很快就会得到数据垃圾。
Stefan Steiger 2014年

27

逻辑删除是惯例吗?是的,我在很多地方都看到了这一点。他们安全吗?这真的取决于它们是否比数据在删除之前安全性低?

当我担任技术主管时,我要求我们的团队保留所有数据,当时我知道我们将使用所有数据来构建各种BI应用程序,尽管当时我们不知道需求会是什么。是。从审核,故障排除和报告的角度来看,这很好(这是一个用于B2B交易的电子商务/工具网站,并且如果有人使用了该工具,即使后来他们的帐户被关闭,我们也要记录下来),它确实有几个缺点。

不利之处包括(不包括已经提到的其他问题):

  1. 保留所有数据的性能含义,我们将开发各种归档策略。例如,应用程序的一个区域即将接近每周生成约1Gb数据。
  2. 保留数据的成本确实会随着时间而增长,而磁盘空间却很便宜,但在线和离线保存和管理terrabytes数据的基础架构的数量却很多。冗余需要大量磁盘,并且人们需要花费大量时间来确保备份快速移动等。

在决定使用逻辑,物理删除或归档时,我会问自己以下问题:

  1. 是否可能需要将这些数据重新插入表中?例如,用户帐户符合此类别,因为您可能会激活或停用用户帐户。在这种情况下,逻辑删除最有意义。
  2. 存储数据有内在价值吗?如果是这样,将生成多少数据。取决于此,我要么进行逻辑删除,要么实施归档策略。请记住,您始终可以归档逻辑删除的记录。

在您的用户帐户示例中,将活动用户和非活动用户保持在单独的表中会很好吗?例如。Activated表和Deactivated表模式- Id,Name,etc..排在Activated- 1001,Smith007,etc...当他被停用,那么我们就可以清除所有,但ID列史密斯Activated和他添加到Deactivated
Erran Morad 2014年

如果您要离开ID和该行,那么移动所有数据有什么好处?也许,如果您的记录是巨大的,但我会将其视为微优化。
JoshBerke

如果您要在表中移动数据,那么具有级联外键约束的运气很好。
CAD bloke'7

20

可能有点晚了,但我建议大家查看Pinal Dave的有关逻辑/软删除的博客文章

我就是根本不喜欢这种设计[soft delete]。我坚信该体系结构,在该体系结构中,仅必要的数据应位于单个表中,而无用的数据应移至存档表中。我建议不要使用isDeleted列,而是建议使用两个不同的表:一个带有订单,另一个带有删除的订单。在那种情况下,您将必须同时维护两个表,但是实际上,这非常容易维护。当将UPDATE语句写入isDeleted列时,将INSERT INTO写入另一个表并从原始表中删除它。如果情况是回滚,则以相反的顺序写另一个INSERT INTO和DELETE。如果您担心事务失败,请将此代码包装在TRANSACTION中。

在上述情况下,较小的表格相对于较大的表格有什么优势?

  • 较小的桌子易于维护
  • 索引重建操作更快
  • 将存档数据移动到另一个文件组将减少主文件组的负载(考虑到所有文件组都在不同的系统上)–这也将加快备份速度。
  • 由于尺寸较小,统计信息将经常更新,这将减少资源消耗。
  • 索引的大小会更小
  • 该表的性能将随着较小的表大小而提高。

16
使用这种方法如何处理外键?可能有1个,10个或更多个其他表引用了要删除的记录并移至另一个表!
sam360 2015年

@ sam360-这是一个很大的挑战。老实说,由于处理表之间的PK和关系,我个人未能在项目中实现上述建议。不幸的是,那篇文章中没有一个真实的例子。我正在为我的一个项目开发一个解决方案,如果结果证明它是一个很好的实现,我将与您分享代码...
Tohid 2015年

这叫什么 ?而不是软删除?
eugene

1
@eugene-我不知道此解决方案的任何特定术语。如果确实可行,确实是“删除”行,并以“归档”表方法保留已删除的记录
Tohid

1
我相信,“将存档数据移动到另一个文件组”可以在Oracle中实现为分区,因此可以得到上面列出的好处...
Betlista 18/10/28

14

我是一名NoSQL开发人员,在上一份工作中,我使用了对某人始终至关重要的数据,如果在创建的同一天意外删除了该数据,则无法在上次备份中找到它从昨天!在这种情况下,软删除始终可以节省一天的时间。

我使用时间戳进行了软删除,并注册了删除文档的日期:

IsDeleted = 20150310  //yyyyMMdd

每个星期天,都有一个进程在数据库中浏览并检查该IsDeleted字段。如果当前日期和时间戳之间的差异大于N天,则该文档被硬删除。考虑到该文档在某些备份上仍然可用,因此这样做是安全的。

编辑:此NoSQL用例是关于在数据库中创建的大型文档,每天数十或数百个,而不是数千或数百万。通常,它们是包含工作流程过程的状态,数据和附件的文档。这就是为什么用户有可能删除重要文档的原因。该用户可以是具有Admin特权的人员,也可以是文档的所有者,仅举几例。

TL; DR我的用例不是大数据。在这种情况下,您将需要其他方法。


9

我使用的一种模式是创建镜像表并将触发器附加到主表上,因此所有删除(如果需要,也可以进行更新)都记录在镜像表中。

这使您可以“重建”已删除/更改的记录,并且仍然可以在主表中进行硬删除并将其保持“干净”-还可以创建“撤消”功能,还可以记录日期,时间,以及在镜像表中执行该操作的用户(在狩猎女巫的情况下非常有价值)。

另一个优点是,查询主数据库时,没有机会意外地包含已删除的记录,除非您故意从镜像表中包含记录(您可能希望显示实时和已删除的记录)。

另一个优点是,镜像表可以独立清除,因为它不应该具有任何实际的外键引用,与从使用软删除但仍具有到其他表的引用连接的主表进行清除相比,这是一个相对简单的操作。

还有什么其他优点?-非常棒,如果您有很多编码员在项目上工作,并且混合技巧和对细节级别的关注在数据库上进行读取,则不必熬夜,希望其中一个不会忘记不包括删除的信息记录(大声笑,不包括已删除的记录= True),这会导致诸如高估客户可用现金头寸之类的事情,然后当您在交易系统上工作时,他们去与客户购买一些股票(例如,在交易系统中)即使它们可能有更多的初始“开销”,它也会很快发现健壮解决方案的价值。

例外:
-作为指导,对“引用”数据(例如用户,类别等)使用软删除,对“事实”类型的数据(即交易历史)使用硬删除到镜像表。


5

我通常使用逻辑删除-当您还将“已删除”的数据断断续续地存档到已存档的表(可以在需要时进行搜索)时,我发现它们很好用,因此没有机会影响应用程序的性能。

它之所以有效,是因为如果您经过审核,您仍然拥有数据。如果您将其物理删除,它就消失了


5

我是逻辑删除的忠实拥护者,尤其是对于“业务线”应用程序或用户帐户而言。我的理由很简单:通常我不希望用户再使用该系统(因此,该帐户被标记为已删除),但是如果我们删除该用户,我们将失去所有工作。

另一个常见情况是,删除用户后可能会重新创建用户。对于用户来说,将其所有数据保留在删除前的状态而不必重新创建,这是一种更好的体验。

我通常认为删除用户更多是无限期地“暂停”他们。您永远不知道他们什么时候会合法地回来。


我们不应该在这里使用诸如帐户激活/停用之类的功能来代替逻辑删除吗?@ jon-dewees
Eagle_Eye

4

我几乎总是进行软删除,原因如下:

  • 如果客户要求您恢复已删除的数据,您可以这样做。软删除使客户更加满意。从备份还原特定数据很复杂
  • 检查isdeleted任何地方都不是问题,userid无论如何都必须检查(如果数据库包含来自多个用户的数据)。您可以通过将这两项检查放在单独的函数中(或使用视图)来通过代码执行检查
  • 优美地删除。处理已删除内容的用户或进程将继续“看到”它,直到他们单击下一次刷新。如果某个进程正在处理一些突然删除的数据,这是一个非常理想的功能
  • 同步:如果您需要设计数据库和移动应用之间的同步机制,则会发现软删除更容易实现

@Jim将数据持久保存到数据库中是非法的。如果即使客户告诉您删除其自己的数据后仍保留记录,也是非法的。软删除与GDPR完全兼容:根据要求,只需用空白数据覆盖敏感数据即可。此外,如果用户删除了一条记录,则可能希望在以后的某个时间撤消该操作或以某种方式恢复数据……这并不意味着他/她希望数据从数据库中完全消失
Gianluca Ghettini

3

回复:“这安全吗?” -这取决于您的意思。

如果您是说要进行物理删除,那么您将阻止任何人找到已删除的数据,是的,这或多或少是正确的;您可以安全地物理删除需要删除的敏感数据,因为这意味着它们已从数据库中永久删除。(但是,请注意,可能还有其他相关数据副本,例如备份,事务日志或传输中的记录版本,例如数据包嗅探器-只是因为您从数据库中删除并没有保证它没有保存在其他地方。)

如果您是说进行逻辑删除,那么您的数据将更加安全,因为您将永远不会丢失任何数据,这也是事实。这对于审核方案很有用;我倾向于,因为它承认这一基本事实:一旦生成的数据,它永远不会设计这种方式真的走(尤其是如果它曾经被,比如说,通过互联网搜索引擎缓存的能力)。当然,真正的审核方案不仅要求删除逻辑合理,而且还必须记录更新以及更改时间和更改者。

如果您的意思是说数据不会落入任何不应该看到的人的手中,那么这完全取决于您的应用程序及其安全性结构。在这方面,逻辑删除的安全性与数据库中的其他安全性差不多。


3

我强烈不同意逻辑删除,因为您会遇到许多错误。

首先,每个查询必须注意IsDeleted字段,并且复杂查询出错的可能性更高。

其次是性能:假设一个表具有100000个recs,而其中只有3个处于活动状态,现在将该数字乘以数据库表即可;另一个性能问题是与旧记录(已删除记录)的新记录可能发生冲突。

我看到的唯一的好处是记录历史,但也有其他方法来实现这一结果,例如,你可以创建一个记录表,你可以保存信息:TableName,OldValues,NewValues,Date,User,[..]哪里*Values可以varchar写这种形式的细节fieldname : value; [..]或将信息存储为xml

所有这些都可以通过代码或触发器来实现,但是您只有一个表,拥有所有的历史记录。另一个选择是查看指定的数据库引擎是否本机支持跟踪更改,例如在SQL Server数据库上有SQL跟踪数据更改。


3

我曾经做过软删除,只是为了保留旧记录。我意识到,用户不必像我想的那样经常查看旧记录。如果用户要查看旧记录,则只能从存档或审计表中查看,对吗?那么,软删除有什么优势?它只会导致更复杂的查询语句等。

在我决定不再软删除之前,以下是我已经实现的事情:

  1. 实施审核,以记录所有活动(添加,编辑,删除)。确保没有链接到审计的外键,并确保此表是安全的,除了管理员,其他任何人都不能删除。

  2. 确定哪些表被视为“事务表”,哪些表很可能会保留很长时间,并且用户很有可能希望查看过去的记录或报告。例如; 购买交易。该表不仅应保留主表的ID(例如dept-id),还应保留其他信息(例如,引用名称(例如dept-name))或任何其他必要的报告字段。

  3. 实现主表的“活动/非活动”或“启用/禁用”或“隐藏/显示”记录。因此,用户可以禁用/禁用主记录而不是删除记录。这样更安全。

只是我的两分钱的意见。


2

逻辑删除(如果很难保证参照完整性)。

当表数据存在时态方面(正确的FROM_DATE-TO_DATE)时,应该做正确的想法。

否则,将数据移至审核表并删除记录。

从积极的一面:

这是回滚的更简单方法(如果可能的话)。

很容易看到特定时间点的状态。


2

如果您想保留某些历史记录(例如,@ Jon Dewees提到的用户帐户),这是相当标准的。如果用户有很大的机会要求删除,这无疑是一个好主意。

如果您担心从混乱的查询中过滤出删除的记录并使查询变得复杂的逻辑,则可以只构建为您进行过滤并使用查询的视图。这将防止在报告解决方案等中泄漏这些记录。


2

除了系统设计之外,还有其他需求需要解决。保留记录有什么法律或法定要求?根据与行相关的内容,可能有一项法律要求,即在“暂停”数据之后将其保留一段时间。

另一方面,要求可能是一旦“删除”记录,就将其真正且不可撤销地删除。在做出决定之前,请与您的利益相关者交谈。


2

依赖于同步的移动应用程序可能会强制使用逻辑删除而不是物理删除:服务器必须能够向客户端指示记录已被(标记为)删除,并且如果记录被物理删除则可能无法实现。


1

他们不让数据库执行,因为它应该使级联功能之类的东西变得无用。

对于简单的事情,例如插入,在重新插入的情况下,其后面的代码将加倍。

您不能只是简单地插入,而是必须检查是否存在,如果之前不存在则插入,或者如果存在则更新删除标志,同时还将所有其他列更新为新值。这被视为对数据库事务日志的更新,而不是导致错误审计日志的全新插入。

它们会导致性能问题,因为表已被冗余数据所阻塞。它在索引方面尤其是在唯一性方面发挥了作用。

我不是逻辑删除的忠实拥护者。


1

为了回复Tohid的评论,我们遇到了同样的问题,我们想要保留记录的历史记录,并且不确定是否需要is_deleted专栏。

我在谈论我们的python实现以及我们遇到的类似用例。

我们遇到了https://github.com/kvesteri/sqlalchemy-continuum,这是一种获取对应表的版本表的简便方法。最少的代码行,并捕获添加,删除和更新的历史记录。

这不仅限于is_deleted专栏。您始终可以使用backref版本表来检查此条目所发生的情况。条目是删除,更新还是添加。

这样,我们根本就不需要is_deleted列,并且删除功能非常简单。这样,我们也无需记住is_deleted=False在任何api中进行标记。


0

软删除是一种编程实践,当数据更相关时,大多数应用程序都将遵循它。考虑金融应用的情况,其中最终用户的错误删除可能是致命的。当软删除变得重要时就是这种情况。在软删除中,用户实际上并没有从记录中删除数据,而是将其标记为IsDeleted为true(按照常规约定)。

在EF 6.x或EF 7及更高版本中,添加了Softdelete作为属性,但是现在我们必须创建一个自定义属性。

我强烈建议在数据库设计中使用SoftDelete,它是编程实践的良好习惯。


0

大多数情况下使用软删除,因为您不想公开某些数据,但是由于历史原因必须保留它(产品可能会停产,因此您不希望使用它进行任何新的交易,但仍需要使用销售交易的历史)。顺便说一句,有些人将产品信息值复制到销售交易数据中,而不是引用产品来处理该信息。

实际上,它看起来更像是对可见/隐藏或活动/非活动功能的重新措词。因为这就是商业世界中“删除”的含义。我想说终结者可以删除人,但老板只是解雇他们。

这种做法是非常常见的模式,由于许多原因,很多应用程序都在使用这种做法。因为这不是实现此目标的唯一方法,所以您将有成千上万的人说这很棒或胡说八道,而且两者都有很好的论据。

从安全的角度来看,SoftDelete不会取代Audit的工作,也不会取代备份的工作。如果您担心“在两个备份案例之间进行插入/删除”,则应阅读有关完整或批量恢复模型的信息。我承认SoftDelete可以使恢复过程变得更简单。

由您决定您的要求。


0

作为替代方案,我们让用户使用通过MobiLink更新的远程设备。如果我们删除服务器数据库中的记录,则这些记录永远不会在客户端数据库中被标记为已删除。

所以我们都做。我们与客户一起确定他们希望能够恢复数据的时间。例如,在我们的客户说应该删除它们之前,通常客户和产品一直处于活动状态,但是销售历史仅保留13个月,然后自动删除。客户可能希望将已删除的客户和产品保留两个月,但将历史记录保留六个月。

因此,我们在一夜之间运行了一个脚本,该脚本根据这些参数将逻辑删除的内容标记为逻辑删除,然后在两个/六个月后,将对今天标记为逻辑删除的所有内容进行硬删除。

我们关心的不是数据安全,而是关于在内存有限的客户端设备(例如智能手机)上拥有庞大的数据库。四年内每周两次订购200种产品的客户将拥有超过81,000行的历史记录,其中75%的客户不在乎是否看到。


0

这完全取决于系统及其数据的用例。

例如,如果您谈论的是政府监管的系统(例如,制药公司的系统被认为是质量系统的一部分,并且必须遵循FDA电子记录的准则),那么您最好不敢进行硬删除!FDA的审核员可以要求系统中与产品编号ABC-123相关的所有记录,并且可以更好地获取所有数据。如果您的业务流程所有者表示系统不允许任何人在以后的新记录上使用产品编号ABC-123,请改用软删除方法,使其在系统内“不活动”,同时仍保留历史数据。

但是,也许您的系统及其数据有一个用例,例如“跟踪北极的天气”。也许您每小时获取一次温度读数,并且一天结束时汇总每天的平均值。汇总后,也许不再使用每小时数据,而您在创建汇总后便会硬删除每小时的读数。(这是一个虚构的小例子。)

关键是,这完全取决于系统的用例及其数据,而不是纯粹从技术角度做出决定。


0

好!众所周知,这取决于情况。

如果您在诸如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再次注册并不意味着还原整个历史记录。众所周知,删除意味着删除。在这种情况下,我们必须进行物理删除。但是为了维护帐户的整个历史记录,我们应该始终将此类记录归档在归档表或已删除表中。

是的,在我们有很多外国表的情况下,处理非常麻烦。

还请记住,软/逻辑删除会增加表的大小,因此会增加索引的大小。


0

我已经在另一篇文章中回答。但是,我认为我的答案更适合这里的问题。

我对软删除实际的解决方案是通过创建具有以下各列的新表归档:original_idtable_namepayload,(和一个可选的主键`ID)。

其中,original_id是已删除记录的原始ID,table_name 是已删除记录的表名("user"在您的情况下), payload是已删除记录的所有列中的JSON字符串化字符串。

我还建议在列上建立索引以original_id供以后的数据检索。

通过这种方式存档数据。您将拥有这些优势

  • 跟踪历史记录中的所有数据
  • 无论删除的记录的表结构如何,只有一个地方可以归档任何表中的记录
  • 不用担心原始表中的唯一索引
  • 不用担心在原始表中检查外来索引
  • WHERE每个查询中没有更多子句可检查是否删除

此处已经在讨论 解释了为什么软删除在实践中不是一个好主意。软删除会在将来引入一些潜在的麻烦,例如计数记录,...


我写了一篇有关所有数据删除方式的博客文章transang.me/database-design-practice-soft-deletion-to
transang

0

冒险是数据保存/永续。当从具有大量软删除的表中查询或检索数据时,性能下降将是浪费。在我们的例子中,我们使用两者的结合:为他人在以前的答案已经提到的,我们soft-delete users/clients/customers举例来说,并hard-deleteitems/products/merchandise那里有没有需要蜜蜂keept重复记录表。


0

视情况而定,请考虑以下因素:

通常,您不需要“软删除”记录。保持简单快速。 例如,删除不再可用的产品,因此您不必检查整个应用程序(计数,产品列表,推荐产品等)中是否都没有软删除该产品。

但是,您可能会考虑数据仓库模型中的“软删除”。例如,您正在查看已删除产品上的旧收据。*

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.