暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

【手写数据库核心揭秘系列】第117节 DELETE执行器,三大删除策略,你选择那一种?

开源无限 2025-08-12
170


💻 深耕数据库内核架构设计与开发十余年,曾主导多款高性能分布式数据库内核研发,攻克高并发、低延迟等核心技术难题。现倾力打造《从零手写数据库》系列教程,首次系统性公开数据库内核源码级实现细节!
🚀 从存储引擎、查询优化到分布式事务,手把手拆解核心模块;从语法解析树构建到执行计划生成,逐行代码还原设计精髓
🌟 无论你是数据库开发者、系统架构师,还是对底层技术充满好奇的极客,这里都有你想要的“硬核干货”!点击关注,与行业老兵共同探索数据库技术的星辰大海!
公众号:开源无限

一、概述 


数据库的 DELETE 操作远非简单的数据擦除,其底层实现深刻影响着系统的事务性能、存储效率和并发能力。

二、删除原理 


目前有三种主流的实现方式:

  • 原地物理删除(In-Place Deletion)
  • 逻辑标记删除(Logical Delete)
  • 日志结构化删除(Log-Structured Delete)


2.1 原地物理删除  

删除表中的某一行数据,大家立马想到就是物理删除。

根据指定的数据行关键字,找到对应的表数据块,将该行数据擦除,同时还需要将当前数据块中后面的数据移动填充空隙,另外如何有索引时,也要一并删除索引项。这一系列操作之后,该行数据完全从表中被擦除了,而且占用的空间也释放了。

原地物理删除是一种常规的删除方式,一次彻底处理,不拖泥带水。


2.2 逻辑标记删除  

通过数据行头的元数据中的成员,来标记该行数据“逻辑删除”,此时删除动作就执行完成了,而真正的物理清理,由另外的后台任务异步完成。逻辑标记删除的方式,常常与MVCC结合使用。

逻辑标记删除,其实将删除执行分成了两部分:逻辑删除和物理清理,在DELETE命令执行期只做了前者,而后者由后台进程默默完成。

相比原地物理删除,逻辑标记删除减少了DELETE命令执行期间占用数据块的时间,也就是锁块时间大大减少;同时数据块中存在冗余数据,增加了查询的负担。


2.3 日志结构化删除  

基于LSM-Tree存储方式,删除操作并不是直接删除数据,而是通过一种叫“墓碑记录”的特殊数据来标识数据的删除。

当删除数据行时,在日志中追加写入 Tombstone 标记(墓碑记录),依赖后台 Compaction任务物理清理数据。日志结构化删除方式适用于大量写入的场景。

三、DELETE执行器 


为DELETE删除命令增加执行器,参数为物理执行计划树。

    int ExecDeleteStmt(Node *rootNode)
    {
        int rowNum = 0;
        Node *resultList = NULL;
        QueryStmt *queryPlan = (QueryStmt*)rootNode;


        do {        
            resultList = ExecNodeProc(queryPlan->plan);
            if(resultList == NULL)
                break;


            rowNum += 1
        }while(resultList != NULL);


        return rowNum;    
    }

    在执行器中遍历执行计划节点,递归调用各节点的处理函数,每次删除一行数据,直到所有数据处理完成,返回处理数据的行数统计。


    3.1 执行算子   

    执行计划树的顶层是删除节点,它对应的处理函数来负责从数据表中删除符合条件的数据行。

      Node* ExecDeleteTbl(Node *planNode)

      删除执行算子的实现分为三部分:

      1. 一是从下层节点中获取符合条件的数据行,每次返回一行,当所有数据处理完时,返回值为空。
            result = ExecNodeProc(modifyNode->subNode);
            if (result == NULL)
            {
                return result;
        }
        1. 二是从结果列表中,找到待删除数据表中的结果行,并记录当前数据行存储的位置。

        执行计划树的叶子节点是扫描节点,扫描信息中当前的扫描位置,也就是当前数据行的存储位置。

              delTup = GetResultNodeFromList(modifyNode->tblInfo, (List*)result);
              if(delTup == NULL)
              {
                  return NULL;
          }
          scanPos = (ScanNode*)((TableRefInfo*)(delTup->tblRefInfo))->scanPos;
          1. 三是根据存储位置,在数据表文件中物理删除本条数据行。
                ret = RemoveTupleChain(scanPos->scanInfo->pageNum, scanPos->scanInfo->item_offset - sizeof(ItemData), delTup->rel);

            数据行的存储有可能是跨数据块存储,在UPDATE命令中已经实现了清理数据行的函数,此处直接调用即可。

            四、总结 


            本次删除实现采用原理物理删除的方式,本节实现位于exam_72目录下的executor.c文件中。


            🌟 点赞收藏,分享给身边的技术伙伴,关注我们,持续获取数据库内核开发的硬核干货!一起从源码级实现到分布式架构,解锁数据库技术的每一个核心细节!🚀

            【往期精彩推荐】

            【手写数据库核心揭秘系列】DELET命令的物理执行树全貌

            【手写数据库核心揭秘系列】第99节 多表联合查询演示,高复杂度查询SQL,四张数据表混合联合类型的查询

            【手写数据库核心揭秘系列】第94讲 带过滤条件的查询演示,仓储信息的清晰可见



            文章转载自开源无限,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

            评论