Медленное обновление заявления - PullRequest
3 голосов
/ 17 ноября 2008

мы имеем дело с очень медленным оператором обновления в проекте 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 занимает всего около 20 секунд, а в некоторых - несколько минут. При использовании большего количества строк проблема усугубляется.

Мы не знаем, что является причиной такого поведения, и хотели бы понять, что происходит, прежде чем предлагать решение.

Есть идеи и предложения? Спасибо Торстен

Ответы [ 6 ]

12 голосов
/ 17 ноября 2008

Одной из возможных причин низкой производительности является цепочка строк. Все ваши строки изначально имеют столбцы 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 цепочками строк!

3 голосов
/ 17 ноября 2008

Вы действительно пытаетесь обновить числовое поле C4 NUMBER (10) символьным значением 'NEU'?

Предполагается, что вы пытаетесь сделать следующее:

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

Возможно, вам потребуется создать индекс в поле поиска и проанализировать таблицу, чтобы ускорить процесс обновления. Если вы действительно пытаетесь обновить всю таблицу, скорость обновления может отличаться. Это зависит от памяти, скорости доступа к диску, создания журналов повторов и т. Д.

Кроме того, как уже упоминалось в другом ответе, вам необходимо зарезервировать некоторое пространство для обновлений с использованием PCTFREE, в противном случае вы получите много строк в таблице, которые влияют на скорость обновления.

3 голосов
/ 17 ноября 2008

Попробуйте это:

insert into j_test (id, C3, C4)
select rownum, 1, 'NEU'
from <dummy_table>
where rownum < 100000;
2 голосов
/ 17 ноября 2008

Вы уверены, что проблема не в том, что вы вставляете NEU в поле Number (10)? Он выполняет преобразование на лету из числа «NEU» в число (??) перед вставкой.

Я имею в виду серьезно, остальные ответы - полезная и полезная информация, но 100к строк при полном обновлении должны быть быстрыми.

Помните - индексы, как правило, ускоряют выбор и замедляют вставки / обновления.

0 голосов
/ 17 ноября 2008

Другая возможность состоит в том, что одно ОБНОВЛЕНИЕ ожидает, потому что таблица заблокирована (например, есть другое незафиксированное ОБНОВЛЕНИЕ в таблице)
Эта ссылка содержит оператор SQL для отображения блокировок

0 голосов
/ 17 ноября 2008

Это очень похоже на вопрос и мой ответ здесь .

Никогда не обновляйте 100% строк в таблице. Просто следуйте процедуре по этой ссылке. создайте «правильный ответ» как новую таблицу, а затем замените эту новую таблицу на старую. То же самое с удалением большого процента строк. Просто гораздо эффективнее использовать описанный мной сценарий.

РЕДАКТИРОВАТЬ: Если это кажется плохой идеей для некоторых из вас, просто знайте, что это техника, рекомендованная Томом Кайтом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...