Чтение столбца CLOB происходит медленно

Надеюсь, кто-то может пролить свет на проблему, которая у меня сейчас возникла с БД Oracle - я уверен, что это что-то простое!!

Мне удалось воссоздать это в образце, так что вот структура БД:

CREATE TABLE MyTable(
    ixMyTable NUMBER,
    clobData CLOB
)
/

CREATE OR REPLACE PACKAGE PKGTEST
AS
    PROCEDURE DoSomething(
        cur_OUT OUT SYS_REFCURSOR
        );
END PKGTEST;
/

CREATE OR REPLACE PACKAGE BODY PKGTEST
AS

PROCEDURE DoSomething(
    cur_OUT OUT SYS_REFCURSOR
)
AS
BEGIN
    OPEN cur_OUT FOR 
        SELECT ixMyTable, clobData
        FROM MyTable;
END;

END PKGTEST;
/

GRANT EXECUTE ON PKGTEST TO TEST_ROLE
/

BEGIN
    FOR i IN 1 .. 7000 LOOP
        insert into mytable values (i, TO_CLOB('123456'));
    END LOOP;
END;
/

Дополнительная информация:

Владелец схемы: TEST_SCHEMA

Пользователь КАРЛ

CARL имеет роль TEST_ROLE

Учитывая настройку базы данных, как указано выше, у меня есть тестовое приложение C#, которое использует стандартный System.Data.OracleClient.OracleCommand и т. д., чтобы выполнить PKGTEST.DoSomething и передать результаты в сетку данных (DevExpress).

Почти уверен, что сетка здесь не имеет значения, поскольку мы сталкиваемся с той же проблемой через С++, используя OTL с открытым исходным кодом (к счастью, не мой отдел).

Ладно, к проблеме....

Время от начала до заполнения сетки составляет ~ 35-40 секунд, ой.

Однако, если я сделаю следующее:

GRANT SELECT ON MyTable TO TEST_ROLE
/

а затем снова выполните запрос, это занимает ~ 5-6 секунд.

Мне кажется, что это как-то связано с привилегиями и т. д., но я не совсем уверен, почему это все еще работает в обе стороны??

Просто чтобы бросить что-то еще в котел, если я изменю процедуру на

SELECT ixMyTable, TO_CLOB(TO_NCLOB(clobData))
FROM MyTable;

Тогда время составляет ~ 5-6 с, с дополнительной привилегией SELECT или без нее.

Любые указатели или прямые решения будут высоко оценены!

Редактировать:

ОС - Vista x86 Business.

Сервер Oracle 10.2.0.1.

Клиент Oracle 10.2.0.3

Редактировать:

Как было предложено, я попытался перейти с MS OracleClient на ODP.NET, и это действительно ускоряется по мере необходимости.

К сожалению, затронутое приложение C# было просто внутренним приложением, которое использовалось для просмотра таблиц/запуска SPROCS и т. д.

