缓慢的更新声明

时间:2008-11-17 16:36:51

标签: sql oracle

我们正在处理Oracle项目中一个非常慢的更新语句。

这是一个重述问题的小脚本:

drop table j_test;

CREATE TABLE J_TEST
(
  ID  NUMBER(10) PRIMARY KEY,
  C1   VARCHAR2(50 BYTE),
  C2   VARCHAR2(250 BYTE),
  C3   NUMBER(5),
  C4   NUMBER(10)
);

-- just insert a bunch of rows
insert into j_test (id)
select rownum 
from <dummy_table>
where rownum < 100000;

-- this is the statement that runs forever (longer than my patience allows)
update j_test
set C3 = 1,
    C1 = 'NEU';    

在某些环境中,Update-Statement只需要大约20秒,其中一些语句运行几分钟。当使用更多行时,问题会变得更糟。

我们不知道导致此行为的原因,并希望在提出解决方案之前了解正在发生的事情。

有什么想法和建议吗? 谢谢 的Thorsten

6 个答案:

答案 0 :(得分:12)

性能不佳的一个可能原因是行链。您的所有行最初都有列C3和C4 null,然后您将所有行更新为具有值。新数据不适合现有的块,因此Oracle必须将行链接到新块。

如果您事先知道您将这样做,您可以预先分配足够的可用空间,如下所示:

CREATE TABLE J_TEST
(
  ID  NUMBER(10) PRIMARY KEY,
  C1   VARCHAR2(50 BYTE),
  C2   VARCHAR2(250 BYTE),
  C3   NUMBER(5),
  C4   NUMBER(10)
) PCTFREE 40;

...其中PCTFREE指定保留免费更新空间的百分比。默认值为10,这对于此示例来说是不够的,其中行的大小增加或减少一倍(根据我的数据库,平均长度为8到16个字节)。

此测试显示了它的不同之处:

SQL> CREATE TABLE J_TEST
  2  (
  3    ID  NUMBER(10) PRIMARY KEY,
  4    C1   VARCHAR2(50 BYTE),
  5    C2   VARCHAR2(250 BYTE),
  6    C3   NUMBER(5),
  7    C4   NUMBER(10)
  8  );

Table created.

SQL> insert into j_test (id)
  2  select rownum 
  3  from transactions
  4  where rownum < 100000;

99999 rows created.

SQL> update j_test
  2  set C3 = 1,
  3      C2 = 'NEU'
  4  /

99999 rows updated.

Elapsed: 00:01:41.60

SQL> analyze table j_test compute statistics;

Table analyzed.

SQL> select blocks, chain_cnt from user_tables where table_name='J_TEST';

    BLOCKS  CHAIN_CNT
---------- ----------
       694      82034

SQL> drop table j_test;

Table dropped.

SQL> CREATE TABLE J_TEST
  2  (
  3    ID  NUMBER(10) PRIMARY KEY,
  4    C1   VARCHAR2(50 BYTE),
  5    C2   VARCHAR2(250 BYTE),
  6    C3   NUMBER(5),
  7    C4   NUMBER(10)
  8  ) PCTFREE 40;

Table created.

SQL> insert into j_test (id)
  2  select rownum 
  3  from transactions
  4  where rownum < 100000;

99999 rows created.

SQL> update j_test
  2  set C3 = 1,
  3      C2 = 'NEU'
  4  /

99999 rows updated.

Elapsed: 00:00:27.74

SQL> analyze table j_test compute statistics;

Table analyzed.

SQL> select blocks, chain_cnt from user_tables where table_name='J_TEST';

    BLOCKS  CHAIN_CNT
---------- ----------
       232          0

正如您所看到的,对于PCTFREE 40,更新需要27秒而不是81秒,结果表消耗232个没有链接行的块,而不是694个块和82034个链接行!

答案 1 :(得分:3)

试试这个:

insert into j_test (id, C3, C4)
select rownum, 1, 'NEU'
from <dummy_table>
where rownum < 100000;

答案 2 :(得分:3)

您是否真的尝试使用“NEU”字符值更新数字字段C4 NUMBER(10)?

假设您正在尝试执行以下操作:

UPDATE j_test
   SET c3 = 3
 WHERE c1 = 'NEU'

您可能需要在搜索字段上创建索引并分析表以加快更新过程。 如果您真的想要更新整个表格,那么更新速度可能会有所不同。这取决于内存,磁盘访问速度,重做日志创建等。

另外,正如在另一个答案中提到的那样,您需要为使用PCTFREE的更新保留一些空间,否则您将在表中获得大量链接行,这会影响更新速度。

答案 3 :(得分:2)

您确定问题不是来自您将'NEU'插入Number(10)字段吗?在插入之前,它正在从“NEU”到数字(??)进行实时转换。

我的意思是认真,其他答案都是很好且有用的信息,但完整更新中的100k行应该很快。

请记住 - 索引往往会加快选择速度,并减慢插入/更新速度。

答案 4 :(得分:0)

这与问题和我的回答here非常相似。

永远不要更新表格中100%的行。只需按照该链接中的步骤操作即可。将“正确答案”构建为新表,然后将旧表换成旧表。与删除大部分行相同。使用我概述的场景会更有效。

编辑:如果对你们这些人来说这似乎是一个坏主意,那就知道这是Tom Kyte推荐的技术。

答案 5 :(得分:0)

另一种可能性是一个UPDATE正在等待,因为表被锁定(例如,表上还有另一个未提交的UPDATE)
This link有一个显示锁的SQL语句