【问题标题】:oracle delete query taking too much timeoracle删除查询花费太多时间
【发布时间】:2010-11-22 13:37:57
【问题描述】:

我有一个类似的查询

DELETE from tablename where colname = value;

这需要很长时间才能执行。 可能是什么原因?我有一个关于 colname 的索引。

【问题讨论】:

  • 您能否发布您的查询的解释计划(在 SQL*Plus 中,运行“SET AUTOTRACE ON EXPLAIN”然后您的查询)?
  • SQL> set autotrace on explain SQL> delete from tablename where nid = 1250626;已删除 1 行。执行计划------------------------------------------------ ---------- 0 DELETE STATEMENT Optimizer=ALL_ROWS (Cost=2 Card=1 Bytes=48) 1 0 DELETE OF 'tablename' 2 1 INDEX (UNIQUE SCAN) OF 'PK_tablename' (INDEX (UNIQUE) ) (Cost=1 Card=1 Bytes=48) 这是为了删除 1 行。原始查询有一个“>=”条件,直到大约 20 分钟我才能看到完成。即使使用'=',也需要一分钟以上
  • SELECT * FROM tablename WHERE nid = 1250626; 的性能如何?
  • 它很快!执行只需几毫秒。!问题实际上是文森特提到的未索引的外键

标签: sql oracle


【解决方案1】:

可能是您的表与多个表相关,并且行数很大。

【讨论】:

    【解决方案2】:

    对于您的查询需要很长时间的原因可能有多种解释:

    1. 您可能被另一个会话阻止(很可能)。在删除之前,您应该确保没有其他人在锁定行,例如:issue SELECT NULL FROM tablename WHERE colname=:value FOR UPDATE NOWAIT
    2. 可能有一个 ON DELETE TRIGGER 可以做额外的工作,
    3. 检查UNINDEXED REFERENCE CONSTRAINTS 是否指向此表(有一个script from AskTom 可以帮助您确定是否存在此类未索引的外键)。

    【讨论】:

    • 第三个实际上是解决方案:)。现在它在 2 秒内执行!
    • 很高兴为您提供帮助 =)。事实上,我可能应该将第三点放在第一位,因为它最有可能被忽视
    • 链接已损坏。可以修一下吗?
    • @Aaron:我更新了答案,感谢您报告损坏的链接
    • @r3ign 该博客似乎已离线,但我在 AskTom 上找到了一个带有 SQL 查询的线程。我已经更新了我的答案。
    【解决方案3】:

    该索引的选择性如何?如果您的表有 100 万行并且该值达到 15 万行,那么您的索引将毫无用处。事实上,如果它被实际使用,它可能比无用更糟糕。请记住,DELETE 类似于 SELECT 语句:我们可以调整它的访问路径。

    此外,删除会占用大量撤消表空间,因此如果系统使用量很大,您可能会遇到争用。在多用户系统中,另一个会话可能会锁定您要删除的行。

    您有 ON DELETE 触发器吗?你有 ON DELETE CASCADE 外键约束吗?

    编辑:鉴于您所说的一切,尤其是有问题的列是主键,因此您尝试删除单行,如果需要很长时间,则更有可能某个其他进程或用户对该行进行了锁定。 V$LOCK 中是否显示任何内容?

    【讨论】:

    • No On Delete 触发器。表有超过 100 万行。索引的列是主键。否 ON DELETE CASCADE 约束。我还有另一个父表,其中 > 100 万行删除一行只需大约一秒钟!
    【解决方案4】:

    您的表是否包含更多记录?
    是否有一些递归程序(一些嵌套循环等)在数据库服务器上运行?
    如果数据库服务器在不同的机器上,检查网络问题?

    【讨论】:

    • 服务器端没有递归 pgms。没有 n/w 问题
    【解决方案5】:

    所以我将发布我的经验。可能对某人有帮助。

    查询

    delete from foo
    where foo_id not in ( 
      select max(foo_id) from foo group by foo_bar_id, foo_qux_id
    );
    

    耗时 16 秒。从表foo的2300条记录中删除1700条记录。

    我按照其他答案中的指示检查了外键上的所有索引。这没有帮助。

    解决方案:

    将查询更改为

    delete from foo
    where foo_id in ( 
      select foo_id from foo
      minus
      select max(foo_id) from foo group by foo_bar_id, foo_qux_id
    );
    

    我已将not in 更改为in 并使用minus 来获得正确的结果。

    现在查询在 0.04 秒内执行。

    【讨论】:

      【解决方案6】:

      Oracle 和 mysql 有很大的区别:

      Oracle 不会自动为外键创建索引,但 mysql 会。然后,如果您有一些可以对其执行删除命令的父表,那么您必须在子表中的外键上创建索引,否则如果子表有很多行,父表上的删除命令将非常慢,因为它必须surf 子表的所有记录删除任何父记录。

      那么当你想从Oracle数据库的父表中删除时要小心。

      【讨论】:

        【解决方案7】:

        如果有帮助,请发布我的经验。

        我遇到了同样的问题(删除或更新时间过长),所以我的直觉反应是寻找并杀死“卡住”的查询。但是我用于查找这些的正常 SQL 没有显示任何内容。经过一番挖掘,我转而寻找“锁定的表”,并且能够找到一个不活动的会话,该会话显然正在积极阻止我试图改变的表。这是我使用 (Oracle 12c) 来查找阻止我的表更新的要终止的会话的查询:

        SELECT
           c.owner,
           c.object_name,
           c.object_type,
           b.sid,
           b.serial#,
           b.status,
           b.osuser,
           b.machine
        FROM
           v$locked_object a ,
           v$session b,
           dba_objects c
        WHERE
           b.sid = a.session_id
        AND
           a.object_id = c.object_id;
        

        然后使用上述查询中的 sid 和序列号:

        ALTER SYSTEM KILL SESSION 'SID, SERIAL#' to end those sessions and free the locks.
        

        我不是 dba,这可能是不好的做法,但它对我有用。

        【讨论】:

        • 谢谢,这是我的问题。我有一个锁定表的非活动会话。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-01-08
        • 2019-11-14
        • 2012-09-03
        • 2013-07-11
        • 2017-09-24
        • 1970-01-01
        相关资源
        最近更新 更多