Кеширование результатов маскирования и сериализации
Описание
Shared Pool — универсальный пул разделяемой памяти, предназначенный для хранения сериализованных бинарных данных, доступных всем серверным процессам СУБД Pangolin (бэкенд-процессам).
В состав продукта включено расширение shared_pool_view для просмотра Shared Pool.
Решение направлено на повышение производительности при повторном использовании результатов вычислений, таких как маскированные SQL-запросы или сериализованные планы выполнения.
Библиотека Shared Pool предоставляется как внутренняя инфраструктура, доступная другим подсистемам СУБД Pangolin (например, модулям маскирования и трассировки планов).
Реализация обеспечивает:
- безопасный многопроцессный доступ;
- универсальный API для сохранения и извлечения бинарных данных;
- эффективное управление памятью без фрагментации.
Общая структура
Shared Pool размещается в разделяемой памяти (shared memory) и представляет собой хеш-таблицу, индексируемую 64-битными целочисленными ключами.
Количество записей задается конфигурационным параметром (GUC) и изменяется только при перезапуске СУБД.
Каждый элемент хранилища Shared Pool включает следующую информацию:
- уникальный 64-разрядный ключ вместе с рассчитанным значением хеша;
- адрес области динамически распределяемой памяти (Dynamic Shared Area – DSA), где находятся двоичные данные записи;
- отметку времени последней операции чтения или изменения (
last_access); - элемент двустороннего связного списка (необходим для механизма вытеснения наименее используемых данных — LRU);
- LWLocks для защиты одновременного доступа к ресурсу несколькими потоками.
Настройка
Механизм работы
Поиск записи
Поиск выполняется под разделяемой (shared) блокировкой соответствующей партиции хеш-таблицы.
Операция обеспечивает исключительно чтение структур данных, но при каждом успешном поиске обновляется поле last_access соответствующей записи. Обновление является атомарным и выполняется без захвата эксклюзивных блокировок.
Другие изменения структуры Shared Pool не выполняются на этапе чтения и поиска, включая:
- обновление LRU-списков;
- удаление записей;
- вставку новых элементов.
Алгоритм поиска:
-
По ключу определяется партиция хеш-таблицы.
-
Под shared-блокировкой выполняется поиск элемента.
-
Если запись найдена:
- возвращается указатель на
entry; - поле
last_accessобновляется.
Если запись отсутствует – возвращается
NULL. - возвращается указатель на
Вставка записи
Вставка возможна только если предварительный поиск вернул NULL. Операция вставки выполняется под эксклюзивной блокировкой партиции.
Алгоритм вставки:
-
Клиент подготавливает бинарные данные для размещения в Shared Pool.
-
Механизм Shared Pool захватывает эксклюзивную блокировку партиции.
-
Повторно выполняется поиск по ключу.
Если запись уже существует – вставка считается успешной, данные клиента не сохраняются. Если записи нет – выполняется попытка вставки.
Вставка считается успешной, если:
- в хеш-таблице есть свободное место;
- или алгоритм вытеснения успешно освободил место;
- или запись появилась между поисками.
Вставка считается неуспешной, если пул достиг максимальной вместимости и алгоритм вытеснения не смог освободить место (например, целевая партиция оказалась пустой).
В случае неуспешной вставки вызывающий код получает диагностическую информацию и должен обработать ситуацию самостоятельно.
Удаление записи (вытеснение)
Удаление записи выполняется только в рамках операции вставки, когда требуется освободить место в хеш-таблице. Прямая операция удаления по инициативе вызывающего кода не предоставляется.
Для выбора записи-кандидата используется приближенный алгоритм LRU:
- Для каждой партиции поддерживается собственный двусвязный LRU-список.
- При необходимости вытеснения рассматриваются последние четыре элемента списка.
- Среди них выбирается запись с минимальным значением
last_access. - Запись удаляется из хеш-таблицы и ее ресурсы освобождаются.
Никакие операции чтения не инициируют удаление или изменение структуры LRU. Корректность механизма вытеснения обеспечивается только в функции вставки. Таким образом, удаление происходит только внутри той партиции, в которую выполняется вставка.
Расчет ключей
Shared Pool использует 64-битные ключи, вычисляемые из исходных данных.
Для различных сценариев применяются отдельные стратегии:
-
Маскирование SQL-запросов:
Ключ формируется на основе структуры запроса, игнорируя константы. Это реализуется путем модификации компонента лексического анализа (
scan.l): при разборе запроса литералы не участвуют в формировании ключа, а идентификаторы и ключевые слова добавляют свои значения в итоговую контрольную сумму.Таким образом, запросы с одинаковой структурой и разными константами попадают под один ключ, что предотвращает дублирование маскированных строк.
-
Сериализация планов:
Ключом служит хеш от полного текстового представления плана.