2009-12-02 19 views
9

Tengo una tabla con más de 20 millones de registros.Selección lenta de SQL Server de la tabla grande

estructura es como:

EventId UNIQUEIDENTIFIER 
SourceUserId UNIQUEIDENTIFIER 
DestinationUserId UNIQUEIDENTIFIER 
CreatedAt DATETIME 
TypeId INT 
MetaId INT 

tabla está recibiendo aproximadamente 100 k + registra cada día.

que tienen índices en cada columna excepto MetaId, ya que no se utiliza en 'dónde' cláusulas

El problema es cuando quiero recoger por ejemplo. los últimos 100 registros para SourceUserId deseada

La consulta a veces tarda hasta 4 minutos en ejecutarse, lo que no es aceptable.

Por ejemplo.

SELECT TOP 100 * FROM Events WITH (NOLOCK) 
WHERE SourceUserId = '15b534b17-5a5a-415a-9fc0-7565199c3461' 
AND 
(
TypeId IN (2, 3, 4) 
    OR 
(TypeId = 60 AND SrcMemberId != DstMemberId) 
) 
ORDER BY CreatedAt DESC 

No puedo hacer particiones, etc., ya que estoy usando la versión estándar de SQL Server y Enterprise es demasiado costosa.

También creo que la mesa es bastante pequeña para ser tan lenta.

Creo que el problema es con la cláusula ORDER BY ya que db debe pasar por un conjunto de datos mucho más grande.

¿Alguna idea de cómo hacerlo más rápido?

Quizás la base de datos relacional no sea una buena idea para ese tipo de datos.

de datos siempre se recogió ordenado por CreatedAt DESC

gracias por leer.

pablox

+0

¡Votando esto, ya que tengo casi exactamente el mismo problema! –

Respuesta

15

Es probable que desee para crear un índice compuesto para este tipo de consulta - cuando la consulta se ejecuta lentamente lo más probable es elegir para escanear hacia abajo un índice en la columna CreatedAt y realizar un filtro residual en el valor de SourceUserId, cuando en realidad lo que desea es saltar directamente a todos los registros de un SourceUserId determinado ordenado correctamente; para lograr esto, querrá crear un compuesto index principalmente en SourceUserId (realizando una comprobación de igualdad) y de forma secundaria en CreateAt (para conservar el orden dentro de un valor dado de SourceUserId). Es posible que desee intentar agregar el TypeId también, dependiendo de la selectividad de esta columna.

Así, los 2 que lo más probable es dar el mejor rendimiento repetible (probarlos y comparar) sería:

  1. índice en (SourceUserId, CreatedAt)
  2. índice en (SourceUserId, TypeId, CreatedAt)

como siempre, también hay muchas otras consideraciones a tener en cuenta en la determinación de cómo/qué/dónde índice, mientras Remus discute en una respuesta por separado una consideración importante está cubriendo la consulta vs manteniendo las búsquedas. Además, tendrá que considerar los volúmenes de escritura, possible fragmentation impact (if any), búsquedas de singleton frente a grandes escaneos secuenciales, etc., etc.

+2

+1: y el 'índice de cobertura 'pre-ordenados en la terminología de SQL Server. –

+1

Yeap, como con muchas otras preguntas de SQL, la respuesta no es un truco mágico SELECCIONAR sino un buen diseño de índice antiguo. –

+0

Hacer clusters de este índice y tener un disco rayado ayudaría también. –

0

me aseguraría CreatedAt está indexado correctamente

+0

¡Sería bueno comparar la consulta con y sin la cláusula 'Order By CreatedAt'! –

+0

Un índice únicamente en CreatedAt probablemente no ayudaría con este tipo de consulta, así como con un compuesto en el filtro de igualdad SourceUserId; un índice únicamente en CreatedAt simplemente permitiría que el optimizador escanee hacia atrás un B-Tree haciendo una verificación de filtro residual en SourceUserId hasta que encuentre 100 con el valor especificado (si SourceUserId es muy selectivo, esto podría causar un escaneo bastante largo). Si compila el índice en SourceUserId y luego en CreatedAt, el optimizador puede omitir el escaneo, buscar el SourceUserId proporcionado y simplemente extraer el 100. – chadhoc

0

