Поиск блока в буферном кэше, cache buffers chains. Параметры _db_block_hash_buckets , _db_block_hash_latches

dbstalker, 20 июня

Предположим, что нам нужно провести определенную работу над конкретным блоком. Из словаря базы данных (Data Dictionary) мы знаем его адрес (DBA – data block address) – file#+block#. Есть ли этот блок в кэше, или ораклу нужно читать его с диска? Как такую процедуру организовал ORACLE? Поделюсь своими представлениями об этом.

Все буфера в буферном кэше распределены по двунаправленным связным спискам cache buffer chains (hash chains, Hash Buckets). Это утверждение справедливо и для пустых, неиспользуемых на данный момент буферов. Эти списки предназначены для решения вопроса: «Есть ли необходимый блок в кэше?». В какой именно список попадет буфер, зависит от хэш-таблицы, которая представляет собой массив hash bucket-ов. В этой таблице по каждому hash bucket хранится адрес начала списка «cache buffer chain», который содержит указатели на заголовки буферов или нули, если список пустой. Эта таблица создается при открытии базы данных – при открытии базы создаются hash buckets.

Чем длиннее список по определенному бакету, тем продолжительнее поиск по нему. Поэтому хорошо когда списков(соответственно записей в хэш-таблице) много. Количество записей в этой таблице по умолчанию равно prime(db_block_buffers*2) (до версии 8 было равно db_block_buffers/4). Использование простого числа объясняется желанием избежать аномалий при использовании хэш-функции для построения хэш-таблицы. Количество бакетов значительно увеличено, чтобы уменьшить слишком большое времени ожидания освобождения защелок при поиске блока, а также уменьшения количества CR блоков в конкретной цепочке. Значение параметра инициализации _db_block_hash_buckets определяет точный размер этой таблицы. Смотрите:

select KSPPINM,KSPPDESC,KSPPSTVL,KSPPSTDVL,KSPPSTDF from X$KSPPSV a,x$ksppi b
where a.indx=b.indx and KSPPINM='_db_block_hash_buckets'

select KSPPINM,KSPPDESC,KSPPSTVL,KSPPSTDVL,KSPPSTDF from X$KSPPSV a,x$ksppi b
where a.indx=b.indx and KSPPINM like '_db_block_buffers'

Во время поиска и манипулирования списки hash chains защищаются защелками cache buffers chains latch. Их количество равно по умолчанию степени двойки (параметр _db_block_hash_latches):

for small caches  is less than 2052 buffers
    2 ^ trunc(log(2, db_block_buffers - 4) - 1) 

for larges caches  is bigger than 131075 buffers  

    2 ^ trunc(log(2, db_block_buffers - 4) - 6)

If db_block_buffers is between 2052 and 131075 buffers, 
 1024 latches .

Значение этого параметра можно посмотреть таким образом:

select power(2, trunc(log(2,(select KSPPSTVL from X$KSPPSV a,x$ksppi b where a.indx=b.indx and KSPPINM like '_db_block_buffers') - 4) - 6)) from dual


select KSPPINM,KSPPDESC,KSPPSTVL,KSPPSTDVL,KSPPSTDF from X$KSPPSV a,x$ksppi b
where a.indx=b.indx and KSPPINM like '_db_block_hash_latches'

Из формул видно, что при увеличении буфера растет количество защелок. (Количество процессоров на сервере не меняется, поэтому конкуренция за защелки cache buffers chains все равно будет возрастать)

Как же формируются hash buckets?

