Хабрахабр

100500 способов кэширования в Oracle Database

Рассказывать, какие есть кэши, что такое Result Cache, как он сделан в Oracle и в других базах данных не очень интересно и довольно шаблонно. Но все приобретает совершенно другие краски, когда речь идет о конкретных примерах. Александр Токарев (shtock) построил свой доклад на Highload++ 2017 исходя из кейсов. И именно опираясь на кейсы, рассказал, когда может быть удобен самодельный кэш, в чем боль server-side Result Cache и как заменить его клиентским, и вообще вывел ряд полезных советов по настройке Result Cache в Oracle.

О спикере: Александр Токарев работает в компании DataArt и занимается вопросами, связанными с базами данных как в части построения систем «с нуля», так и оптимизации имеющихся.

Вы работали с Oracle Result Cache? Начнем с нескольких риторических вопросов. По опыту Александра большинство людей на последний вопрос отвечает отрицательно, на сто суровых прагматиков приходится один мечтатель. Вы верите, что Oracle — это база данных, удобная на все случаи? Но благодаря его вере двигается прогресс.

Кстати, у Oracle уже 14 баз данных — пока 14 — что будет в будущем, неизвестно.

Это будет два кейса из проектов DataArt, и один сторонний пример.
Как уже говорилось, все проблемы и решения будут проиллюстрированы конкретным кейсами.

Database caches

Начнем с того, какие в базах данных есть кэши. Тут все понятно:

  • Buffer cache — кэш данных — cache for data pages/data blocks;
  • Statement cache — кэш операторов и их планов — cache of queries plan;
  • Result cache — кэш результатов строк — rows from queries;
  • OS cache — кэш операционной системы.

Причем Result cache, по большому счету, используется только в Oracle. Он когда-то был в MySQL, но потом его героически выпилили. В PostgreSQL его тоже нет, он присутствует в том или ином виде только в стороннем продукте pgpool.

Кейс 1. Хранилище ретейлера

Получение его занимало около 20 минут, и пользователи печалились. Выше схема продукта, который был у нас на сопровождении — хранилище (Oracle 11, 20 Tb, 300 пользователей), и в нём какой-то тоскливый отчёт, в котором на 5000 строк данных было 350 уникальных товаров.

Презентация этого доклада, как и всех остальных, размещена на сайте конференции Highload++.

В этом отчете есть SELECT, JOIN’ы и функция. Функция как функция, все бы хорошо, только она рассчитывает загадочный параметр, который называется «величина трансфертного ценообразования», работает 0,2 с — вроде ни о чем, но вызывается она столько раз, сколько строк в таблице. В этой функции 400 строк SQL+PL/SQL, а т.к. продукт на поддержке, менять её боязно.

По этой же причине нельзя было использовать result_cache.

Чтобы решить проблему, используем стандартный подход с hand-made кэшированием: первые 3 блока схемы оставляем, как было, нашу функцию sku_detail() просто переименовываем в sku_full() и объявляем ассоциативный массив, где соответственно:

  • ключи — это наши SKU (товарные позиции),
  • значения — это рассчитанная цена трансферного преобразования.

Делаем очевидную функцию cache(sku): если в нашем ассоциативном массиве нет такого id, запускается наша функция, результат помещается в кэш, сохраняется и возвращается. Соответственно, если такой id есть, то всего этого не происходит. Фактически мы получили on demand cache.

Время обработки отчета уменьшилось до 4 минут, всем пользователям стало хорошо. Таким образом, мы свели количество вызовов функции к тому количеству, которое на самом деле надо.

Hand-made Cache Memory

Недостатки и достоинства данной системы понятны из этой большой умной картинки, к которой мы будем много обращаться — это архитектура памяти.

Помещаются они в область памяти, которая называется PGA. Важно понимать, в какой из областей памяти расположены коллекции. Именно это определяет достоинства и недостатки, поскольку больше подключений — больше памяти, а память дорогая, серверная, админы нежные. Program global area инстанциируется на каждое подключение к базе данных.

  • Плюсы: все работает очень быстро, очень легко делается, конфигурировать не надо, нет никаких заморочек с межпроцессным задействованием.
  • Минусы понятны: если в проекте запрещена хранимая логика их невозможно использовать, нет механизма автоматической инвалидации и так как память на кэш выделяется в рамках одной сессии БД, а не экземпляра, то её потребление завышено. Более того, в случае с вариантом использования connection pool необходимо не забывать сбрасывать кэши, если для каждой сессии кэширование должно быть разное.

