2012-04-26 2 views
25

У меня есть процедура PL/SQL, которая делает много SUBSTR s по параметру VARCHAR2. Я хотел бы удалить ограничение длины, поэтому я попытался изменить его на CLOB.Производительность SUBSTR на CLOB

Работает хорошо, но производительность страдает, поэтому я провел несколько тестов (на основе these tests с 2005 года).


UPDATE: Я могу воспроизвести это на нескольких различных экземпляров с различными версиями Oracle и другими аппаратными средствами, dbms_lob.substr всегда заметно медленнее, чем substr(CLOB), и намного медленнее, чем SUBSTR(VARCHAR2).

Результаты и тесты Боба в приведенной выше ссылке рассказывают другую историю.

Может ли кто-нибудь объяснить это или хотя бы воспроизвести либо результаты Боба, либо мои результаты? Благодаря!


Результаты тестов:

+000000000 00:00:00. (VARCHAR2)
+000000000 00:00:00. (CLOB SUBSTR)
+000000000 00:00:00. (DBMS_LOB.SUBSTR)

Код испытания:

DECLARE 
    l_text VARCHAR2(30) := 'This is a test record'; 
    l_clob CLOB := l_text; 
    l_substr VARCHAR2(30); 
    t TIMESTAMP; 
BEGIN 
    t := SYSTIMESTAMP; 
    FOR i IN 1..100000 LOOP 
    l_substr := SUBSTR(l_text,1,14); 
    END LOOP; 
    dbms_output.put_line(SYSTIMESTAMP - t || ' (VARCHAR2)'); 

    t := SYSTIMESTAMP; 
    FOR i IN 1..100000 LOOP 
    l_substr := SUBSTR(l_clob,1,14); 
    END LOOP; 
    dbms_output.put_line(SYSTIMESTAMP - t || ' (CLOB SUBSTR)'); 

    t := SYSTIMESTAMP; 
    FOR i IN 1..100000 LOOP 
    l_substr := DBMS_LOB.SUBSTR(l_clob,14,1); 
    END LOOP; 
    dbms_output.put_line(SYSTIMESTAMP - t || ' (DBMS_LOB.SUBSTR)'); 
END; 
+0

Примечание: Тест три - это «14,1», а остальные - «1,14». Я также испытал бы что-то вроде '10000, 5000', поскольку дело в том, что вы хотите сломать 4-килобайтный лимит VARCHAR.Кроме того, поскольку результаты примерно на 75 раз медленнее для VARCHAR, можете ли вы рассмотреть алгоритм, который имеет дело с несколькими VARCHAR? * [Например, нормализованная таблица, в которой одно поле является «sequence_id», показывающее относительное положение этой строки, а другое - VARCHAR] *. Наконец, хотя существует большая * относительная * разница, * абсолютная * разница низкая. Так ли это имеет значение? * [Предварительная оптимизация] * – MatBailie

+1

@ Dems: Спасибо за ваш вклад! «14,1» и «1,14» верны (спасибо Oracle за совместимые API). Я пытаюсь разбить ограничение байта '32767' (PL/SQL, а не SQL), и результаты более или менее одинаковы при использовании текста с этой длиной' (LPAD ('X', 32767, 'X')) '. Я подумал об этом решении с несколькими varchar-таблицами, но я бы хотел его избежать :) И это имеет значение, поскольку процедура называется очень часто, но больше всего мне любопытно, есть ли альтернативы ... –

+1

На моих машинах DBMS_LOB.SUBSTR немного медленнее CLOB_SUBSTR (20%). И оба mooore медленнее, чем varchar2 (в 70 раз медленнее). Я бегу на 11gR2. –

ответ

17

(Lies, проклятые ложь, и эталоны ...)

Я снова побежал ваш тест 10 раз, расширив колонну так, чтобы она была длинной 30 символов, и получила следующие усредненные результаты:

+000000000 00:00:00.011694200 (VARCHAR2) 
+000000000 00:00:00.901000600 (CLOB SUBSTR) 
+000000000 00:00:00.013169200 (DBMS_LOB.SUBSTR) 

Затем я изменил диапазон подстроки в 5,14 (14,5 для DBMS_LOB.SUBSTR) и получил:

