Este artigo vai expor alguns conceitos teóricos e seu reflexo prático para melhor entendimento. Nós, DBAs, quando nos deparamos com um cenário de lentidão, geralmente precisamos avaliar como está o consumo de recurso de CPU dos servidores que hospedam o banco de dados. É nessa análise que vamos nos ater.
Load Average
Para poder utilizar o processador do servidor, o S.O usa o conceito de fila de Jobs. A partir dessa fila, os Jobs poderão assumir alguns status, mas vamos nos concentrar inicialmente em 2: os que estão em execução (status R) ou os que estão aguardando recurso de I/O de disco (status D). Essa quantidade média de itens processando e aguardando processamento são apresentados como Load Average nos comandos “uptime” ou “top”, por padrão dos últimos 1, 5 e 15 minutos. Exemplo:
[root@oel7 ~]# uptime
18:39:25 up 19 min, 1 user, load average: 0.38, 0.81, 0.72
[root@oel7 ~]#
top - 14:44:41 up 24 min, 1 user, load average: 0.75, 0.82, 0.75
Tasks: 326 total, 1 running, 237 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.8 us, 0.6 sy, 0.0 ni, 98.6 id, 0.0 wa, 0.0 hi, 0.1 si, 0.0 st
KiB Mem : 16150712 total, 10392080 free, 1152292 used, 4606340 buff/cache
KiB Swap: 1261564 total, 1261564 free, 0 used. 11646372 avail Mem
%CPU usage
Uma vez que sabemos a quantidade de Jobs em nossa fila, precisamos saber quantos Cores estão disponíveis no S.O. Para isso, podemos usar os seguintes comandos (que reportam 6 cores em meu lab):
[root@oel7 ~]# cat /proc/cpuinfo | grep "cpu cores"
cpu cores : 6
cpu cores : 6
cpu cores : 6
cpu cores : 6
cpu cores : 6
cpu cores : 6
[root@oel7 ~]# sar -r 1 1
Linux 4.14.35-1902.3.2.el7uek.x86_64 (oel7.localdomain) 01/16/2023 _x86_64_ (6 CPU)
Para termos dimensão de utilização das CPUs, podemos seguir os seguintes exemplos/cenários como referência:
Servidor Single Core | Se o Load estiver em “1.0”, significa que a CPU sendo totalmente utilizada, e se novos Jobs forem criados, ficarão no final da fila, aguardando execução. |
Servidor Single Core | Se o Load estiver em “2.0”, significa que a CPU sendo totalmente utilizado, alguns Jobs JÁ estão enfileirados (ou seja, aguardando processamento), e se novos Jobs forem criados, ficarão no final da fila, aguardando execução. |
Servidor Multi-Core (6 cores) | Se o Load estiver em “1.0”, significa que a 1/6 da capacidade total está sendo utilizada. Ou seja, 1 atividade está sendo executada por algum core, e os outros 5 estarão em modo iddle. |
Servidor Multi-Core (6 cores) | Se o Load estiver em “6.0”, significa que a CPU sendo totalmente utilizada, e se novos Jobs forem criados, ficarão no final da fila, aguardando execução. |
Sendo assim, em condições normais, o ideal é que o Load fique no máximo com o número de cores presentes no S.O. Caso esteja sempre no valor máximo ou em quantidades maiores, pode haver algum problema nos aplicativos que ali executam e que precisem de tuning, ou realmente o servidor está mal dimensionado para o nível de processamento necessário.
A porcentagem de utilização já é calculada no comando top. No exemplo abaixo, 98.7% do meu recurso de CPU está Iddle.
top - 15:08:22 up 48 min, 1 user, load average: 0.32, 0.50, 0.59
Tasks: 324 total, 1 running, 239 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.6 us, 0.6 sy, 0.0 ni, 98.7 id, 0.0 wa, 0.0 hi, 0.2 si, 0.0 st
KiB Mem : 16150712 total, 10368512 free, 1172140 used, 4610060 buff/cache
KiB Swap: 1261564 total, 1261564 free, 0 used. 11626436 avail Mem
VMSTAT Tool
Para nos dar uma visão de tudo quanto foi escrito, podemos ver na prática as informações no comando “vmstat”. Este é um exemplo de seu output:
[root@oel7 ~]# vmstat 1 10 -S m
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 0 10520 40 4749 0 0 8 0 327 433 1 1 98 0 0
0 0 0 10519 40 4749 0 0 40 0 1931 2579 1 1 99 0 0
1 0 0 10519 40 4749 0 0 32 0 1587 2227 1 1 99 0 0
0 0 0 10519 40 4749 0 0 16 0 1651 2209 1 1 98 0 0
0 0 0 10519 40 4749 0 0 32 0 1840 2258 1 1 98 0 0
0 0 0 10519 40 4749 0 0 0 0 1942 2487 1 1 98 0 0
1 0 0 10519 40 4749 0 0 16 0 1960 2622 1 1 98 0 0
0 0 0 10519 40 4749 0 0 44 0 1763 2517 1 1 99 0 0
0 0 0 10520 40 4749 0 0 32 0 1865 2531 1 1 99 0 0
0 0 0 10519 40 4749 0 0 16 0 1867 2499 1 1 99 0 0
A primeira coluna “r” representa a Run-Queue, que demonstra os Jobs em execução e os que estão aguardando processamento. Caso esta coluna tenha um número maior do que a quantidade de cores do servidor, já podemos visualizar jobs enfileirados aguardando por execução.
A segunda coluna “b” são os processos em “Blocked state”, que podem ser vinculado ao status D que vimos no começo do artigo (que aguardam o processo de I/O de disco). Caso esta coluna apresente valores altos, pode ser um indício de problemas de Storage (ISCSI/NFS/NIC/HBA). Nota: Problemas na camada de Network também podem provocar um alto valor de Load.
Para simular um alto consumo de CPU devido wait de I/O, vou fazer o seguinte teste: rodar o pacote stress no Linux, consumindo apenas 2 cores, porém colocando 8 Jobs escrevendo 1GB em paralelo. Será possível notar no vmstat um acréscimo de valores na coluna “b”, um pico na coluna SY, e vemos que a CPU ficará em constante uso (coluna id):
[root@oel7 ~]# stress --cpu 2 --hdd 8 --timeout 100s
stress: info: [18038] dispatching hogs: 2 cpu, 0 io, 0 vm, 8 hdd
[root@oel7 ~]# vmstat 1 100 -S m
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 0 9989 73 5138 0 0 90 0 327 422 1 1 97 0 0
0 0 0 9989 73 5138 0 0 0 0 1494 2072 1 1 99 0 0
0 0 0 9989 73 5138 0 0 0 0 2057 2650 1 1 98 0 0
13 0 0 9986 73 5138 0 0 48 0 2507 2802 6 1 93 0 0
14 1 0 7313 73 7805 0 0 49192 0 8860 4847 24 76 0 0 0
3 7 0 6159 73 8960 0 0 36864 0 7841 4908 33 53 0 14 0
2 8 0 5848 73 9271 0 0 45104 0 6396 6052 36 8 17 39 0
4 8 0 5736 73 9383 0 0 188416 0 5846 5604 35 3 17 45 0
3 9 0 5685 73 9432 0 0 32808 0 5845 5522 35 1 29 35 0
2 10 0 5685 73 9433 0 0 4200 0 5418 4827 34 1 37 28 0
2 10 0 5685 73 9433 0 0 36 0 5403 4740 34 0 34 32 0
2 10 0 5685 73 9433 0 0 0 0 5417 4722 34 1 33 32 0
2 8 0 5645 73 9472 0 0 159780 0 5559 5078 34 2 34 30 0
2 9 0 5613 73 9504 0 0 65552 0 5602 5209 34 1 33 31 0
2 10 0 5604 73 9513 0 0 32836 0 5469 4756 34 1 33 32 0
2 10 0 5605 73 9513 0 0 32 0 5620 4894 34 1 33 32 0
2 10 0 5604 73 9513 0 0 44 0 5659 4900 35 1 32 32 0
2 9 0 5590 73 9528 0 0 24576 0 5485 5103 34 1 33 32 0
2 8 0 5564 73 9554 0 0 106588 0 5584 5199 34 2 33 31 0
2 1 0 5557 73 9561 0 0 16 0 5429 4898 34 0 43 22 0
2 9 0 5548 73 9570 0 0 28700 0 5449 4937 34 1 42 23 0
3 9 0 5527 73 9591 0 0 135284 0 5540 5029 34 2 33 31 0
3 9 0 5506 73 9612 0 0 100368 0 5479 5124 34 1 33 32 0
2 8 0 5490 73 9628 0 0 22528 0 5572 5350 34 1 33 32 0
4 9 0 5474 73 9643 0 0 59392 0 5713 5520 34 1 33 32 0
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
3 1 0 5466 73 9651 0 0 32816 0 5755 5238 35 1 30 34 0
0 10 0 5466 73 9651 0 0 2100 0 2386 2778 7 0 48 45 0
0 10 0 5465 73 9651 0 0 52 0 1920 2534 1 1 50 49 0
^C
Caso seja identificado um alto Load, é possível verificar os processos que estão mais consumindo CPU. Caso um PID específico apresente uma % maior que 100%, significa que ele está sendo executado por mais de um core. Exemplo: 300% = o PID está usando 3 cores para sua operação.
[root@oel7 ~]# top -H
top - 16:39:13 up 2:19, 1 user, load average: 0.60, 0.58, 0.59
Threads: 602 total, 1 running, 517 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.8 us, 0.7 sy, 0.0 ni, 98.4 id, 0.0 wa, 0.0 hi, 0.1 si, 0.0 st
KiB Mem : 16150712 total, 10244624 free, 1215848 used, 4690240 buff/cache
KiB Swap: 1261564 total, 1261564 free, 0 used. 11573988 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
27135 root 20 0 164124 4984 3808 R 1.6 0.0 0:00.36 top
3762 oracle -2 0 2983032 60736 57468 S 1.0 0.4 1:19.47 ora_vktm_cortex
3642 grid -2 0 1554232 64464 61232 S 0.7 0.4 1:19.24 asm_vktm_+asm
3659 grid 20 0 1559032 79188 72596 S 0.7 0.5 0:07.00 asm_dia0_+asm
4573 root 20 0 165688 10136 8568 S 0.7 0.1 0:11.28 sshd
3410 grid 20 0 974652 41844 30300 S 0.3 0.3 0:02.93 cssdagent
3400 grid 20 0 2143220 135968 90192 S 0.3 0.8 0:17.10 oraagent.bin
Oracle Recommendations
- Manter seu workload com consumo não excedendo os 65-75%. Motivos: para que o S.O tenha fôlego suficiente para dar vazão em suas tarefas internas, como em ambientes com Alta Disponibilidade (com dinâmica de Heartbeat, para evitar evictions), e ambientes com alta taxa de I/O e tráfego de rede.
- Da porcentagem de utilização de CPU, podemos atrelar aos aplicativos que rodam no servidor o consumo apresentado na coluna “US” (User). Para o Kernel, a coluna SY (System). Problemas de rede ou I/O vão apresentar um valor alto na coluna SY, por exemplo. Mas nem sempre a origem do problema é o Kernel. Podemos ter aplicações que fazem demasiadas “sys-calls” e provocam o problema. Exemplo: uma aplicação extremamente transacional e que abre um processo no banco de dados por vez.
[root@oel7 ~]# vmstat 1 5 -S m
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 0 10483 49 4755 0 0 6 0 316 418 1 1 98 0 0
0 0 0 10483 49 4755 0 0 48 0 1916 2664 1 1 99 0 0
4 0 0 10483 49 4755 0 0 0 0 1971 2620 1 1 99 0 0
0 0 0 10483 49 4755 0 0 16 0 1634 2231 1 1 98 0 0
3 0 0 10483 49 4755 0 0 48 0 1825 2657 0 0 99 0 0
[root@oel7 ~]#
Hyper-Threading considerations
Em minhas investigações, ao ler um artigo do Jared Still (link AQUI), o mesmo questiona essa % de utilização que é geralmente reportada em ambientes com HT habilitado. E creio que o faz com certa razão, e isso também é citado pela própria Oracle no Note “Oracle Linux: How to understand OS load average and run queue / blocked queue in terms of CPU utilization (Doc ID 2221159.1)“:
O referido documento da Intel não consegui acessar pelo link citado, mas creio que achei o mesmo AQUI. Neste artigo, pelo que pude compreender, é feito um teste de desempenho em diferentes tipos de aplicações para aferir sua melhora de desempenho (sem mexer em código) ao utilizar o HT. Vou colocar abaixo os destaques que fiz durante meus estudos:
Ou seja, a mensagem que fica é que sendo HT, a quantidade de cores não é tão linear quanto podemos pensar. Em suma, é um core lógico competindo por um recurso físico, e cabe a nós considerarmos isso em nossas análises e investigações.