Наш основной результат — приложение C++, использующее OTL (http://otl.sourceforge.net/otl3_intro.htm) для доступа к базе данных. На самом деле это не то, что можно изменить в настоящее время, и поэтому я действительно хотел бы понять причины разницы, не разбрасывая волей-неволей необоснованные GRANT SELECT.

Если отсутствие привилегии SELECT вызвало полный сбой, то я, вероятно, мог бы с этим смириться, но отсутствие SELECT, по-видимому, приводит к более медленному маршруту для доступа к данным CLOB.

Я пометил 3 ответа - спасибо за них - но мне действительно не помешала бы причина, поэтому я добавлю к этому награду.

P.S. Мы действительно хотели использовать OCCI с самого начала для нашего C++, но, поскольку Oracle всегда поддерживает более раннюю версию IDE, мы не смогли заставить ее хорошо работать с нашей Visual Studio 2008.


person Carl    schedule 23.01.2009    source источник


Ответы (7)


Вы уверены, что каждый раз читаете блоб с диска, а не читаете из кеша диска второй и последующие?

Я видел эту проблему при тестировании производительности, особенно в Oracle, где первый запуск теста ужасен. Затем с одним незначительным (и кажущимся незначительным изменением) производительность внезапно значительно улучшается. Но на самом деле произошло то, что данные, которые вы запрашиваете, были загружены в кеш и могут быть доступны с 10-кратной или 20-кратной скоростью (память по сравнению с диском).

Правильный способ выполнить этот тест — отскочить базу данных между запусками запроса. Загрузите копию Oracle XE на свой компьютер, если администратор базы данных не позволит вам вернуть тестовый рабочий сервер для этого теста.

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

person Thomas Jones-Low    schedule 23.01.2009
comment
Да, у нас были проблемы с кешем раньше, когда мы тестировали производительность, но это постоянная проблема со скоростью. Даже самый первый вызов с использованием метода TO_CLOB(TO_NCLOB()) выполняется быстро, и я не ожидал, что он попадет в кеш при первом запуске. Спасибо. - person Carl; 24.01.2009

Я бы попробовал odp.net http://www.oracle.com/technology/tech/windows/odpnet/index.html вместо System.Data.OracleClient.

person tuinstoel    schedule 23.01.2009

Следуя приведенному выше предложению, можете ли вы попробовать другой драйвер ODBC или клиентское программное обеспечение. Тот факт, что TO_CLOB(TO_NCLOB()) работает намного быстрее, чем прямой, похоже, не указывает на то, что проблема существует именно здесь.

Сначала я возьму два запроса, прогоню их через SQLDeveloper и получу план объяснения. Это должно дать вам основу для выполнения на стороне Oracle. Добавление преобразований не должно влиять на путь выполнения. Запускайте запросы поочередно и измеряйте время обоих, чтобы увидеть, насколько они быстры. Если нет никакой разницы, я бы предположил, что ваша проблема заключается в клиентском программном обеспечении.

Если мое предположение верно, это также объясняет изменение GRANT SELECT. Клиентское программное обеспечение выполняет какое-то дорогостоящее преобразование CLOB. Предоставление и/или явное преобразование позволяет клиенту избежать этого. Я не знаю почему.

person Thomas Jones-Low    schedule 24.01.2009

Я знаю, что это действительно старый вопрос

Я говорю о своем опыте работы с библиотекой Oracle OCCI, но я считаю, что это применимо к любому клиенту Oracle. Причины, по которым операции чтения blob/clob выполняются медленно, следующие:

  1. Выбор из таблиц со столбцами blob/clob предотвращает предварительную выборку. Вы можете поэкспериментировать с этим в sqlplus, используя set arraysize 1 и set arraysize 5000 для таблиц со столбцами blob/clob и без них. Для таблицы с большим количеством записей и без столбцов blob/clob arraysize будет иметь большое значение. Для таблицы со столбцами blob/clob это не будет иметь большого значения. Таким образом, чтение каждой записи совершит круговой путь к серверу. В более медленных сетях (например, WAN) это имеет большое значение.
  2. Для извлечения столбцов blob/clob требуется отдельное извлечение локатора и данных. Так что это 2 поездки туда и обратно на сервер. Теперь объедините это с # 1, и вы поймете, почему это медленно.

Глянь сюда. https://docs.oracle.com/cd/E18283_01/appdev.112/e10646/oci07lob.htm#CHDDHFAB

Вы можете смягчить это, включив предварительную выборку строк для больших объектов. https://docs.oracle.com/en/database/oracle/oracle-database/12.2/jajdb/index.html?oracle/jdbc/OracleStatement.html

Я никогда не заставлял это работать для OCCI, но реализовал обходной путь, упомянутый здесь (Есть ли способ предварительной выборки данных больших объектов в occi?)

person zrb    schedule 07.02.2021

Я искренне думаю, что проблема здесь в том, что у драйвера OTL и OracleClient возникают проблемы с обработкой CLOB, поступающего из SYS_REFCURSOR. SYS_REFCURSOR являются слабыми, что означает, что сам курсор может возвращать данные любого типа, а это означает, что драйверы должны постоянно возвращаться к базе данных и запрашивать метаданные для курсора (т. е. получать типы данных в курсоре), а затем загружать поток CLOB. , затем загрузите данные. Очевидно, что если пользователь, выполняющий запрос, имеет доступ к запрашиваемым таблицам, драйвер может лучше выполнять работу по извлечению метаданных и возврату правильных данных.

Работает ли изменение хранимой процедуры для возврата надежного ref_cursor?

CREATE OR REPLACE PACKAGE PKGTEST
    AS
    TYPE C_DoSomething IS REF CURSOR RETURN MyTable%ROWTYPE;
    PROCEDURE DoSomething(
        cur_OUT OUT c_DoSomething
     );
END PKGTEST;

Можете ли вы изменить хранимую процедуру, чтобы она выполняла TO_CLOB(), так как это тоже работает?

person Thomas Jones-Low    schedule 27.01.2009
comment
К сожалению, возврат сильного курсора ничего не изменил. Можно было бы изменить хранимую процедуру, но сама по себе TO_CLOB() не работает - она ​​должна быть TO_CLOB(TO_NCLOB()). Я не уверен, что на самом деле делают эти звонки, то есть насколько они дороги? - person Carl; 28.01.2009
comment
Стоимость TO_CLOB(TO_NCLOB()) зависит от набора символов вашей базы данных. Каковы значения NLS_CHARACTERSET и NLS_NCHAR_CHARACTERSET из следующего запроса? выберите * из nls_database_parameters; - person Thomas Jones-Low; 30.01.2009

Что такое NLS_CHARACTERSET и NLS_NCHAR_CHARACTERSET для вашей базы данных. Бежать

select * from nls_database_parameters;

чтобы получить результаты. И какова настройка NLS_LANG для вашего клиентского программного обеспечения?

Это может пролить свет на проблему и поможет ответить на вопрос, насколько затратен вызов TO_CLOB(TO_NCLOB()).

person Thomas Jones-Low    schedule 30.01.2009
comment
NLS_CHARACTERSET — это AL32UTF8, а NLS_NCHAR_CHARACTERSET — это AL16UTF16 — я считаю, что это значения по умолчанию для установки на сервере под управлением Win. Я даже не уверен, можно ли/как изменить NLS_LANG для материала ADO.NET. - person Carl; 02.02.2009
comment
to_NCLOB() преобразует в набор символов UTF-16, затем TO_CLOB() преобразует его обратно в набор символов UTF-8. Таким образом, вызовы не будут иметь большого влияния на производительность. У меня до сих пор нет веских причин почему, но если это сработает... - person Thomas Jones-Low; 03.02.2009

Не все они обязательно связаны напрямую, но вам, вероятно, следует проверить каждый из них на случай, если он связан. Я подозреваю, что это связано с кешированием: как только вы сделали свой первый запрос, вы затем применяете выбор, а затем он выполняется быстро. Посередине вам нужно подпрыгнуть на сервере, чтобы избавиться от кешей, если вы хотите правильно протестировать его производительность. Если вы сделаете этот тест, и он вдруг покажет себя лучше, попробуйте закрепить таблицу в кеше. См. ниже встроенное хранилище clob, так как это, вероятно, будет связано.

Примерно год назад у меня были проблемы с производительностью clob в Oracle 10g. Мы обошли большинство из них, как только наш замечательный администратор баз данных помог нам. На доведение производительности до адекватной скорости ушло около 2 месяцев.

Какую версию Oracle вы используете? В Oracle 10g (ранние версии) были серьезные проблемы с производительностью clob. На самом деле в некоторых случаях было быстрее просто использовать две таблицы и столбец varchar (объедините varchars вместе, и вы получите свой clob). Мы обновились до более поздней версии, и она стала намного лучше.

Кроме того, где хранятся ваши данные? Также есть возможность сохранить clob в самой таблице. В зависимости от того, насколько велики ваши данные, вы можете обнаружить, что это помогает повысить производительность. Если ваши данные хранятся в SAN, стоит также обратить внимание на размер кэша в SAN, а также на размеры блоков. Oracle + SAN может быть немного забавным, когда размеры кэширования неверны.

Другой обходной путь: если вы обнаружите, что постоянство работает медленно или даже медленный доступ, и вы не привязаны к ЦП, заархивируйте данные и сохраните их в большом двоичном объекте. Здесь мы также увидели большой выигрыш в производительности.

Если вы видите проблемы с производительностью (связанные с памятью?) во всем, что обрабатывает clobs, мы обнаружили, что будем воссоздавать объекты как новые строки. Драйверы предварительно создавали строки размером 32 КБ, даже если данные были меньше.

Я действительно задавался вопросом, могут ли системные таблицы быть фрагментированы? Много ли таблиц/схем? А синонимы?

Кроме того, когда вы храните clobs, разве они не сохраняются в одном массивном файле в Oracle? Если я правильно помню, вы должны быть осторожны с фрагментацией; хранилище не освобождается для повторного использования.

Возможно, вы могли бы разместить веб-службу .NET перед своей базой данных? Это может быть одним из вариантов, если вы не можете решить проблемы с производительностью.

person Egwor    schedule 31.01.2009