2008-09-19 9 views
17

Siempre he tenido el enfoque de implementar primero la base de datos con un conjunto mínimo de índices y luego agregar/cambiar índices según lo dicte el rendimiento.¿Cuáles son las mejores técnicas de optimización de rendimiento de SQL Server?

Este enfoque funciona razonablemente bien. Sin embargo, todavía no me dice dónde podría mejorar el rendimiento. Solo me dice que el rendimiento es tan malo que los usuarios se quejan de él.

Actualmente, estoy en proceso de refactorizar objetos de base de datos en muchas de nuestras aplicaciones.

Entonces, ¿no debería molestarme en buscar mejoras en el rendimiento ya que "la optimización prematura es la raíz de todo mal"?

Al refaccionar el código de la aplicación, el desarrollador busca constantemente maneras de mejorar la calidad del código. ¿Hay alguna manera de buscar constantemente mejoras en el rendimiento de la base de datos también? Si es así, ¿qué herramientas y técnicas ha encontrado que son más útiles?

He jugado brevemente con el "Asesor de ajuste del motor de base de datos", pero no creo que sea útil en absoluto. Tal vez solo necesito más experiencia interpretando los resultados.

Respuesta

11

Mi enfoque es reunir comandos contra el servidor o la base de datos en una tabla usando SQL Server Profiler. Una vez que tenga eso, puede realizar consultas en función de los tiempos de ejecución max y avg, los tiempos de CPU max y avg, y (también muy importante) el número de veces que se ejecutó la consulta.

Como trato de poner todo el código de acceso a la base de datos en procedimientos almacenados, es fácil para mí desglosar las consultas. Si usa SQL en línea podría ser más difícil, ya que un cambio en un valor en la consulta lo haría parecer una consulta diferente. Puede intentar solucionar esto utilizando el operador LIKE para colocar los mismos tipos de consultas en los mismos intervalos para calcular los agregados (max, avg, count).

Una vez que tenga una lista "top 10" de los problemas potenciales que puede empezar a buscar en ellos de forma individual para ver si alguna de la consulta puede ser reelaborado, un índice podría ayudar o hacer un cambio de arquitectura menor está en orden. Para llegar a los 10 principales, intente ver los datos de diferentes maneras: promedio * para el costo total durante el período, máximo para el peor delincuente, simplemente promedio, etc.

Por último, asegúrese de controlar a través de diferentes períodos de tiempo si es necesario. El uso de la base de datos puede ser diferente en la mañana cuando todos están ingresando y ejecutando sus informes diarios que a mediodía cuando los usuarios ingresan datos nuevos. También puede decidir que, aunque un proceso nocturno lleva más tiempo que cualquier otra consulta, no importa, ya que se ejecuta fuera de horario.

¡Buena suerte!

1

perfil de sus consultas, no los más obvios, pero el complejo que acceder a las diferentes tablas, vistas, etc., y/o los que regresan muchas filas de diferentes tablas

que le dirá exactamente donde debe centrarse

0

Parece que estás hablando de MS SQL.

Inicie el generador de perfiles y registre las consultas más comunes que ejecuta en la base de datos. Luego ejecute esas consultas con el plan de ejecución activado y verá qué (si hay algo) está desacelerando sus consultas. A continuación, puede ir y optimizar las consultas o agregar más índices en sus campos.

Los libros SQL le darán una buena descripción general de las funciones de análisis de perfiles y de consultas.

1

perfilar es clave, pero al usar un conjunto de perfiles DEBE asegurarse de que sea un conjunto de datos de prueba exactos; de lo contrario, las herramientas de ajuste no podrán obtener un resultado preciso de lo que se necesita.

¡También los objetos de gestión con fragmentación e informes de uso en 2005 son muy útiles!

1

Después de su perfil, coloque las consultas que ve como problemáticas en el Analizador de consultas SQL y muestre el plan de ejecución. Identifique porciones de las consultas que realizan escaneos costosos de tablas y vuelva a indexar esas tablas para minimizar este costo.

Pruebe estas referencias:

Optimizing SQL
How to Optimize Queries

0

Es posible que desee comprobar la fragmentación interna y externa de los índices actuales y soltarlos y volver a crearlos o reorganizarlos.