Существуют и другие варианты hand-made кэшей на основе materialized views, temporary tables, но от них идёт большая нагрузка на систему ввода-вывода, поэтому здесь мы их не рассматриваем. Они более применимы для других баз данных, в которых обычно подобные проблемы решаются тем, что хранимая процедура материализуется в какую-нибудь промежуточную таблицу и до обращения к тяжелому запросу данные берутся из нее. И только, если там не нашлось нужного, то вызывается исходный запрос.

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

Его и как не получилось quick win мы рассмотрим далее. В общем, самодельные result_cache активно используются, но иным подходом к реализации данной задачи является in-database result_cache.

Кейс 2. Обработка финансовой документации

Итак, наш второй случай.

Это система полуавтоматизированной обработки финансовой документации — тоскливый enterprise с классической архитектурой, которая включает в себя:

  • тонкий клиент;
  • 4000 пользователей, которые живут в разных частях земного шара;
  • балансировщик;
  • 2 JBoss для расчёта бизнес-логики;
  • in-memory cluster;
  • основной Oracle;
  • резервный экземпляр Oracle.

Одна из множества задач этой системы — это расчет рекомендаций.

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

Пользователь выбирает нужное значение и повторяет операцию для каждой пустой строчки.

Надо сделать так, чтобы в итоге пользователи обработали документы и все значения стали распознаны. Упрощённо эта задача состоит в следующем: поступают документы в виде key-value пар от разных систем распознавания, причем где-то параметры распознаны, а где-то нет. Рекомендация как раз нацелена на упрощения этой задачи и учитывает:

  1. Мультиязычность — порядка 30 языков. Для каждого языка свой стемминг, синонимы и другие особенности.
  2. Предыдущие данные этого клиента, или, в случае их отсутствия, данные клиента из такой же индустрии или похожего по прибыли клиента.

На самом деле это порядка 12 весьма сложных правил.

Изначальные допущения:

  • Не больше 100 пользователей одновременно;
  • 2-3 колонки для распознавания;
  • 100 строчек.

Никакого highload вообще — все скучно.

Произошел Code freeze, Java все боятся трогать, а на обработку документа уходит минимум 5 минут. Итак, наступает время релиза.

Конечно, ведь, если что-то тормозит в JVM, то само собой, надо менять или чинить базу данных. В команду разработки баз данных приходят с просьбой о помощи.

Соответственно, решили использовать базу данных, чтобы кешировать, потому что она уже протестирована. Мы изучили документы и поняли, что в key-value парах довольно часто повторяются значения — по 5-10 раз.

Мы решили использовать Oracle server-side Result Cache, потому что:

  1. возможности по оптимизации SQL исчерпаны, потому что там используется Oracle full text search engine;
  2. будет использоваться кэш для повторяющихся параметров;
  3. большинство данных для рекомендаций пересчитываются раз в час, так как используют полнотекстовый индекс;
  4. PL/SQL запрещен.

Oracle Result Cache

Result cache — технология от Oracle по кэшированию результатов — обладает следующими свойствами:

  • это область памяти, в которой шарятся все результаты запросов;
  • read consistent, и происходит автоматическая его инвалидация;
  • требуется минимальное количество изменений в приложении. Можно сделать так, что приложение вообще не потребуется менять;
  • бонус — можно кэшировать логику PL/SQL, но она у нас запрещена.

Как его включить?

Способ № 1

На слайде видно, что появился идентификатор результата. Очень просто — указать инструкцию result_cache. Все хорошо. Соответственно, при первом выполнении запроса, база данных произведет какую-то работу, при последующем исполнении в данном случае никакая работа не нужна.

Способ № 2

Мы для таблицы указываем галочку, что запрос к ней должен помещаться в result_cache. Второй способ позволяет разработчикам приложений ничего не делать — это так называемые аннотации. Соответственно, никакого hint нет, приложение не трогаем, а все уже в result_cache.

