Выпуск 28

Сброс буферного кэша и распространенные заблуждения

Уважаемые подписчики! Хочу обратить ваше внимание на еще один свой проект, Хроника бегства от безопасности". Очередной блог, но, вдруг кому-то интересно, чем я занимаюсь каждый день...

А этот выпуск посвящен сбросу содержимого буферного кэша и типичным заблуждениям, связанным с настройкой производительности приложений Oracle. Последний раз Том Кайт вернулся к этому вопросу 21 января 2003 года.

Как сбросить буферный кэш

Том!

Оператор ALTER SYSTEM FLUSH SHARED_POOL позволяет сбросить содержимое разделяемого пула. А нет ли способа сбросить все блоки из буферного кэша? Это необходимо при настройке, когда последовательно применяются различные методы настройки и хотелось бы уменьшить влияние наличия необходимых блоков в буферном кэше, не прибегая к перезапуску экземпляра.

Ответ Тома Кайта

На самом деле, при настройке этого делать как раз НЕ НАДО...

Надо запустить тест, проигнорировать результаты, запустить его еще два или три раза, и записать средние результаты (показатели производительности).

В реальном мире буферный кэш никогда не будет пуст.

При настройке надо заботиться о сокращении количества операций логического ввода-вывода -- о физическом вводе-выводе позаботится сам сервер.

Сброс разделяемого пула и буферного кэша создает более "искусственные" условия, чем выполнение последовательности тестов БЕЗ сброса.

В любом случае, поскольку этого совера все равно никто не слушает  -- подозреваю, потому, что он противоречит распространенному заблуждению  -- я расскажу вам, как это сделать. Просто отключите (offline) и снова включите (online) соответствующее табличное пространство (Но это будет искусственное, надуманное действие!!! Да и вообще неверное.)

Позвольте продемонстрировать, как это сделать, и ПОЧЕМУ это бессмыссленно. У меня есть небольшая C-программа flushcache, выделяющая всю память на ноутбуке и проверяющая ее, а затем сбрасывающая кэш ОС и выполняющая ряд весьма деструктивных действий:

big_table@ORA920> select count(data_object_id) from big_table;

1 row selected.

Elapsed: 00:00:01.10

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1329 Card=1 Bytes=2)
   1    0   SORT (AGGREGATE)
   2    1     TABLE ACCESS (FULL) OF 'BIG_TABLE' (Cost=1329 Card=1000000 Bytes=2000000)

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

big_table@ORA920> alter tablespace users offline;

Tablespace altered.

Elapsed: 00:00:00.05
big_table@ORA920> alter tablespace users online;

Tablespace altered.

Elapsed: 00:00:00.03
big_table@ORA920> !flushcache;
allocated 512m of ram 0x4212F008
touching it all.................done...

big_table@ORA920> select count(data_object_id) from big_table;

1 row selected.

Elapsed: 00:00:12.03

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1329 Card=1 Bytes=2)
   1    0   SORT (AGGREGATE)
   2    1     TABLE ACCESS (FULL) OF 'BIG_TABLE' (Cost=1329 Card=1000000 Bytes=2000000)

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

big_table@ORA920> select count(data_object_id) from big_table;

1 row selected.

Elapsed: 00:00:01.10

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1329 Card=1 Bytes=2)
   1    0   SORT (AGGREGATE)
   2    1     TABLE ACCESS (FULL) OF 'BIG_TABLE' (Cost=1329 Card=1000000 Bytes=2000000)

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

Ну, и как это может быть? Так 1 секунду или 12 секунд надо для выполнения 13656 операций физического ввода-вывода?

На самом деле, это зависит от...

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

Делать надо так:

Выполняем тест один раз.
Выполняем его снова, многократно, записывая время выполнения каждой попытки.

Как в старой поговорке: "Копейка рубль бережет" -- если сократить количество операций логического ввода-вывода, о физическом вводе-выводе позаботится сам сервер.

Комментарий читателя от 20 января 2003 года

Я согласен, что логический ввод-вывод важнее, и именно за его объемом и надо следить для получения максимально достоверных результатов. Мне просто надо было доказать руководству глупость идей по технологии настройки производительности, которые я считал глупыми. Но, это уже другая проблема...

Мне непонятно, почему количество операций физического ввода-вывода одинаково в обоих операторах select count(object_id) from big_table. При первом вызове, после выполнения программы flushcache, я понимаю, что физический ввод-вывод нужен. Однако, при следующем вызове, не доложны ли блоки браться из буферного кэша? Почему опять выполняется физический ввод-вывод?

Ответ Тома Кайта

На моем ноутбуке буферного кэша не хватает, чтобы вместить все блоки. 13K блоков его полностью сбросили.

Комментарий читателя от 21 января 2003 года

Том, в ответе на предыдущий комментарий ты упомянул, что один и тот же объем физического ввода-вывода пришлось выполнять потому, что буферный кэш на 13K блоков меньше, чем таблица. Мне казалось, что при полном просмотре не кэшированной таблицы сервер Oracle читает блоки в наиболее недавно использованную (LRU) часть буферного кэша порциями по DB_MULTI_BLOCK_READ_COUNT + 1 блок. Поэтому, в данном случае, ПРИ КАЖДОМ повторном запросе count, серверу и приходится выполнять один и тот же объем физического ввода-вывода для получения результата.

Может ли различие времени выполнения быть связано с загрузкой блокой в кэш диска?

Ответ Тома Кайта

13K - это количество блоков. Буфер не на 13K меньше, просто прочитать надо 13000 блоков.

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

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

Различие по времени (как я пытался показать) было связано именно с этим. Я сбросил буферный кэш ОС, выделив и изменив каждый байт ОЗУ на диске. Я пытался продемонстрировать, что в большинстве систем, простой сброс буферного кэша -- действие из серии "да, ну и что". Оно никак не помогает в настройке -- это сбивает с толку и попросту бессмыссленно.

Хороший совет по настройке!

Я всегда считал такой метод настройки лучшим! Спасибо, что подсказал и доказал обратное!

Ответ Тома Кайта

Другие распространенные заблуждения (на 100% ошибочные представления):


Оригинал обсуждения этого вопроса можно найти здесь.


Copyright © 2003 Oracle Corporation


В следующем выпуске

Скорее всего, перевод второй статьи из серии публикаций Джонатана Льюиса, посвященных индексам на основе битовых карт. Или перевод очередного блестящего ответа Тома Кайта... Следите за новостями на сайте проекта Open Oracle.

С наилучшими пожеланиями,

  В.К.

OpenXS Rambler's Top100 Rambler's 
Top100