0

Asegúrate de realizar perfiles utilizando volúmenes de producción, en términos de número de filas y de carga. Las consultas y sus planes se comportan de forma diferente en diferentes escenarios de carga/volumen

1

Por supuesto, debe configurar sus consultas y ver el plan de ejecución. Pero las dos cosas principales que aparecen una y otra vez son filtrar tanto como puedas tan pronto como puedas y tratar de evitar los cursores.

Vi una aplicación en la que alguien descargaba una tabla de eventos de una base de datos completa a un cliente y luego revisaba cada fila, uno por uno, filtrando según algunos criterios. Hubo un ENORME aumento en el rendimiento al pasar los criterios de filtro a la base de datos y hacer que la consulta aplicara los criterios en una cláusula where. Esto es obvio para las personas que trabajan con bases de datos, pero he visto surgir cosas similares. Además, algunas personas tienen consultas que almacenan un conjunto de tablas temporales llenas de filas que no necesitan, que luego se eliminan en una combinación final de las tablas temporales. Básicamente, si elimina de las consultas que pueblan las tablas temporales, hay menos datos para el resto de la consulta y toda la consulta se ejecuta más rápido.

Los cursores son obvios. Si tienes un millón de filas e irás fila por fila, tardarás una eternidad. Si realiza algunas pruebas, si se conecta a una base de datos incluso con un lenguaje dinámico "lento" como Perl y realiza una operación fila a fila en un conjunto de datos, la velocidad seguirá siendo mucho mayor que la de un cursor en la base de datos. Hazlo con algo como Java/C/C++ y la diferencia de velocidad es aún mayor. Si puede encontrar/eliminar un cursor en el código de la base de datos, se ejecutará mucho más rápido ... Si debe usar un cursor, reescribir esa parte en cualquier lenguaje de programación y sacarla de la base de datos probablemente genere un gran aumento en el rendimiento.

Una nota más en los cursores, tenga cuidado con el código como SELECT @ col1 = col1, @ col2 = col2, @ col3 = col3 donde id = @currentid en un bucle que pasa por ID y luego ejecuta instrucciones en cada columna. Básicamente esto es un cursor también. No solo eso, sino que el uso de cursores reales suele ser más rápido que esto, especialmente estático y forward_only. Si puede cambiar la operación para establecerla, será mucho más rápido ... Dicho esto, los cursores tienen cabida para algunas cosas ... pero desde el punto de vista del rendimiento, existe una penalización al usarlas en un conjunto basado en enfoques.

También tenga cuidado con el plan de ejecución. En ocasiones, estima que las operaciones que toman segundos son muy costosas y las operaciones que toman minutos son muy baratas. Al ver un plan de ejecución, asegúrese de verificar todo insertando SELECCIONAR 'En esta área', OBTENER() en su código.

11

"optimización prematura es la raíz de todo mal"

En cuanto a la programación de bases de datos, creo que esta cita es un disparate. Es extremadamente costoso volver a escribir toda la aplicación porque a los desarrolladores no les importa escribir un código eficiente la primera vez. Se debe pensar en todos los códigos t-sql en términos de cómo afectará el rendimiento de la base de datos en segundo lugar (la integridad de los datos es, por supuesto, primero). El rendimiento debe triunfar sobre todo excepto la integridad de datos.

Sí, hay cosas de optimización que no debes hacer hasta que tengas problemas, pero algunas cosas se deben hacer de forma rutinaria y no se corrigen más tarde. No se necesita más tiempo para escribir un código que tenga más posibilidades de ser eficiente que un código que no será una vez que comprenda cómo está afectando la eficiencia con el código incorrecto. La discusión de Cervo sobre el código del cursor es un ejemplo. Las acciones basadas en conjuntos son casi siempre mucho más rápidas que las soluciones de cursor, por lo que los cursores no deberían escribirse inicialmente cuando una solución basada en conjuntos funcionaría. Casi siempre me toma menos tiempo escribir una solución basada en conjuntos que escribir un cursor, pero la única manera de hacerlo es nunca escribir cursores.

