警示:软删除引发泼天大祸!
原文 The Day Soft Deletes Caused Chaos.
在我作为软件工程师的生涯中,最大的失误莫过于 5 年前合并了一个表面看起来毫无问题的 Pull Request。
简而言之:在生产级系统中,我们不应该使用软删除 —— —这是一个我通过一次严重的失误痛苦学到的教训。那次失误导致同一场音乐会的座位能被无限次地售卖给不同的买家。
所谓软删除,是一种在不真正执行删除操作的情况下保留数据的简便方法,仅通过标记一个「DELETE」标志来实现。
你在表格中增加了一个新的列,这样在进行「删除」操作时,实际上是更新了这条数据的状态,并在进行数据查询时自动排除那些标记为已删除的数据。
尽管这种做法初看起来简单易行,但它隐藏着潜在的风险 —— 如果不小心让一些本不应该被展示的数据泄露出去,后果可能非常严重。
重大事件警示
我在一家活动票务公司工作时,创建了一个与此类似的拉取请求:
在座位预订流程中,顾客可以在结账过程中暂时锁定座位 5 分钟。这个过程是通过一个后台任务实现的,它会在时间到后删除锁定记录,使得座位重新可被预订。我当时的任务是将这种基于「软删除」的锁定机制迁移到一个新的数据库集合中,这个集合专门用来存放被删除的记录。
问题就出在了一行代码上:
这一操作移除了模型上负责处理软删除逻辑的 Paranoia 库,即通过设置一个 deleted_at 字段为当前时间来标记记录为已删除。我当时没有意识到的是,这个库还自动地从 ORM 查询中过滤掉了所有标记为软删除的记录。
由于自动排除软删除记录的机制失效,且数据迁移尚未完成,后台任务开始错误地处理那些已经被标记为「已删除」的座位锁定记录 —— 这导致一些已经成功售出的座位被错误释放,再次对外开放预订!
我永远也忘不了,当我意识到发生了什么事时,那种心沉如石、惶恐不安的感觉。
这导致了在如 Shawn Mendes 音乐会这样的事件中,同一座位被多次出售的情况。放大到多个座位、多个活动,影响简直糟透了。
诚然,软删除机制并非唯一的问题所在。对于这次改动,我本应采取更多的预防措施,比如分步骤进行。CI/CD 流程中的自动化测试本应该能够发现这个错误,但它却还是漏了。万幸的是,这个领域的监控做得很好,问题几乎立刻就被发现并解决了。但是,由此造成的影响和后果还是非常严重的,包括数百个需要退款的重复预订、取消的订单、发送给受影响顾客的道歉邮件,还有一份深夜编写的事故总结报告。
不要使用软删除!
即便在像 GDPR 这样的法规监管下,我们想要保留被删除数据的倾向是可以理解的。开发者可能出于合规、报告、分析的需要,或者仅仅是希望有一个后备方案 —— 以防万一误删数据或需要排查已删除记录的错误。想象这样的场景:一个顾客不慎删除了一张至关重要的发票,或者一个社交媒体用户删去了一条违反规则的评论。在某个宽限期内保留这些被删除的数据看似有其价值。然而,软删除策略实际上带来的问题要比它解决的更多。
复杂性增加
软删除像病毒一样扩散,让数据查询变得异常复杂。虽然应用程序的 ORM 层通常会自动排除被标记为「已删除」的记录,这种看似方便的做法却可能在手动编写复杂 SQL 查询时造成重大疏漏。正如我所经历的那样,你可能会得到不精确的结果,甚至可能暴露敏感信息,或基于片面信息做出错误判断。诚然,创建数据库视图似乎是一种更安全的做法,但这仍然增加了不必要的复杂性和额外的负担。
墨菲定律:任何可能出错的事情都会出错
索引、唯一约束和外键关系也都需要考虑「删除」状态,这使得它们的创建和维护更加复杂。
为 active users 的 email 字段创建唯一索引
即便引入了部分索引,软删除还是可能引起表的膨胀,不利地影响表的大小和性能。在高流量的环境下,这个问题可能更加突出,可能需要进行性能调整或数据分区来保持效率。
数据完整性
通过软删除来处理应用层中的删除操作会失去数据库的一个优势,即数据库会尽力为您保持数据的有效性。
ERROR: delete on table "users" violates foreign key constraint "orders_user_id_fkey" on table "orders" DETAIL: Key (id)=(456) is still referenced from table "orders".
数据库外键违规错误
自行执行参照完整性可能容易出错,并会增加大量的开发和维护开销。
软删除的替代方案
软删除的另一种替代方法是将删除的数据存档到历史表中。这样做仍然很简单,而且可以消除软删除带来的长期责任和维护负担。在删除之前,可以将删除的记录插入到一个单独的表中。
删除前归档数据的事务
如果不想在整个代码库中手动归档数据,最好的办法就是在数据库层建立审计跟踪。《PostgreSQL 数据更改跟踪终极指南》概述了 PostgreSQL 的不同策略。我还推荐大家查看我参与的一个名为 Bemi 的开源项目,该项目旨在通过插入数据库和应用程序(支持大量不同的 ORM,如 Bemi-rails )来简化这一过程,从而自动提供上下文数据变更记录。
底线
远离软删除。虽然它们看上去似乎是处理已删除数据的便捷方法,但实际上,它们就像一个即将爆炸的定时炸弹。这是我几年前吃过的苦头,绝对是你不愿意重蹈覆辙的教训。相比之下,使用历史记录或审计表是一个更为明智的选择。这种做法不仅更整洁、更安全,而且长远来看能够避免无数的麻烦。
💡 更多资讯,请关注 Bytebase 公号:Bytebase

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
更轻松地部署和升级 NGINX Service Mesh
原文作者:Amir Rawdat of F5 原文链接:更轻松地部署和升级 NGINX Service Mesh 转载来源:NGINX 开源社区 NGINX 唯一中文官方社区 ,尽在nginx.org.cn Service mesh(服务网格)正迅速成为云原生堆栈的重要组成部分,尤其是对于Kubernetes平台的用户而言。服务网格提供了关键的可观察性、安全性及流量控制,这样您的 Kubernetes 应用就无需实施这些特性,从而使开发人员能够专注于优化业务逻辑。 NGINX Service Mesh是我们全面集成式的服务网格平台。它提供了服务网格的所有优势,同时利用基于NGINX Plus的数据平面支持 mTLS、流量管理及高可用性等关键特性。 NGINX Service Mesh 1.1.0 版引入了三个关键增强功能,有助于在 Kubernetes 中更轻松地部署并管理我们的生产就绪型服务网格:Helm 支持、物理隔离安装及就地升级。 Helm 支持 NGINX Service Mesh 包括nginx-meshctlCLI 工具,可作为任何 CI/CD 流水线的一部分进行完全可脚...
- 下一篇
钱大妈生鲜如何利用 CCR 实现 Apache Doris 集群读写分离
钱大妈是社区生鲜连锁品牌的开拓者,经过十一年的稳健运营,已成为行业内的领军品牌,截至 2023 年 7 月已全国布局超 30 多座城市,门店总数 3000 余家,服务家庭超 1000 万。近年来,随着业务的高速发展以及门店的快速扩张,钱大妈需要对生鲜产品的采购、销售、库存等数据进行实时监控和分析,以保障食品的新鲜度及品质。同时需要管理众多门店与供应链信息,以了解各区域销售趋势和顾客偏好,从而优化商品结构和库存管理。 在此背景下,钱大妈基于 Apache Doris 搭建了实时数仓,为业务用户提供实时精准的数据查询及分析服务。自引入 Apache Doris 后,钱大妈的报表和 BI 分析能力有了质的飞跃,能够轻松面对海量数据的处理,并实现秒级别的查询响应。凭借 Apache Doris 强大的性能,钱大妈能够实时监控生鲜产品的流通情况,为商品结构的优化和食品新鲜度的保障提供坚实的数据支撑。 读写分离需求背景 在当前的数据仓库架构中,从 ODS 层 - DWD 层 - DWS 层 - ADS 层的数仓分层均在 Apache Doris 内部构建,采用微批调度机制实现数据分层加工处理。随着...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- 2048小游戏-低调大师作品
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- SpringBoot2全家桶,快速入门学习开发网站教程
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- CentOS7,8上快速安装Gitea,搭建Git服务器
- CentOS6,CentOS7官方镜像安装Oracle11G