2009-07-06 5 views
7

В SQL Server (2005+) мне нужно индексировать столбец (только точные совпадения), который равен nvarchar(2000+). Каков наиболее масштабируемый, эффективный способ приблизиться к этому?Производительность индекса SQL Server - длинный столбец

В SQL Server (2005+), что было бы практическое значение в индексации на колонке со следующими типами:

  • nvarchar(2000)
  • char(40)
  • binary(16)

Например будет ли поиск по индексированному столбцу binary(16) быть значительно быстрее, чем поиск по индексированному nvarchar(2000)? Если да, то сколько?

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

+0

вам нужно искать или для обеспечения уникальности? –

+0

@Alex Мне нужно обеспечить уникальность, но будем делать только точные совпадения. –

+0

Я бы использовал триггеры. –

ответ

6

Вы думаете об этом от неправильного направления:

  • ли создать индексы, необходимые для достижения целей в области производительности
  • НЕ создавать индексы вам не нужно

ли столбец binary(16) или nvarchar(2000) не имеет большого значения, потому что вы не просто добавляете индексы волей-неволей.

Не допускайте, чтобы выбор индекса определял типы столбцов. Если вам нужно индексировать nvarchar(2000), рассмотрите полнотекстовый индекс или добавьте хеш-значение для столбца и индекса, который.


На основе вашего обновления, я бы, вероятно, создать либо столбец контрольной суммы или вычисляемый столбец, используя HashBytes() функцию и индекс этого. Обратите внимание, что контрольная сумма не совпадает с криптографическим хэшем, и поэтому у вас есть несколько более вероятные столкновения, но вы также можете сопоставить все содержимое текста и сначала фильтровать индекс. У HashBytes() меньше вероятность столкновения, но это все еще возможно, поэтому вам все равно нужно сравнить фактический столбец. HashBytes также дороже для вычисления хэша для каждого запроса и каждого изменения.

+0

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

+0

Столбец хэша может искать точное совпадение. Если вам не нужны частичные совпадения (LIKE 'foo%') и диапазоны (BETWEEN 'A' AND 'B'), вы можете использовать хэши. –

+1

Итак, теперь мы рассматриваем другой вопрос: «Мне нужно индексировать столбец nvarchar (2000). Цель состоит в том, чтобы сделать этот тип запроса быстрее: ______. Как мне это сделать?» –

6

КОНЕЧНО бинарные (16) будет гораздо быстрее - просто сделать быстрые расчеты: страница

  • SQL Server всегда 8K
  • , если у вас есть 16 байт на запись, вы можете хранить 500 записей на странице
  • с 4000 байт для каждой записи (NVARCHAR), вы будете в конечном итоге с 2 записей на странице (в худшем случае, если ваш NVARCHAR (2000) полностью заселен)

Если у вас есть таблица со 100 000 записей, вам нужно будет иметь 200 страниц для индекса с двоичным (16) ключом, тогда как вам понадобится 50 000 страниц для одного индекса с nvarchar (2000)

Даже просто добавил I/O читать и просматривать все эти страницы собирается убить любую работу вы могли бы имели ........

Marc

UPDATE:
для мои обычные индексы, я стараюсь избегать сложных индексов столько, сколько я могу - ссылки на них из других таблиц просто становятся довольно грязными (предложения WHERE с несколькими сравнениями равенств).

Кроме того, регулярно проверяйте и поддерживайте свои индексы - если у вас более 30% фрагментации, перестройте - если у вас есть фрагментация 5-30%, реорганизовать. Проверьте автоматический, хорошо проверенный сценарий обслуживания индекса DB на http://sqlfool.com/2009/06/index-defrag-script-v30/

Для кластерного ключа на столе SQL Server, попытайтесь избежать GUID-х, так как они случайные характера и, таким образом, привести к потенциально массивной фрагментации индекса и поэтому больно представление. Кроме того, если вы не являетесь жестким требованием, попробуйте убедиться, что ваш кластерный ключ уникален - если это не так, SQL Server добавит к нему четырехбайтовый уникальный идентификатор. Кроме того, кластеризованный ключ добавляется к каждой записи в каждом некластеризованном индексе - поэтому в кластерном ключе чрезвычайно важно иметь небольшой, уникальный, стабильный (неизменяемый) столбец (оптимально он постоянно увеличивается , что дает вам наилучшие характеристики и производительность -> INT IDENTITY отлично).

+0

Что еще помимо соображений чистого пространства? Если в индексе хранится несколько других столбцов, то сравнение ваших # страниц не так сильно, какие другие отличия будут? –

3

