Цель

Защелки - это блокировка словарей данных. И также как и обычная блокировка строки влияет на время выполнения запроса. Чем больше защелок - тем дольше выполняется запрос.

Далее мы разберем как узнать их количество - эта информация поможет в анализе производительности запросов, поможет выявить слабые места.

Описание компонентов

v$latch

Представление выводит информацию о защелках по последней выполненной транзакции. Группируя информацию о родительской и дочерней защелке. Состоит из следующих полей:

Поле Тип Описание
addr raw(4|8) адрес защелки
latch# number идентификатор защелки
level# number уровент защелки
name varchar2(64) имя защелки
hash number хэш защелки
gets number количество запросов на защелку в режиме ожидания
misses number cколько раз была запрошена защелка в режиме ожидания, и запрашивающая сторона должна была ждать
sleeps number количество раз, когда запрос защелки с ожиданием приводил к спящему сеансу в ожидании защелки
immediate_gets number количество запросов на защелку в режиме без ожидания
immediate_misses number количество раз, когда запрос блокировки без ожидания не удался (то есть был пропущен)
waiters_woken number неподдерживаемый Oracle столбец, всегда будет равен 0
waits_holding_latch number неподдерживаемый Oracle столбец, всегда будет равен 0
spin_gets number запросы с готовностью подождать защелки, которые пропустили первую попытку, но были успешными во время вращения
sleep1-11 number неподдерживаемые Oracle столбцы, всегда будут равны 0
wait_time number время ожидания защелки (в микросекундах)
con_id number идентификатор контейнера (для мультиарендной контейнерной базы данных CDB)

Права

Для выполнения представления у пользователя (в моем случае PROD) должен быть доступ на все таблицы, подключаемся к БД с помощью пользователя SYS с ролью SYSDBA и выполняем скрипты ниже:

grant select on v_$latch to prod;

Пример измерения количества защелок

Получение количества одной защелки

Рассмотрим как узнать конкретную защелку DML lock allocation (Распределение блокировки DML). В примере показанном ниже, будут меняться ранее сгенерированные данные, умышленно будет добавлен rollback в цикл, чтобы увидеть защелки.

--включаем вывод данных в output set serveroutput on; declare v_get_before number; --количество защелок ДО v_get_after number; --количество защелок ПОСЛЕ v_get_result number; --разница защелок ДО и ПОСЛЕ, итог v_latch_name varchar2(255 char) := 'DML lock allocation'; --имя защелки begin --фиксируем количество защелок ДО select l.gets into v_get_before from v$latch l where l.name = v_latch_name; --запустим 1000 раз цикл, который обновит дату у первых попавшихся строк for rec in 1..1000 loop update prod.operation set op_date = trunc(sysdate) where rownum = 1 and op_date != trunc(sysdate); --сразу откатываем изменения rollback; end loop; --фиксируем количество защелок ДО select l.gets into v_get_after from v$latch l where l.name = v_latch_name; --считаем общее время выполнения в сотых долях секунды v_get_result := v_get_after - v_get_before; --выводим результат на экран dbms_output.put_line('Зарос 1.' || v_latch_name || ': ' || to_char(v_get_result)); --ДАЛЕЕ ВСЕ ТОЖЕ САМОЕ ТОЛЬКО УБИРАЕМ ROLLBACK ИЗ ЦИКЛА select l.gets into v_get_before from v$latch l where l.name = v_latch_name; for rec in 1..1000 loop update prod.operation set op_date = trunc(sysdate) where rownum = 1 and op_date != trunc(sysdate); end loop; rollback; select l.gets into v_get_after from v$latch l where l.name = v_latch_name; v_get_result := v_get_after - v_get_before; dbms_output.put_line('Зарос 2.' || v_latch_name || ': ' || to_char(v_get_result)); end;

Результат:
Вывод количества зашелки

Получение количества всех защелок

Усложним задачу, чтобы видеть все защелки по двум запросам и сразу выводить по ним разницу для этого:

  • Необходимо добавить себе программу по сбору и сравнению статистики, если уже есть, идем далее.
  • Расширим наше представление prod.stats_vw, добавив в конце 4-й скрипт вывода информации о защелках:
    create or replace view prod.stats_vw as --первый скрипт по выводу статистики select 'STAT' as inf_type, -- для inf_type в stats_tbl a.name as name, -- для name в stats_tbl b.value as value -- для value в stats_tbl from v$statname a join v$mystat b on a.statistic# = b.statistic# --команда объединения union all --второй скрипт по фиксации общего времени select 'ETIME', -- сокращенно от Elapsed time (общее время) 'Elapsed Time', hsecs from v$timer --команда объединения union all --третий скрипт по фиксации времени CPU select 'CTIME', --сокращенно от CPU time (время ЦПУ) 'CPU Time', dbms_utility.get_cpu_time from dual --команда объединения union all --четвертый скрипт по фиксации защелок select 'LATCH', --сокращенно от CPU time (время ЦПУ) l.name, l.gets from v$latch l;
  • Преобразуем пример, показанный ранее, в новый, с применением программы:
    begin --фиксируем начало работы fix_inf_prc(1,'before'); --ROLLBACK в конце for rec in 1..1000 loop update prod.operation set op_date = trunc(sysdate) where rownum = 1 and op_date != trunc(sysdate); end loop; rollback; --фиксируем завершение работы fix_inf_prc(1,'after'); --фиксируем начало работы fix_inf_prc(2,'before'); --ROLLBACK в цикле for rec in 1..1000 loop update prod.operation set op_date = trunc(sysdate) where rownum = 1 and op_date != trunc(sysdate); rollback; end loop; rollback; --фиксируем завершение работы fix_inf_prc(2,'after'); --вывести отчет stats_report_prc(1000); --показывать значение, отличающиеся на 1000 end;

    Результат:
    Вывод всех зашелок


Спасибо за внимание!