Разбивка на страницы в SQL Server

17

У меня очень большая база данных, примерно 100 ГБ. Я выполняю запрос:

select * from <table_name>;

и я хочу показать только от 100 до 200 строк.

Я хочу понять, как это происходит внутри. Извлекает ли база данных все записи с диска в память и отсылает от 100 до 400 строк запрашивающему клиенту? Или существует какой-либо механизм, так что только те записи (100-200) выбираются из базы данных - с использованием механизма индексации, такого как B-деревья и т. Д.?

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

AV94
источник

Ответы:

37

В запросе вы разместили:

select * from <table_name>;

Нет такой вещи, как сотые-200-е строки, потому что вы не указываете ORDER BY. Заказ не гарантируется, если вы не включите ORDER BY по целому ряду интересных причин, но здесь дело не в этом.

Итак, чтобы проиллюстрировать вашу точку зрения, давайте использовать таблицу - я собираюсь использовать таблицу Users из дампа данных переполнения стека и выполнить этот запрос:

SELECT * FROM dbo.Users ORDER BY DisplayName;

По умолчанию в поле DisplayName нет индекса, поэтому SQL Server должен просканировать всю таблицу, а затем отсортировать ее по DisplayName. Вот план выполнения :

Сканирование кластерного индекса с сортировкой

Это не красиво - это много работы с оценочной стоимостью поддерева около 30 тыс. (Вы можете увидеть это, наведя указатель мыши на оператор выбора в PasteThePlan.) Так что же произойдет, если нам нужны только строки 100-200? Мы можем использовать этот синтаксис в SQL Server 2012+:

SELECT * FROM dbo.Users ORDER BY DisplayName OFFSET 100 ROWS FETCH NEXT 100 ROWS ONLY;

План выполнения этого тоже довольно уродлив:

Сканирование кластерного индекса с сортировкой и вершиной

SQL Server все еще сканирует всю таблицу, чтобы создать отсортированный список, чтобы получить 100–200 строк, а стоимость по-прежнему составляет около 30 тыс. Хуже того, весь этот список будет перестраиваться при каждом выполнении вашего запроса (потому что, в конце концов, кто-то мог изменить свое DisplayName.)

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

CREATE INDEX IX_DisplayName ON dbo.Users(DisplayName);

С этим индексом план выполнения нашего запроса теперь выполняет поиск по индексу:

Поиск по индексу и поиск ключа

Запрос завершается мгновенно и имеет приблизительную стоимость поддерева всего 0,66 (в отличие от 30k).

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

Брент Озар
источник
«По умолчанию в поле DisplayName нет индекса, поэтому SQL Server должен отсканировать всю таблицу, а затем отсортировать ее по DisplayName». Простите, если это очень простой вопрос - в том случае, когда я цитировал ваш ответ: Когда вы сказал "Сканирование всей таблицы", означает ли это, что все данные будут занесены в память и отсортированы (что не выглядит правильным образом)?
AV94
Из вашего ответа я понимаю, что если поле проиндексировано, то создание запросов типа - получить 100-ю-200-ю строку очень эффективно, поскольку SQL просматривает индекс (B-дерево и т. Д.) И напрямую переходит к этой точке (100-я строка). Не могли бы вы сказать мне, если это правильное понимание?
AV94
@AnilVedala о вашем первом вопросе - да, данные должны быть отсортированы. Как еще база данных может сделать это с помощью несортированного списка?
Брент Озар
1
@AnilVedala о вашем втором вопросе - вот куда приходит последний план выполнения, который я вам дал. (Если вы спрашиваете о том, как прочитать план выполнения, возьмите книгу «Планы выполнения» Гранта Фричи.)
Брент Озар
15

Как дополнение к ответу Брента при использовании непокрытого индекса, чтобы избежать сортировки, существует потенциальная проблема с более поздними номерами страниц, которую можно увидеть при выполнении приведенного ниже

SELECT * 
FROM dbo.Users 
ORDER BY DisplayName 
OFFSET 100000 ROWS 
FETCH NEXT 100 ROWS ONLY;

План выполнения показывает, что поиск был выполнен 100 100 раз, хотя все операторы, кроме 100, затем фильтруются оператором TOP.

введите описание изображения здесь

Это может быть смягчено с помощью шаблона ниже

WITH T
     AS (SELECT Id,
                DisplayName
         FROM   dbo.Users
         ORDER  BY DisplayName
        OFFSET 100000 ROWS 
        FETCH NEXT 100 ROWS ONLY
        )
SELECT U.*
FROM   dbo.Users U
       JOIN T
         ON U.Id = T.Id
ORDER  BY T.DisplayName 

Это отфильтровывает все, кроме последних 100 строк перед выполнением поиска, что может оказать значительное влияние на скорость при больших значениях смещения.

введите описание изображения здесь

Мартин Смит
источник
3

Это действительно зависит от того, как вы реализуете нумерацию страниц в своем запросе, характер данных и способ настройки вашей системы. Можно с уверенностью сказать, что SQL Server будет пытаться вернуть ваши данные, используя то, что, по его мнению, требует минимальных усилий. Если у вас нет явного порядка сортировки, фильтрации, группировки или какого-либо управления окнами, то SQL Server может оптимизировать план запроса таким образом, чтобы он мог возвращать только те страницы с диска, которые содержали данные, необходимые для вашего запроса, или, что еще лучше, непосредственно из буферный пул. Как только вы начнете изменять запрос, включив в него сортировку, группировку, управление окнами и фильтрацию, он начинает усложняться.

Существует очень хорошая статья о SQL Performance здесь , который идет в детали различных методов верстки и как они влияют на план выполнения запроса. Я настоятельно рекомендую прочитать его, а затем опробовать некоторые из различных методов, которые они указывают, и посмотреть, какой план запроса выбран в вашей собственной системе.

Mr.Brownstone
источник