se puede dividir la consulta en dos con una unión para evitar el quirófano (que puede causar que su índice no para ser utilizado), algo así como

SElect * FROM(
SELECT TOP 100 * FROM Events WITH (NOLOCK) 
WHERE SourceUserId = '15b534b17-5a5a-415a-9fc0-7565199c3461' 
AND TypeId IN (2, 3, 4) 
UNION SELECT TOP 100 * FROM Events WITH (NOLOCK) 
WHERE SourceUserId = '15b534b17-5a5a-415a-9fc0-7565199c3461' 
AND TypeId = 60 AND SrcMemberId != DstMemberId 
) 
ORDER BY CreatedAt DESC 

Además, verifique que los índices uniqueidentifier no están agrupados.

1

lo recomiendo conseguir los datos en tablas var 2 Sep

INSERT INTO @Table1 
SELECT * FROM Events WITH (NOLOCK) 
WHERE SourceUserId = '15b534b17-5a5a-415a-9fc0-7565199c3461' 
AND 
(
TypeId IN (2, 3, 4) 
) 
INSERT INTO @Table2 
SELECT * FROM Events WITH (NOLOCK) 
WHERE SourceUserId = '15b534b17-5a5a-415a-9fc0-7565199c3461' 
AND 
(
(TypeId = 60 AND SrcMemberId != DstMemberId) 
) 

luego aplicar una Unoin de las selecciona, ordenados y superior. Limite los datos desde el principio.

1

sugiere emplear una UNION:

SELECT TOP 100 x.* 
    FROM (SELECT a.* 
      FROM EVENTS a 
     WHERE a.typeid IN (2, 3, 4) 
     UNION ALL 
     SELECT b.* 
      FROM EVENTS b 
     WHERE b.typeid = 60 
      AND b.srcmemberid != b.dstmemberid) x 
WHERE x.sourceuserid = '15b534b17-5a5a-415a-9fc0-7565199c3461' 
6

tengo índices en cada columna excepto MetaId

índices no cubren es probable que golpear el 'tipping point' y la consulta sería volver a una escaneo de tabla. Solo agregar un índice en cada columna porque se usa en una cláusula where no equivale a un buen diseño de índice. Para tomar su consulta, por ejemplo, un buen índice de cobertura del 100% sería:

INDEX ON (SourceUserId , CreatedAt) INCLUDE (TypeId, SrcMemberId, DstMemberId) 

Siguiendo índice es también útiles, Altough que todavía va a hacer que las búsquedas:

INDEX ON (SourceUserId , CreatedAt) INCLUDE (TypeId) 

y finaly un índice w/O todos los que incluyan la columna puede ayuda, pero es tan probable será ignorado (depende de las estadísticas de la columna y estimaciones de cardinalidad):

INDEX ON (SourceUserId , CreatedAt) 

Bu t un índice separado en SourceUSerId y uno en CreatedAt es básicamente inútil para su consulta.

Ver Index Design Basics.

+0

¡Gracias! Eso me ayudó :) – pablox

0

Si se agregan 100K registros cada día, debe verificar la fragmentación de su índice. Y reconstruir o reorganizar en consecuencia. Más información: SQLauthority

+0

sí, estoy haciendo esto también – pablox

4

El hecho de que la mesa ha índices construida sobre valores GUID, indica una posible serie de problemas que podrían afectar el rendimiento:

  • alta fragmentación del índice: ya que se generan nuevos GUID aleatoriamente, el índice no puede organizarlos en un orden secuencial y los nodos se distribuyen de manera desigual.
  • Alto número de divisiones de página: el tamaño de un GUID (16 bytes) provoca muchas divisiones de página en el índice, ya que hay una mayor probabilidad de que un nuevo valor no entre en el espacio disponible en una página.
  • Comparación de valores lentos: La comparación de dos GUID es una operación relativamente lenta porque los 33 caracteres deben coincidir.

aquí un par de recursos sobre cómo investigar y resolver estos problemas:

0

Nos hemos dado cuenta de una ganancia menor moviendo a una clave BIGINT IDENTITY para nuestra tabla de eventos; al usar eso como una clave primaria agrupada, podemos hacer trampa y usar eso para ordenar la fecha.

Cuestiones relacionadas