最近几天有点忙,所以我们今天来一篇短的,简单地介绍一下数据库设计中的一种模式——Soft Delete。

  可以说,该模式毁誉参半,甚至有非常多的人认为该模式是一个Anti-Pattern。因此在本篇文章中,我们不仅仅会对该模式进行介绍,同时也会列出该模式可能导致的一系列问题,以帮助大家正确地决定是否使用该模式。

Soft Delete简介

  首先先来想一个需求,那就是对用户操作的回滚支持。例如我现在正在用Word编写这篇文章。当我执行了一个错误操作的时候,我仅仅需要键入Ctrl + Z就可以进行回滚。而在有些Web应用中,我们同样需要这种功能。

  例如Rally是一个Web应用,以用来在软件开发过程中对进度和任务进行管理。在任务管理功能中,每次对任务的创建,修改以及删除都会被记录在系统中。

  现在问题来了,如果需要支持回滚,那么系统该如何记录一条已经被用户删除了的任务呢?最直观的想法就是在数据库中添加一列deleted来记录该任务是否已经被删除:

 @Entity
class Task extends … {
private boolean deleted;
……
private boolean isDeleted() {
return deleted;
} private void setDeleted(boolean deleted) {
this.deleted = deleted;
}
}

  如果一个任务被删除了,那么它的deleted将为true。也就是说,在用户删除一个任务的时候,系统实际上并没有将该任务彻底地从数据库中删除,而仅仅是通过deleted来标示其已经被删除了。而在恢复该任务的时候,只需要将deleted设置为false即可。

  OK,这就是有关Soft Delete模式的介绍。是不是很简单?但也正是因为它非常简单,进而导致了对它的滥用,从而使它成为了一个很多人眼中的Anti-Pattern。这种事情在IT技术中发生的还真是不少。最简单的就是Java的Checked Exception。的确它是一个好的功能,让使用Java编程变得更加严谨。但是过分的滥用导致很多类库都将用户完全无法处理的异常暴露在了类库接口中,反而使很多软件开发人员养成了直接忽略所有异常的坏习惯:

 try {
obj.someFunction();
} catch (Throwable e) {
}

  相信读者已经看出了这么做的危害:catch甚至将表示系统错误的Error类型实例都抓住了。但这里不能忽略的一个事实是,当软件开发人员对某些行为无能为力,那么他极有可能忽略某些编码准则,而首先选择使用一种能让系统在正确运行的情况下工作起来的方法。例如对于上面的函数调用obj.someFunction(),如果其抛出的异常和类库内部运行逻辑相关,而且每次都可能导致这种问题,那么软件开发人员就极有可能使用上面的代码忽略掉该异常。这种问题甚至在一些广为使用的类库中存在着。例如OData4j曾经把取得OData元数据时产生的所有异常都当作是目标服务没有暴露元数据的情况来处理。

  OK,说得有点远了。总结起来就是,一旦一个技术过于简单而且能够处理某个情况,那么软件开发人员将不会仔细研究使用该技术所需要的语境,从而导致滥用。和Checked Exception一样,Soft Delete也是这样的一个例子。

Soft Delete的问题

  那么该数据库模式有什么问题呢?简单地说,那就是太容易出错,而且是隐蔽的错误。试想一下,如果用户需要列出所有的任务,那么在SQL语句中就需要使用WHERE deleted = ‘N’这样的条件。而且该条件几乎在所有处理任务的SQL语句中都要出现。一旦在一个SQL语句中忘记了该条件,那么这极有可能是一个Bug,而且这种Bug有时候还非常隐蔽。例如如果在一个COUNT语句中忘记标示了该条件,而且系统中任务很多,但是被删除的任务很少,那么该Bug可能存在几年都不会被发现。

  同时如果一旦决定需要在系统中大量地使用Soft Delete,那么SQL将变得非常混杂。在统计功能中,我们可能需要筛选出所有包含任务的用例的个数,甚至是包含这些用例的项目的个数,那么我们就需要在SQL中同时标明多个WHERE deleted = ‘N’的条件:

 SELECT COUNT(*)
