2007/08/14

Стоимость Row Level Security

Комментируя статью, я подумал о том, что хорошо бы попытаться простым экспериментом оценить стоимость использования, так сказать, Row Level Security (RLS, DBMS_RLS). Причем, сделать оценку с точки зрения использования защелок (latch), поскольку, похоже, именно они являются причиной проблем на нагруженных многопользователями системах, использующих RLS (по крайней мере я с такими проблемами сталкивался).

Для проведения измерений я создал небольшой полигон, как водится, в схеме scott. Полигон (скрипт /test1/_test1_1.sql и все прочие файлы — в архиве) состоит из таблицы, пакета инициализации контекста (test_pkg), пакета (test_pkg1) с функцией-предикатом (test_rls), двух представлений, на одно из которых (v_data1_rls) наложена политика доступа с помощью DBMS_RLS, а второе представление (v_data1_wo_rls) имеет фразу where с фильтрующим предикатом, точно таким же, как и в политике доступа к первому представлению.

В том же скрипте (/test1/_test1_1.sql) собственно проводятся измерения с помощью утилиты runstat . Результат измерений — в файле _test1_1.log. Ежели рассматривать только защелки, относящиеся к shared pool, то разница достаточно существенна: около 60% в пользу представления без RLS:


Name Run1 Run2 Diff
LATCH.library cache 74 72 -2
LATCH.row cache objects 133 54 -79


Во втором эксперименте я попытался оценить, насколько сильно эта разница влияет на время отклика в многопользовательской среде. Суть эксперимента сводится к тому, что на соседнем с работающей БД хосте запускаются несколько экземпляров sqlplus в фоне. Они исполняют скрипт /test3/_test3.sql, который, в свою очередь, исполняет хранимую процедуру ExecQueryN. Процедура в цикле обращается к ранее созданным представлениям, перед каждым вызовом dbms_sql.execute меняется значение переменных контекста. Вызов DBMS_SQL.PARSE делается вне цикла. Кроме того, используется DBMS_LOCK для синхронного старта всех конкурирующих сессий. В дополнение: сессии соединялись через dedicated server.
Измерение времени отклика проводилось для одновременно работающих сессий в количестве от 25 до 700. Результат подтверждает измерения, сделанные с помощью runstat.

Результат

Как видно из графиков - цена достаточно высока, особенно в многопользовательских системах.
Вот еще один характерный момент: после окончания второго эксперимента для 200 сессий (с предварительной очисткой shared pool — alter system flush...) в v$sqlarea вижу:

SQL> select sql_text,version_count,loaded_versions,parse_calls,concurrency_wait_time,cpu_time,elapsed_time,child_latch
2 from v$sqlarea
3 where sql_text like '%from v_data1%rls%' and sql_text not like '%v$sqlarea%';

SQL_TEXT VERSION_COUNT LOADED_VERSIONS PARSE_CALLS CONCURRENCY_WAIT_TIME CPU_TIME ELAPSED_TIME CHILD_LATCH
------------------------------------ ------------- --------------- ----------- --------------------- ---------- ------------ -----------
select count(1) from v_data1_wo_rls 2 2 200 1849425984 71965982 2229756524 3
select count(1) from v_data1_rls 1 1 200 12875615402 259624533 14155267759 5


Для варианта с RLS CHILD_LATCH = 5, без RLS этот показатель равен 3. Надо полагать, именно эта небольшая разница в количестве защелок приводит к столь разительному отличию в других показателях (CONCURRENCY_WAIT_TIME, CPU_TIME, ELAPSED_TIME).