Должен ли я индексировать битовое поле в SQL Server?

100

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

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

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

Jeremcc
источник
Это общий вопрос? Это может стоить того при поиске "горстки" записей, но не поможет вам в других строках. Есть ли другие способы идентифицировать данные?
Джейсон Сальдо,
4
Хотя я не думаю, что я бы индексировал ТОЛЬКО битовый столбец сам по себе, очень часто включать битовые столбцы как часть составного индекса. Простым примером может быть указатель на ACTIVE, LASTNAME, а не только на фамилию, когда ваше приложение почти всегда ищет активных клиентов.
BradC,
«Я помню, как однажды прочитал, что индексирование поля с низкой мощностью (малое количество различных значений) на самом деле не стоит делать» Это потому, что SQL Server почти всегда найдет более эффективным просто выполнить сканирование таблицы, чем читать индекс. Таким образом, ваш индекс никогда не будет использоваться, и поддерживать его - пустая трата времени. Как говорили другие, это может быть нормально в составном индексе.
DJ.
5
Я бы не согласился. Если ваше распределение составляет 50/50, вы никогда не будете использовать индекс, так как это будет быстрее сканировать таблицу. Однако, если у вас есть только 5, 1 значение и 1 миллион 0 значений, весьма вероятно, что будет использован индекс при поиске 1.
Кибби
1
В приведенном вами примере я бы предпочел поставить на первое место LastName. Это зависит от конкретной рабочей нагрузки запроса, но, как правило, наличие более избирательного столбца первым означает, что индекс будет использоваться с большей вероятностью.
Митч Уит,

Ответы:

72

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

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

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

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

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

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

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

Джефф Кокс
источник
Итак, похоже, что если бы у меня было только несколько строк, в которых битовое поле равно 1 (например, отслеживание «IsProcessed»), тогда индекс был бы хорош, потому что он упорядочит их по значению, а затем сможет выбрать небольшой рабочий набор очень быстро. Если вы согласны, добавьте это, и я приму это.
jeremcc
2
В своем предыдущем комментарии я имел в виду, что это утверждение: «Когда вы индексируете битовое поле (или некоторый узкий диапазон), вы уменьшаете рабочий набор только наполовину», неверно, если распределение сильно взвешено в сторону одного значения. Но мне нравится остальная часть вашего ответа, поэтому, если вы это исправите, я приму его.
jeremcc
1
Готово. Я думал, что для миллиона строк битовое поле будет иметь 50% -ное распределение, но вы правы в том, что для конкретной проблемной области это может значительно уменьшить рабочий набор.
Джефф Кокс,
Стоит взглянуть на планы выполнения с индексом и без него и посмотреть, используется ли индекс и действительно ли он снижает стоимость ваших запросов. Легко и научно!
onupdatecascade
А как насчет индексации битового поля + другого поля? Например. в журнале веб-активности можно проиндексировать временную метку, но другой полезный индекс может быть в битовом поле «IsHTTPS» + временная метка, чтобы быстро просмотреть все действия https. Было бы это тоже неэффективно?
ингредиент_15939
19

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

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

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

Бен Тул
источник
1
Стоит отметить, что предикат в запросе должен быть жестко привязан к значению отфильтрованного индекса. Если вы передадите значение в параметре yourBitColumn = @value, оптимизатор не сможет определить, можно ли использовать отфильтрованный индекс.
geofftnz
2
Есть способы обойти это, но вы правы; оптимизатору требуется гарантия во время компиляции, что значения для любых предикатов, соответствующих предикату отфильтрованного индекса, являются статическими / инвариантными, поскольку задача оптимизатора - создать общий план, который будет работать для любого набора параметров.
Бен Тул
9

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

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

С. Дракон 76
источник
8

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

Филипп Буше
источник
2
Если 99% строк имеют бит = 1, оптимизатор должен игнорировать индекс и выполнить сканирование таблицы. Использование индекса на самом деле будет хуже, чем сканирование таблицы, по крайней мере, на вращающемся диске, большем количестве операций ввода-вывода и непоследовательных чтений с диска. Отфильтрованный индекс (эквивалент Postgres: частичный индекс) - это правильный путь. Я полагаю, поскольку прошло много лет после вопроса, этот ответ не получил заслуженных голосов.
Эндрю Лазарь
7

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

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

BradC
источник
7
В приведенном вами примере я бы предпочел поставить на первое место LastName. Это зависит от конкретной рабочей нагрузки запроса, но, как правило, наличие более избирательного столбца первым означает, что индекс будет использоваться с большей вероятностью.
Митч Уит,
7

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

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

Обратный автомат для ранее «Новой» статьи. http://web.archive.org/web/20120201122503/http://sqlserverpedia.com/blog/sql-server-bloggers/

Новое местоположение 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-никогда-не будет использоваться.aspx

