Firebird, group by и временные файлы
Покопался в исходниках Firebird (2.5.2) на тему размеров временных файлов.
В итоге, там картина примерно такая:
* Запрос с условием и group by, входной датасет - десятки миллионов записей, результатирующий датасет - 150000 записей, размер записи порядка 150 байт (varchar расширены до максимального размера).
* Для выполнения запроса, подпадающие под условие записи (десятки миллионов) пишутся в сортировщик, затем досортировываются и выгребаются из него в правильном порядке с вычислением аггрегатов.
* Сортировщик реализован поверх временного хранилища данных, которое в свою очередь представляет последовательность блоков размером 1 мб, поначалу находящихся в памяти, а по мере превышения порога (8 МБ по умолчанию для классик-сервера) - записываемых во временные файлы. По виду это все крайне напоминает самодельную реализацию pagefile.
* Временные файлы расширяются кусками по 64 мб, с записью 262144 байтных буферов с нулями в эти куски.
Что здесь очень мне не нравится - это то, что обмен с временными файлами в принципе не выровнен по границам блоков диска, а является кратным размеру записи. Вот, например, из отладочного лога TempSpace::write: 2898976500 1048500
Второе - по-моему, конкретно для данного случая положить готовый результат group by в памяти в виде какого-нибудь дерева поиска и считать аггрегаты на ходу в него было бы гораздо эффективнее, чем долбится в i/o. Фактически мы при запросе читаем всю таблицу, пишем часть прочитанного в временный файл, потом сортируем, потом читаем обратно.
Но, возможно, нету эффективных способов отличить небольшой размер результата от большого размера, который эффективнее делать через сортировку.
В итоге, там картина примерно такая:
* Запрос с условием и group by, входной датасет - десятки миллионов записей, результатирующий датасет - 150000 записей, размер записи порядка 150 байт (varchar расширены до максимального размера).
* Для выполнения запроса, подпадающие под условие записи (десятки миллионов) пишутся в сортировщик, затем досортировываются и выгребаются из него в правильном порядке с вычислением аггрегатов.
* Сортировщик реализован поверх временного хранилища данных, которое в свою очередь представляет последовательность блоков размером 1 мб, поначалу находящихся в памяти, а по мере превышения порога (8 МБ по умолчанию для классик-сервера) - записываемых во временные файлы. По виду это все крайне напоминает самодельную реализацию pagefile.
* Временные файлы расширяются кусками по 64 мб, с записью 262144 байтных буферов с нулями в эти куски.
Что здесь очень мне не нравится - это то, что обмен с временными файлами в принципе не выровнен по границам блоков диска, а является кратным размеру записи. Вот, например, из отладочного лога TempSpace::write: 2898976500 1048500
Второе - по-моему, конкретно для данного случая положить готовый результат group by в памяти в виде какого-нибудь дерева поиска и считать аггрегаты на ходу в него было бы гораздо эффективнее, чем долбится в i/o. Фактически мы при запросе читаем всю таблицу, пишем часть прочитанного в временный файл, потом сортируем, потом читаем обратно.
Но, возможно, нету эффективных способов отличить небольшой размер результата от большого размера, который эффективнее делать через сортировку.
no subject
no subject
Проблема в том, что классик-сервер создает по процессу на каждого юзера и если туда поставить хотя бы немного осмысленный лимит памяти - на серваке после 2-3 юзеров кончится память :)
no subject
(или хотя бы в более приличном sql)
no subject
На данный момент будет быстрее сделать мемоизацию результатов запросов с проверками на валидность, чуть-чуть подкрутить размеры сортировочных записей и прочую оптимизацию, чем мигрировать на другую СУБД.
no subject
no subject
no subject
У меня уже с сервером были проблемы, когда там кончалась память, от чего все дохло с концами. Но это проблема не столько в FB, сколько в том, что это запущено в не совсем вменяемом окружении.
no subject
no subject
при этом вопрос о "как кэшировать/аллоцировать замапленое" решает ОС и, по идее, решает кошерно.
no subject
no subject
no subject
no subject
no subject
По моим представлениям, это не критично, оверхед минимален. Главное, чтобы размер однократно читаемого куска был побольше, а число чтений соответственно — поменьше.
no subject
no subject
Это называется Sort-based group by.
* Сортировщик реализован поверх временного хранилища данных, которое в свою очередь представляет последовательность блоков размером 1 мб, поначалу находящихся в памяти, а по мере превышения порога (8 МБ по умолчанию для классик-сервера) - записываемых во временные файлы.
Это есть в любой СУБД
> По виду это все крайне напоминает самодельную реализацию pagefile.
Именно так, и только так и можно работать.
> Временные файлы расширяются кусками по 64 мб, с записью 262144 байтных буферов с нулями в эти куски.
Угу
no subject
это называется Hash Group By. Для него нужно, чтобы ключи влезли в память, и нифига не факт, что они лезут.
Впрочем, это уже фундаментальная проблема любой СУБД - сделать правильный estimate для row count, и на основе этой информации выбрать между sort, hash и index алгоритмами
no subject
Ребе, вы сами всё поняли! В данном случае нужно знать unique cardinality по некоторому ключу.
Если у вас по этому ключи есть B-Tree индекс - estimate сделать легко, а вот если нету - сосите хуй. СУБД в такой ситуции работает по пессимистичному сценарию, и это правильно.
Другой вопрос в том, что можно научить её обучаться по итогам выполнения запросов.
no subject