前言
简单做个小白文描述。
真删 :
指的就是 彻底地删除, 从数据库表内将数据 进行 移除 delete 。
假删:
指的就是 逻辑上的删除 , 数据库表内, 数据会包含一个标识flag字段 , 例如: status(删除标识,0代表未删除,1代表删除,默认为0) ,执行假删时,只是将数据的 删除标识 status从 0 改 1,update。
正文
真删, 我们就不展开了。
该篇文章其实主要是想聊聊 这个 假删 - 逻辑删除 。
1. 假删的 查询条件问题
既然是对某张表做了假删, 使用 status , 那么在一些业务 查询里面 , 切记切记一定不能忘了,只要使用到 ‘ 假删 ’的数据表, 那么需要带上 status=0 .
2.假删 的验重,数据唯一 问题
验重, 字面意思就是说数据唯一, 而这个数据的唯一保证 ,我们常规地首先是在代码层面做唯一校验,然后在数据库层面也做唯一设置。
为什么说 存在问题?
按照咱们的风格,举个例子来看看:
首先我们现在有一张用于保存企业enterprise信息的表 , 业务上简单理解,需要保证企业唯一,也就是企业名 name唯一(简单举个例子,具体看项目业务需求) 。
enterprise表
保证name的唯一,那么在 企业数据新增时, 按照常规来说我们需要做验重处理。
select id from enterprise where name= ' xxxx' ;
看到这里, 可能已经有小伙伴有不一样的看法了, 这个查询的sql是否要带上删除标识?
select id from enterprise where name= ' xxxx' and status = 0 ;
这里也就是指的是, 被执行了假删的数据,是否还需要加入 验重的校验逻辑里?
其实,不同的业务场景,规则肯定不同。 例如上面例子里面介绍的企业信息表, 那么如果企业信息进行假删后, 后面再一次进行企业信息录入时, 企业的名字肯定还是不会变化的。
那么这时候,如果对所谓的 假删数据 stauts=1 还挂钩上,那么就有些不符合常理了,
也就是说,这里的假删不能影响业务,在业务上其实就是已经真正删除掉了。
所以我们需要带上 status= 0 ,保证现在 表内 有效的 企业信息数据中, 不能包含重复数据!
select id from enterprise where name= ' xxxx' and status = 0 ;
ok,这么一看,假删的验重问题似乎已经考虑齐全了,就这么操作。
但是,目前来说也仅仅是代码层面,调用sql时做的处理。 那往往很多时候, 在数据库表创建的时候也需要对数据唯一字段进行唯一索引的创建。
于是乎我们其实还需要考虑一下以下这个引申出来的问题:
假删 数据表 的 唯一索引的创建 问题
回到刚刚例子中的 enterprise表 :
上边说了下,我们需要保证企业信息唯一,保证企业名 name 的唯一。
那么,按照常规,name字段的 唯一索引(name_index )不可或缺 ,需要创建。
但是,此时这张表其实在验重的时候,进行的逻辑判断 是需要 带上条件 status的。
那么问题就出现了, 对于数据表的真正唯一来说,
其实是 name + status 这两个字段同时保证唯一时, 数据才算是唯一。
也就是说 唯一索引需要升级为唯一组合索引 name字段 & status字段 (name_status_index) .
到这里, 好像是这么一个意思。
这样的唯一组合索引, 跟代码层面的校验规则似乎保持了一致的验重逻辑处理。
保证了吗? 能这样创建吗?
然而并不然!
是的, 这个问题的介绍描述 转折确实有点多了。 不过我就是想这么地表达出 一个 针对一个问题的处理、设计解决方案, 需要一步步每个环节都进行把控。
回到正文,为什么说这样 还不行?? 唯一组合索引 还不行?
直接看 enterprise表 的模拟数据 :
可以看到 test公司 这条数据的status是 1 , 那么也就是进行了 所谓的假删 ,逻辑删除。
那么再次创建时, 我们允许创建, 因为在有效数据内 (status=0) ,数据唯一即可,所以表内数据出现:
这么一看,id为3 的数据 在表内 存在合情合理, 跟id为2 的 被假删数据 ,隔离得很好,没有什么问题。
但是, 再想想,这时候,管理员又把id为 3的数据 进行了 假删。
会出现什么情况?
没错如果我们做了 唯一组合索引 ,那么 再次进行逻辑删除时, 问题出现, 唯一组合索引 不允许 再次 存入 name 为 test公司、status 为 1 的数据,因为之前存在过了。
那这时候,死局了。
破局思考&方案:
想法一:
不设置索引了。 仅仅在代码层面做 stauts=0 的有效数据 验重算了。
带来的不好影响:
数据库层面不做唯一拦截,那就会允许存在有误数据。
想法二:
引入一个新的字段 delete_uuid , 这个字段 作为 唯一组合索引 的成员 ,name_deleteuuid_index.
那么表结构将会变成:
怎么使用呢, 可以看到 现在的组合唯一索引是 name & delete_uuid ,。
新增数据验重时, 查询加上条件 status =0 and delete_uuid !=NULL 保证有效数据内的唯一性。
然后是针对删除操作, 进行逻辑假删时, 把status改为1 ,并 设置 delete_uuid 为NULL。
(估计有些看官,看到这里会有疑惑, 如果数据 name为test公司 ,删除创建再删除,那么产生了两条 name一样且 delete_uuid 也都是NULL 的数据,数据库允许么? 这就需要给有疑惑的看官科普一下NULL 这个东西的知识了,可以看下下面这篇:
Mysql 唯一索引的字段值 允许多个NULL值存在吗 ? Mysql 唯一索引的字段值 允许多个NULL值存在吗_默默不代表沉默-CSDN博客)
回归话题内容,
这么一来,即使出现id 为3 和id为2 这种数据场景,需要进行删除时,也不会因为组合索引的唯一性无法进行删除。
带来的不好影响:
复杂度提高了;
多了一个字段,那么一些查询、删除时,就需要考虑到这个字段的使用;
想法三:
逻辑删除的实现上做改变。 本来常规的是 把status从 0 改为 1 ;
那么改为 从 原表 把执行逻辑删除的数据, 迁移插入到历史表 ,
这样原表可以一直保证 name 字段 唯一索引的校验即可。
带来的不好影响:
既然出现了历史表用于单独存储 假删除的数据, 那么意味着 存储 同个业务数据表其实 一共有两张表 。
我们再回到逻辑删除这个东西的出现, 既然要进行逻辑删除,那么意思就是说,这个数据在一定的情况下,是不允许直接删除,会存在 '恢复' 的情况。
就好比如微信的聊天记录, 删除了,但是微信还是提供了一定条件下做聊天记录的恢复。
那么也就是说,如果有这种恢复的场景 就需要 考虑到两张表的使用操作了。
额外,如果就比如本文中 企业表, 假如 进行逻辑删除的数据需要加入一些数据统计分析业务, 那么 两张表的数据都需要使用到,随之很多业务的实现也会因此而变得不那么直接。
好吧,啰里啰唆地也说了这么多,那就先聊到这了。 大家有什么想法,可以直接评论,都聊一聊假删 这个话题。