Кстати, как вы думаете, если запрос обращается к двум таблицам, одна из которых помечена как result_cache, а вторая — нет, закэшируется ли результат такого запроса?

Ответ — нет, вообще никак.

Чтобы он закэшировался, все таблицы, участвующие в запросе, должны иметь аннотацию result_cache.

Dependency Tracking

Есть соответствующие представления, в которых можно посмотреть, какие есть зависимости.

Почему? На примере выше запрос JOIN какая-то таблица, в которой одна зависимость. Потому, что Oracle определяет dependency не просто синтаксическим анализом, а осуществляет его по результатам плана выполнения работы.

Если мы уберем foreign key constraint, который позволяет сделать это преобразование join elimination, то мы увидим две зависимости, потому что так поменяется план. В данном случае выбран такой план, потому что используется только одна таблица, и на самом деле таблица jobs связана с таблицой employees через foreign key constraint.

Oracle не отслеживает то, что не надо отслеживать.

В PL/SQL dependency работает в run-time, чтобы можно было использовать динамический SQL и прочие вещи делать.

Предположим, для чего-то одного нам нужен кэш, а другое лучше бы читать из базы данных, чтобы ее не напрягать. Обратите внимание, что кэшировать можно не только весь запрос целиком, можно кэшировать inline view как в виде with, так и в виде from. Мы берем inline view, опять объявляем как result_cache и видим — идет кэширование только по одной части, а за второй мы каждый раз обращаемся к базе данных.

Мы берем view, ставим в нем result_cache, и наши программисты даже не догадываются, что оно закэшировано. И, наконец, в базах данных тоже есть инкапсуляция, хотя в это никто не верит. Ниже мы видим, что на самом деле только одна его часть работает.

Инвалидация

Итак, посмотрим когда же Oracle инвалидирует result_cache.Статус Published показывает текущее состояние валидности кэша. Когда запрос к result_cache, как я уже говорил, в базе данных нет никаких работ

Это та самая пресловутая консистентность по чтению. Когда мы сделали апдейт, статус все равно Published, потому что апдейт не закоммитился и другие сессии должны видеть старый result_cache.

Это вполне разумно, сделаем commit — результат станет Invalid, все работает само. Но в текущей сессии мы увидим, что нагрузка пошла, так как именно в этой сессии кэш игнорируется.

Dependency считаются правильно — просто в зависимости от запроса. Казалось бы — мечта! Oracle производит инвалидации и в ряде неочевидных случаев: Но нет, вскрылся ряд нюансов.

  1. При любом вызове SELECT FOR UPDATE зависимости слетают.
  2. Если в таблице есть неиндексированные внешние ключи, и произошел апдейт по таблице, помеченной result_cache, который вообще ничего не затронул, но при этом что-то поменялось в родительской таблице, кэш тоже станет невалидным.
  3. Это самое интересное, что максимально портит жизнь — если есть какой-то неудачный апдейт по таблице, помеченной как result_cache, ничего не отработало, но потом в этой же транзакции применили любые другие изменения, которые как-то повлияли на первую таблицу, то все равно result_cache сбросится.

Еще есть такой антипаттерн про result_cache, когда разработчики, услышав, что есть такая классная вещь, думают: «О, есть хранилище! Сейчас возьмем какой-нибудь запрос, который на 2-3 партициях работает — на текущей дате и на предыдущей, пометим его как result_cache, и он будет всегда браться из памяти!»

Но когда меняют патрицию задним числом, весь кэш слетает, потому что на самом деле единица отслеживания dependency в result_cache — это всегда таблица, и не знаю, будут ли когда-нибудь партиции или не будут.

Мы подумали и решили, что пойдем в продакшен рекомендательной системы с такими вещами:

  • Мы не будем кэшировать все наши таблицы, возьмем только нужные.
  • Поставим result_cache для long-running query.

Все проверили, провели performance-тесты, время обработки — 30 с. Все замечательно, идем в продакшен!

Приходим с утра. Накатили — ушли спать. Почему они зависают? Видим письмо: «Распознавание занимает минимум 20 минут, сессии зависают». Каким образом 30 секунд превратились в 20 минут?