+000000000 00:00:00.011731000 (VARCHAR2) 
+000000000 00:00:01.010840000 (CLOB SUBSTR) 
+000000000 00:00:00.011427000 (DBMS_LOB.SUBSTR) 

Затем я изменил диапазон 17,14 (14,17 для DBMS_LOB.SUBSTR) и получил

+000000000 00:00:00.013578900 (VARCHAR2) 
+000000000 00:00:00.964527400 (CLOB SUBSTR) 
+000000000 00:00:00.011416800 (DBMS_LOB.SUBSTR) 

Наконец, я изменил диапазон 25,14 (14,25 для DBMS_LOB.SUBSTR) и получил

+000000000 00:00:00.011210200 (VARCHAR2) 
+000000000 00:00:00.916439800 (CLOB SUBSTR) 
+000000000 00:00:00.013781300 (DBMS_LOB.SUBSTR) 

Мой вывод й при работе с CLOB лучше всего использовать DBMS_LOB.SUBSTR, поскольку он, по-видимому, не имеет эффективного снижения производительности по сравнению с использованием SUBSTR против «нормального» VARCHAR2. SUBSTR против CLOB, похоже, страдает от значительного нарушения производительности. Для записи - OS = HP/UX (вариант Unix), версия Oracle = 11.1, процессор = HP Itanium 2-plex. YMMV.

Делитесь и наслаждайтесь.


И потому что, если это стоит делать это стоит чрезмерно делать, вот еще некоторые результаты с строками расширенной до 32767 символов. Диапазоны подстроек, заданные с каждым набором результатов:

1, 25000 
+000000000 00:00:00.198466400 (VARCHAR2) 
+000000000 00:00:02.870958700 (CLOB SUBSTR) 
+000000000 00:00:00.174490100 (DBMS_LOB.SUBSTR) 

1000, 25000 
+000000000 00:00:00.253447900 (VARCHAR2) 
+000000000 00:00:02.491790500 (CLOB SUBSTR) 
+000000000 00:00:00.193560100 (DBMS_LOB.SUBSTR) 

10000, 25000 
+000000000 00:00:00.217812000 (VARCHAR2) 
+000000000 00:00:02.268794800 (CLOB SUBSTR) 
+000000000 00:00:00.222200200 (DBMS_LOB.SUBSTR) 

В тот же день, такой же вывод.

Cthulhu fhtagn.


(еще раз к бреши, дорогие друзья, еще раз ...)

Re-запускали тесты, изменяя размер CLOB к 3276700, и принимая подстроку от средней начальной на 2475000 для длины 25000 Я получаю:

+000000000 00:00:00.176883200 (VARCHAR2) 
+000000000 00:00:02.069482600 (CLOB SUBSTR) 
+000000000 00:00:00.175341500 (DBMS_LOB.SUBSTR) 

(Обратите внимание, что изменения влияют только на последние два теста).

AND ... такой же результат, другой день.

YMMV.

+0

Спасибо! Я пробовал ваш последний тест (расширяя 'l_text' до 50 символов, поскольку 30 больше не имеет смысла), но результаты все же были сопоставимы с моим оригинальным тестом (после его вызова несколько раз):' 0,006', '0,679',' 1.064'. Какую версию Oracle вы используете? Можете ли вы воспроизвести свои результаты при вызове несколько раз? –

+0

Oracle 11.1. И да, результаты тестов повторяемы. Я изменил петли на итерацию по 1 миллиону раз, затем разделил результирующий временной интервал на 10, чтобы сделать их примерно сопоставимыми с исходными тестами. Я повторно запускал тесты несколько раз, и каждый результат был сопоставим. –

+0

Это странно. Я пробовал свои тесты на трех разных экземплярах (11.2 на Windows, 11.2 на Linux и 10.2 на Linux), и результаты более или менее такие же, как и в моем сообщении. Было бы здорово получить еще несколько результатов от других людей ... –

2

Ран сценарий три раза по следующей системе:

Oracle Database 11g Enterprise Edition Release 11.1.0.7.0 - 64bit Производство

Вот результаты:

+000000000 00:00:00.007787000 (VARCHAR2) 
+000000000 00:00:03.093258000 (CLOB SUBSTR) 
+000000000 00:00:00.340017000 (DBMS_LOB.SUBSTR) 

