Optimizar el uso elevado de CPU en las instancias

En este documento se explica cómo revisar y optimizar una instancia de Cloud SQL para SQL Server si el recomendador de instancias con aprovisionamiento insuficiente la identifica como una instancia con un uso elevado de la CPU.

Si una vCPU de una instancia no tiene el tamaño adecuado, puede convertirse en una fuente de contención. Para comprobar si la CPU es un cuello de botella o está infraprovisionada, usa las consultas de este documento.

Usar Información útil sobre las consultas para identificar las consultas que tienen un consumo de CPU elevado

Información valiosa sobre las consultas te ayuda a detectar, diagnosticar y evitar problemas de rendimiento de las consultas en las bases de datos de Cloud SQL.

Consultar el recuento medio de tareas

Ejecuta esta consulta un par de veces para comprobar el número medio de tareas. Si el número medio de tareas es siempre alto, es posible que la instancia esté sufriendo presión de la CPU.


SELECT AVG(current_tasks_count) AS [Avg Task Count],
AVG(work_queue_count) AS [Avg Work Queue Count],
AVG(runnable_tasks_count) AS [Avg Runnable Task Count],
AVG(pending_disk_io_count) AS [Avg Pending DiskIO Count],
GETDATE() AS [System Time]
FROM sys.dm_os_schedulers WITH (NOLOCK)
WHERE scheduler_id < 255 OPTION (RECOMPILE);

Determinar si es necesario añadir más vCPUs

En determinadas condiciones, puede que quieras aumentar la cantidad de vCPUs. Usa esta consulta para determinar si es necesario añadir más vCPUs.

-- Shows queries where max and average CPU time exceeds 200 ms and executed more than 1000 times
DECLARE @cputime_threshold_microsec INT = 200*1000
DECLARE @execution_count INT = 1000
SELECT qs.total_worker_time/1000 total_cpu_time_ms,
      qs.max_worker_time/1000 max_cpu_time_ms,
      (qs.total_worker_time/1000)/execution_count average_cpu_time_ms,
      qs.execution_count,
      q.[text]
FROM sys.dm_exec_query_stats qs CROSS APPLY sys.dm_exec_sql_text(plan_handle)
AS q
WHERE (qs.total_worker_time/execution_count > @cputime_threshold_microsec
       OR qs.max_worker_time > @cputime_threshold_microsec )
       AND execution_count > @execution_count
       ORDER BY qs.total_worker_time DESC

Comprobar si faltan índices

Comprueba si faltan índices con la siguiente consulta. Prueba estos índices en una instancia que no sea de producción para ver cómo afectan al rendimiento de la CPU.

SELECT
CONVERT(
  decimal(18, 2), migs.user_seeks * migs.avg_total_user_cost * (migs.avg_user_impact * 0.01))
  AS [index_advantage],
CONVERT(nvarchar(25), migs.last_user_seek, 20) AS [last_user_seek],
mid.[statement] AS [Database.Schema.Table],
COUNT(1) OVER (PARTITION BY mid.[statement]) AS [missing_indexes_for_table],
COUNT(1)
  OVER (PARTITION BY mid.[statement], mid.equality_columns)
  AS [similar_missing_indexes_for_table],
mid.equality_columns,
mid.inequality_columns,
mid.included_columns,
migs.user_seeks,
CONVERT(decimal(18, 2), migs.avg_total_user_cost) AS [avg_total_user_cost],
migs.avg_user_impact
FROM sys.dm_db_missing_index_group_stats AS migs WITH(NOLOCK)
INNER JOIN sys.dm_db_missing_index_groups AS mig WITH(NOLOCK)
ON migs.group_handle = mig.index_group_handle
INNER JOIN sys.dm_db_missing_index_details AS mid WITH(NOLOCK)
ON mig.index_handle = mid.index_handle
ORDER BY index_advantage DESC

Comprobar si hay índices con demasiada sobrecarga

Añadir un índice puede ayudar a optimizar el rendimiento, pero también puede añadir una sobrecarga que contribuya a un mayor uso de la CPU y la memoria. Revisa los índices devueltos que tengan un número muy alto de escrituras en comparación con un número muy bajo de lecturas y plantéate eliminarlos.

SELECT
 SCHEMA_NAME(o.[schema_id]) AS [Schema Name],
 OBJECT_NAME(s.[object_id]) AS [TABLE Name],
 i.name AS [Index Name],
 i.index_id,
 i.is_disabled,
 i.is_hypothetical,
 i.has_filter,
 i.fill_factor,
 s.user_updates AS [Total Writes],
 s.user_seeks + s.user_scans + s.user_lookups AS [Total Reads],
 s.user_updates - (s.user_seeks + s.user_scans + s.user_lookups) AS [Difference]
FROM sys.dm_db_index_usage_stats AS s WITH(NOLOCK)
INNER JOIN sys.indexes AS i WITH(NOLOCK) ON s.[object_id] = i.[object_id]
AND i.index_id = s.index_id
INNER JOIN sys.objects AS o WITH(NOLOCK) ON i.[object_id] = o.[object_id]
WHERE
 OBJECTPROPERTY(s.[object_id], 'IsUserTable') = 1
 AND s.database_id = DB_ID()
 AND s.user_updates > (s.user_seeks + s.user_scans + s.user_lookups)
 AND i.index_id > 1
 AND i.[type_desc] = N'NONCLUSTERED'
 AND i.is_primary_key = 0
 AND i.is_unique_constraint = 0
 AND i.is_unique = 0
ORDER BY [Difference] DESC, [Total Writes] DESC, [Total Reads] ASC

Buscar las consultas principales que consumen la mayor parte de la CPU

Revisa las 20 consultas principales por uso de CPU o tiempo de trabajador. Estas son las consultas que usan la mayor parte de la CPU, según las estadísticas de ejecución de consultas. Estas estadísticas se agregan a lo largo del tiempo y se vinculan a los planes de la caché.

SELECT
top 20
SUBSTRING(qt.TEXT, (qs.statement_start_offset/2)+1,
((CASE qs.statement_end_offset
WHEN -1 THEN DATALENGTH(qt.TEXT)
ELSE qs.statement_end_offset
END - qs.statement_start_offset)/2)+1),
qs.execution_count,
qs.total_logical_reads, qs.last_logical_reads,
qs.total_logical_writes, qs.last_logical_writes,
qs.total_worker_time,
qs.last_worker_time,
qs.total_elapsed_time/1000000 total_elapsed_time_in_S,
qs.last_elapsed_time/1000000 last_elapsed_time_in_S,
qs.last_execution_time,
qp.query_plan
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) qt
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) qp
ORDER BY qs.total_worker_time DESC -- CPU time

Comprobar si hay conversiones implícitas en los planes de consulta

Esta operación es costosa y suele mostrarse como una advertencia en el plan de ejecución de la consulta. El mensaje suele incluir una advertencia que indica que puede afectar a CardinalityEstimate en la elección del plan de consulta. Puedes identificar las conversiones implícitas cuando veas los planes de consulta en SQL Server Management Studio (SSMS).

Siguientes pasos