Стали разбираться, смотреть в базу данных:

  • активных сессий — 400;
  • в среднем строчек в документе для распознавания — 500;
  • колонок минимум — 5-8;
  • количество сессий в базе данных всегда равно количеству application пользователя, умноженное на 3! А result_cache не любит частого к нему обращение.

Проведя внутреннее расследование, мы выяснили, что Java-разработчики делают распознавание в 3 потока.

Мы расстроились — 5-кратная нагрузка, падение, деградация, причем даже при таких параметрах такого проседания не должно было быть.

Очевидно, надо разбираться.

Мониторинг

Для мониторинга у нас есть две ключевых вещи:

  1. V$RESULT_CACHE_OBJECTS — список всех объектов;
  2. V$RESULT_CACHE_STATISTICS — агрегатная статистика result_cache в целом.

MEMORY_REPORT — это вариации на тему, они нам не понадобятся.

Есть великолепная документация, но она рассчитана на тех, кто переходит с других баз данных, чтобы они читали и думали, что Oracle — это очень круто! Oracle — волшебный! А вот вся информация по result_cache лежит только на support.

До Oracle12. Есть нюанс, который состоит в том что, как только мы обращаемся к этим объектам, чтобы разрешить проблему, мы ее усугубляем, окончательно закапывая себя! 2, до патча которой вышел в октябре прошлого года, эти запросы делают result_cache недоступным на статус и на запись до тех пор, пока они полностью не посчитаются.

Причем, это были объекты из каких-то не наших запросов по странным таблицам — маленькие таблички, и запросы last_modified_date. Итак, воспользовавшись представлением v$result_cache_objects, мы выяснили, что в списке закэшированных объектов тысячи записей — намного больше, чем мы ожидали. Очевидно, кто-то натравил на нашу базу ETL.

Перед тем как идти ругаться на разработчиков ETL, мы проверили, что для этих таблиц включена опция result_cache force, и вспомнили, что мы сами её включили, так как некоторые из этих данных часто требовались приложению и кэширование было уместно.

К счастью, у разработчиков была возможность повлиять на ETL в продакшене, поэтому мы смогли изменить result_cache, чтобы исключить эти ежеминутные запросы. А получилось, что все эти запросы просто берут и вымывают наш кэш.

— Не полегчало! Как вы думаете, полегчало? Мы продолжили изучать, что же ещё кэшируется, так как скорость не менялась. Количество кэшируемых объектов уменьшилось, а потом снова выросло до 12000.

Хотя тот, кто работал с Oracle 12, знает, что DS SVC — это адаптивная статистика. Смотрим — куча запросов, и такие умные, но все непонятные. Это само собой, написано только на support. Она нужна для улучшения производительности, но когда есть result_cache, она оказывается, его убивает, потому что происходит конкуренция.

Поэтому мы героически ее отключили — результат, как и написано в секретном мануале — 10 минут на документ. Мы знали, как устроен workload и понимали, что в нашем случае адаптивная статистика не особо радикально улучшит наши планы. Неплохо, но еще недостаточно.

Защелки

Конкуренция между result_cache и DS SVC возникает из-за того, что в Oracle есть защелки (latches) — легковесные маленькие блокировочки.

Не вдаваясь в детали, как они работают, пытаемся поставить именованную защелку несколько раз — не получилось — Oracle берет и засыпает

Это детали. Тот, кто в теме, может сказать, что в result_cache ставится по две защелки на каждый блок при fetch. В result_cache есть два вида защелок:

Защелка на тот период, пока мы записываем в result_cache данные.    1.

Другие запросы запишутся, но подождут блокировку только на первую строчку. То есть если у вас запрос работает 8 с, на период этих 8 с другие такие же запросы (ключевое слово «такие же») не смогут ничего сделать, потому что они ждут, пока данные запишутся в result_cache. После этого они начинают как бы игнорировать result_cache, и работают медленно. Сколько им придется ждать, неизвестно, это недокументированный параметр result_cache_timeout. Правда, как только блокировка с последней строчки при помещении снялась, они автоматом начинают снова работать с result_cache.

Второй тип блокировок — на получение из result_cache тоже с 1-й строчки по последнюю.
Но так как fetch происходит из мгновенной памяти, то они снимаются очень быстро.    2.