У вас может быть не более 900 байт на запись индекса, поэтому ваш nvarchar (2000) не будет летать.Наибольшее различие будет представлять собой глубину индекса - количество страниц, проходящих от корня до листа листа. Так что, если вам нужно искать, вы можете индекс CHECKSUM, как это:

alter table recipe add text_checksum as checksum(recipe_text) 
create index text_checksum_ind on recipe(text_checksum) 

(пример здесь) Indexes on Computed Columns: Speed Up Queries, Add Business Rules , который не даст вам точное совпадение, только сузить свой поиск очень хорошо.

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

Еще одна идея - закрепить свой nvarchar меньшим двоичным значением и проиндексировать его, но можете ли вы гарантировать, что каждое значение всегда заархивировано до 900 байт или меньше?

+1

+1 прекрасная точка, да - 900 байт - это максимальное значение для записи индекса. –

+0

Вам нужен гораздо больший хеш, чем 32-битная контрольная сумма. CHECKSUM возвращает int, и в случае * наилучшего * вероятность столкновения 50% после всего лишь 64 тыс. Записей - очень маленькая таблица. http://rusanu.com/2009/05/29/lockres-collision-probability-magic-marker-16777215/ –

+0

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

2

In index max length is 900 bytes anyway, поэтому вы не можете индексировать NVARCHAR (2000).

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

В конечном счете самым распространенным показателем вождения в запросе является количество страниц, отсканированных/искаженных. Это означает физическое чтение (= время ожидания ввода-вывода) или логическое чтение (= загрязнение кеша).

Помимо соображений, связанных с пространством, типы данных практически не влияют на поведение запроса. char/varchar/nchar/nvarchar имеют сопоставления, которые необходимо учитывать при сравнении, но стоимость поиска порядка сортировки обычно не является решающим фактором.

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

И иногда вам приходится учитывать проблемы параллелизма, например, когда вам нужно устранить deadlocks caused by distinct update access path to the same record.

Update после после редактирования

используйте устройство PERSISTED MD5 хэш-столбец:

create table foo (
    bar nvarchar(2000) not null, 
    [hash] as hashbytes('MD5', bar) persisted not null, 
    constraint pk_hash unique ([hash])); 
go 


insert into foo (bar) values (N'Some text'); 
insert into foo (bar) values (N'Other text'); 
go 

select * from foo 
    where [hash] = hashbytes('MD5', N'Some text'); 
go 

Вы должны быть очень осторожны с вашим ищет, хэш будет дико отличаться для каких-либо различий на входе, т.е. , если вы ищете параметр Ascii вместо Unicode one ...

У вас будет decent collision chance, если ваш стол станет большим.

0

На самом деле лучше проверить и убедиться сами. Например, следующий скрипт сравнивает поиск индекса с помощью 4-байтового целого по сравнению с поиском через 50-байтовый символ. Это 3 чтения для int (глубина B-дерева, построенного на столбце INT), и 4 чтения для символа (глубина B-дерева, построенного на столбце CHAR).

CREATE TABLE dbo.NarrowKey(n INT NOT NULL PRIMARY KEY, m INT NOT NULL) 
GO 
DECLARE @i INT; 
SET @i = 1; 
INSERT INTO dbo.NarrowKey(n,m) SELECT 1,1; 
WHILE @i<1024000 BEGIN 
    INSERT INTO dbo.NarrowKey(n,m) 
    SELECT n + @i, n + @i FROM dbo.NarrowKey; 
    SET @i = @i * 2; 
END; 
GO 
DROP TABLE dbo.WideKey 
GO 
CREATE TABLE dbo.WideKey(n CHAR(50) NOT NULL PRIMARY KEY, m INT NOT NULL) 
GO 
DECLARE @i INT; 
SET @i = 1; 
INSERT INTO dbo.WideKey(n,m) SELECT '1',1; 
WHILE @i<1024000 BEGIN 
    INSERT INTO dbo.WideKey(n,m) 
    SELECT CAST((m + @i) AS CHAR(50)), n + @i FROM dbo.WideKey; 
    SET @i = @i * 2; 
END; 
GO 
SET STATISTICS IO ON; 
SET STATISTICS TIME ON; 
GO 
SELECT * FROM dbo.NarrowKey WHERE n=123456 
SELECT * FROM dbo.WideKey WHERE n='123456' 

Индекс стремится 33% медленнее, для более широкого ключа, но таблица в 4 раза превышает:

EXEC sp_spaceused 'dbo.NarrowKey'; 
-- 32K 
EXEC sp_spaceused 'dbo.WideKey'; 
-- 136K 
Смежные вопросы