Trucos de ajuste de rendimiento favoritos [cerrado]


126

Cuando tiene una consulta o procedimiento almacenado que necesita ajuste de rendimiento, ¿cuáles son algunas de las primeras cosas que intenta?



Estoy de acuerdo en que esto no es constructivo y se puede buscar en Google, pero ¿por qué tiene 118 uv? :)
FLICKER

Respuestas:


114

Aquí está la lista práctica de cosas que siempre le doy a alguien que me pregunta sobre la optimización.
Utilizamos principalmente Sybase, pero la mayoría de los consejos se aplicarán en todos los ámbitos.

SQL Server, por ejemplo, viene con una gran cantidad de bits de supervisión / ajuste del rendimiento, pero si no tiene nada de eso (y tal vez incluso si lo tiene), consideraría lo siguiente ...

El 99% de los problemas que he visto son causados ​​por poner demasiadas tablas en una unión . La solución para esto es hacer la mitad de la unión (con algunas de las tablas) y almacenar en caché los resultados en una tabla temporal. Luego haga el resto de la consulta uniéndose en esa tabla temporal.

Lista de verificación de optimización de consultas

  • Ejecute ACTUALIZAR ESTADÍSTICAS en las tablas subyacentes
    • Muchos sistemas ejecutan esto como un trabajo semanal programado
  • Eliminar registros de las tablas subyacentes (posiblemente archivar los registros eliminados)
    • Considere hacer esto automáticamente una vez al día o una vez a la semana.
  • Reconstruir índices
  • Reconstruir tablas (datos bcp fuera / dentro)
  • Volcar / Recargar la base de datos (drástica, pero podría corregir la corrupción)
  • Cree un índice nuevo y más apropiado
  • Ejecute DBCC para ver si hay posibles daños en la base de datos
  • Cerraduras / Deadlocks
    • Asegúrese de que no se ejecuten otros procesos en la base de datos
      • Especialmente DBCC
    • ¿Está utilizando el bloqueo de nivel de fila o página?
    • Bloquee las tablas exclusivamente antes de comenzar la consulta
    • Verifique que todos los procesos accedan a las tablas en el mismo orden
  • ¿Se utilizan los índices de manera adecuada?
    • Las uniones solo usarán índice si ambas expresiones son exactamente del mismo tipo de datos
    • El índice solo se usará si los primeros campos del índice coinciden en la consulta
    • ¿Se utilizan índices agrupados cuando corresponde?
      • datos de rango
      • DONDE campo entre valor1 y valor2
  • Las pequeñas uniones son buenas uniones
    • Por defecto, el optimizador solo considerará las tablas 4 a la vez.
    • Esto significa que en uniones con más de 4 tablas, tiene una buena posibilidad de elegir un plan de consulta no óptimo
  • Romper la unión
    • ¿Puedes romper la unión?
    • Preseleccionar claves externas en una tabla temporal
    • Haz la mitad de la unión y coloca los resultados en una tabla temporal
  • ¿Está utilizando el tipo correcto de tabla temporal?
    • #tempLas tablas pueden funcionar mucho mejor que las @tablevariables con grandes volúmenes (miles de filas).
  • Mantener tablas de resumen
    • Construir con disparadores en las tablas subyacentes
    • Construir diariamente / por hora / etc.
    • Construir ad-hoc
    • Construir incrementalmente o derribar / reconstruir
  • Vea cuál es el plan de consulta con SET SHOWPLAN ON
  • Vea lo que realmente está sucediendo con SET STATS IO ON
  • Forzar un índice utilizando el pragma: (index: myindex)
  • Forzar el orden de la tabla usando SET FORCEPLAN ON
  • Parámetro olfateo:
    • Divida el procedimiento almacenado en 2
    • llamar a proc2 desde proc1
    • permite al optimizador elegir el índice en proc2 si @cármetro ha sido cambiado por proc1
  • ¿Puedes mejorar tu hardware?
  • A que hora estas corriendo ¿Hay un momento más tranquilo?
  • ¿Se está ejecutando el servidor de replicación (u otro proceso continuo)? ¿Puedes suspenderlo? Ejecutar, por ejemplo. ¿cada hora?