Wait time — все пропало! Обязательно надо иметь в виду, что, когда DBA видит в базе данных защелки, он начинает говорить: «Защелки! » И тут начинается самая интересная игра: убеди DBA, что wait time от защелок на самом деле несравненно меньше, чем время повторения запроса.

Как показывает наш опыт, наши измерения, защелки на result_cache занимают 10% от самих запросов.

То, что все плохо, можно понять по тому, что забит кэш. Это агрегированная статистика. То есть кэш перезатирается. Еще одно подтверждение — Proper results are deleted. Вроде бы, мы умные и всегда считаем размеры памяти — взяли размер строчек нашего кэшируемого результата для нашей рекомендации, умножили на количество строк, и что-то пошло не так.

И это тоже исправили в том самом секретном патче. На support мы нашли 2 бага, которые говорят, что при переполнении result_cache происходит деградация производительности.

В нашем случае, конечно, еще добавилось то, что workload вырос в 5 раз. Секрет в том, что память выделяется блоками. Поэтому при расчете память не надо умножать на ширину ваших данных, а умножайте на размер блока, и тогда будет счастье.

Что еще можно настроить?

На самом деле, нам не нужны все эти параметры. Параметров море: есть документированные и недокументированные параметры.

По факту достаточно 4 параметров:

  • RESULT_CACHE_MAX_SIZE;
  • RESULT_CACHE_MAX_RESULT;
  • RESULT_CACHE_MODE;
  • _RESULT_CACHE_MAX_TIMEOUT.

После того, как мы заменили 100 Мб на 512Мб, время обработки документа сократилось до 6 минут. Нам хватило даже одного — размер кэша.

Например, Invalidation Count = 10000. Все равно копаем дальше, вдруг есть еще что-нибудь странное.

Путём неких изысканий мы обнаружили, что отключен job обновления рекомендаций, что приводило к постоянному обновлению данных. Не ошибается тот, кто ничего не делает. Мы запустили job с часовым интервалом, как и было задумано, что автоматически отключило постоянное обновление таблицы. Соответственно, кэш постоянно инвалидировался.

При пятикратном увеличении нагрузки мы получили скорость обработки документа 40 с. Всегда есть свободное место, invalid только в момент пересчета кэша, и удаления данных нет.

Пока мы все это изучали, мы обнаружили кучу недокументированных фишек, которые есть в ядре Oracle. Самое важное, чтобы кэш не переполнялся. Они великолепны!

Этот параметр был встроен в новую версию приложения. SHELFLIVE — параметр, который позволяет обеспечить read-consistent умирание кэша, то есть кэшируемый запрос умрет через 10 с, и кэш почистится. Важно, что кэш так же удаляется, если было изменение данных.

Она удобна, если факт изменения не критичен для кэша, нет необходимости в сохранении read-consistent и нет защелок — тогда изменения данных не будут инвалидировать кэш. Есть еще более интересная опция — SNAPSHOT.

Ограничения понятны:

  1. Словари — нет возможности кэширования объектов в схеме SYS.
  2. Нельзя кэшировать временные и внешние таблицы. Важно, что по факту можно, и Oracle явно это не ограничивает, но это приводит к тому, что можно увидеть содержимое временных таблиц других пользователей. Более того, Oracle декларирует, что это исправлено, но в 12.2 до сих данная проблема есть. Кстати, про external таблицы почему-то тоже написано в support, в официальной документации нет.
  3. Нельзя использовать недетерминированные sql и pl/sql функции: current_date, current_time и пр. Есть секретный ход, как обойти ограничения с current_time, потому что всегда же хочется кэшировать данные за текущую дату.
  4. Нельзя использовать конвейерные функции.
  5. Входные и выходные параметры кэша должны быть простых типов данных, то есть никаких CLOB, BLOB и пр.

Result cache inside Oracle

Result_cache — это фишка Oracle Core. Она используется на самом деле в куче всего, все, что связано с job использует result_cache (кстати, выделен тот секретный hint, где мы это обнаружили) и везде, что связано с APEX.

Все, что связано с Dynamic sampling и с адаптивной статистикой, то есть все, что делает ваше предсказание более правильным, работает на result_cache.

Oracle internals for result cache

