Базы данных Oracle - статьи

         

Кэш результатов SQL


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

Преимущества кэширования особенно заметны в случае с относительно маленькими таблицами, имеющими статические данные, например, справочными таблицами, такими как STATES, PRODUCT_CODES и так далее. Рассмотрим, однако, случай большой таблицы CUSTOMERS, которая хранит сведения о клиентах компании. Данные в ней относительно статичные, но не совсем: таблица меняется редко, когда клиенты добавляются или удаляются из списка. Кэширование, вероятно, имело бы здесь некоторый смысл. Но, если таблица будет как-то кэшироваться, как можно быть уверенным, что получишь верные данные, когда что-то изменится? В базе данных Oracle 11g есть ответ: использование кэша результатов SQL (SQL Result Cache). Рассмотрим следующий запрос. Запустим его, чтобы получить статистику выполнения и время отклика.

SQL> set autot on explain stat

select state_code, count(*), min(times_purchased), avg(times_purchased) from customers group by state_code /

Вот результаты:

ST COUNT(*) MIN(TIMES_PURCHASED) AVG(TIMES_PURCHASED) -- ---------- -------------------- -------------------- NJ 1 15 15 NY 994898 0 15.0052086 CT 5099 0 14.9466562 MO 1 25 25 FL 1 3 3

5 rows selected.

Elapsed: 00:00:02.57

Execution Plan ---------------------------------------------------------- Plan hash value: 1577413243

-------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 5 | 30 | 1846 (25)| 00:00:23 | | 1 | HASH GROUP BY | | 5 | 30 | 1846 (25)| 00:00:23 | | 2 | TABLE ACCESS FULL| CUSTOMERS | 1000K| 5859K| 1495 (7)| 00:00:18 | --------------------------------------------------------------------------------


Statistics ---------------------------------------------------------- 1 recursive calls 0 db block gets 5136 consistent gets 5128 physical reads 0 redo size 760 bytes sent via SQL*Net to client 420 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 5 rows processed

Обратим внимание на следующие моменты:


  • План выполнения показывает, что выполнялся полный просмотр таблицы.
  • Выполнено 5,136 согласованных чтений (consistent gets) (логических операций ввода/вывода).


  • Время выполнения - 2.57 секунд.


Поскольку таблица почти совсем не меняется, можно использовать подсказку, которая сохранит результаты запроса в кэше памяти: select /*+ result_cache */ state_code, count(*), min(times_purchased), avg(times_purchased) from customers group by state_code /

Запрос идентичен первому за исключением подсказки. Вот результат (второе выполнение этого запроса): ST COUNT(*) MIN(TIMES_PURCHASED) AVG(TIMES_PURCHASED) -- ---------- -------------------- -------------------- NJ 1 15 15 NY 994898 0 15.0052086 CT 5099 0 14.9466562 MO 1 25 25 FL 1 3 3



5 rows selected.

Elapsed: 00:00:00.01

Execution Plan ---------------------------------------------------------- Plan hash value: 1577413243

-------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 5 | 30 | 1846 (25)| 00:00:23 | | 1 | RESULT CACHE | gk69saf6h3ujx525twvvsnaytd | | | | | | 2 | HASH GROUP BY | | 5 | 30 | 1846 (25)| 00:00:23 | | 3 | TABLE ACCESS FULL| CUSTOMERS | 1000K| 5859K| 1495 (7)| 00:00:18 | --------------------------------------------------------------------------------------------------

Result Cache Information (identified by operation id): ------------------------------------------------------

1 - column-count=4; dependencies=(ARUP.CUSTOMERS); parameters=(nls); name="select /*+ result_cache */ state_code, count(*), min(times_purchased), avg(times_purchased) from customers group by state_c"



Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 0 consistent gets 0 physical reads 0 redo size 760 bytes sent via SQL*Net to client 420 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 5 rows processed

Обратите внимание на некоторые отличия от первого запроса:


  • Время ответа составляет теперь 0.01 секунду вместо почти 3 секунд.
  • Согласованных чтений теперь 0; операции логического ввода/вывода не выполняются для этого запроса. (Действительно, при первом выполнении запроса с подсказкой количество логических операций ввода/вывода останется таким же (как и без подсказки – прим. переводчика), потому что база данных должна выполнить операции ввода/вывода, чтобы построить кэш. Последующие вызовы будут брать данные из кэша, не выполняя логического ввода/вывода).
  • План выполнения (explain plan) показывает операцию RESULT CACHE.
  • Примечание после плана выполнения показывает, какой тип кэширования выполнялся и над каким результатом.


Экономия времени поразительна: от 3 секунд до почти ничего! Это благодаря тому, что второй запрос, в котором мы использовали кэш, возвращает данные непосредственно из памяти базы данных (кэша результатов), а не после выполнения запроса.

Кэш результатов SQL – это новый кэш в SGA, подобный буферному кэшу или программной глобальной области. При выполнении запроса с подсказкой result_cache, Oracle выполняет запрос также как любой другой запрос, но результаты сохраняются в кэше результатов SQL. Последующие вызовы того же самого запроса не обращаются к таблице (или таблицам), а берут результаты из кэша. Размер кэша определяется несколькими параметрами инициализации:

Параметр

Описание

result_cache_max_size Максимальный размер кэша (например, 5M для 5 MB). Если установить в 0, кэш результатов будет полностью выключен.
result_cache_max_result пределяет процент от максимального размера кэша (result_cache_max_size), который может использоваться одним запросом.
result_cache_mode Если задано значение FORCE, то все запросы кэшируются, если они умещаются в кэш. Значение по умолчанию MANUAL означает, что кэшируются только запросы с подсказкой.
result_cache_remote_expiration Определяет количество минут, которое результат в кэше, который обращался к удаленному объекту, остается действительным. По умолчанию 0.
<


Теперь логичный вопрос: что происходит, когда меняются строки таблицы? Получит ли запрос новое значение или старое? Действительно, давайте посмотрим, что происходит. Из другой сессии SQL*Plus изменим строку таблицы: SQL> update customers set times_purchased = 4 2 where state_code = 'FL';

1 row updated.

но не будем выполнять commit. Из первого окна, в котором мы запускали запрос в первый раз, запустим его снова. Используется еще кэшированный результат, поскольку изменения ещё не зафиксированы. Сессия, которая выполняет запрос, видит все ещё актуальную версию данных, и кэш всё ещё действителен.

Теперь в сессии, в которой выполнялось обновление, выполним commit и запустим запрос.

ST COUNT(*) MIN(TIMES_PURCHASED) AVG(TIMES_PURCHASED) -- ---------- -------------------- -------------------- NJ 1 15 15 NY 994898 0 15.0052086 CT 5099 0 14.9466562 MO 1 25 25 FL 1 4 4

Обратите внимание, что данные для FL изменили значение на 4 автоматически. Изменения в основной таблице просто сделали кэш недействительным, что привело к его динамическому обновлению при следующем запросе. Вы гарантировано получите правильные результаты вне зависимости от того, используется ли кэш результатов SQL или нет.


Содержание раздела