Answers
简单地说,看情况。
下面具体说说我的理解
“删除”指的是什么?弄清这个很重要。
-
“删除”(delete)一条记录,意思是把数据库表中的一行删掉,删掉后这条数据就没了。
-
客户“删除”(取消, cancel)了订单,意思是这个订单还在,只是状态变了。
这两个场景中“删除”的含义是不同的。第1个场景,delete是针对数据库的操作,是
业务之外的概念
;第2个场景,delete是
业务范围里
的概念。
所以,如果是业务需要,“软删除”,业务不需要,“物理删除”。
从数据库术语的角度看,删除指的是“物理删除”。“软删除”这个词起得很不好,容易误导人,因为实际上并没有删除。更贴切的词是“取消”,“归档”,等等。
“删除”的含义很广,如果混淆了概念,提问和回答都失去了准确性。大家各执一词,引经据典,最终还是没有结果。
关系数据库可以看成是一个信息系统,用来记录
客观事实
,用户或应用程序可以从这些事实中
推导结论
。数据库里的表(关系,relation)就像几何证明中的公理。因此,删除一条记录可以看成是改变了事实(客观状态)。
比如,有一张表 TeacherClass(TeacherName, ClassName)。
数据((张三, 1班), (李四,2班))的含义是:张三老师给1班教课,李四老师给2班教课。每条记录都是一个事实(“真值”)。
删除一条记录后成为((张三, 1班)),含义是:张三老师给1班教课,李四老师
不
给2班教课。“李四老师给2班教课”为
假
,因为(李四,2班)没有出现在表中。
所以,到底怎么“删除”,看情况。
哪些事实是需要记录的?如果客户“删除”订单是需要记录的事实,就用“软删除”(不是数据库层面的delete,而是业务范围的“取消”)。一个可行的设计是:
Customer(
CustomerId
)
Order(
CustomerId
,
OrderId
)
OrderStatus(
CustomerId
,
OrderId
,
Timestamp
, Status)
每个Customer可以有0-n个Order
每个Order可以有0-n个OrderStatus
用户取消订单,就在OrderStatus里添加一条记录(insert,不是delete,因为多了一个客观事实)
反之,如果业务不需要记录的“删除”就用“物理删除”
Person(
PersonId
, PersonName)
Hobby(
HobbyId
, HobbyName)
PersonHobby(
PersonId
,
HobbyId
)
每个Person有0-n个Hobby
每个Hobby被0-n个Person拥有
如果张三喜欢打篮球,过了些日子,不喜欢打篮球了,就应该从PersonHobby里delete一条数据。当然,前提是业务需求中不需要记录为什么删除。
关系数据库用来记录
当前
的客观事实,增、删、改,对应事实的改变。
有一种类型的表称为“history table”(或"audit table"),他们是专门用来记录历史数据的。通过trigger或存储过程实现。他们的功能跟其他表不同,他们记录的是对数据库的操作,不是业务范围的事实。history table通常数据量会随时间越来越大,它们没有删、改,只有增、查(少量的查),而current table中的数据量相对较小,正好满足业务需要(增、删、改、查)。
关于soft delete的好处,justjavac的介绍很棒。
不过是否应该使用soft delete,还是需要慎重考虑。
在关系型数据库中,软删除(soft delete)主要影响的是write的效率,而不是read,所以在这种情况下,考虑采用soft delete的可行性的时候,也要考虑你的table/database面对的是否是write intensive的需求。另外,采用soft delete,一般是为了可能的恢复,为了audit,为了保存历史数据等等目的。那么为了某个或者某些数据,是否soft delete真的是最好的处理方式呢?比如为了log的auditing,而对log采用soft delete,或许就有一些小题大做了。
而对于NoSQL数据库,软删除通常来讲是很容易接收的。尤其是Key-Value的NoSQL数据库,比如Amazon的Dynamo DB和Riak等等,实现原理是hash table,所以就算有大量的soft delete数据,也不会对性能造成巨大的影响。而对于其他类型的NoSQL数据库,Document-based(比如CouchDB)或者Column-based(比如hbase)等等,data integrity重要性也是远远超过采用soft delete带来的性能下降的。
不要删除数据
Oren Eini(又名 Ayende Rahien)建议开发者尽量避免数据库的软删除操作,读者可能因此认为硬删除是合理的选择。作为对 Ayende 文章的回应,Udi Dahan 强烈建议完全避免数据删除。
所谓软删除主张在表中增加一个 IsDeleted 列以保持数据完整。如果某一行设置了IsDeleted标志列,那么这一行就被认为是已删除的。Ayende 觉得这种方法“简单、容易理解、容易实现、容易沟通”,但“往往是错的”。问题在于:
删除一行或一个实体几乎总不是简单的事件。它不仅影响模型中的数据,还会影响模型的外观。所以我们才要有外键去确保不会出现“订单行”没有对应的父“订单”的情况。而这个例子只能算是最简单的情况。……
当采用软删除的时候,不管我们是否情愿,都很容易出现数据受损,比如谁都不在意的一个小调整,就可能使“客户”的“最新订单”指向一条已经软删除的订单。
如果开发者接到的要求就是从数据库中删除数据,要是不建议用软删除,那就只能硬删除了。为了保证数据一致性,开发者除了删除直接有关的数据行,还应该级联地删除相关数据。可Udi
Dahan提醒读者注意,真实的世界并不是级联的:假设市场部决定从商品目录中删除一样商品,那是不是说所有包含了该商品的旧订单都要一并消失?再级联下去,这些订单对应的所有发票是不是也该删除?这么一步步删下去,我们公司的损益报表是不是应该重做了?
没天理了。
问题似乎出在对“删除”这词的解读上。Dahan 给出了这样的例子:
我说的“删除”其实是指这产品“停售”了。我们以后不再卖这种产品,清掉库存以后不再进货。以后顾客搜索商品或者翻阅目录的时候不会再看见这种商品,但管仓库的人暂时还得继续管理它们。“删除”是个贪方便的说法。
他接着举了一些站在用户角度的正确解读:
订单不是被删除的,是被“取消”的。订单取消得太晚,还会产生花费。
员工不是被删除的,是被“解雇”的(也可能是退休了)。还有相应的补偿金要处理。
职位不是被删除的,是被“填补”的(或者招聘申请被撤回)。
在上面这些例子中,我们的着眼点应该放在用户希望完成的任务上,而非发生在某个
实体身上的技术动作。几乎在所有的情况下,需要考虑的实体总不止一个。为了代替 IsDeleted 标志,Dahan 建议用一个代表相关数据状态的字段:有效、停用、取消、弃置等等。用户可以借助这样一个状态字段回顾过去的数据,作为决策的依据。
删除数据除了破坏数据一致性,还有其它负面的后果。Dahan建议把所有数据都留在数据库里:“ 别删除。就是别删除 。”
—— 《NoSQL数据库笔谈》