Fazendo amigos do SQL Server e do PerfMon

Na foto, há uma cena da boa e velha comédia “Airplane” - os boomers lembram disso.



Ela não está aqui por acaso. Mas útil depois.

Dba não gosta de perfmon


Isso não acontece porque a interface PerfMon não mudou com o Windows NT 3.1 (?) E devolve o calor de dois milésimos. A propósito, talvez alguém saiba a explicação para esse fato estranho. O painel de controle é reescrito várias vezes ao ano. Até a calculadora foi reescrita. Mas não perfMon.

O DBA pensa com palavras de capa. Quando um bom médico do DBA chega a um servidor SQL doente, ele abre sua mala e na mala há um grande número de quartos que o servem fielmente. Ele tira o menor deles da memória. Às vezes, um DBA pode examinar as métricas do PerfMon - disco físico ou comprimento da fila de disco. No entanto, um DBA real terá suas próprias métricas de servidor SQL, não através do PerfMon, mas com um patch, por exemplo, como este:

select * from sys.dm_os_performance_counters 
  where object_name='SQLServer:Buffer Manager' 
    and counter_name like 'Page life expectancy%' 
	and instance_name=''

Quando os representantes de TI, divorciados da esfera do DBA, perguntam a ele: "que métricas você deseja que coligamos?" (bem, existem todos os tipos de splunk, quadrado etc.), então o DBA começa a pensar em scripts e o restante em métricas perfmon. Ao mesmo tempo, eles tentam despejar todas as métricas no DBA. Quando isso acontece, a imagem é inútil em seu congestionamento. E então um pequeno videoclipe do filme "Avião" vai me ajudar .

Como resultado, o DBA grava o keri, que grava estatísticas a cada N minutos em alguma placa no banco de dados do DBAtasks. Você se reconhece?

Advogado Devil (perfmon)


No entanto, os contadores perfmon são uma ferramenta padrão. Eles podem ser registrados, definir alertas, mas o mais importante é que há um grande número de sistemas que "mesclam" essas métricas de muitas máquinas em um repositório centralizado, permitem analisá-las e criar belos gráficos (e não como perfmon).

Acontece que podemos fazer amizade com o mundo SQL e o mundo PerfMon!

Abaixo vou mostrar como.

Agora os detalhes irão


Como exemplo, mostraremos como exportar métricas que o próprio SQL não publica no PerfMon. Por exemplo, a consulta abaixo:

select
  convert(numeric(10,2),round(((sum(version_store_reserved_page_count)*1.0)/128.00),2)),
  convert(numeric(10,2),round(((sum(user_object_reserved_page_count)*1.0)/128.00),2)),
  convert(numeric(10,2),round(((sum(internal_object_reserved_page_count)*1.0)/128.00),2))
from
  tempdb.sys.dm_db_file_space_usage;