+000000000 00:00:00.019460000 (VARCHAR2) 
+000000000 00:00:03.302425000 (CLOB SUBSTR) 
+000000000 00:00:00.336915000 (DBMS_LOB.SUBSTR) 

+000000000 00:00:00.007773000 (VARCHAR2) 
+000000000 00:00:03.210619000 (CLOB SUBSTR) 
+000000000 00:00:00.336689000 (DBMS_LOB.SUBSTR) 
+0

Спасибо! Не могли бы вы поделиться какой ОС вы используете, если это имеет значение? –

+0

@PeterLang Обязательно. Redhat Enterprise Linux: 2.6.9-67.ELsmp – tp9

2

Я вижу что на тестах 11gR1 выполнялось гладко для DBMS_LOB.substr, но для 11gR2 функция медленная.

Под моим тестом на Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production на AIX6.

+000000000 00:00:00.009440000 (VARCHAR2) 
+000000000 00:00:00.749113000 (CLOB SUBSTR) 
+000000000 00:00:01.177685000 (DBMS_LOB.SUBSTR) 
2

Я знаю, что это очень старый, но все равно может иметь отношение к людям в старых системах. Это похоже на проблему преобразования типа данных. Основываясь на чем-то, что я заметил, глядя на эффект @ bernhard.weingartner увидел, тип данных смещения и суммы аргументов, по-видимому, имеет огромное значение.

Это работает на 11.2.0.3 на Linux (OEL 5,6), и увеличивается до миллиона итераций только, чтобы сделать различие еще более очевидно:

DECLARE 
    l_text VARCHAR2(30) := 'This is a test record'; 
    l_clob CLOB := l_text; 
    l_substr VARCHAR2(30); 
    t TIMESTAMP; 
BEGIN 
    t := SYSTIMESTAMP; 
    FOR i IN 1..1000000 LOOP 
    l_substr := SUBSTR(l_text,1,14); 
    END LOOP; 
    dbms_output.put_line(SYSTIMESTAMP - t || ' (VARCHAR2)'); 

    t := SYSTIMESTAMP; 
    FOR i IN 1..1000000 LOOP 
    l_substr := SUBSTR(l_clob,1,14); 
    END LOOP; 
    dbms_output.put_line(SYSTIMESTAMP - t || ' (CLOB SUBSTR)'); 

    t := SYSTIMESTAMP; 
    FOR i IN 1..1000000 LOOP 
    l_substr := DBMS_LOB.SUBSTR(l_clob,14,1); 
    END LOOP; 
    dbms_output.put_line(SYSTIMESTAMP - t || ' (DBMS_LOB.SUBSTR with 14,1)'); 

    t := SYSTIMESTAMP; 
    FOR i IN 1..1000000 LOOP 
    l_substr := DBMS_LOB.SUBSTR(l_clob,14.0,1.0); 
    END LOOP; 
    dbms_output.put_line(SYSTIMESTAMP - t || ' (DBMS_LOB.SUBSTR with 14.0,1.0)'); 

    t := SYSTIMESTAMP; 
    FOR i IN 1..1000000 LOOP 
    l_substr := DBMS_LOB.SUBSTR(l_clob,cast(14 as number), cast(1 as number)); 
    END LOOP; 
    dbms_output.put_line(SYSTIMESTAMP - t || ' (DBMS_LOB.SUBSTR with casts)'); 
END; 
/
+000000000 00:00:00.043019000 (VARCHAR2) 
+000000000 00:00:03.671621000 (CLOB SUBSTR) 
+000000000 00:00:05.704337000 (DBMS_LOB.SUBSTR with 14,1) 
+000000000 00:00:00.040097000 (DBMS_LOB.SUBSTR with 14.0,1.0) 
+000000000 00:00:00.040907000 (DBMS_LOB.SUBSTR with casts) 

показать формальный параметры как тип INTEGER, но фактически передача целого числа (или pls_integer, или binary_double) происходит медленно, в то время как явно передается число быстро.

Из вашего исходного вопроса и результатов Боба это выглядит как нечто, что изменилось между 11.1 и 11.2. У меня нет экземпляра 12c для тестирования, поэтому не знаю, изменилось ли оно снова. Является ли это из-за изменения в dbms_lob или более широкого изменения того, как PL/SQL обрабатывает числовые значения по умолчанию, неясно. Я ничего не нашел в MOS, который выглядит актуальным.

Смежные вопросы