2009-07-06 15 views
7

En SQL Server (2005+) necesito indexar una columna (solo coincidencias exactas) que es nvarchar(2000+). ¿Cuál es la forma más escalable y efectiva de abordar esto?SQL Server Rendimiento del índice - columna larga

En SQL Server (2005+), ¿cuál sería la diferencia práctica en la indexación en una columna con los siguientes tipos:

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

P.ej ¿sería una búsqueda contra una columna indexada binary(16) mensurablemente más rápida que una búsqueda contra un nvarchar(2000) indexado? Si es así, ¿cuánto?

Obviamente, algo más pequeño siempre es mejor en algún aspecto, pero no estoy lo suficientemente familiarizado con la forma en que SQL Server optimiza sus índices para saber cómo se trata de la longitud.

+0

se necesita para buscar o para hacer cumplir la singularidad? –

+0

@Alex Necesito forzar la exclusividad, pero solo haré las coincidencias exactas. –

+0

Usaría disparadores. –

Respuesta

6

Usted está pensando en esto desde la dirección equivocada:

  • sí crean índices que necesita para cumplir con los objetivos de rendimiento
  • no crean índices que no es necesario

El que una la columna es binary(16) o nvarchar(2000) hace poca diferencia allí, porque no solo va a agregar índices de todas maneras.

No deje que la elección del índice dicte sus tipos de columna. Si necesita indexar un nvarchar(2000), considere un índice de texto completo o agregue un valor hash para la columna e indexe eso.


En base a su actualización, probablemente habría crear una columna suma de comprobación o una columna calculada usando la función HashBytes() y el índice de eso. Tenga en cuenta que una suma de comprobación no es lo mismo que un hash criptográfico, por lo que es más probable que tenga colisiones, pero también puede hacer coincidir todo el contenido del texto y se filtrará primero con el índice. HashBytes() es menos probable que tenga colisiones, pero aún es posible, por lo que aún necesita comparar la columna real. HashBytes también es más costoso para calcular el hash para cada consulta y cada cambio.

+0

En realidad, esa es una de las razones por las que estoy preguntando esto: ¿sería mejor indexar un hash binario corto de un campo grande? –

+0

Una columna hash solo puede buscar una coincidencia exacta. Si no necesita coincidencias parciales (LIKE 'foo%') ni rangos (ENTRE 'A' Y 'B'), entonces puede usar hashes. –

+1

Bien: ahora estamos mirando una pregunta diferente: "Necesito índice de una columna nvarchar (2000) El objetivo es hacer este tipo de ejecución de una consulta más rápida:.? ______ ¿Cómo debo hacer eso" –

6

DE CURSO un binario (16) será mucho más rápido - sólo lo hacen el más rápido de los cálculos: página

  • un SQL Server es siempre 8K
  • si tiene 16 bytes por registro, puede almacenar 500 entradas en una página
  • con 4000 bytes por entrada (nvarchar) que va a terminar con 2 entradas por página (en el peor caso, si su NVARCHAR (2000) son totalmente poblada)

Si tiene una tabla con 100'000 entradas, tendrá que tener 200 páginas para el índice con una clave binaria (16), mientras que necesitará 50,000 páginas para el mismo índice con nvarchar (2000)

Incluso sólo la e/S agregado a leer y analizar todas esas páginas que va a matar a cualquier actuación que podría haber tenido ........

Marc

ACTUALIZACIÓN:
para mis índices habituales, trato de evitar los índices compuestos tanto como puedo - hacer referencia a ellos desde otras tablas simplemente se vuelve un tanto desordenado (cláusulas WHERE con varias comparaciones de igualdad).

Además, verifique regularmente y mantenga sus índices; si tiene más del 30% de fragmentación, reconstruya, si tiene una fragmentación del 5-30%, reorganícelos. Desproteger un bien probado script de mantenimiento automático, Índice de DB en http://sqlfool.com/2009/06/index-defrag-script-v30/

Para el agrupado clave en una tabla de SQL Server, tratar de evitar de GUID ya que son de naturaleza aleatoria y por lo tanto causar la fragmentación del índice potencialmente masiva y por lo tanto daño actuación. Además, si bien no es un requisito difícil, intente asegurarse de que su clave en clúster sea única; si no lo es, SQL Server le agregará un singularizador de cuatro bytes. Además, la clave agrupada se agrega a todas y cada una de las entradas en todos y cada uno de los índices no agrupados, por lo que en la clave agrupada, es extremadamente importante tener una columna pequeña, única, estable (no cambiante) (de manera óptima es cada vez mayor) , que te ofrece las mejores características y rendimiento -> INT IDENTITY es perfecto).

