Есть ли подсказка для генерации плана выполнения, игнорирующего существующий из общего пула? - PullRequest
3 голосов
/ 19 октября 2019

Есть ли подсказка для генерации плана выполнения, игнорирующего существующий из общего пула?

Ответы [ 2 ]

1 голос
/ 20 октября 2019

Нет подсказки для создания плана выполнения, который игнорирует планы в общем пуле. Более распространенный способ сформулировать этот вопрос: как заставить Oracle всегда выполнять жесткий анализ?

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

  1. Странная проблема с производительностью. Oracle выполняет некоторую динамическую повторную оптимизациюОператоры SQL после первого запуска, такие как адаптивное совместное использование курсора и обратная связь по количеству элементов. В редких случаях, когда эти функции имеют обратный эффект, вы можете их отключить.
  2. Динамический запрос. У вас есть динамический запрос, который использует картридж данных Oracle для извлечения данных на этапе разбора, но Oracleне будет выполнять шаг разбора, потому что запрос выглядит статическим для Oracle.
  3. Недоразумение. Что-то пошло не так, и это проблема XY.

Решения

Самый простой способ решить эту проблему -используя решение Торстена Кеттнера об изменении запроса каждый раз.

Если это не вариант, второе простейшее решение - сбросить запрос из общего пула, например:

--This only works one node at a time.
begin
    for statements in
    (
        select distinct address, hash_value
        from gv$sql
        where sql_id = '33t9pk44udr4x'
        order by 1,2
    ) loop
        sys.dbms_shared_pool.purge(statements.address||','||statements.hash_value, 'C');
    end loop;
end;
/

Еслиу вас нет контроля над SQL, и вам нужно решить проблему с помощью решения в стиле побочных эффектов. Джонатан Льюис и Рэндольф Гейст имеют решение с использованием виртуальной частной базы данных , которое добавляет уникальный предикат к каждому оператору SQLна конкретном столе. Вы просили что-то странное, вот странное решение. Buckle up.

-- Create a random predicate for each query on a specific table.
create table hard_parse_test_rand as
select * from all_objects
where rownum <= 1000;

begin
  dbms_stats.gather_table_stats(null, 'hard_parse_test_rand');
end;
/

create or replace package pkg_rls_force_hard_parse_rand is
  function force_hard_parse (in_schema varchar2, in_object varchar2) return varchar2;
end pkg_rls_force_hard_parse_rand;
/

create or replace package body pkg_rls_force_hard_parse_rand is
  function force_hard_parse (in_schema varchar2, in_object varchar2) return varchar2
  is
    s_predicate varchar2(100);
    n_random pls_integer;
  begin
    n_random := round(dbms_random.value(1, 1000000));
    -- s_predicate := '1 = 1';
    s_predicate := to_char(n_random, 'TM') || ' = ' || to_char(n_random, 'TM');
    -- s_predicate := 'object_type = ''TABLE''';
    return s_predicate;
  end force_hard_parse;
end pkg_rls_force_hard_parse_rand;
/

begin
  DBMS_RLS.ADD_POLICY (USER, 'hard_parse_test_rand', 'hard_parse_policy', USER, 'pkg_rls_force_hard_parse_rand.force_hard_parse', 'select');
end;
/

alter system flush shared_pool;

Вы можете увидеть жесткий анализ в действии, выполнив один и тот же запрос несколько раз:

select * from hard_parse_test_rand;
select * from hard_parse_test_rand;
select * from hard_parse_test_rand;
select * from hard_parse_test_rand;

Теперь в GV $ SQL есть три записи для каждого выполнения,В виртуальной частной базе данных есть странное поведение, которое анализирует запрос несколько раз, хотя окончательный текст выглядит одинаково.

select *
from gv$sql
where sql_text like '%hard_parse_test_rand%'
    and sql_text not like '%quine%'
order by 1;
1 голос
/ 19 октября 2019

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

Это то, что мы хотели бы для select * from mytable where is_active = :active, при этом is_active равен 1 для очень небольшого количества строк. и 0 для миллиардов других строк. Нам нужен индексный доступ для :active = 1 и полное сканирование таблицы для :active = 0. Два разных плана.

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

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

select /* 1234567 */ * from mytable where is_active = :active;

Или просто не используйте переменные связывания, если вы хотите решить эту проблему. адрес:

select * from mytable where is_active = 0;

select * from mytable where is_active = 1;
...