数据库设计中的Soft Delete模式
最近几天有点忙,所以我们今天来一篇短的,简单地介绍一下数据库设计中的一种模式——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模式的更多相关文章
- MySQL优化技巧之四(数据库设计中的一些技巧)
1. 原始单据与实体之间的关系 可以是一对一.一对多.多对多的关系.在一般情况下,它们是一对一的关系:即一张原始单据对应且只对应一个实体.在特殊情况下,它们可能是一对多或多对一的关系,即一张原始单证对 ...
- SQL语句的使用,SELECT - 从数据库表中获取数据 UPDATE - 更新数据库表中的数据 DELETE - 从数据库表中删除数据 INSERT INTO - 向数据库表中插入数据
SQL DML 和 DDL 可以把 SQL 分为两个部分:数据操作语言 (DML) 和 数据定义语言 (DDL). SQL (结构化查询语言)是用于执行查询的语法. 但是 SQL 语言也包含用于更新. ...
- Django数据库设计中字段为空的方式
今天在做数据库设计的时候,设计了如下User表,其中我把email和phone字段设置为允许为空: class User(models.Model): username = models.CharFi ...
- 范式及其在mysql数据库设计中的应用
一.什么是范式 1.1.范式:Normal Format,是离散数学的知识,是为了解决数据的存储与优化而提出来的.要求存储数据后,凡是能够通过关系寻找出来的数据,坚决不再重复存储,终极目标是为了减少数 ...
- MongoDB数据库设计中6条重要的经验法则
Part 1 原文:6 Rules of Thumb for MongoDB Schema Design: Part 1 By William Zola, Lead Technical Support ...
- 解决Sybase PowerDesigner 数据库设计中 Name 自动填充Code
在使用 Sybase PowerDesigner 进行数据库设计时,为了理清思路,需要将name改为中文名称,但是这个软件会自动将name填 充为code,可以通过如下配置修改: 选择tools-&g ...
- <<MySchool数据库设计优化>> 内部测试
1) 在SQL Server 中,为数据库表建立索引能够( C ). A. 防止非法的删除操作 B. 防止非法的插入操作 C. 提高查询性能 D. 节约数据库的磁盘空间 解析:索引的作用是通过使用索引 ...
- 《MySchool数据库设计优化》内部测试
1) 在SQL Server 中,为数据库表建立索引能够( C ). A. 防止非法的删除操作 B. 防止非法的插入操作 C. 提高查询性能 D. 节约数据库的磁盘空间 解析:索引的作用是通过使用索引 ...
- 数据库设计的误区—>CHAR与VARCHAR
字符型字段是数据库表中最常见的字段,而字符型字段又分为定长和变长两种.一般来说,VARCHAR类型用于存储内容长度变化较大的数据,CHAR类型用于存储内容长度没有变化或变化不大的数据. 在数据的内部存 ...
随机推荐
- 故障重现, JAVA进程内存不够时突然挂掉模拟
背景,服务器上的一个JAVA服务进程突然挂掉,查看产生了崩溃日志,如下: # Set larger code cache with -XX:ReservedCodeCacheSize= # This ...
- mysql每秒最多能插入多少条数据 ? 死磕性能压测
前段时间搞优化,最后瓶颈发现都在数据库单点上. 问DBA,给我的写入答案是在1W(机械硬盘)左右. 联想起前几天infoQ上一篇文章说他们最好的硬件写入速度在2W后也无法提高(SSD硬盘) 但这东西感 ...
- Visual Studio Code 代理设置
Visual Studio Code (简称 VS Code)是由微软研发的一款免费.开源的跨平台文本(代码)编辑器,在十多年的编程经历中,我使用过非常多的的代码编辑器(包括 IDE),例如 Fron ...
- 10个最好用的HTML/CSS 工具、插件和资料库
大家在使用HTML/CSS开发项目的过程中,有使用过哪些工具,插件和库?下面介绍的10种HTML/CSS工具,插件和资料库,是国外程序员经常用到的. Firebug Lite FirebugLite ...
- 如何正确使用日志Log
title: 如何正确使用日志Log date: 2015-01-08 12:54:46 categories: [Python] tags: [Python,log] --- 文章首发地址:http ...
- 【原】FMDB源码阅读(二)
[原]FMDB源码阅读(二) 本文转载请注明出处 -- polobymulberry-博客园 1. 前言 上一篇只是简单地过了一下FMDB一个简单例子的基本流程,并没有涉及到FMDB的所有方方面面,比 ...
- 对百度WebUploader开源上传控件的二次封装,精简前端代码(两句代码搞定上传)
前言 首先声明一下,我这个是对WebUploader开源上传控件的二次封装,底层还是WebUploader实现的,只是为了更简洁的使用他而已. 下面先介绍一下WebUploader 简介: WebUp ...
- java中的内部类
/** * 内部类 ?? * 定义在一个内部的类,被称为内部类. * 内部类里有类体,方法体 * 内部类所在的类,被称为外部类. * --------------------------------- ...
- 网站里加入QQ在线客服
1.开启"QQ在线状态"服务 http://jingyan.baidu.com/article/b24f6c823425a586bfe5da1f.html http://www. ...
- BPM Domino集成解决方案
一.需求分析 Lotus Notes/Domino是IBM的协同办公平台,在国内有广泛的用户. 但由于推出年头较早.采用文档数据库等特点, 导致其流程集成能力弱.统计分析难.不支持移动办公等问题,很多 ...