Cuando tiene una consulta o procedimiento almacenado que necesita ajuste de rendimiento, ¿cuáles son algunas de las primeras cosas que intenta?
Cuando tiene una consulta o procedimiento almacenado que necesita ajuste de rendimiento, ¿cuáles son algunas de las primeras cosas que intenta?
Respuestas:
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.
#temp
Las tablas pueden funcionar mucho mejor que las @table
variables con grandes volúmenes (miles de filas).Ligeramente fuera de tema, pero si tiene control sobre estos temas ...
Alto nivel y alto impacto.
CREATE INDEX
Asegúrese de que haya índices disponibles para sus cláusulas WHERE
y 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 DELETE
operaciones).
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, idx01
proporciona 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.
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
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 .
@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í).
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.
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)
)
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.
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.
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
@ 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.
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).
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.
Cuido de:
SET NOCOUNT ON
Por lo general, la primera línea dentro de mis procedimientos almacenados, a menos que realmente necesite usar @@ROWCOUNT
.
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'
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.
Me gusta usar
isnull(SomeColThatMayBeNull, '')
Encima
coalesce(SomeColThatMayBeNull, '')
Cuando no necesito el soporte de argumentos múltiples que te da la fusión.
http://blog.falafel.com/2006/04/05/SQLServerArcanaISNULLVsCOALESCE.aspx
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.
set transaction isolation level read uncommitted
Evita bloqueos muertos donde la integridad transaccional no es absolutamente necesaria (lo cual suele ser cierto)
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.