Вернемся к схеме памяти и кратко подытожим работу result_cache:

  1. данные при запросе попадают с уровня хранения (storage) в буферный кэш;
  2. данные из буферного кэша попадают в область памяти result_cache;
  3. result_cache находится в shared pool.

Плюсы:

  • Минимальное влияние на код приложения.
  • Не надо заморачиваться на хранимую логику в базе данных и read-consistent.
  • Result_cache, как мы убедились, довольно быстр.

Минусы:

  • Дорогая память базы данных.
  • Может привести к деградации производительности, если неправильно настроить кэш.

Мы не одиноки!

Из всего сказанного может возникнуть ощущение, что мы такие криворукие. Но посмотрите в support Oracle, например, за 29 сентября 2017 г.: новая версия Oracle E-Business suite падает по причине result_cache, потому что они решили ее ускорить.

В дебрях интернета я нашел очень познавательное письмо службе support одной из ведущих российских облачных систем расчета лояльности, в которой произошел сбой, и система была недоступна суммарно час. Однако, нам интересен не сам факт наличия проблем, а способы их не допустить.

Драматическое увеличение времени выдачи из кэша во время наката патчей произошло в следствие:

  1. блокировки из-за неправильно рассчитанного размера кэша;
  2. и, возможно, блокировок, например, в v$result_cache_memory или dbms_result_cache.memory_report, так как баг по ним не закрыт.

Однако, тесты багов написаны так хитро, что в них фактически явно говорится, что в v_result_cache_objects есть ошибка.

Не читайте документацию, читайте support note — везде на support написано, что будет плохо.

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

  1. убрать hint result_cache;
  2. выставить hint no result_cache;
  3. использовать black_list, то есть, не меняя приложение, запретить кэшировать что-либо.

Какой отсюда можно сделать вывод?

  • Всегда перед тем, как что-то накатить, посчитайте, как это отразится на использовании памяти;
  • Отключите кэш на период загрузки, то есть быстренько отключили, налили и включили. Лучше, чтобы система чуть-чуть потормозила, но заработала, чем потом легла.

Как мы заметили, основная проблема кэшей на сервере — это расход дорогой серверной памяти. У Oracle есть третье, заключительное решение.

Client side result cache

Схема его устройства изображена на выше, это главные компоненты БД и драйвер.

Кэшируемый запрос первый раз обращается к базе данных, и записывает данные в кэш. При первом обращении client-side Result Cache идет в базу данных, которая предварительным образом настроена, получает размер клиентского кэша из базы данных и инстанциирует у себя на клиенте этот кэш разово при первом подключении. Кстати, иногда в зависимости от нагрузки драйвер присылает в БД статистику по использованию кэша, которую потом можно будет посмотреть. Остальные потоки запрашивают общий кэш драйвера, тем самым экономя память и ресурсы сервера.

Интересен вопрос, как происходит инвалидация?

Это то, сколько Oracle позволяет кэшу на драйвере быть не консистентным. Есть два режима инвалидации, которые заточены на параметр Invalidation lag.

В таком случае поток пойдёт в базу данных, обновит кэши и считает данные из него. Первый режим используется, когда запросы идут часто и не наступает Invalidation lag.

Соответственно они помечаются в кэше как инвалидные, и все работает как на картинке из первого сценария. Если Invalidation lag не прошел, то любой некэшируемый запрос, обращаясь к базе данных, кроме результатов запроса приносит список инвалидных объектов.

Во втором случае, если прошло больше времени, чем Invalidation lag, то сам клиентский result_cache идет в базу данных и говорит: «А дай-ка мне список изменений!» То есть он сам в себе поддерживает свое адекватное состояние.

Есть 2 параметра: Сконфигурировать client-side Result Cache очень просто.

  1. CLIENT_RESULT_CACHE_LAG —величина отставания кэша;
  2. CLIENT_RESULT_CACHE_SIZE — размер (минимальный 32 Кб, максимальный — 2 Гб).

Если он был, то он просто начнет использоваться клиентский — что на . С точки зрения разработчика приложений клиентский кэш особо не отличается от серверного, также вписали hint result_cache. Net, что на Java.

Сделав 10 итераций запроса, я получил следующее.

