Oracle 10g маленький Blob или Clob не хранится в строке? - PullRequest
9 голосов
/ 09 сентября 2011

Согласно документам, которые я прочитал, хранилище по умолчанию для CLOB или BLOB является встроенным, что означает, что, если его размер меньше приблизительно 4 КБ, он будет храниться в таблице.

Но когда я проверяю это на фиктивной таблице в Oracle (10.2.0.1.0), производительность и ответ от Oracle Monitor (от Allround Automations) предполагают, что он удерживается вне таблицы.

Вот мой тестовый сценарий ...

create table clobtest ( x int primary key, y clob, z varchar(100) )  
;
insert into clobtest 
   select object_id, object_name, object_name  
   from all_objects where rownum < 10001  
;
select COLUMN_NAME, IN_ROW 
from user_lobs 
where table_name = 'CLOBTEST'  
;

Это показывает: ДА ДА (предполагая, что Oracle будет хранить сгусток в строке)

select x, y from CLOBTEST where ROWNUM < 1001 -- 8.49 seconds  
select x, z from CLOBTEST where ROWNUM < 1001 -- 0.298 seconds  

Так что в этом случае значения CLOB будут иметь максимальную длину 30 символов, поэтому всегда должны быть встроенными. Если я запускаю Oracle Monitor, он показывает LOB.Length, за которым следует LOB.Read () для каждой возвращаемой строки, что снова указывает на то, что значения clob хранятся вне таблицы.

Я также пытался создать такую ​​таблицу

create table clobtest 
    ( x int primary key, y clob, z varchar(100) ) 
    LOB (y) STORE AS     (ENABLE STORAGE IN ROW)  

но получил точно такие же результаты.

Есть ли у кого-нибудь предложения, как я могу заставить (убедить, поощрить) Oracle хранить значение clob in-line в таблице? (Я надеюсь добиться того же времени отклика, что и при чтении столбца varchar2 z)

ОБНОВЛЕНИЕ: если я запускаю этот SQL

select COLUMN_NAME, IN_ROW, l.SEGMENT_NAME, SEGMENT_TYPE, BYTES, BLOCKS, EXTENTS 
from user_lobs l 
      JOIN USER_SEGMENTS s
       on (l.Segment_Name = s. segment_name )
where table_name = 'CLOBTEST'  

тогда я получаю следующие результаты ...

Y   YES SYS_LOB0000398621C00002$$   LOBSEGMENT  65536   8   1  

Ответы [ 5 ]

9 голосов
/ 06 января 2012

Поведение Oracle LOB выглядит следующим образом.

LOB хранится в строке, когда:

(
  The size is lower or equal than 3964
  AND
  ENABLE STORAGE IN ROW has been defined in the LOB storage clause
) OR (
  The value is NULL
)

LOB хранится вне строки, когда:

(
  The value is not NULL
) AND (
  Its size is higher than 3964
  OR
  DISABLE STORAGE IN ROW has been defined in the LOB storage clause
)

Теперь это не единственная проблема, которая может повлиять на производительность.

Если, наконец, большие объекты не сохраняются встроенными, поведение Oracle по умолчанию состоит в том, чтобы избегать их кэширования (в буферном кеше кэшируются только встроенные большие объекты с другими полями строки). Чтобы указать Oracle также кэшировать не встроенные большие объекты, следует использовать опцию CACHE, когда определен большой объект.

Поведение по умолчанию: ENABLE STORAGE IN ROW и NOCACHE, что означает, что маленькие большие объекты будут встроены, большие большие не будут (и не будут кэшироваться).

Наконец, существует также проблема производительности на уровне протокола связи. Типичные клиенты Oracle будут выполнять 2 дополнительных обращения к каждому LOB для их извлечения: - один для получения размера LOB и выделения памяти соответственно - один для извлечения самих данных (при условии, что LOB невелик)

Эти дополнительные обходы выполняются, даже если для получения результатов используется интерфейс массива. Если вы извлекаете 1000 строк и размер вашего массива достаточно велик, вы будете платить за 1 поездку туда и обратно, чтобы извлечь строки, и 2000 поездок туда и обратно, чтобы получить содержимое больших объектов.

Обратите внимание, что не зависит от того, хранится ли LOB в строке или нет. Это совершенно разные проблемы.

Для оптимизации на уровне протокола Oracle предоставил новый глагол OCI для выборки нескольких больших объектов за один прием (OCILobArrayRead). Я не знаю, существует ли что-то подобное с JDBC.