Y no hay razón para usar select * en vez de especificar sus nombres de campo. En MSSQL puedes arrastrar esos nombres desde el explorador de objetos para que no puedas decirme que es muy difícil hacerlo. Pero al especular solo con los campos que realmente necesita, ahorra recursos de red y recursos del servidor de bases de datos y recursos del servidor web. Entonces, ¿por qué un programador debe tomar la opción perezosa de seleccionar * y preocuparse por la optimización posterior?

Lo mismo con los índices. Usted dice que hace un conjunto mínimo de índices. Dependiendo de cómo defina el mínimo, podría estar bien, pero es fundamental tener índices en todas las claves externas y no me gustaría insertar una base de datos que no tuviera índices en algunos campos que están más a menudo en el cláusulas. Si sus usuarios son clientes externos y no internos, no se quejarán de lo lento que es su sitio, se irán a otro lado. Solo tiene sentido tener la capacidad de planificar el acceso eficiente a la base de datos desde el principio.

Una de mis principales preocupaciones sobre no considerar la eficiencia desde el principio es que las primeras veces que las cosas son demasiado lentas las empresas tienden a simplemente tirar más equipo al tema que a la melodía de rendimiento. En el momento en que la gente comienza a ajustar el rendimiento, tiene una base de datos de varios gigabytes o más con muchos clientes descontentos que están obteniendo tiempos de espera más que resultados. En este punto, a menudo casi todo en la base de datos debe ser reescrito y, mientras tanto, está perdiendo clientes. Recuerdo haber brindado soporte en una compañía con una aplicación comercial, literalmente demoró diez minutos para que los representantes del servicio al cliente se movieran de una pantalla a otra mientras intentaban ayudar a los clientes que ya estaban disgustados por teléfono. Puede imaginar cuántos clientes perdió la empresa debido a consultas de bases de datos mal diseñadas en el producto comercial que no pudimos cambiar.

+0

Estoy de acuerdo con esto: los sistemas de bases de datos involucran trozos de metal que van y vuelven. Estos trozos de metal no se están volviendo exponencialmente más rápidos en la forma en que son los IC y el error en la consulta puede hacer órdenes de diferencia de magnitud en el rendimiento. – ConcernedOfTunbridgeWells

+0

De hecho, reemplacé los cursores con un código basado en conjuntos que tomaba el rendimiento en minutos a milisegundos y algunas veces de horas a segundos. No es necesario escribir este tipo de código para comenzar. – HLGEM

+1

La optimización prematura se trata de evitar optimizar algo cuando no se tienen medidas sobre cuánto tiempo/esfuerzo se guardará. Evitar el uso de cursores no es una optimización prematura, ya que hay una mejora muy convincente y bien documentada en el rendimiento para evitarlos. – Nat

-1

Mi consejo sería comenzar con las técnicas aplicables a todas las bases de datos y luego probar las específicas de MsSQL.

Optimizar el SQL es difícil, y no existen reglas estrictas. Hay muy pocas pautas genéricas que puede seguir, como por ejemplo:

  • El 95% de las mejoras de rendimiento provendrán de la aplicación, no de la configuración del motor del servidor o la base de datos.
  • Diseño para la corrección primero, ajustar para obtener un rendimiento más adelante
  • reducir los viajes a la base de datos
  • tratar de expresar las cosas de una manera que se adapte a su modelo de datos
  • ignoran los consejos genéricos sobre el rendimiento - si, en algún momento Encontraré un sistema o declaración SQL donde una de esas reglas no se aplica.

Pero el punto clave es que siempre debe aplicar la regla 80-20. Lo que significa que en cualquier sistema necesita ajustar un 20% (a menudo mucho menos) de su código para las mayores ganancias de rendimiento. Ahí es donde el proveedor proporcionó las herramientas usually fail, ya que generalmente no pueden adivinar el contexto de aplicación/negocio de la ejecución.

1

Mi consejo es que "la optimización prematura es la raíz de todos los males" en este contexto no tiene sentido.

En mi opinión, todo se trata de diseño: necesita pensar en simultaneidad, puntos de acceso, indización, escala y patrones de uso cuando está diseñando su esquema de datos.

Si no sabe qué índices necesita y cómo deben configurarse desde el principio sin hacer un perfil, ya ha fallado.

