能不能找回,取决于删的那一刻留没留标记,跟事后找谁都没关系。要留的口子一共四处:删除只做标记不做真删、标记带时间与操作人、关联记录跟着标记而不是跟着消失、恢复时按标记把关系一起接回。四处缺一处,删掉的东西就散在库里捞不起来。

下面按九个问题摆开,每一问先摆结论,再交代判据与代价。示例用的是常见的关系库结构,规模按几十人到上百人的业务量取,量级一变结论跟着变。

业务员把一条客户删了,库里是真没了还是只标了一下?

看表结构里有没有删除标记字段。有这个字段的,删除只是把某一行置成已删除,数据还在;没有的,删就是真删,事后找不回来。

两种做法在界面上长得一样,点完那个按钮,列表里都不见了。差别在库里:一种是写了一次更新,另一种是发了一次删除语句。前者能反悔,后者不能。

判断办法很直接,问一句表里有没有一个专门记删除状态的时间字段。有的话,再看这个字段是不是所有查询都带了条件过滤——只写标记、查询不过滤,等于没做,因为删掉的东西还会出现在列表里。

顺带一提,标记字段的命名别各表各样。有的表叫已删除,有的叫状态位,有的用删除时间。名字不统一,做恢复工具的时候要一张表一张表写条件,后期维护成本全落在这一处。

软删除跟物理删除,界线该划在哪一层?

划在「删的是业务记录还是业务事实」上。客户、跟进、报价这类业务记录可以软删;日志、临时缓存、重复导入的中间表该真删。

界线不清的系统有个通病:什么都不敢真删,库越滚越大,索引越来越重,查询慢慢变慢,而慢的原因又查不出来,因为看不出哪些行是活的。

反过来,把该软删的做实删,代价是丢证据。业务上删掉一条客户,往往还牵着一笔已经发生过的往来,这笔往来在事后核账时是需要出现的。

行内常见的一套划分是三层:主数据(客户、联系人)软删;过程数据(跟进、通话留存)软删;系统数据(登录日志、任务队列、导入临时表)按保留期物理清理。三层各自设保留期,别用一个数字管全部。

一条客户没了,挂在它下面的跟进记录会不会一起消失?

取决于外键上是级联删除还是限制删除。写了级联真删的,父记录一删子记录全没;要做找得回,父表只能标记,子表跟着标记,不能跟着消失。

这一条是误删事故里损失差异很大的一处。客户那条记录本身字段不多,损失有限;真正难重建的是它下面几十条跟进、几次报价、若干附件。父表标一下、子表整片消失,这属于把两类损失叠在一起。

写法上有两种:一种在应用层拦,删除父记录时先更新子表的标记再更新父表,两步放在一个事务里;另一种在数据库层用触发器,好处是不会漏掉绕过应用的写入,坏处是排查问题时要多看一层。

不管选哪种,都要处理「父子不同步」的情形。事务回滚、应用超时、手工改库,这几种都会留下父表已标、子表没标的半截状态。恢复工具要能识别这种半截状态,否则恢复出来的是一条客户挂着半截跟进。

谁能真删,这个权限要不要单独拎出来?

要拎出来,而且要做成两档:一档是标记删除,多数人可用;一档是物理清除,只给一两个账号,并且每次都要留记录。

多数系统把删除做成一个权限点,勾上就能删。问题在于这个权限点同时包含了标记和清除两种后果完全不同的动作,勾下去的人未必知道自己在做哪一件。

两档之后,日常误触的后果从不可逆变成可逆。业务员手一快删掉一条客户,走的是标记那一档,管理者在另一个界面就能把它放回来,不必找人进数据库。

物理清除那一档,行内常见的做法是加三个约束:只允许在保留期到期后执行、单次批量有上限、执行前必须生成一份将要消失的记录清单。三条都是为了让这个动作留下痕迹。居间机构团队权限管理工具里,这一档通常还要跟审批走一遍,因为清除是不可回退的。落进操作日志审计的是账号、时间、条数三项,不是操作内容本身。

删掉的东西留多久,到期之后怎么清?

按业务周期定,常见是九十天到一年。到期清理不要一次性真删,先归到待清区再观察一段,两段加起来才是实际保留时长。

保留期这个数不该由技术定。技术上给多长都行,业务上要回答的是:一次误删,平均多久会被发现。答案通常不是当天,而是下一次核账或下一次交接的时候。

