Вопрос, возникший в чате:
Я знаю, что внутреннее переключение хеш-соединения используется для создания вложенных циклов.
Что делает SQL Server для восстановления совокупного хэша (если это вообще может произойти)?
Вопрос, возникший в чате:
Я знаю, что внутреннее переключение хеш-соединения используется для создания вложенных циклов.
Что делает SQL Server для восстановления совокупного хэша (если это вообще может произойти)?
Ответы:
Оба хеш-соединения и хеш-агрегаты используют внутри один и тот же код оператора, хотя хеш-агрегат использует только один вход (сборка). Основная работа агрегата хэша будет описана Craig Freedman :
Как и в случае с хеш-соединением, для хеш-агрегата требуется память. Перед выполнением запроса с агрегатом хэшей SQL Server использует оценки количества элементов, чтобы оценить, сколько памяти нам нужно для выполнения запроса. При хеш-соединении мы сохраняем каждую строку сборки, поэтому общая потребность в памяти пропорциональна количеству и размеру строк сборки. Количество соединяемых строк и выходная мощность объединения не влияют на требования к объему памяти для объединения. С помощью хэш-агрегата мы сохраняем по одной строке для каждой группы, поэтому общее требование к памяти фактически пропорционально количеству и размеру выходных групп или строк. Если у нас меньше уникальных значений группы по столбцу (столбцам) и меньше групп, нам нужно меньше памяти. Если у нас есть больше уникальных значений для группы по столбцу (столбцам) и больше групп, нам нужно больше памяти.
Он продолжает говорить о рекурсии хэша:
Итак, что произойдет, если у нас закончится память? Опять же, подобно хэш-соединению, если нам не хватает памяти, мы должны начать проливать строки в базу данных tempdb. Мы разливаем один или несколько сегментов или разделов, включая любые частично агрегированные результаты, а также любые дополнительные новые строки, которые хэшируют разлитые сегменты или сегменты. Хотя мы не пытаемся агрегировать новые строки, мы хэшируем их и делим их на несколько сегментов или сегментов. Как только мы закончим обработку всех входных групп, мы выводим законченные группы в памяти и повторяем алгоритм, считывая и агрегируя один разлитый раздел за раз. Разделив пробеленные строки на несколько разделов, мы уменьшаем размер каждого раздела и, таким образом, уменьшаем риск повторения алгоритма много раз.
Вывод хэша слегка документирован, но упоминается Начо Алонсо Портильо в Каков максимальный уровень рекурсии для итератора хэша до его форсирования?
Значение является постоянным, жестко закодированным в продукте, и его значение равно пяти (5). Это означает, что до того, как оператор сканирования хэша прибегнет к алгоритму сортировки для любого данного подраздела, который не помещается в предоставленную память из рабочего пространства, должно было произойти пять предыдущих попыток подразделить исходный раздел на более мелкие разделы.
Упомянутый «оператор сканирования хеша» содержит ссылку на внутренний класс CQScanHash
в sqlmin.dll
. Этот класс возглавляет реализацию хеш-оператора (во всех его формах, включая частичные агрегаты и отдельные потоки), которые мы видим в планах выполнения.
Это подводит нас к сути ваших вопросов - что именно делает алгоритм спасения? Является ли это "сортировка на основе" или на основе "нечто вроде вложенных циклов"?
Это возможно оба, в зависимости от вашей точки зрения. Когда рекурсия хеша достигает уровня 5, хеш-раздел в памяти изменится с хеш-таблицы на изначально пустой индекс b-дерева для значений хеш-функции. Каждая строка из одного ранее разлитого хеш-раздела ищется в индексе b-дерева и вставляется (новая группа) или обновляется (поддерживая агрегаты) в зависимости от ситуации.
Эта серия неупорядоченных вставок в b-дерево также может рассматриваться как сортировка вставок или как поиск по индексированным вложенным циклам.
В любом случае этот резервный алгоритм гарантированно завершится в конечном итоге без выделения большего количества памяти. Может потребоваться несколько проходов, если места, доступного для b-дерева, недостаточно для хранения всех ключей группировки и агрегатов из раздела переполнения.
После исчерпания памяти, доступной для хранения индекса b-дерева, любые дополнительные строки (из текущего разлитого раздела) отправляются в один новый раздел tempdb (который гарантированно будет меньше), и процесс повторяется по мере необходимости. Уровень разлива остается на уровне 5, поскольку рекурсия хеша завершена. Некоторые детали обработки можно наблюдать с помощью недокументированного флага трассировки 7357.