How to identify CPU Bottlenecks on Oracle Linux

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

Definição da tecnologia de HT.
Comentário sobre o ILP, que já existia e foi aprimorado conforme sessão abaixo.
Pormenores técnicos da tecnologia usada, que permite escalar o trabalho de processamento.
Tipos de aplicações utilizadas no teste realizado.
“Naturally, highly scalable applications; that is, those that
speed up best when run on multiple, physical
processors, are the best candidates for performance
improvement on a threaded processor.”

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.

Leave a Comment

Your email address will not be published.