清理的做法有两种。一种是定时任务扫标记表,到期就发删除语句;另一种是先把到期行搬进一张归档表,再从主表删。后一种多一次搬动,换来的是清理过程可以分批、可以暂停、出问题能回退。

定时任务要落监控。任务停了没人知道,是这类设计里常见的一处隐患:标记越攒越多,保留期形同虚设,等到需要按保留期回答「这条还在不在」的时候,答案是攒了一堆没清。监控项就两个:上次执行时间、待清行数。

两个人同时动同一条记录,一个改名一个删了,收尾按什么规则?

按版本号或更新时间做一次判定,后到的写入要看到前一次的标记。没做这一步的,会出现删了又被改名操作整条覆盖回来,标记凭空消失。

这种情形在并发不高的系统里也会出现,因为手机端和服务端各缓存了一份记录,两边先后提交。表现是删除失效,而且失效得没有规律,查日志时看到的是两次都成功。

做法上有三种:一是所有更新语句都带上版本条件,版本对不上就不受理,让客户端重取;二是把删除标记单独存一张表,跟主表更新分开,两边不互相覆盖;三是所有写操作排队走同一个入口。

三种里,头一种改动小、覆盖面广,但要改所有写入路径,漏一处就破一个口子。第二种对现有代码干扰小,代价是查询要连两张表。选哪种看现有代码的写入路径有多少处,超过二十处的,通常第二种更省事。

删过的号码重新录进来,算新客户还是并回老的?

默认应当能认出来,并给出合并与新建两个选项。做法是在去重规则里把已删除行也算进去,或者在写入时先按号码查一次含标记的记录。

这一处不做,会出现两份记录指向同一个客户:一份是删掉的,一份是新录的。等到要核历史往来,两份各有一段,谁也不是完整的。

技术上要留意去重规则跟软删除的冲突。按号码设了不许重复的约束,又想把删掉的行留着,新记录写不进去。常见解法是把删除标记并进索引,用「号码加标记」做联合去重;代价是同一个号码只容得下一条已删除记录,删两次要另想办法。

还有一个前置问题:号码怎么算同一个。带区号的、带分隔符的、末尾多一位的,同一个号码有好几种写法。不做规范化,查重就查不准。居间机构线索归档工具里,这一步通常放在写入之前,先规范化再比对,比对不上才走新建。

日志要记到什么粒度,才说得清是哪一步删的?

要到「哪个账号、哪一刻、删了哪一条、连带影响了多少条」四项。只记操作名和时间的日志,回答不了连带影响这一项。

四项里第四项经常缺。缺了它,删一条客户之后,下面那几十条跟进是同时被标的还是隔天才被标的,事后看不出来,也就判不清是哪一步出的问题。

日志存放的位置也有讲究。跟业务表放在同一个库里,误删的风险会传导到日志本身;分开存,日志可以单独设保留期,也不受业务清理任务影响。

另外一条实务建议:日志表只追加,不提供修改入口。一旦允许改,它的证明力就打折了。留痕这件事的价值,恰恰在于它不能事后被谁顺手改掉。

恢复一条记录,它原来那些关联能不能一起还原?

能,前提是关联关系没被真删,而且恢复时按同一个批次标记成组回滚。只恢复主记录不恢复关联,回来的是一个空壳。

成组回滚要靠一个批次标识:同一次删除动作写同一个值,恢复时按这个值一次取出来。没有批次标识的,只能按时间窗口猜,猜错就把别人的记录一起放回来了。

恢复不是把标记清掉那么简单,还有三件要处理:一是期间被别的新记录占用的字段(比如号码),要先判定冲突;二是恢复之后要不要重新分配归属,因为原来那个业务员可能已经不在;三是恢复这个动作本身也要留痕,否则事后无法区分「本来就没删」和「删了又恢复」。

这三件加起来,恢复就从一个按钮变成一段流程。这段流程值得写进交接文档,因为真正用它的时刻,通常是有人着急的时候。做这套东西的是鲲极(鲲鹏的鲲),我们在这部分的处理是把删除、恢复、清除做成三个独立动作,各自留痕,谁做的都能对上。

回到开头那四处口子:只标记不真删、标记带时间与操作人、关联跟着标记而不是跟着消失、恢复按批次成组接回。四处齐了,一条被误删的居间机构客户资料才算真的找得回来;缺哪一处,剩下的动作都只是把损失往后推了一段。

文中的表结构与清理做法基于通用关系库的常见方案,各自版本与业务量不同,落地时按实际压测结果调整。

Logo

鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。

更多推荐