Hay millones de formas de optimizar la ejecución de consultas que están muy bien pero al final del día los datos aterrizan donde se lo indica.

0

Aplicar correcta indexación en las columnas de las tablas en la base de datos

  • asegurarse de que cada tabla en su base de datos tiene una clave principal.

Esto asegurará que cada tabla tenga un índice agrupado creado (y por lo tanto, las páginas correspondientes de la tabla se clasifican físicamente en el disco de acuerdo con el campo de clave principal). Entonces, cualquier operación de recuperación de datos de la tabla usando la clave primaria, o cualquier operación de clasificación en el campo de clave primaria o cualquier rango de valores de clave primaria especificados en la cláusula where recuperará datos de la tabla muy rápido.

  • crear índices no agrupados en columnas, que son

    utiliza con frecuencia en los criterios de búsqueda.

    Se utiliza para unir otras tablas.

    Se utiliza como campo de clave externa.

    De alta selectividad (columna que devuelve un bajo porcentaje (0-5%) de filas de un número total de filas en un valor particular).

    Se utiliza en la cláusula ORDER BY.

No utilice "SELECT *" en una consulta SQL

columnas innecesarias pueden obtener inverosímil que se sumará a expensas del tiempo de recuperación de datos. El motor de base de datos no puede utilizar el beneficio de "Índice cubierto" y, por lo tanto, la consulta se realiza lentamente.

Ejemplo:

SELECT Cash, Age, Amount FROM Investments; 

En lugar de:

SELECT * FROM Investments; 

Trate de evitar tener Cláusula en las sentencias SELECT

HAVING se utiliza para filtrar las filas después de todas las filas son seleccionado y se usa como un filtro. Intenta no usar la cláusula HAVING para ningún otro propósito.

Ejemplo:

SELECT Name, count (Name) FROM Investments WHERE Name!= ‘Test’ AND Name!= ‘Value’ GROUP BY Name; 

En lugar de:

SELECT Name, count (Name) FROM Investments GROUP BY Name HAVING Name!= ‘Test’ AND Name!= ‘Value’ ; 

tratar de minimizar el número de bloques de consulta sub dentro de una consulta

A veces puede tener más de una consulta sub en nuestra consulta principal. Deberíamos intentar minimizar el número de bloque de sub consulta en nuestra consulta.

Ejemplo:

SELECT Amount FROM Investments WHERE (Cash, Fixed) = (SELECT MAX (Cash), MAX (Fixed) FROM Retirements) AND Goal = 1; 

En lugar de:

SELECT Amount FROM Investments WHERE Cash = (SELECT MAX (Cash) FROM Retirements) AND Fixed = (SELECT MAX (Fixed) FROM Retirements) AND Goal = 1; 

Evita columnas innecesarias en la lista SELECT y mesas innecesarios en condiciones de combinación

Selección de columnas innecesarias en una consulta de selección agrega sobrecarga a la consulta real, especialmente si las columnas innecesarias son de tipos LOB. La inclusión de tablas innecesarias en condiciones de combinación fuerza al motor de la base de datos a recuperar y recuperar datos innecesarios y aumenta el tiempo de ejecución de la consulta.

No utilice el agregado COUNT() en una subconsulta que hacer una comprobación de la existencia

Cuando se utiliza COUNT(), SQL Server no sabe que usted está haciendo una comprobación de la existencia. Cuenta todos los valores coincidentes, ya sea haciendo un escaneo de tabla o escaneando el índice no agrupado más pequeño. Cuando utiliza EXISTS, SQL Server sabe que está haciendo una comprobación de existencia. Cuando encuentra el primer valor coincidente, devuelve TRUE y deja de buscar.

Trate de evitar de unión entre dos tipos de columnas

Cuando unión entre dos columnas de diferentes tipos de datos, una de las columnas deben ser convertidos con el tipo de la otra.La columna cuyo tipo es más bajo es la que se convierte. Si une tablas con tipos incompatibles, una de ellas puede usar un índice, pero el optimizador de consultas no puede elegir un índice en la columna que convierte.

No trate de usar COUNT (*) para obtener el número de registros en una tabla

Para obtener el número total de filas en una tabla, que suelen utilizar la siguiente instrucción SELECT:

