Поиск SQL в кластерном индексе SQL Server медленный для определенных диапазонов

На моей работе в настоящее время у нас есть таблица с 50 миллионами строк, которая имеет индекс по двум столбцам Varbinary(16), которые являются ip_start и ip_end.

PRIMARY KEY CLUSTERED 
(
    [ip_end] ASC,
    [ip_start] ASC
)

Первые несколько строк в таблице выглядят так:

ip_start   ip_end      id
0x00000000  0x00000000  0
0x00000001  0x000000FF  1
0x00000100  0x00FFFFFF  2
0x01000000  0x010000FF  3

Запрос, который мы используем для поиска совпадений:

SELECT TOP 1 id
FROM dbo.ip_ranges WITH (NOLOCK)
WHERE @lookup <= ip_end AND @lookup >= ip_start

Когда я смотрю IP как 0x00000002 он мгновенно возвращает id 1, но если я ищу диапазон, который находится между 0x000000000000001 для возврата NULL требуется несколько секунд. Разве SQL Server не должен понимать, что индекс varbinary упорядочен и поэтому быстро возвращается, если нет совпадений?

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

1 ответ

Разве SQL Server не должен понимать, что индекс varbinary упорядочен и поэтому быстро возвращается, если нет совпадений?

SQL Server понимает, что индекс упорядочен, но не понимает, что диапазоны не перекрываются. Это условие @lookup >= ip_start Это верно для нескольких диапазонов IP-адресов (в среднем около половины), и это производительность, которую вы видите для несоответствия. Индекс B-Tree не использует второй ключ для поиска индекса, когда первый ключ имеет неравенство.

К сожалению, стандартные индексы B-Tree не оптимальны для этого типа поиска (неравенства по двум измерениям). R-дерево (которое я изначально изучил как RD-дерево) лучше подходит. Они используются в основном для пространственных индексов.

Я думаю, что у меня был успех с запросом, подобным этому:

SELECT ir.*
FROM (SELECT TOP 1 ir.*
      FROM dbo.ip_ranges ir
      WHERE @lookup >= ip_start
      ORDER BY ip_start
     ) ir
WHERE @lookup <= ir.ip_end ; 

SQL Server должен использовать индекс для подзапроса, чтобы быстро найти первую подходящую строку. Затем можно отдельно проверить, находится ли конец диапазона в этом ряду. Это работает, потому что диапазоны IP-адресов не перекрываются.

Создать некластеризованный индекс на ip_start с включенным идентификатором столбца

Или обновите кластерный индекс для одного столбца ip_start и создайте некластеризованный на ip_end с идентификатором включаемого столбца

Другие вопросы по тегам