В таблице отмечено, что память тоже выделяется блоками. Первое обращение — создание, далее 9 обращений к кэшу. Я, если честно, до того, как начал с этим разбираться, даже не знал, что есть такое представление GV$SESSION_CONNECT_INFO. Еще обратите внимание на SELECT — он не очень интуитивный. Но именно поэтому я считаю, что эта функциональность не очень востребована, хотя, как мне кажется, очень полезна. Почему Oracle не вынес это прямо в данную таблицу (а это таблица, а не view) я понять не смог.

Достоинства клиентского кэширования:

  • дешевая клиентская память;
  • доступны любые драйвера —JDBC, .NET и т.д.;
  • минимальное влияние в код приложения.
  • Сокращение нагрузки на CPU, ввод/вывод и вообще базу данных;
  • не надо учить и использовать всякие умные кэширующие слои и API;
  • нет защелок.

Недостатки:

  • согласованность по чтению с задержкой — в принципе, сейчас это тренд;
  • нужен Oracle OCI client;
  • ограничение 2Гб на cклиент, но в целом 2 Гб — это очень много;
  • Лично для меня ключевое ограничение — это мало информации о production.

На support, который мы всегда используем, когда работаем с result_cache, я нашел всего лишь 5 багов. Это говорит о том, что, скорее всего, это мало кому нужно.

Итак, сводим в кучу все, что сказано выше.

Hand-made cache

Плохие сценарии:

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

Хорошие сценарии:

  • Есть сильная команда разработчиков БД.
  • Реализована PL/SQL логика.
  • Есть ограничения, которые не позволяют использовать другие техники кэширования.

Server side Result cache

Плохие сценарии:

  • Очень много различных результатов, которые просто вымоют весь кэш;
  • Запросы занимают больше времени, чем _RESULT_CACHE_TIMEOUT или этот параметр настроен неверно.
  • В кэш загружаются результаты из очень больших сессий параллельными потоками.

Хорошие сценарии:

  • Разумное количество кэшируемых результатов.
  • Относительно небольшие наборы данных (200–300 строк).
  • Достаточно дорогой SQL, иначе все время уйдет на защелки.
  • Более или менее статичные таблицы.
  • Есть DBA, который в случае чего придет и всех спасет.

Client side Result cache

Плохие сценарии:

  • Когда возникает та самая проблема мгновенной инвалидации.
  • Требуются thin drivers.

Хорошие сценарии:

  • Есть нормальная команда разработки среднего слоя.
  • Уже используется много SQL без использования внешнего кэширующего слоя, который можно легко подключить.
  • Есть ограничения на железки.

Выводы

Я считаю, что мой рассказ про боль Server side Result cache, поэтому выводы таковы:

  1. Всегда оценивайте размер памяти правильно с учётом с учётом количества запросов, а не количества результатов, т.е.: блоков, APEX, job, адаптивной статистики и пр.
  2. Не бойтесь использовать параметры автоматического вымывания из кэша (snapshot + shelflife).
  3. Не перегружайте кэш запросами во время загрузки больших объемов данных, отключайте result_cache перед этим. Прогревайте кэш.
  4. Убедитесь, что _result_cache_timeout соответствует вашим ожиданиям.
  5. НИКОГДА не используйте FORCE для всей базы данных. Нужна база данных в памяти — используйте специализированное in-memory решение.
  6. Проверяйте, адекватно ли используется опция FORCE для отдельных таблиц, чтобы не вышло, как у нас со сторонним ETL.
  7. Решите, так ли хороша адаптивная статистика, как ее описывает Oracle (_optimizer_ads_use_result_cache = false).

В тему этой статьи есть несколько докладов: Highload++ Siberia уже в следующий понедельник, расписание готово и опубликовано на сайте.

  • Александр Макаров (ГК ЦФТ) продемонстрирует метод выявления узких мест в работе серверной части ПО на примере БД Oracle.
  • Иван Шаров и Константин Полуэктов расскажут, какие проблемы возникают при миграциях продукта на новые версии базы данных Oracle, а также обещают дать рекомендации по организации и проведению подобных работ.
  • Николай Голов расскажет, как без распределенных транзакций и жесткой связности обеспечить целостность данных в микросервисной архитектуре.

Встретимся в Новосибирске!

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть