Optimiza 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 no tiene alta disponibilidad de CPU.

Si la CPU virtual de una instancia no tiene el tamaño adecuado, puede convertirse en una fuente de contención. Para verificar si la CPU es un cuello de botella o tiene aprovisionamiento insuficiente, usa las consultas en este documento.

Verifica el recuento promedio de tareas

Ejecuta esta consulta un par de veces para verificar el recuento promedio de las tareas. Si el recuento promedio de tareas es alto constantemente, es posible que la instancia esté experimentando la 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);

Determina si es necesario agregar más CPU virtuales

En ciertas condiciones, es posible que desees aumentar la CPU virtual. Usa esta consulta para determinar si es necesario agregar más CPU virtuales.


-- 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

Verifica los índices faltantes

Verifica los índices faltantes mediante la siguiente consulta. Prueba estos índices en una instancia que no sea de producción para ver cómo afectan al rendimiento de 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

Verifica los índices que tienen demasiada sobrecarga

Agregar un índice puede ayudar con la optimización, pero también puede agregar una sobrecarga de rendimiento que contribuye a un mayor uso en la CPU y la memoria. Revisa los índices que tienen una cantidad muy alta de operaciones de escritura en comparación con una cantidad muy baja de lecturas y considera quitarlas.


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

Encuentra las consultas principales que consumen más CPU

Revisa las 20 consultas principales por uso de CPU o tiempo de trabajador. Estas son las consultas basadas en las estadísticas de ejecución de consultas que usan la mayor parte de la CPU. Estas estadísticas se agregan con el tiempo y están vinculadas a los planes en 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

Verifica las conversiones implícitas en los planes de consultas

Esta operación es costosa y, por lo general, aparece como una advertencia en el plan de ejecución de consultas. Por lo general, el mensaje tiene una advertencia que indica que podría afectar a CardinalityEstimate en la elección del plan de consultas. Puedes identificar con facilidad las conversiones implícitas cuando consultas los planes de consulta en SQL Server Management Studio (SSMS).