Другой вариант - привязать большой объект на стороне клиента, как если бы это был большой RAW / VARCHAR2. Это работает только в том случае, если можно определить максимальный размер большого объекта (поскольку максимальный размер должен быть указан во время привязки). Этот трюк позволяет избежать лишних разбивок: большие объекты обрабатываются как RAW или VARCHAR2. Мы часто используем его в наших интенсивных приложениях LOB.

Как только количество обращений было оптимизировано, размер пакета (SDU) может быть изменен в конфигурации сети, чтобы лучше соответствовать ситуации (то есть ограниченному количеству больших обращений). Это имеет тенденцию уменьшать события ожидания «SQL * Net больше данных для клиента» и «SQL * Net больше данных от клиента».

1 голос
/ 08 марта 2012

Если вы «надеетесь достичь того же времени отклика, что и при чтении столбца z varchar2», то в большинстве случаев вы будете разочарованы. Если вы используете CLOB, я думаю, вам нужно хранить более 4000 байтов, верно? Тогда, если вам нужно прочитать больше байтов, это займет больше времени.

НО, если у вас есть случай, когда да, вы используете CLOB, но вас интересуют (в некоторых случаях) только первые 4000 байтов столбца (или меньше), тогда у вас есть шанс получить аналогичную производительность , Похоже, что Oracle может оптимизировать поиск, если вы используете что-то вроде DBMS_LOB.SUBSTR и предложение ENABLE STORAGE IN ROW CACHE с вашей таблицей. Пример:

CREATE TABLE clobtest (x INT PRIMARY KEY, y CLOB)
LOB (y) STORE AS (ENABLE STORAGE IN ROW CACHE);

INSERT INTO clobtest VALUES (0, RPAD('a', 4000, 'a'));
UPDATE clobtest SET y = y || y || y;
INSERT INTO clobtest SELECT rownum, y FROM all_objects, clobtest WHERE rownum < 1000;

CREATE TABLE clobtest2 (x INT PRIMARY KEY, z VARCHAR2(4000));

INSERT INTO clobtest2 VALUES (0, RPAD('a', 4000, 'a'));
INSERT INTO clobtest2 SELECT rownum, z FROM all_objects, clobtest2 WHERE rownum < 1000;

COMMIT;

В моих тестах на блоке 10.2.0.4 и 8K эти два запроса дают очень похожую производительность:

SELECT x, DBMS_LOB.SUBSTR(y, 4000) FROM clobtest;
SELECT x, z FROM clobtest2;

Пример из SQL * Plus (я запускал запросы несколько раз, чтобы удалить физические операции ввода-вывода):

SQL> SET AUTOTRACE TRACEONLY STATISTICS
SQL> SET TIMING ON
SQL>
SQL> SELECT x, y FROM clobtest;

1000 rows selected.

Elapsed: 00:00:02.96

Statistics
------------------------------------------------------
          0  recursive calls
          0  db block gets
       3008  consistent gets
          0  physical reads
          0  redo size
     559241  bytes sent via SQL*Net to client
     180350  bytes received via SQL*Net from client
       2002  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
       1000  rows processed

SQL> SELECT x, DBMS_LOB.SUBSTR(y, 4000) FROM clobtest;

1000 rows selected.

Elapsed: 00:00:00.32

Statistics
------------------------------------------------------
          0  recursive calls
          0  db block gets
       2082  consistent gets
          0  physical reads
          0  redo size
      18993  bytes sent via SQL*Net to client
       1076  bytes received via SQL*Net from client
         68  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
       1000  rows processed

SQL> SELECT x, z FROM clobtest2;

1000 rows selected.

Elapsed: 00:00:00.18

Statistics
------------------------------------------------------
          0  recursive calls
          0  db block gets
       1005  consistent gets
          0  physical reads
          0  redo size
      18971  bytes sent via SQL*Net to client
       1076  bytes received via SQL*Net from client
         68  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
       1000  rows processed

Как вы можете видеть, согласованные значения гораздо выше, но циклические обращения и байты SQL * Net практически идентичны в последних двух запросах, и это, очевидно, имеет большое значение во времени выполнения!

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

Удачи!

Приветствия

0 голосов
/ 07 августа 2013

Это ключевая информация (как читать большой объект без дополнительных циклических переходов), которая недоступна в документации Oracle. Я думаю:

Другой вариант - привязать большой объект на стороне клиента, как если бы онбыл большой RAW / VARCHAR2.Это работает только в том случае, если можно определить максимальный размер большого объекта (поскольку максимальный размер должен быть указан во время привязки).Этот трюк позволяет избежать лишних разбивок: большие объекты обрабатываются как RAW или VARCHAR2.Мы часто используем его в наших приложениях, интенсивно работающих с большими объектами.

У меня была проблема с загрузкой простой таблицы (несколько ГБ) с одним столбцом больших двоичных объектов (14 КБ => тысячи строк), и я исследовал егодолгое время пробовал много настроек хранилища больших объектов (DB_BLOCK_SIZE для нового табличного пространства, спецификации хранилища больших объектов - CHUNK), настроек sqlnet.ora, атрибутов предварительной выборки клиента, но это (обрабатывать BLOB как LONG RAW с OCCI ResultSet-> setBufferData на стороне клиента)было самым важным (убедить оракула немедленно отправить столбец BLOB-объекта, не отправляя сначала локатор лобов и не загружая каждый лоб отдельно в зависимости от локатора лобов.

Теперь я могу получить пропускную способность даже ~ 500 Мбит / с (со столбцами <3964 Б)Наш двоичный объект размером 14 КБ будет разделен на несколько столбцов, поэтому он будет храниться в строке для получения почти последовательных чтений с жесткого диска. С одним BLOB-объектом объемом 14 КБ (один столбец) я получаю ~ 150 Мбит / с из-за непоследовательных операций чтения (iostat).: небольшое количество объединенных запросов на чтение). </p>

ПРИМЕЧАНИЕ : не забудьте установитьтакже размер / длина предварительной выборки lob:

err = OCIAttrSet (сессия, (ub4) OCI_HTYPE_SESSION, (void *) & default_lobprefetch_size, 0, (ub4) OCI_ATTR_DEFAULT_LOBPREFET * ERPEET1018 * Но я не знаю, как можно добиться такой же пропускной способности при получении с помощью разъема ODBC.Я пытался безуспешно.

0 голосов
/ 09 сентября 2011

Действительно, оно хранится в строке. Вы, вероятно, имеете дело с простыми издержками использования LOB вместо varchar. Ничто не бесплатно. БД, вероятно, не знает заранее, где найти строку, поэтому она, вероятно, все еще «следует за указателем» и выполняет дополнительную работу на случай, если большой объект большой. Если вы можете обойтись с varchar, вы должны. Даже старые хаки, такие как 2 varchars для работы с 8000 символов, могут решить вашу бизнес-задачу с более высокой производительностью.

LOBS медленны, сложны для запроса и т. Д. На положительном уровне они могут быть 4G.

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

Предупреждение. В какой-то момент сетевой трафик на ваш компьютер замедляет выполнение подобных тестов.

Сведите к минимуму это, обернув счет, это изолирует работу на сервере:

выберите количество (*) из (выберите x, y из самого близкого значения, где rownum <1001) </p>

Подобных эффектов можно добиться с помощью «set autot trace», но при этом также будут отслеживаться накладные расходы.

0 голосов
/ 09 сентября 2011

Применительно к объектам CLOB и BLOB существует два направления:

  1. Значение LOB может храниться в другом сегменте базы данных, чем остальная часть строки.

  2. Когда вы запрашиваете строку, в результирующем наборе содержатся только не-большие объекты, а для доступа к LOB-полям требуется одно или несколько дополнительных циклов между клиентом и сервером (для каждой строки!).

Я не совсем знаю, как вы измеряете время выполнения, и я никогда не использовал Oracle Monitor, но в первую очередь на вас может повлиять вторая косвенность. В зависимости от используемого клиентского программного обеспечения, можно сократить количество поездок в оба конца. Например. при использовании ODP.NET параметр называется InitialLobFetchSize .

Обновление:

Чтобы определить, какое из двух указаний является релевантным, вы можете выполнить свой запрос большого объекта с 1000 строками дважды. Если время значительно уменьшается с первого до второго запуска, это косвенное значение 1. При втором запуске кэширование окупается, и доступ к отдельному сегменту базы данных больше не актуален. Если время остается примерно одинаковым, это второе косвенное обращение, а именно циклические переходы между клиентом и сервером, которые нельзя улучшить между двумя запусками.

Время более 8 секунд для 1000 строк в очень простом запросе указывает на его косвенность 2, потому что 8 секунд для 1000 строк не могут быть объяснены доступом к диску, если ваши данные не очень разбросаны и ваша дисковая система находится под большой нагрузкой .

...