Tengo una tabla en SQL Server 2014 que se parece a la siguiente:
CREATE TABLE dbo.MyTable
(
[id1] [bigint] NOT NULL,
[id2] [bigint] NOT NULL,
[col1] [int] NOT NULL default(0),
[col2] [int] NOT NULL default(0)
)
con (id1, id2) siendo la PK. Básicamente, id1 es un identificador para agrupar un conjunto de resultados (id2, col1, col2), cuyo pk es id2.
Estoy tratando de usar una tabla en memoria para deshacerme de una tabla basada en disco existente, que es mi cuello de botella.
- Los datos en la tabla se escriben -> leer -> borrados una vez.
- Cada valor de id1 tiene varios (decenas / cientos de) miles de id2.
- Los datos se almacenan en la tabla durante un período de tiempo muy corto, por ejemplo, 20 segundos.
Las consultas realizadas en esta tabla son las siguientes:
-- INSERT (can vary from 10s to 10,000s of records):
INSERT INTO MyTable
SELECT @fixedValue, id2, col1, col2 FROM AnotherTable
-- READ:
SELECT id2, col1
FROM MyTable INNER JOIN OtherTbl ON MyTable.id2 = OtherTbl.pk
WHERE id1 = @value
ORDER BY col1
-- DELETE:
DELETE FROM MyTable WHERE id1 = @value
Aquí está la definición actual que usé para la tabla:
CREATE TABLE dbo.SearchItems
(
[id1] [bigint] NOT NULL,
[id2] [bigint] NOT NULL,
[col1] [int] NOT NULL default(0),
[col2] [int] NOT NULL default(0)
CONSTRAINT PK_Mem PRIMARY KEY NONCLUSTERED (id1,id2),
INDEX idx_Mem HASH (id1,id2) WITH (BUCKET_COUNT = 131072)
) WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_ONLY)
Desafortunadamente, esta definición da como resultado una degradación del rendimiento con respecto a la situación anterior con una tabla basada en disco. El orden de magnitud es más o menos un 10% más alto (que en algunos casos alcanza el 100%, entonces el doble de tiempo).
Sobre todo, esperaba obtener una superventaja en escenarios de alta concurrencia, dada la arquitectura sin bloqueo anunciada por Microsoft. En cambio, las peores actuaciones son exactamente cuando hay varios usuarios concurrentes que ejecutan varias consultas en la tabla.
Preguntas:
- ¿Cuál es el BUCKET_COUNT correcto para establecer?
- ¿Qué tipo de índice debo usar?
- ¿Por qué el rendimiento es peor que con la tabla basada en disco?
Una consulta de sys.dm_db_xtp_hash_index_stats devuelve:
total_bucket_count = 131072 empty_bucket_count = 0 avg_chain_len = 873 max_chain_length = 1009
Cambié el conteo del depósito, por lo que la salida de sys.dm_db_xtp_hash_index_stats es:
total_bucket_count = 134217728 empty_bucket_count = 131664087 avg_chain_len = 1 max_chain_length = 3
Aún así, los resultados son casi los mismos, si no peores.
fuente
OPTION(OPTIMIZE FOR UNKNOWN)
(ver Sugerencias de tabla )?select * from sys.dm_db_xtp_hash_index_stats
? Además, este enlace debe responder a la mayoría de sus preguntas: msdn.microsoft.com/en-us/library/…Respuestas:
Si bien esta publicación no será una respuesta completa debido a la falta de información, debería ser capaz de orientarlo en la dirección adecuada o, de lo contrario, obtener información que luego podrá compartir con la comunidad.
Esto es preocupante ya que definitivamente no debería ser el caso. Ciertas cargas de trabajo no son para tablas de memoria (SQL 2014) y algunas cargas de trabajo se prestan a ello. En la mayoría de las situaciones, puede haber un aumento mínimo en el rendimiento simplemente migrando y eligiendo los índices adecuados.
Originalmente estaba pensando muy estrechamente sobre sus preguntas con respecto a esto:
Inicialmente creía que había un problema con la tabla de memoria real y los índices no eran óptimos. Si bien hay algunos problemas con la definición del índice de hash optimizado para la memoria, creo que el problema real es con las consultas utilizadas.
Este inserto debería ser extremadamente rápido si solo involucrara la tabla en memoria. Sin embargo, también involucra una tabla basada en disco y está sujeta a todos los bloqueos y bloqueos asociados con eso. Por lo tanto, la pérdida de tiempo real aquí está en la tabla basada en disco.
Cuando hice una prueba rápida contra la inserción de 100,000 filas de la tabla basada en el disco después de cargar los datos en la memoria, fueron tiempos de respuesta de menos de un segundo. Sin embargo, la mayoría de sus datos solo se conservan durante un período de tiempo muy corto, menos de 20 segundos. Esto no le da mucho tiempo para vivir realmente en caché. Además, no estoy seguro de qué tan grande
AnotherTable
es realmente y no sé si los valores se leen del disco o no. Tenemos que confiar en usted para estas respuestas.Con la consulta Seleccionar:
Nuevamente, estamos a merced del rendimiento de la tabla basada en interoperabilidad + disco. Además, los tipos no son baratos en los índices HASH y se debe usar un índice no agrupado. Esto se menciona en la guía de índice que vinculé en los comentarios.
Para dar algunos datos reales basados en la investigación, cargué la
SearchItems
tabla en memoria con 10 millones de filas yAnotherTable
con 100,000 ya que no sabía el tamaño real o las estadísticas de la misma. Luego usé la consulta de selección anterior para ejecutar. Además, creé una sesión de eventos extendidos en wait_completed y la puse en un búfer de anillo. Se limpió después de cada ejecución. También corríDBCC DROPCLEANBUFFERS
para simular un entorno donde todos los datos pueden no residir en la memoria.Los resultados no fueron nada espectaculares al mirarlos en el vacío. Dado que la computadora portátil en la que estoy probando esto está usando un SSD de mayor grado, reduje artificialmente el rendimiento basado en disco para la VM que estoy usando.
Los resultados llegaron sin información de espera después de 5 ejecuciones de la consulta solo en la tabla basada en memoria (eliminando la unión y sin subconsultas). Esto es más o menos como se esperaba.
Sin embargo, cuando utilicé la consulta original, tuve que esperar. En este caso, fue PAGEIOLATCH_SH lo que tiene sentido ya que los datos se leen del disco. Ya que soy el unico usuario en este sistema y no dediqué tiempo a crear un entorno de prueba masivo para inserciones, actualizaciones y eliminaciones en la tabla unida, no esperaba que entrara en vigencia ningún bloqueo o bloqueo.
En este caso, una vez más, la porción significativa de tiempo se gastó en la tabla basada en disco.
Finalmente la consulta de eliminación. Encontrar las filas basadas solo en ID1 no es extremadamente eficiente con un índice has. Si bien es cierto que los predicados de igualdad son para lo que son adecuados los índices hash, el depósito en el que se encuentran los datos se basa en las columnas hash completas. Por lo tanto, id1, id2 donde id1 = 1, id2 = 2 e id1 = 1, id2 = 3 se dividirán en diferentes segmentos, ya que el hash estará en (1,2) y (1,3). Este no será un simple escaneo de rango B-Tree ya que los índices hash no están estructurados de la misma manera. Entonces esperaría que este no sea el índice ideal para esta operación, sin embargo, no esperaría que tome órdenes de magnitud más tiempo de lo experimentado. Me interesaría ver wait_info sobre esto.
Si bien es cierto que los bloqueos se usan para lograr coherencia lógica, las operaciones aún deben ser atómicas. Esto se realiza a través de un operador especial de comparación basado en CPU (razón por la cual In-Memory solo funciona con ciertos procesadores [aunque casi todos los cpus fabricados en los últimos 4 años]). Por lo tanto, no obtenemos todo gratis, aún habrá tiempo para completar estas operaciones.
Otro punto a destacar es el hecho de que en casi todas las consultas, la interfaz utilizada es T-SQL (y no SPROC compilados de forma nativa) que tocan al menos una tabla basada en disco. Es por eso que creo que, al final, en realidad no estamos teniendo un mayor rendimiento, ya que todavía estamos limitados al rendimiento de las tablas basadas en disco.
Seguimiento:
Cree una sesión de evento extendida para wait_completed y especifique un SPID conocido por usted. Ejecute la consulta y bríndenos el resultado o consúmalo internamente.
Danos una actualización de la salida del # 1.
No hay un número mágico para determinar el recuento de depósitos para los índices hash. Básicamente, siempre y cuando los cubos no se llenen por completo y las cadenas de fila permanezcan por debajo de 3 o 4, el rendimiento debe ser aceptable. Esto es como preguntar: "¿En qué debo configurar mi archivo de registro?" - va a depender por proceso, por base de datos, por tipo de uso.
fuente