2
¿A qué parte te refieres?
AJ.

2
Estas son algunas cosas interesantes, pero desearía que tuviera algunas referencias para algunas afirmaciones. Por ejemplo: nunca había escuchado que la optimización considera solo 4 tablas por vez en una unión. No entiendo cómo esto podría ser correcto. ¿Podría proporcionar algunas referencias para eso en particular? Me encantaría ver de dónde sacas esto.
SheldonH

19
  1. Tenga una idea bastante buena de la ruta óptima para ejecutar la consulta en su cabeza.
  2. Verifique el plan de consulta, siempre.
  3. Active STATS, para que pueda examinar el rendimiento de IO y CPU. Concéntrese en reducir esos números, no necesariamente el tiempo de consulta (ya que puede estar influenciado por otra actividad, caché, etc.).
  4. Busque grandes cantidades de filas que entran en un operador, pero pequeñas cantidades que salen. Por lo general, un índice ayudaría al limitar el número de filas que ingresan (lo que ahorra lecturas de disco).
  5. Concéntrese primero en el subárbol de mayor costo. Cambiar ese subárbol a menudo puede cambiar todo el plan de consulta.
  6. Los problemas comunes que he visto son:
    • Si hay muchas uniones, a veces SQL Server elegirá expandir las uniones y luego aplicará las cláusulas WHERE. Por lo general, puede solucionar esto moviendo las condiciones WHERE a la cláusula JOIN, o una tabla derivada con las condiciones en línea. Las vistas pueden causar los mismos problemas.
    • Uniones subóptimas (LOOP vs HASH vs MERGE). Mi regla de oro es usar una unión LOOP cuando la fila superior tiene muy pocas filas en comparación con la inferior, un MERGE cuando los conjuntos son más o menos iguales y ordenados, y un HASH para todo lo demás. Agregar una sugerencia de unión te permitirá probar tu teoría.
    • Parámetro olfateando. Si ejecutó el proceso almacenado con valores poco realistas al principio (por ejemplo, para pruebas), entonces el plan de consulta en caché puede ser subóptimo para sus valores de producción. Ejecutar de nuevo CON RECOMPILE debería verificar esto. Para algunos procesos almacenados, especialmente aquellos que tratan con rangos de diferentes tamaños (por ejemplo, todas las fechas entre hoy y ayer, lo que implicaría una BÚSQUEDA DE ÍNDICE, o todas las fechas entre el año pasado y este año, que sería mejor con un ESCANEO DE ÍNDICE ) puede que tenga que ejecutarlo CON RECOMPILE cada vez.
    • Mala sangría ... Bien, entonces Sql Server no tiene un problema con esto, pero estoy seguro de que es imposible entender una consulta hasta que haya arreglado el formato.

1
+1 por la inclusión de mala sangría. El formateo es la clave! :)
mwigdahl

18

Ligeramente fuera de tema, pero si tiene control sobre estos temas ...
Alto nivel y alto impacto.

  • Para entornos de alta IO, asegúrese de que sus discos sean RAID 10 o RAID 0 + 1 o alguna implementación anidada de raid 1 y raid 0.
  • No use unidades de menos de 1500K.
  • Asegúrese de que sus discos solo se usen para su base de datos. IE sin registro sin sistema operativo.
  • Desactiva el crecimiento automático o una función similar. Deje que la base de datos use todo el almacenamiento previsto. No necesariamente lo que se está utilizando actualmente.
  • diseñe su esquema e índices para las consultas de tipo.
  • si es una tabla de tipo de registro (solo insertar) y debe estar en la base de datos, no la indexe.
  • si está haciendo una gran cantidad de informes (selecciones complejas con muchas uniones), entonces debería considerar crear un almacén de datos con un esquema de estrella o copo de nieve.
  • ¡No tenga miedo de replicar datos a cambio de rendimiento!

8

CREATE INDEX

Asegúrese de que haya índices disponibles para sus cláusulas WHEREy JOIN. Esto acelerará el acceso a los datos en gran medida.

Si su entorno es un data mart o un almacén, los índices deberían abundar para casi cualquier consulta concebible.