Джефф
источник
эта статья больше не видна
Homer6
@ Homer6 Я добавил ссылку на то, что выглядит как новый дом для этой статьи.
Джефф
Новая ссылка ведет на домашнюю страницу Toad World.
Северо-запад,
Нашел статью, используя машину Wayback, и нашел новую статью по теме. Надеюсь это поможет.
Джефф
2

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

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

Богдан Максим
источник
2

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

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

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

Иеремия Пешка
источник
Важна не столько мощность столбца в целом. Это избирательность предложения WHERE. Так что, если есть несколько столбцов со значением 1, индекс все равно можно индексировать. Если это 50/50 (например, мужчина / женщина), то не стоит того.
WW.
2

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

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

DJ.
источник
2

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

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

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


источник
2

Если вы хотите узнать, оказывает ли индекс желаемый эффект: протестируйте и проверьте еще раз.

В общем, вам не нужен индекс, который недостаточно сужает вашу таблицу из-за затрат на поддержание индекса. (стоимость> прибыль). Но если индекс в вашем случае сократит таблицу пополам, вы можете кое-что получить, кроме как положить это на стол. Все зависит от точного размера / структуры вашей таблицы и от того, как вы ее используете (количество операций чтения / записи).

thijs
источник
1

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

Крейг Николсон
источник
1

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

немного

Целочисленный тип данных 1, 0 или NULL.

Замечания

Столбцы типа bit не могут иметь индексы.

Да, если у вас всего несколько строк из миллионов, индекс поможет. Но если вы хотите это сделать, вам нужно сделать столбец a tinyint.

Примечание : Enterprise Manager не позволит вам создать индекс для битового столбца. Если вы хотите, вы все равно можете вручную создать индекс для битового столбца:

CREATE INDEX IX_Users_IsActiveUsername ON Users
(
   IsActive,
   Username
)

Но SQL Server 2000 на самом деле не будет использовать такой индекс - выполнение запроса, в котором индекс будет идеальным кандидатом, например:

SELECT TOP 1 Username 
FROM Users
WHERE IsActive = 0

Вместо этого SQL Server 2000 выполнит сканирование таблицы, действуя так, как будто индекса даже не существует. Если вы измените столбец на tinyint SQL Server 2000 воли делать поиск по индексу. Также следующий непокрытый запрос:

SELECT TOP 1 * 
FROM Users
WHERE IsActive = 0

Он выполнит поиск по индексу, а затем по закладке.


SQL Server 2005 имеет ограниченную поддержку индексов по битовым столбцам. Например:

SELECT TOP 1 Username 
FROM Users
WHERE IsActive = 0

вызовет поиск по индексу покрытия. Но не покрытый случай:

SELECT TOP 1 * 
FROM Users
WHERE IsActive = 0

не вызовет поиск по индексу с последующим поиском по закладкам, он будет выполнять сканирование таблицы (или сканирование кластерного индекса), а не выполнять поиск по индексу с последующим поиском по закладке.

Проверено экспериментально и прямым наблюдением.

Ян Бойд
источник
К вашему сведению - SQL Server 2005 Management Studio действительно позволяет вам это делать.
jeremcc
Моя копия SQL Server 2000 позволила мне установить индекс для битового столбца.
Кибби
Моя копия SQL Server 2000 не позволяет мне устанавливать индекс для битового столбца.
Ян Бойд
1

очень поздний ответ ...

Да, это может быть полезно по мнению команды SQL CAT (обновлено, консолидировано)

gbn
источник
1
Ссылка теперь мертва. Однако этот пост, похоже, был объединен вместе с несколькими другими в электронную книгу . Упомянутый раздел начинается на странице 86. Электронную книгу можно загрузить с сайта SQLCAT.com eBooks по ссылке «SQLCAT's Guide to Relational Engine».
mwolfe02 05
0

Это общий вопрос? Это может стоить того при поиске "горстки" записей, но не поможет вам в других строках. Есть ли другие способы идентифицировать данные?

Джейсон Сальдо
источник
0

Кардинальность - это один фактор, другой - насколько хорошо индекс разделяет ваши данные. Если у вас есть примерно половина единиц и половина нулей, это поможет. (Предполагая, что этот индекс - лучший путь для выбора, чем какой-либо другой индекс). Однако как часто вы вставляете и обновляете? Добавление индексов для производительности SELECT также ухудшает производительность INSERT, UPDATE и DELETE, так что имейте это в виду.

Я бы сказал, что если от 1 до 0 (или наоборот) не лучше, чем от 75% до 25%, не беспокойтесь.

Энтони Поттс
источник
1
Я бы не согласился. Если ваше распределение составляет 50/50, вы никогда не будете использовать индекс, так как это будет быстрее сканировать таблицу. Однако, если у вас есть только 5, 1 значение и 1 миллион 0 значений, весьма вероятно, что будет использован индекс при поиске 1.
Кибби
0

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

Стивен А. Лоу
источник
0

Иэн Бойд прав, когда говорит, что вы не можете сделать это с помощью Enterprise Manager for SQL 2000 (см. Его примечание относительно создания этого с помощью T-SQL.

Джон Б.
источник
0

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

Четан Верма
источник