SELECT COUNT(*) FROM [dbo].[PercentageForGoal] 

Esta consulta realizará un escaneo completo de la tabla para obtener el recuento de filas. La siguiente consulta no requerirá un escaneo de tabla completo. (Tenga en cuenta que esto no se podría dar el 100% siempre resultados perfectos, pero esto es útil sólo si no es necesario un recuento perfecto.)

SELECT rows FROM sysindexes 
WHERE id = OBJECT_ID('[dbo].[PercentageForGoal]') AND indid< 2 

intenta utilizar operadores como Existe, y se une apropiadamente en su consulta

  • Normalmente, IN tiene el rendimiento más lento.
  • IN es eficiente, solo cuando la mayoría de los criterios de filtro para la selección se colocan en la subconsulta de una instrucción SQL.
  • EXISTS es eficiente cuando la mayoría de los criterios de filtro para la selección se encuentra en la consulta principal de una instrucción de SQL.

Trate de evitar el SQL dinámico

A menos que realmente se requiere, trate de evitar el uso de SQL dinámico porque: SQL dinámico es difícil de depurar y solucionar problemas. Si el usuario proporciona la entrada al SQL dinámico, existe la posibilidad de ataques de inyección SQL.

Trate de evitar el uso de tablas temporales

A menos que realmente necesario, tratar de evitar el uso de tablas temporales. Más bien use variables de tabla. En el 99% de los casos, las variables de tabla residen en la memoria, por lo tanto, es mucho más rápido. Las tablas temporales residen en la base de datos TempDb. Entonces, operar en tablas temporales requiere comunicación entre bases de datos y, por lo tanto, será más lento.

lugar de como la búsqueda, usar la búsqueda de texto completo para la búsqueda de datos textuales

búsquedas de texto completo siempre superan COMO búsquedas. Las búsquedas de texto completo le permitirán implementar criterios de búsqueda complejos que no pueden implementarse utilizando una búsqueda LIKE, como buscar en una sola palabra o frase (y opcionalmente, clasificar el conjunto de resultados), buscar en una palabra o frase cercana a otra palabra o frase, o buscar en formas sinónimas de una palabra específica. La implementación de la búsqueda de texto completo es más fácil de implementar que la búsqueda LIKE (especialmente en el caso de los requisitos de búsqueda compleja).

Intente utilizar UNION para implementar un "OR"

trate de no usar "O" en una consulta. En su lugar, use "UNION" para combinar el conjunto de resultados de dos consultas distinguidas. Esto mejorará el rendimiento de la consulta. Mejor uso UNIÓN TODO si no se requiere un resultado distinguido. UNION ALL es más rápido que UNION ya que no tiene que ordenar el conjunto de resultados para descubrir los valores distinguidos.

implementar una estrategia de carga diferida para grandes objetos

Tienda columnas de objetos grandes (como VARCHAR (MAX), imagen, texto, etc.) en una tabla diferente a la tabla principal, y poner una referencia a la objeto grande en la mesa principal. Recupere todos los datos de la tabla principal en una consulta, y si es necesario cargar un objeto grande, obtenga los datos de objetos grandes de la tabla de objetos grandes solo cuando sea necesario.

implementar las siguientes buenas prácticas en las funciones definidas por el usuario

No llame a funciones repetidamente dentro de sus procedimientos almacenados, triggers, funciones y lotes. Por ejemplo, puede necesitar la longitud de una variable de cadena en muchos lugares de su procedimiento, pero no llame a la función LEN cuando sea necesario; en su lugar, llame a la función LEN una vez, y almacene el resultado en una variable para su uso posterior.

implementar las siguientes buenas prácticas en disparadores

  • tratar de evitar el uso de los factores desencadenantes. Disparar un disparador y ejecutar el evento desencadenante es un proceso costoso.
  • Nunca utilice desencadenantes que puedan implementarse utilizando restricciones.
  • No utilice el mismo activador para diferentes eventos de activación (Insertar, Actualizar y Eliminar).
  • No utilice código transaccional dentro de un desencadenador. El desencadenador siempre se ejecuta dentro del alcance transaccional del código que desencadena el activador.
Cuestiones relacionadas