En un entorno transaccional , el número de índices debería ser menor y sus definiciones más estratégicas para que el mantenimiento del índice no reduzca los recursos. (El mantenimiento del índice es cuando las hojas de un índice deben cambiarse para reflejar un cambio en la tabla subyacente, como con INSERT, UPDATE,y DELETEoperaciones).

Además, tenga en cuenta el orden de los campos en el índice: cuanto más selectivo (mayor cardinalidad) sea un campo, más temprano debería aparecer en el índice. Por ejemplo, supongamos que está buscando automóviles usados:

SELECT   i.make, i.model, i.price
FROM     dbo.inventory i
WHERE    i.color = 'red'
  AND    i.price BETWEEN 15000 AND 18000

El precio generalmente tiene mayor cardinalidad. Puede haber solo unas pocas docenas de colores disponibles, pero posiblemente miles de precios diferentes.

De estas opciones de índice, idx01proporciona la ruta más rápida para satisfacer la consulta:

CREATE INDEX idx01 ON dbo.inventory (price, color)
CREATE INDEX idx02 ON dbo.inventory (color, price)

Esto se debe a que menos automóviles satisfarán el precio que la elección del color, lo que le da al motor de consulta muchos menos datos para analizar.

Se sabe que tengo dos índices muy similares que difieren solo en el orden de los campos para acelerar las consultas (nombre, apellido) en uno y (apellido, nombre) en el otro.


6

Un truco que aprendí recientemente es que SQL Server puede actualizar variables locales, así como campos, en una declaración de actualización.

UPDATE table
SET @variable = column = @variable + otherColumn

O la versión más legible:

UPDATE table
SET
    @variable = @variable + otherColumn,
    column = @variable

He usado esto para reemplazar cursores / combinaciones complicadas al implementar cálculos recursivos, y también gané mucho en rendimiento.

Aquí hay detalles y código de ejemplo que hizo mejoras fantásticas en el rendimiento: http://geekswithblogs.net/Rhames/archive/2008/10/28/calculating-running-totals-in-sql-server-2005---the-optimal. aspx


5

Suponiendo que MySQL aquí, use EXPLAIN para averiguar qué está pasando con la consulta, asegúrese de que los índices se usen de la manera más eficiente posible e intente eliminar los tipos de archivos. MySQL de alto rendimiento: optimización, copias de seguridad, replicación y más es un gran libro sobre este tema, al igual que el Blog de rendimiento de MySQL .


3
Eso es bueno para MySQL, pero la pregunta fue etiquetada "sqlserver". Aún así, es bueno hacer eso. Lo análogo a hacer en SSMS es usar "Mostrar plan de ejecución estimado" e "Incluir plan de ejecución real". Si puede eliminar grandes escaneos de tablas y utilizar búsquedas de índices agrupados, entonces está en camino de obtener un rendimiento óptimo.
eksortso

5

@Terrapin hay algunas otras diferencias entre isnull y coalesce que vale la pena mencionar (además del cumplimiento de ANSI, que es muy importante para mí).

Coalesce vs. IsNull


3

A veces, en SQL Server, si usa un OR en una cláusula where, realmente aumentará el rendimiento. En lugar de usar el OR, simplemente haga dos selecciones y únalas. Obtiene los mismos resultados a 1000x la velocidad.


He visto este comportamiento inexplicable.
Esen

2

Mire la cláusula where: verifique el uso de índices / verifique que no se esté haciendo nada tonto

where SomeComplicatedFunctionOf(table.Column) = @param --silly

2

En general, comenzaré con las uniones: eliminaré cada una de ellas de la consulta una por una y volveré a ejecutar la consulta para tener una idea de si hay una unión particular con la que tengo un problema.


2

En todas mis tablas temporales, me gusta agregar restricciones únicas (cuando corresponda) para crear índices y claves primarias (casi siempre).

declare @temp table(
    RowID int not null identity(1,1) primary key,
    SomeUniqueColumn varchar(25) not null,
    SomeNotUniqueColumn varchar(50) null,
    unique(SomeUniqueColumn)
)

2

