Должен ли я индексировать битовое поле в SQL Server? - PullRequest
92 голосов
/ 23 октября 2008

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

Так что, если у меня есть таблица с 100 миллионами строк, и я выбираю записи, где битовое поле равно 1? И скажем, что в любой момент времени существует только несколько записей, где битовое поле равно 1 (в отличие от 0). Стоит ли индексировать это битовое поле или нет? Почему?

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

Ответы [ 19 ]

66 голосов
/ 24 октября 2008

Рассмотрим, что такое индекс в SQL - и индекс действительно является фрагментом памяти, указывающим на другие фрагменты памяти (то есть указатели на строки). Индекс разбит на страницы, так что его части можно загружать и выгружать из памяти в зависимости от использования.

Когда вы запрашиваете набор строк, SQL использует индекс для поиска строк быстрее, чем сканирование таблицы (просмотр каждой строки).

SQL имеет кластеризованные и некластеризованные индексы. Мое понимание кластерных индексов состоит в том, что они группируют одинаковые значения индексов на одной странице. Таким образом, когда вы запрашиваете все строки, соответствующие значению индекса, SQL может возвращать эти строки из кластеризованной страницы памяти. Вот почему попытка кластеризовать индекс столбца GUID - плохая идея - вы не пытаетесь кластеризовать случайные значения.

Когда вы индексируете целочисленный столбец, индекс SQL содержит набор строк для каждого значения индекса. Если у вас диапазон от 1 до 10, то у вас будет 10 указателей индекса. В зависимости от количества строк это может быть разбито на страницы по-разному. Если ваш запрос ищет индекс, соответствующий «1», а затем, где Name содержит «Fred» (при условии, что столбец «Name» не проиндексирован), SQL очень быстро получает набор строк, соответствующих «1», затем таблица сканирует, чтобы найти остальные.

Итак, что в действительности делает SQL - это пытается уменьшить рабочий набор (количество строк), который он должен перебирать.

Когда вы индексируете битовое поле (или некоторый узкий диапазон), вы только уменьшаете рабочий набор на количество строк, соответствующих этому значению. Если у вас есть небольшое количество соответствующих строк, это значительно уменьшит ваш рабочий набор. Для большого числа строк с распределением 50/50 это может принести вам очень небольшой прирост производительности по сравнению с поддержанием индекса в актуальном состоянии.

Причина, по которой все говорят о тестировании, заключается в том, что SQL содержит очень умный и сложный оптимизатор, который может игнорировать индекс, если он решит, что сканирование таблицы выполняется быстрее, или может использовать сортировку, или может организовать страницы памяти, как ему чертовски нравится.

16 голосов
/ 24 сентября 2013

Я только что натолкнулся на этот вопрос другим способом. Если предположить, что ваше утверждение о том, что только горстка записей принимает значение 1 (и это те, которые вас интересуют), тогда отфильтрованный индекс может быть хорошим выбором. Что-то вроде:

create index [IX_foobar] on dbo.Foobar (FooID) where yourBitColumn = 1

Это создаст существенно меньший индекс, который оптимизатор будет достаточно умен, чтобы использовать его, когда это предикат в вашем запросе.

9 голосов
/ 24 октября 2008

100 миллионов записей, и только у немногих битовое поле установлено в 1? Да, я думаю, что индексирование битового поля определенно ускорит запрос записей с битом = 1. Вы должны получить логарифмическое время поиска из индекса, а затем только коснуться нескольких страниц с битами = 1 записей. В противном случае вам нужно будет коснуться всех страниц таблицы записей с 100 миллионами.

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

7 голосов
/ 21 мая 2015

