一次数据清理引发的教训:缺失外键索引导致 DELETE 性能问题

一次数据清理引发的教训:缺失外键索引导致 DELETE 性能问题



最近在做一次数据清理(data purge)时,我遇到了一个之前没有预料到的问题。 从业务逻辑上看,这只是一个很普通的 DELETE 操作,根据条件删除历史数据而已。

但实际执行时,DELETE 语句几乎卡住了。 没有报错,看起来也在运行,但迟迟没有完成。

问题出在哪里?

在 DBA 的帮助下,我们开始从数据库层面分析这个问题。 最终发现,真正的原因是:

部分外键(Foreign Key)字段缺少对应的索引。

在 Oracle 中,当对父表执行 DELETE 操作时,数据库需要检查子表中是否存在 引用该记录的外键数据。 如果外键列没有索引,Oracle 就只能对子表做全表扫描。

当数据量变大时,这种全表扫描会极大地拖慢 DELETE 操作, 甚至导致长时间锁表,看起来就像 SQL “卡住”了一样。

解决方案

在确认问题后,我们为所有缺失的外键字段补充了索引(涉及多个关联表)。 效果非常明显:

  • DELETE 操作执行速度大幅提升
  • 锁定时间明显缩短
  • 数据库整体负载显著下降

SQL 没变,数据没变,只是补了索引,结果却完全不同。

如何定位缺失的外键索引

DBA 使用下面的 SQL 来检查哪些外键约束没有对应的索引。 以下示例中,schema 名、表名和索引名均已做匿名处理。


WITH fk_constraints AS (
    SELECT
        c.table_name,
        c.constraint_name                                       AS fk_name,
        LISTAGG(c.column_name, ', ')
            WITHIN GROUP (ORDER BY c.position)                  AS fk_columns
    FROM dba_cons_columns c
    JOIN dba_constraints k
        ON k.constraint_name = c.constraint_name
    WHERE k.constraint_type = 'R'
      AND c.owner            = 'APP_SCHEMA_X'
      AND k.owner            = 'APP_SCHEMA_X'
    GROUP BY c.table_name, c.constraint_name
)
SELECT
    fk.table_name,
    fk.fk_name,
    fk.fk_columns,
    idx.index_name              AS existing_index,
    idx.index_columns           AS index_columns,
    'CREATE INDEX ' || fk.fk_name ||
    ' ON ' || fk.table_name ||
    ' (' || fk.fk_columns || ');' AS suggested_fix
FROM fk_constraints fk
LEFT JOIN (
    SELECT
        ic.table_name,
        ic.index_name,
        LISTAGG(ic.column_name, ', ')
            WITHIN GROUP (ORDER BY ic.column_position) AS index_columns
    FROM dba_ind_columns ic
    WHERE ic.index_owner = 'APP_SCHEMA_X'
    GROUP BY ic.table_name, ic.index_name
) idx
    ON idx.table_name = fk.table_name
   AND idx.index_columns LIKE fk.fk_columns || '%'
WHERE idx.index_name IS NULL
ORDER BY fk.table_name, fk.fk_name;

这段 SQL 的作用包括:

  • 列出所有外键约束
  • 检查是否存在以外键列开头的索引
  • 找出缺失索引的外键
  • 生成建议的 CREATE INDEX 语句

经验总结

  • Oracle 并不会自动为外键创建索引
  • 缺失外键索引会严重影响 DELETE / UPDATE 性能
  • 很多问题在数据量小时不明显,数据增长后才暴露
  • “能用”的数据库结构,并不等于“设计良好”

回头看,这些索引本应该在最初的表结构设计或开发阶段就补齐, 但当时被忽略了。

结语

这是一次比较痛苦的线上问题,但也是一次非常有价值的学习经历。

如果你在生产环境中遇到 DELETE 语句执行缓慢、长时间锁表的问题, 第一件事就应该检查外键是否缺少索引

希望这次记录,能帮你少踩一个坑。

❤️ Support This Blog


If this post helped you, you can support my writing with a small donation. Thank you for reading.


Comments

Popular posts from this blog

fixed: embedded-redis: Unable to run on macOS Sonoma

Copying MDC Context Map in Web Clients: A Comprehensive Guide

Reset user password for your own Ghost blog