Me he acostumbrado a usar siempre variables de enlace. Es posible que las variables de enlace no ayuden si el RDBMS no almacena en caché las declaraciones SQL. Pero si no utiliza variables de enlace, el RDBMS no tiene la posibilidad de reutilizar los planes de ejecución de consultas y las instrucciones SQL analizadas. Los ahorros pueden ser enormes: http://www.akadia.com/services/ora_bind_variables.html . Trabajo principalmente con Oracle, pero Microsoft SQL Server funciona casi de la misma manera.

En mi experiencia, si no sabe si está utilizando variables de enlace, probablemente no lo esté. Si el idioma de su aplicación no los admite, busque uno que sí lo haga. A veces puede arreglar la consulta A utilizando variables de enlace para la consulta B.

Después de eso, hablo con nuestro DBA para averiguar qué está causando más dolor al RDBMS. Tenga en cuenta que no debe preguntar "¿Por qué esta consulta es lenta?" Eso es como pedirle a su médico que saque su apéndice. Seguro que su consulta podría ser el problema, pero es muy probable que algo más esté saliendo mal. Como desarrolladores, tendemos a pensar en términos de líneas de código. Si una línea es lenta, arregle esa línea. Pero un RDBMS es un sistema realmente complicado y su consulta lenta podría ser el síntoma de un problema mucho mayor.

Demasiados consejos de ajuste de SQL son ídolos de culto de carga. La mayoría de las veces el problema no está relacionado o está mínimamente relacionado con la sintaxis que usa, por lo que normalmente es mejor usar la sintaxis más limpia que pueda. Luego puede comenzar a buscar formas de ajustar la base de datos (no la consulta). Solo modifique la sintaxis cuando eso falle.

Al igual que cualquier ajuste de rendimiento, siempre recopile estadísticas significativas. No use el tiempo de reloj de pared a menos que sea la experiencia del usuario lo que está sintonizando. En cambio, mire cosas como el tiempo de CPU, las filas recuperadas y los bloques leídos del disco. Con demasiada frecuencia, las personas optimizan para algo incorrecto.


2

Primer paso: ¡Mira el plan de ejecución de consultas!
TableScan -> mal
NestedLoop -> meh advertencia
TableScan detrás de un NestedLoop -> DOOM!

SET STATISTICS IO ON
SET STATISTICS TIME ON


2

Ejecutar la consulta usando WITH (NoLock) es una operación bastante estándar en mi lugar. Cualquiera que sea sorprendido ejecutando consultas en las tablas de decenas de gigabytes sin que sea sacado y disparado.


2
Esto debe usarse con prudencia, no habitualmente. El bloqueo no es malo, solo mal entendido.

2

Convierta las consultas NOT IN a LEFT OUTER JOINS si es posible. Por ejemplo, si desea encontrar todas las filas en la Tabla1 que no se utilizan por una clave externa en la Tabla2, puede hacer esto:

SELECT *
FROM Table1
WHERE Table1.ID NOT IN (
    SELECT Table1ID
    FROM Table2)

Pero obtienes un rendimiento mucho mejor con esto:

SELECT Table1.*
FROM Table1
LEFT OUTER JOIN Table2 ON Table1.ID = Table2.Table1ID
WHERE Table2.ID is null

1

@ DavidM

Suponiendo que MySQL aquí, use EXPLAIN para averiguar qué está pasando con la consulta, asegúrese de que los índices se usen de la manera más eficiente posible ...

En SQL Server, el plan de ejecución le da lo mismo: le dice qué índices están siendo alcanzados, etc.


1

Indice la (s) tabla (s) por el clm (s) por el que filtra


1

No necesariamente un truco de rendimiento de SQL per se, pero definitivamente relacionado:

Una buena idea sería usar memcached cuando sea posible, ya que sería mucho más rápido simplemente obtener los datos precompilados directamente de la memoria en lugar de obtenerlos de la base de datos. También hay un sabor de MySQL que se incorporó a memcached (de terceros).


1

Asegúrese de que la longitud de su índice sea lo más pequeña posible. Esto permite que el DB lea más claves a la vez del sistema de archivos, acelerando así sus uniones. Supongo que esto funciona con todos los DB, pero sé que es una recomendación específica para MySQL.


1