FROM project, story, task
WHERE … project.deleted = ‘N’ AND story.deleted = ‘N’ AND task.deleted = ‘N’

  那么在调试这些语句的时候,或者查看这些语句的执行计划以进行性能调优的时候,软件开发人员都会发现由于这些条件的引入导致SQL的执行变得非常复杂。

  还有一个问题就是,如果一个系统常常执行对记录的软删除,那么数据库中所记录的数据将比实际所需要记录的数据多得多。这种垃圾数据可能会导致数据库的索引变得很大,甚至可能会由此而严重影响数据库的性能。

  另一个问题则和级联有关。数据库提供了级联操作,在删除一个数据记录的时候,数据库会根据该数据与其它记录之间的关联关系来自动完成对其它关联记录的操作。这也是数据库保持其数据完整性的一种方法。但是一旦用户使用了Soft Delete,那么在对其进行软删除的时候就不会将其从数据库中移除,那么与其关联的那些记录也便不会被数据库移除。也就是说,软件开发人员需要自行完成数据完整性的管理。除此之外,软件开发人员还需要在数据访问层(DAL,Data Access Layer)中完成事务的组织,并且一旦数据库表的定义发生了变化,这些事务组织的逻辑也可能需要进行更改。

Soft Delete的实现

  也正是由于Soft Delete拥有这么多的问题,因此软件开发人员们提出了很多Soft Delete的变通实现方法,大大地减少了开发和维护Soft Delete模式数据的成本。

  一种方法就是利用数据库所提供的View功能。在该方法中,我们需要在数据库中创建一个View,以显示表中deleted值为’N’的各行数据。而在对数据进行操作的各SQL语句中,我们只需要直接操作该View,从而避免了每次都需要在SQL语句中标明WHERE deleted = ‘N’这种条件。

  而另一种方法则是将这些数据分散到两个不同的表中。这两个表中的一个表记录deleted值为’N’的各行数据,而另一个表则记录已经被软删除的deleted值为’Y’的各行数据。而且在具有两个表的情况下,系统甚至可以很较为容易地实现垃圾箱的功能。

  而在某些情况下,我们也可以在数据库设计中借鉴Soft Delete的思路。Soft Delete需要用户自行管理数据库中数据的关联关系。这是一份额外的工作,但也带来了更多的灵活性。

  Rally中的任务删除操作的回滚自然不必多说,垃圾箱功能的添加也将变为非常容易的事。而对于某些自定义的删除逻辑,Soft Delete所带来的灵活性将更为突出。例如在Rally中,如果我们需要实现“删除用户用例时如果用户用例中包含任务,那么这些任务将挪至父用例中”这样一个需求,那么我们就可以在Soft Delete的自定义删除逻辑中完成该功能。

  好了,今天就到这里。

数据库设计中的Soft Delete模式的更多相关文章

  1. MySQL优化技巧之四(数据库设计中的一些技巧)

    1. 原始单据与实体之间的关系 可以是一对一.一对多.多对多的关系.在一般情况下,它们是一对一的关系:即一张原始单据对应且只对应一个实体.在特殊情况下,它们可能是一对多或多对一的关系,即一张原始单证对 ...

  2. SQL语句的使用,SELECT - 从数据库表中获取数据 UPDATE - 更新数据库表中的数据 DELETE - 从数据库表中删除数据 INSERT INTO - 向数据库表中插入数据

    SQL DML 和 DDL 可以把 SQL 分为两个部分:数据操作语言 (DML) 和 数据定义语言 (DDL). SQL (结构化查询语言)是用于执行查询的语法. 但是 SQL 语言也包含用于更新. ...

  3. Django数据库设计中字段为空的方式

    今天在做数据库设计的时候,设计了如下User表,其中我把email和phone字段设置为允许为空: class User(models.Model): username = models.CharFi ...

  4. 范式及其在mysql数据库设计中的应用

    一.什么是范式 1.1.范式:Normal Format,是离散数学的知识,是为了解决数据的存储与优化而提出来的.要求存储数据后,凡是能够通过关系寻找出来的数据,坚决不再重复存储,终极目标是为了减少数 ...

  5. MongoDB数据库设计中6条重要的经验法则

    Part 1 原文:6 Rules of Thumb for MongoDB Schema Design: Part 1 By William Zola, Lead Technical Support ...

  6. 解决Sybase PowerDesigner 数据库设计中 Name 自动填充Code

    在使用 Sybase PowerDesigner 进行数据库设计时,为了理清思路,需要将name改为中文名称,但是这个软件会自动将name填 充为code,可以通过如下配置修改: 选择tools-&g ...

  7. <<MySchool数据库设计优化>> 内部测试

    1) 在SQL Server 中,为数据库表建立索引能够( C ). A. 防止非法的删除操作 B. 防止非法的插入操作 C. 提高查询性能 D. 节约数据库的磁盘空间 解析:索引的作用是通过使用索引 ...

  8. 《MySchool数据库设计优化》内部测试

    1) 在SQL Server 中,为数据库表建立索引能够( C ). A. 防止非法的删除操作 B. 防止非法的插入操作 C. 提高查询性能 D. 节约数据库的磁盘空间 解析:索引的作用是通过使用索引 ...

  9. 数据库设计的误区—>CHAR与VARCHAR

    字符型字段是数据库表中最常见的字段,而字符型字段又分为定长和变长两种.一般来说,VARCHAR类型用于存储内容长度变化较大的数据,CHAR类型用于存储内容长度没有变化或变化不大的数据. 在数据的内部存 ...