retorna a quantidade de espaço tempdb usado pelo armazenamento de versão (para captura instantânea), objetos de usuário (guia #tab e ##) e espaço de armazenamento temporário / de classificação. Vamos criar métricas para eles (maiores de 18 anos, as equipes não estão documentadas)

dbcc addinstance ('SQLServer:User Settable', 'TempDB version store KB')
dbcc addinstance ('SQLServer:User Settable', 'TempDB user store KB')
dbcc addinstance ('SQLServer:User Settable', 'TempDB sort store KB')

Isso criará três métricas no SQLServer -> UserSettable -> Query. As métricas são números inteiros - int (não bigint). Além disso, seu significado é interpretado como é. Ou seja, se você precisar de um delta do valor anterior, essa é sua preocupação. Mas, neste caso, só precisamos atribuir valores às métricas:

declare @TEMPDBver int, @TEMPDBuser int, @TEMPDBsort int
select

@TEMPDBver = convert(numeric(10,2),round(((sum(version_store_reserved_page_count) *1.0)/128.00),2)),
@TEMPDBuser = convert(numeric(10,2),round(((sum(user_object_reserved_page_count) *1.0)/128.00),2)),
@TEMPDBsort = convert(numeric(10,2),round(((sum(internal_object_reserved_page_count) *1.0)/128.00),2))
from tempdb.sys.dm_db_file_space_usage;

if @TEMPDBver is not null    
  dbcc setinstance ('SQLServer:User Settable', 'Query', 
    'TempDB version store KB', @TEMPDBver)
if @TEMPDBuser is not null   
  dbcc setinstance ('SQLServer:User Settable', 'Query', 
   'TempDB user store KB', @TEMPDBuser)
if @TEMPDBsort is not null   
  dbcc setinstance ('SQLServer:User Settable', 'Query', 
    'TempDB sort store KB', @TEMPDBsort)

Na verdade, é tudo. Agora, basta chamar esse código regularmente e tudo funcionará.

Contadores Delta


Agora vamos fazer um contador delta. Este será o tempo limite do bloqueio em ms.

dbcc addinstance ('SQLServer:User Settable', 'LOCK ms per s')

Aqui você pode me opor que essa métrica já exista:

select * from sys.dm_os_performance_counters 
  where counter_name like 'Lock Wait Time (ms)%' 
    and instance_name='_Total'
    and object_name='SQLServer:Locks'

Mas essa métrica é apenas eventualmente consistente. Como você se lembra, se os prótons são instáveis, qualquer banco de dados no universo é eventualmente consistente. Ou seja, se você criar um bloqueio e observar o valor dessa métrica, ela não aumentará! E somente no final do bloqueio, seu valor aumentará bastante durante todo o tempo de espera. No gráfico, em vez do esperado 'platô', você obterá um pico acentuado, que também estraga sua escala ao longo do eixo Y.

O mesmo problema é com o kvery:

select sum(wait_time_ms) 
  from sys.dm_os_wait_stats 
  where wait_type like 'LCK_%'

Portanto, devemos adicionar o horário dos atuais ao horário dos bloqueios 'concluídos':

declare @NEWlock bigint
select @NEWlock=sum(wait_time_ms) 
  from sys.dm_os_wait_stats 
  where wait_type like 'LCK_%' -- finished waits
select @NEWlock=@NEWlock+isnull(sum(waittime),0) 
  from master.dbo.sysprocesses 
  where blocked>0 and lastwaittype like 'LCK_%' -- waits in progress

Essa consulta já fornece o platô correto no gráfico.

Agora precisamos considerar o delta do valor anterior. Existem duas maneiras:

  • Crie um processo como um trabalho chamado com frequência (digamos, uma vez por minuto). Crie um rótulo para armazenar valores anteriores. De fato, muitas vezes é ruim chamar trabalho (sobrecarga + competição pelo histórico de execução de Trabalhos) e você pode fazer isso sem uma placa adicional:
  • Crie um processo que é executado em um loop sem fim com waitfor. O processo lembra o valor anterior na variável. Processaremos o processo como um trabalho com dois agendamentos: na inicialização e uma vez por hora (caso o trabalho caia, ele será reiniciado)

No segundo caso, é bem possível fazer um ciclo a cada cinco segundos ou até com mais frequência. Então, nós temos:

declare @OLDlock bigint, @NEWlock bigint
declare @lock int, @seconds int = 5 -- must match WAITFOR
loop:
  set @OLDlock=@NEWlock -- shift new to old
  select @NEWlock=sum(wait_time_ms) 
    from sys.dm_os_wait_stats 
	where wait_type like 'LCK_%' -- finished waits
  select @NEWlock=@NEWlock+isnull(sum(waittime),0) 
    from master.dbo.sysprocesses 
	where blocked>0 and lastwaittype like 'LCK_%' -- waits in progress
  set @lock=(@NEWlock-@OLDlock)/@seconds -- this is delta
  if @lock is not null    
    dbcc setinstance ('SQLServer:User Settable', 'Query', 
	  'LOCK ms per s', @lock)
  waitfor delay '00:00:05' 
goto loop

Observe que na primeira vez em que você executar um loop DBCC, ele será ignorado (o que é correto), uma vez que @OLDlock (e trava) será nulo.

All Articles