В Соколе можно выделить следующие фазы трансляции SQL-запроса:
1. Поиск по тексту запроса готового кода исполнения в кеше (назовем его кеш №1) для текущей схемы.
2. Синтаксический анализ и параметризация запроса (константы тоже выносятся в параметры), на выходе - абстрактное синтаксическое дерево.
3. Компоновка, связывание символических объектных ссылок с объектами базы данных, на выходе - связанное синтаксическое дерево.
4. Поиск по сериализованной структуре связанного синтаксического дерева готового кода исполнения запроса в кеше (назовем его кеш №2).
5. Оптимизация:
- построение разных вариантов реляционных деревьев и выбор оптимального на основе связанного синтаксического дерева;
- построение вариаций физических планов и выбор оптимального на основе реляционных деревьев.
6. Кодогенерация:
- генерация виртуального IR (Internal Representation) кода;
- (опционально) генерация нативного JIT кода на основе LLVM.
7. Сохранение кода откомпилированного запроса в кешах запросов:
- по тексту запроса для текущей схемы в кеш №1;
- по сериализованной структуре связанного синтаксического дерева в кеш №2.
Тему устройства кешей рассмотрим в отдельном посте.
В прочих СУБД процесс трансляции может отличаться, но в целом соответствует описанной схеме.
Принципиальные отличия:
- обозначенные 2 кеша запросов (по тексту и по по сериализованной структуре связанного синтаксического дерева) реализованы на основе неблокирующих подходов;
- наличие обязательного этапа кодогенерации и на каких принципах происходит генерация кода (о чем чуть подробней далее).
JIT трансляция активируется хинтом
/*+ JIT_CODEGEN */, который нужно разместить после ключевых слов операторов SELECT/INSERT/UPDATE/DELETE. Для процедур и функций хинт должен находиться после спецификации аргументов, а для анонимных блоков после ключевого слова DECLARE.Для генерации кода используется дата-центричный подход. Идея была озвучена в статье: http://sites.computer.org/debull/A14mar/p3.pdf
Изначальная идея исполнительной системы Сокола формировалась на основе генерации нативного кода. Тем не менее, дата-центричный подход хорошо работает и для виртуального кода. Для коротких запросов из TPC-C бечмарка разница в пиковых результатах между JIT кодом и виртуальным не превышает 10%. Для более серьезных и длительных запросов JIT показывает двукратное повышение производительности. Использование виртуального кода обусловлено высокой ценой JIT-трансляции.
Другая отличительная черта исполнительной системы Сокола - это бесшовный переход исполнения от процедурного кода к коду запроса. Обе подсистемы исполнены в единой архитектуре исполнительной системы. Генерируемый код процедуры включает в себя код статических запросов, используемых внутри процедуры, без какого-либо переключения контекста (привет Ораклу).
Идею генерации кода можно развить дальше, например:
- код хранимых процедур оформить в виде динамически загружаемых библиотек. В качестве единицы трансляции (DLL-библиотека) здесь подходит пакет хранимых процедур;
- сделать трансляцию запросов в JIT быстрой (и обязательной) за счет использования собственного быстрого кодогенератора на основе подхода
copy and patch - https://en.wikipedia.org/wiki/Copy-and-patchКак оцениваете идею помещения кода хранимых процедур в DLL (не путать с DDL :) ) на фоне однородности кода хранимок и SQL-запросов, которой удалось достичь в Соколе?
С какими узкими местами в описанном процессе приходилось бороться?