随机推荐

  1. Asp.Net Mvc 使用WebUploader 多图片上传

    来博客园有一个月了,哈哈.在这里学到了很多东西.今天也来试着分享一下学到的东西.希望能和大家做朋友共同进步. 最近由于项目需要上传多张图片,对于我这只菜鸟来说,以前上传图片都是直接拖得控件啊,而且还是 ...

  2. 闲来无聊,研究一下Web服务器 的源程序

    web服务器是如何工作的 1989年的夏天,蒂姆.博纳斯-李开发了世界上第一个web服务器和web客户机.这个浏览器程序是一个简单的电话号码查询软件.最初的web服务器程序就是一个利用浏览器和web服 ...

  3. Azure Service Fabric 开发环境搭建

    微服务体系结构是一种将服务器应用程序构建为一组小型服务的方法,每个服务都按自己的进程运行,并通过 HTTP 和 WebSocket 等协议相互通信.每个微服务都在特定的界定上下文(每服务)中实现特定的 ...

  4. netty5 HTTP协议栈浅析与实践

      一.说在前面的话 前段时间,工作上需要做一个针对视频质量的统计分析系统,各端(PC端.移动端和 WEB端)将视频质量数据放在一个 HTTP 请求中上报到服务器,服务器对数据进行解析.分拣后从不同的 ...

  5. 使用HTML5的cavas实现的一个画板

    <!DOCTYPE html><html><head> <meta charset="utf-8"> <meta http-e ...

  6. 【SAP业务模式】之ICS(五):定价配置

    本篇博文讲述ICS业务中的定价配置. 1.定义销售订单类型 目录:SPRO-销售与分销-销售-销售凭证-销售凭证抬头-定义销售凭证类型 事务代码:VOV8 2.定义销售订单类型 目录:SPRO-销售与 ...

  7. 编译器开发系列--Ocelot语言6.静态类型检查

    关于"静态类型检查",想必使用C 或Java 的各位应该非常熟悉了.在此过程中将检查表达式的类型,发现类型不正确的操作时就会报错.例如结构体之间无法用+ 进行加法运算,指针和数值之 ...

  8. Android中的多线程断点下载

    首先来看一下多线程下载的原理.多线程下载就是将同一个网络上的原始文件根据线程个数分成均等份,然后每个单独的线程下载对应的一部分,然后再将下载好的文件按照原始文件的顺序"拼接"起来就 ...

  9. ios 类似微信红点显示功能

    设计思路:给UIView增加一个分类 所有的视图都可以根据需要来进行红点显示 #import <UIKit/UIKit.h> @interface UIView (CHRRedDot) @ ...

  10. 我将系统从Windows迁移至Linux下的点点滴滴

    一.写在最前 由于本人的技术水平有限,难免会出现错误.本文对任何一个人有帮助都是我莫大的荣幸,任何一个大神对我的点拨,我都会感激不尽. 二.技术选型 在2013年8月低的时候,公司中了XXX市场监督局 ...