Для каждого буфера, исходя из DBA (10 бит - file#, 22 бита - block#) содержащегося в нем блока, рассчитывается индекс бакета в хэш-таблице или другими словами номер записи в хэш-таблице по следующей формуле (на самом деле формула сложнее):

bucket#=mod(DBA,_db_block_hash_buckets)  

Исходя из полученного значения, блок попадает в соответствующую цепочку. Эта процедура наглядно изображена на картинке (для примера используется mod 4):

Все буферы (а точнее, указатели на буферы), попавшие в один bucket организованы в списки (cachе buffer chains, CBC). В общем случае в списке находятся

  • нужный нам блок
  • CR-блоки (consistent read) (ограничено параметром _db_block_max_cr_dba , т.е. теоретически длина списка не превышает это значение)
  • блок с другим DBA, но по которому был получен такой же bucket# (такая коллизия при больших кэшах, когда есть много списков, возможна, но маловероятна).

Эти списки защищены защелками CBC latch, которые отвечают за один или более бакетов.

Как осуществляется поиск нужного блока?

  1. Рассчитываем bucket# по формуле (см. выше)
  2. По значению bucket# берем защелку
  3. По значению bucket# попадаем на нужную цепочку
  4. В цикле её просматриваем:
  5.  Loop until end of chain or buffer found
    If buffer.dba = requested.dba & buffer.class = requested.class
    Buffer found, exit loop
    
  6. Результат работы цикла - два варианта:
    • Блок найден: в buffer header'е устанавливаем pin (закрепляем блок за собой) и работаем с ним. Если же буфер используется, на него уже очередь, то защелка освобождается, для целей согласованности блок клонируется и используется. Для использования в несовместимом режиме записываемся в очередь, освобождаем защелку, наращиваем статистику 'buffer busy waits', засыпаем в ожидании.
    • Блок не найден: освобождаем защелку и переходим к чтению блока.

В таблице X$BH вся эта кухня отображена таким образом:

http://oraclemaniacs.blogspot.com/2007/01/oracle.html

http://www.dbanotes.net/Books/oracle8i_internal_services_for_waits,_latches,_locks.pdf

http://www.fors.com/velpuri2/PERFORMANCE/dbwr.pdf

7 комментариев

Прокоментировать

Vladimir
24 июня 2008 г. в 12:08

Заметка касается только блоков данных? А как обстоит дело с блоками данных, в которых находятся индексы? Или я что-то пропустил?

dbstalker
25 июня 2008 г. в 09:25

Буферный кэш блоков данных ( data block buffer cache)-это кэш области SGA, используемый для хранения блоков данных, которые считываются из сегментов данных: из таблиц, индексов и кластеров.

Vladimir
25 июня 2008 г. в 09:39

Я имел ввиду, зависит ли распределение блока данных между списками от его типа (табличный, индексный, кластерный)? Или они попадают как получится, вперемешку и всё зависит от вычисленного хэш-значения?

dbstalker
25 июня 2008 г. в 09:55

Блоки данных распределяются по бакетам в зависимости от формулы bucket#=mod(DBA,_db_block_hash_buckets). Изходя из неё можно сделать вывод, что распределение зависит только от file# и block#. Если учесть, что количество бакетов в 2 раза больше размера кэша, то предполагается, что каждая цепочка в основном состоит из одного блока и , при необходимости, его CR-"копий".

Vladimir
25 июня 2008 г. в 12:01

Понятно. Oracle не делает разницы между типами блоков данных. Табличный, индексный и кластерный блоки данных размещаются в зависимости только от их file# и block#. Спасибо.

Apex
29 декабря 2008 г. в 12:56

Не совсем понятет следующий момент:
"блок с другим DBA, но по которому был получен такой же bucket# (такая коллизия при больших кэшах, когда есть много списков, возможна, но маловероятна)."
Что занчит маловероятна? Блоков в кэше в любом случае гораздо больше чем бакетов, т.о. попадание блоков с разными DBA в один бакет закономерно и желательно. Другое дело, насколько равномерно будут заполнены бакеты - это уже вопрос качества хэш-функции.
Или я не понял о чем идет речь?

dbstalker
30 декабря 2008 г. в 16:42

Вы однозначно правы. Но сейчас памяти на серверах становится все больше и меряется она у же в гигабайтах. поэтому чаще бывает, что в цепочке один блок. посмотрите у себя в базе на параметр _db_block_hash_buckets.

 

Новый комментарий

Я не спамер: введите суму 8+4



 

От авторов блога

О Блоге - прочитай перед началом.

Задать вопрос и получить ответ - уже решено 94 вопросов

Глоссарий - список терминов и сокращений


 
 

Бизнес форум

Последние темы:

Досуг для взрослых
19 июня, 1 ответа
авто
19 июня, 1 ответа
Отдых
18 июня, 2 ответа