+0

¿Qué más aparte de consideraciones de espacio puro? Si se almacenan otras columnas con el índice, entonces su comparación de número de páginas no es tan drástica, ¿qué otras diferencias habría? –

3

Puede tener un máximo de 900 bytes por entrada de índice, por lo que su nvarchar (2000) no funcionará.La mayor diferencia será la profundidad del índice: la cantidad de páginas que se recorrerán desde la página raíz hasta la hoja. Por lo tanto, si es necesario buscar, se puede indexar en suma de comprobación, así:

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

(ejemplo de aquí Indexes on Computed Columns: Speed Up Queries, Add Business Rules) la que no le dará una coincidencia exacta, solamente reducir su búsqueda muy bien.

Por supuesto, si necesita imponer la exclusividad, tendrá que usar activadores.

Otra idea es comprimir el nvarchar en un valor binario más pequeño e indexarlo, pero ¿puede garantizar que cada valor siempre esté comprimido a 900 bytes o menos?

+1

+1 punto excelente, sí - 900 bytes es el máximo para una entrada de índice. –

+0

Necesita un hash mucho más grande que una suma de comprobación de 32 bits. CHECKSUM devuelve int y tendrá, en el * mejor * caso, una colisión de probabilidad del 50% después de solo 64k registros, una tabla muy, muy pequeña. http://rusanu.com/2009/05/29/lockres-collision-probability-magic-marker-16777215/ –

+0

Remus, con un hash más grande que usted tendrá menos posibilidades de obtener falsos positivos, pero todavía tendrá alguna. Solo desencadena en este caso. –

2

In index max length is 900 bytes anyway, por lo que no puede indexar NVARCHAR (2000).

Una clave de índice más grande significa menos teclas en las páginas de índice, por lo que crea un árbol más grande, más disco utilizado, más E/S, más extracción del búfer, menos almacenamiento en caché. Para las claves en clúster, esto es mucho peor porque el valor de la clave agrupada se utiliza como el valor de búsqueda en todos los demás índices no agrupados, por lo que aumenta el tamaño de todos los índices.

En última instancia, la métrica de rendimiento de rendimiento más prevalente en una consulta es el número de páginas escaneadas/buscadas. Esto se traduce en lecturas físicas (= tiempo de espera de E/S) o lecturas lógicas (= contaminación de la memoria caché).

Aparte de las consideraciones de espacio, los tipos de datos hacen poca o ninguna diferencia en el comportamiento de una consulta. char/varchar/nchar/nvarchar tienen intercalaciones que deben tenerse en cuenta en las comparaciones, pero el costo de la búsqueda de orden de intercalación generalmente no es un factor decisivo.

Y por último pero no menos importante, probablemente el factor más importante, es su patrón de acceso a las aplicaciones. Indexe las columnas que hacen las consultas de SARGable, no hay absolutamente ningún beneficio en tener que mantener un índice que no sea utilizado por el optimizador.

Y a veces usted tiene que considerar los problemas de concurrencia, como cuando se tiene que eliminar deadlocks caused by distinct update access path to the same record.

actualización después del post editar

uso de una columna de hash MD5 PERSISTED:

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 

Tienes que ser muy cuidado con su busca, el hash será diferente violentamente por cualquier diferencia en la entrada, es decir, . si buscan parámetro ASCII en lugar de Unicode uno ...

Vas a tener un decent collision chance si la tabla crece grande.

0

En realidad es mejor referencia y ver por sí mismo. Por ejemplo, la siguiente secuencia de comandos compara una búsqueda de índice a través de un entero de 4 bytes frente a una búsqueda a través de un carácter de 50 bytes. Son 3 lecturas para una int (la profundidad del árbol B integrado en una columna INT) frente a 4 lecturas para una char (la profundidad del árbol B construida en una columna 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' 

Índice trata son 33% más lento para una clave más amplia, pero la mesa es 4 veces más grande:

EXEC sp_spaceused 'dbo.NarrowKey'; 
-- 32K 
EXEC sp_spaceused 'dbo.WideKey'; 
-- 136K 
Cuestiones relacionadas