Cuido de:

  • Desenrolle los bucles CURSOR y conviértalos en declaraciones UPDATE / INSERT basadas en conjuntos.
  • Esté atento a cualquier código de aplicación que:
    • Llama a un SP que devuelve un gran conjunto de registros,
    • Luego, en la aplicación, revisa cada registro y llama a un SP con parámetros para actualizar los registros.
    • Convierta esto en un SP que haga todo el trabajo en una transacción.
  • Cualquier SP que haga mucha manipulación de cadenas. Es evidencia de que los datos no están estructurados correctamente / normalizados.
  • Cualquier SP que reinventa la rueda.
  • ¡Cualquier SP que no pueda entender lo que está tratando de hacer en un minuto!

1
SET NOCOUNT ON

Por lo general, la primera línea dentro de mis procedimientos almacenados, a menos que realmente necesite usar @@ROWCOUNT.


2
@@ ROWCOUNT está configurado de todos modos. NOCOUNT deshabilita las declaraciones "xx filas afectadas".
Sklivvz

¿Esto realmente alguna vez hace una diferencia apreciable en el rendimiento?
JohnFx

Sí, entonces el recuento no se calcula automáticamente cada vez que se ejecuta una instrucción SQL. Es bastante fácil evaluar una consulta con y sin ver si hace la diferencia.
travis

El recuento se realiza un seguimiento en SQL Server de todos modos. Cualquier diferencia de rendimiento que vea se debe a que los recuentos deben pasar por la red a su front-end. Si está haciendo un solo SELECCIONAR, no habrá una diferencia apreciable. Si tiene un bucle con 100000 inserciones, es mucho más en la red.
Tom H

1

En SQL Server, use la directiva nolock. Permite que el comando de selección se complete sin tener que esperar, generalmente otras transacciones para finalizar.

SELECT * FROM Orders (nolock) where UserName = 'momma'

3
NOLOCK es solo para consultas para las que no le interesan los resultados correctos
Mark Sowul

1

Retire los cursores donde no sean necesarios.


Sí, los cursores son una maldición! ;)
Sklivvz

8
Ugh No tires eso sin calificar así. Los cursores son como pistolas. No son malos por sí mismos, es solo que las personas hacen cosas realmente malas con ellos.
JohnFx

1

Elimine las llamadas a funciones en Sprocs donde muchas filas llamarán a la función.

Mi colega utilizó llamadas de función (obteniendo lastlogindate de userid como ejemplo) para devolver conjuntos de registros muy amplios.

Encargado de la optimización, reemplacé las llamadas de función en el sproc con el código de la función: obtuve el tiempo de ejecución de muchos sprocs de> 20 segundos a <1.


0
  • Prefije todas las tablas con dbo. para evitar recompilaciones.
  • Ver planes de consulta y buscar escaneos de tabla / índice.
  • En 2005, recorra las vistas de gestión en busca de índices faltantes.


0

No prefije los nombres de Procedimiento almacenado con "sp_" porque todos los procedimientos del sistema comienzan con "sp_", y SQL Server tendrá que buscar más para encontrar su procedimiento cuando se lo llame.


1
¿Realmente comparaste este? Si SQL Server está haciendo lo que es razonable (usando un algoritmo hash para ubicar el Proceso almacenado), entonces esto no haría ninguna diferencia. De hecho, si SQL Server no estaba haciendo eso, parece que el rendimiento del sistema apestaría (ya que presumiblemente llama a sus propios procesos).
John Stauffer

1
Creo que esto cae en el cubo de la optimización prematura. Probablemente sea una buena práctica evitar la confusión de las personas, pero como un consejo de optimización ... D-
JohnFx

0

Lecturas sucias -

set transaction isolation level read uncommitted

Evita bloqueos muertos donde la integridad transaccional no es absolutamente necesaria (lo cual suele ser cierto)


1
Sí, pero esto puede conducir a errores extraños que son MUY difíciles de encontrar.
Grant Johnson

0

Siempre voy al Analizador de SQL (si es un procedimiento almacenado con muchos niveles de anidamiento) o al planificador de ejecución de consultas (si se trata de unas pocas instrucciones SQL sin anidamiento) primero. El 90% del tiempo puede encontrar el problema de inmediato con una de estas dos herramientas.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.