Если ваш дистрибутив довольно известен и несбалансирован, например, 99% строк имеют бит = 1, а 1% - бит = 0, когда вы выполняете предложение WHERE с битом = 1, полное сканирование таблицы будет происходить вокруг в то же время, как индекс сканирования. Если вы хотите получить быстрый запрос, где бит = 0, лучший способ, который я знаю, - это создать отфильтрованный индекс, добавив предложение WHERE bit = 0. Таким образом, этот индекс будет хранить только строку 1%. Затем выполнение WHERE bit = 0 просто позволит оптимизатору запросов выбрать этот индекс, и все строки из него будут иметь бит = 0. У вас также есть преимущество, заключающееся в том, что требуется очень небольшое количество дискового пространства, сравнивая полный индекс на бит .

7 голосов
/ 09 января 2009

Если вы еще не читали это, Джейсон Масси недавно написал статью, в которой обсуждалась эта тема.

http://statisticsio.com/Home/tabid/36/articleType/ArticleView/articleId/302/Never-Index-a-BIT.aspx

Редактировать: Новое местоположение статьи - http://sqlserverpedia.com/blog/sql-server-bloggers/never-index-a-bit

Wayback machine для ранее "Нового" местоположения статьи: http://web.archive.org/web/20120201122503/http://sqlserverpedia.com/blog/sql-server-bloggers/never-index-a-bit/

Новая папка SQL Server Pedia - Toadworld, в которой есть новая статья Кеннета Фишера, посвященная этой теме:

http://www.toadworld.com/platforms/sql-server/b/weblog/archive/2014/02/17/dba-myths-an-index-on-a-bit-column-will-never-be-used.aspx

Машина обратного хода: http://web.archive.org/web/20150508115802/http://www.toadworld.com/platforms/sql-server/b/weblog/archive/2014/02/17/dba-myths-an-index-on-a-bit-column-will-never-be-used.aspx

7 голосов
/ 23 октября 2008

Хотя я не думаю, что индексировал бы JUST сам по себе битовый столбец, очень часто включать битовые столбцы как часть составного индекса.

Простым примером будет индекс ACTIVE, LASTNAME вместо просто фамилии, когда ваше приложение почти всегда ищет активных клиентов.

2 голосов
/ 24 октября 2008

Если ваша цель состоит в том, чтобы сделать запрос для записей, где значение битового поля равно 1, вы можете попробовать индексированное представление вашей базовой таблицы, которое содержит только записи, в которых ваше битовое поле равно 1. В корпоративной версии, если запрос может использовать индексированное представление вместо указанной таблицы для повышения производительности запроса, он будет использовать это представление. Теоретически это увеличит скорость запросов на выборку, которые ищут только записи со значением битового поля «1».

http://www.microsoft.com/technet/prodtechnol/sql/2005/impprfiv.mspx

Все это предполагает, что вы Microsoft SQL Server 2005 Enterprise. То же самое может относиться к 2008 году, я не знаком с этой версией.

2 голосов
/ 24 октября 2008

«Я помню, как однажды читал, что индексирование поля с низкой мощностью (малое количество различных значений) не стоит делать»

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

2 голосов
/ 23 октября 2008

Как уже говорили другие, вы захотите измерить это. Я не помню, где я читал это, но столбец должен иметь очень высокую мощность (около 95%), чтобы индекс был эффективным. Лучшим тестом для этого будет создание индекса и проверка планов выполнения для значений 0 и 1 поля BIT. Если вы видите операцию поиска по индексу в плане выполнения, то вы знаете, что ваш индекс будет использоваться.

Ваш лучший способ действий - это проверить с помощью базового SELECT * FROM table WHERE BitField = 1; Выполняйте запрос и медленно наращивайте функциональность оттуда шаг за шагом, пока у вас не появится реалистичный запрос для вашего приложения, проверяя план выполнения с каждым шагом, чтобы убедиться, что поиск по индексу все еще используется. Следует признать, что нет гарантии, что этот план выполнения будет использоваться в производстве, но есть хорошие шансы, что он будет.

Некоторую информацию можно найти на форумах sql-server-performance.com и в упомянутой статье

2 голосов
/ 23 октября 2008

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...