Oracle e Hugepages

Em diversos artigos no MOS encontramos informações importantes resumidas a uma simples nota de rodapé ou a uma única linha no meio do texto. Geralmente isso nos leva a pesquisar mais, nos aprofundar no assunto. Foi isso que aconteceu quando estava começando a trabalhar com o Exadata em 2010. Eu estava garimpando algumas notas no MOS atrás de informações sobre o uso do Exadata com OLTP e encontrei uma breve referência ao uso de Hugepages.

Estava lendo a nota MOS# 1067520.1 (que é uma das principais notas sobre desempenho para o Exadata) e lá estava a seguinte informação: “Hugepages should be used, as this reduces the page table size and also reduces process startup time. Review  MOS Note 361323.1 and Note 401749.1 for details on hugepages”.

Era uma informação literalmente solta no meio do texto e achei melhor investigar. A nota MOS# 361323.1 tinha o sugestivo nome de “HugePages on Linux: What It Is… and What It Is Not…” e era uma ótima introdução ao que é Hugepages. Já tinha ouvido sobre Hugepages e Oracle, mas isso era para Oracle 9 em ambientes 32 bits com mais de 4GB de memória.

De forma resumida hoje em dia Hugepages é a forma como podemos acessar ou utilizar páginas de memória com tamanho maior que o convencional no Linux kernel 2.6 em diante. Antes de começar a falar de Hugepages precisamos apresentar alguns pontos:

  • Page Table: é uma estrutura que tem o mapeamento entre os endereços de memória física e virtual. Isso quer dizer que quando um aplicativo acessar a memória, primeiro ele lê a Page Table para identificar o endereço físico necessário para o acesso.
  • TLB: significa Translation Lookaside Buffer, é um pequeno buffer/cache que fica no processador para armazenar a Page table (ou parte desta). Está no CPU para permitir uma tradução mais rápida dos endereços.
  • Hugetlb: São as entradas na TLB que apontam para endereços de hugepages, assim o acesso é direto e está mapeado no cache do CPU.

De forma simplificada, Hugepages permite que sejam utilizadas páginas de memória com um tamanho maior que o padrão do sistema operacional. Com isso, temos algumas vantagens em relação a sistema tradicional.

Partindo do principio que de que uma página de memória é uma porção da memória total do sistema, com páginas no tamanho padrão do Linux (4k) temos uma grande quantidade de páginas disponíveis. Mas, fazendo uma conta rápida, o tamanho da Page Table para armazenar todos os endereços e páginas de memórias ficará grande. Imagine o overhead para percorrer a tabela em busca de um endereço disponível.

Utilizando Hugepages tomos algumas vantagens quando comparamos com o modelo acima:

  • Sem Swap: Por definição, Hugepages não vão para Swap. Imagine um banco de dados que não vai para swap, que não precisa trocar contexto para acessar a memória, sem Page fault. Para entender porque swap deve ser evitada no Oracle, basta ler a nota MOS# 1295478.1.
  • Menor overhead sobre a TLB: Com o uso de Hugepages, a lista de endereços de acesso para mapear toda a memória é menor, com isso a TLB fica menor e mais rápida.

A Oracle recomenda o uso de Hugepages não somente para o Exadata, mas para todo o banco de dados que rode no Linux e que tenha uma grande quantidade de memória. Não existe limite mínimo nem máximo para o uso de Hugepages, se a soma de suas SGA’s que rodam no servidor for maior que 8GB, recomenda-se o uso.

Para utilizar Hugepages alguns passos são necessários, na nota MOS# 361468.1 temos listados alguns passos. A nota esconde alguns detalhes que gostaria de resaltar, em comparação com o que está na lista da nota os passos que utilizei foram diferentes.

Primeiramente temos que verificar qual o tamanho da nossa página de memória para a HugePages. Geralmente é padronizada por versão do Kernel, para o 2.6 x64 bits é de 2MB, mas é sempre bom verificar. Para isso basta dar uma passada no /proc/memeinfo |grep Huge. Abaixo, um exemplo extraído de um DBNode do Exadata:

[root@exadb01 ~]# cat /proc/meminfo |grep Huge
HugePages_Total: 20250
HugePages_Free:   4580
HugePages_Rsvd:   4567
Hugepagesize:     2048 kB
[root@exadb01 ~]#

Com isso sabemos que o tamanho da página tem 2MB, mas isso pode ser alterado via sysctl. Não achei nada relacionado ao Oracle quanto ao tamanho de página, acredito que o tamanho padrão do Linux seja suficiente.

Quando utilizamos Hugepages temos que nos acostumar a consultar o /proc/meminfo, pois ali é a fonte principal para verificar a saúde da sua Hugepages. Com este output podemos observar que temos:

  • HugePages_Total: representam o total de Hugepages que foram alocadas. Um detalhe importante é que a Hugepages fica previamente alocada, se você alocou 20GB de Hugepages, mas só usa 5, os 20GB já ficarão alocados. Não que isso seja ruim, se você tem um servidor dedicado ao banco de dados a memória deveria ser dedicada a ele.
  • HugePages_Free: o número de páginas que não foram utilizadas.
  • HugePages_Rsvd: número de páginas que foram reservadas para uso, significa que foram sinalizadas ao S.O. a intenção de uso mão ainda não o foram utilizadas.

Antes de prosseguir com o passo a passo uma historinha sobre como calcular o uso de HugePages. Para saber quantas páginas foram utilizadas e ou estão livres temos que fazer algumas contas e ai temos um segredo. Tomando o exemplo acima, apesar de aparecer 4580 páginas livres isso não reflete o valor correto de páginas livres, vou exemplificar.

Na versão 11.2.0.2 temos um (vamos chamar de) “bug” na identificação das Hugepages, nada relacionado ao seu uso (erros de acesso ou alocação) mas na forma como o Oracle conta o que está livre e o que está reservado. No ambiente de onde retirei o exemplo eu tinha alocado 19990 para a Hugepages e destas estava com 10033 livres. Mesmo assim o Oracle reclamada em subir uma instância com SGA de 1GB. O log era o seguinte:

****************** Huge Pages Information *****************
Huge Pages memory pool detected (total: 19990 free: 10735)
Huge Pages allocation failed (free: 10033 required: 1025) <<<< FAILED
Allocation will continue with default/smaller page size

Como você pode ver, ele tentava alocar 1025 páginas e não conseguia, mesmo tendo 10033 listadas como livres. Se eu subisse um pouco o número de Hugepages no Linux para 20250 tudo iniciava corretamente. Bom, depois de uma SR nível 1 e um bug aberto veio a explicação do problema.

Em síntese, a versão 11.2.0.2 utiliza uma lógica errada para o cálculo de páginas livres, o Oracle lia o valor de HugePages_free e usava isso como referência, e infelizmente isso não é verdade. Na época do exemplo eu tinha estes valores aproximadamente:

  • HugePages_Total: 19990 – número total de páginas para Hugepages.
  • HugePages_Free: 8999 – páginas que ainda não foram utilizadas, mas podem estar reservadas.
  • HugePages_Rsvd: 8221 – reservadas, mas não usadas.

O segredo é que HugePages_Free representa o número de páginas livres + as reservadas (HugePages_Rsvd). O método correto para o cálculo de Hugepages livres é a seguinte HugePages_Free – HugePages_Rsvd. No meu caso eu tinha só 769 livres, o que causava um erro já que eu precisava de 1025. Como falei antes o “bug” estava na forma como Oracle apresentava esta informação no alertlog, ele simplesmente imprima HugePages_Free sem desconsiderar as reservadas.

Isso já foi corrigido na 11.2.0.3, agora um alertlog bem mais informativo está presente. O bug relacionado era o 12976195 (POOR DIAGNOSTICS WHEN STARTING INSTANCE AND HUGEPAGES ARE ENABLED). Portanto, se você estiver usando a 11.2.0.2 cuidado com a informação presente no alertlog (isso inclui a versão do último BP para o Exadata).

Então a fórmula correta para saber o que já foi utilizado e o tamanho total é: HugePages_Total – (HugePages_Free – HugePages_Rsvd) = Total em uso x Hugepagesize. No meu caso são aproximadamente 40GB de HugePages ( 20250 – (4580-4567) = 20237 x 2048 kB = 41445376 / 1024  = 40474MB ) em uso e consequentemente só 13 páginas livres.

Para quem queira usar Hugepages é fundamental verificar se o seu banco de dados está utilizando Automatic Memory Management (AMM). Caso esteja usando, ele deve ser desligado, pois o AMM é incompatível com o Hugepages. Para bases OLTP verifique que você não está usando os parâmetros MEMORY_TARGET e MEMORY_MAX_TARGET, defina ambos como 0 e ajuste a sua SGA.

Com AMM a memória é alocada através da /dev/shm. O Flávio Soares fez uma análise bem interessante disso que pode ser lida aqui. Em resumo, como o Hugepages referencia diretamente a páginas de memória e não a arquivos no /dev/shm. O AMM não tem como alocar a memória, assim não  configure os parâmetros MEMORY_TARGET e MEMORY_MAX_TARGET. Caso tenha dúvidas você pode ler a nota MOS# 749851.1.

Após sabermos o tamanho da nossa página, temos que alocar memória para a Hugepages. Aqui temos outro pequeno detalhe, para calcularmos o valor correto podemos utilizar o script disponibilizado na nota MOS# 401749.1. Para ilustrar vamos fazer a conta por nós mesmo, assim caso você não tenha acesso ao MOS pode fazer voc6e mesmo. O script presente na nota faz uma conta simples, ele pega a quantidade de segmentos de shared memory atualmente utilizados pelo Oracle (através do comando ipcs –m) e divide pelo tamanho da página do Hugepages. Assim no meu caso temos:

[root@exadb01 ~]# ipcs -m |grep oracle
0x69700aac 4554759    oracle    660        4096       0
0xe882257c 4685832    oracle    660        2149580800 57
0xe3b39988 5537801    oracle    660        4297064448 94
0xcbb9b93c 5603338    oracle    660        4297064448 91
0x79aceee8 4849675    oracle    660        6712983552 51
0x47eafa00 4882444    oracle    660        6712983552 50
0x158e4cf8 4947981    oracle    660        2149580800 55
0x99f7c454 5668878    oracle    660        4297064448 85
0x554012e8 5079055    oracle    660        2149580800 50
0xf0a0c2b0 5734416    oracle    660        2149580800 75
0xc5602aa0 5275665    oracle    660        2149580800 59
0x4300d7a0 5865490    oracle    660        1612709888 63
0x98686b58 5373971    oracle    660        1612709888 83
0x1c03c7c0 5406740    oracle    660        2149580800 64
0x4fb2246c 5472277    oracle    660        4096       0
[root@exadb01 ~]#

O detalhe aqui é que tanto o script quanto o ipcs não são muito seletivos quanto as bases que devem ou não entrar na conta. Não conheço nenhum método para filtrar a informação no ipcs que nos leve a associação de quem é o processo dono da memória listada na saída do comando. No meu caso, tenho 13 bases OLTP rodando no meu Exadata, mais uma DW e mais o ASM. Como visto no output tenho 15 processos Oracle. Você pode parar as bases que não são OLTP para que elas não entrem na conta (ignore o fato do ASM aparecer na lista). Lembre-se que Hugepages são recomendadas somente para bases OLTP.

Se você não tiver o script a conta é simples, para cada uma das linhas reportadas no ipcs você divide este valor pelo tamanho da página da Hugepages e multiplica por 1024 (pois como o shared memory é em bytes temos que igualar as grandezas). Assim, temos a seguinte fórmula aplicada a cada linha do ipcs: <shared memory>/(Hugepagesize*1024).

Se este valor for maior ou igual a 1 deve-se somar este resultado com os das outras linhas para chegar no total de HugePages. A Oracle ainda recomenda somar uma página a mais para cada linha devido a um pequeno overhead que pode existir. No caso acima teríamos um valor de 20250 Hugepages. Claro que o ideal seria ter o script, mas a conta básica é essa.

Com esse número em mãos podemos alocar as Hugepages. Para isso, você pode utilizar o sysctl vm.nr_hugepages para algo momentâneo, ou escrever no /etc/sysctl.conf para ficar salvo entre cada reboot. Um exemplo do meu sysctl está abaixo:

[root@exadb01 tmp]# cat /etc/sysctl.conf |grep huge
vm.nr_hugepages = 20250
[root@exadb01 tmp]#

Caso você esteja fazendo isso no Exadata ignore mensagens de alerta do sysctl.conf quanto ao parâmetro de Hugepages. Com isso elas podem ser alocadas, mas antes um outro segredo operacional. Nunca aloque as suas Hugepages com as suas instâncias iniciadas. Pegando o exemplo acima, eu tenho toas as minhas instâncias ativas ocupando em torno de 55GB de memória, se eu for alocar o Hugepages agora posso travar o meu Linux, pois estou tentando alocar 40GB de Hugepages (20250*2048) e somando isso ao que já está sendo consumido irei extrapolar a quantidade de memória disponível. E ainda piorando, pois como Hugepages não vão para swap, entregamos uma bomba para o S.O. resolver. Então, recomendo parar todas as instâncias antes de alocar a sua Hugepages via sysctl.

Você pode fazer a alocação dinâmica da Hugepages através do sysctl, isso pode ser útil para testar se suas instâncias estão corretamente configuradas e sem AMM. Assim você pode testar subir cada uma das instâncias e acompanhar no alertlog se as Hugepages estão sendo alocadas. Caso elas estejam sendo utilizadas corretamente, no alertlog teremos algo parecido com isso na versão 11.2.0.2:

****************** Huge Pages Information *****************
Huge Pages memory pool detected (total: 20250 free: 8603)
DFLT Huge Pages allocation successful (allocated: 2049)
***********************************************************

E na versão 11.2.0.3 em diante:

****************** Large Pages Information *****************
Total Shared Global Region in Large Pages = 32 GB (100%)
Large Pages used by this instance: 16367 (32 GB)
Large Pages unused system wide = 49992 (98 GB) (alloc incr 64 MB)
Large Pages configured system wide = 150000 (293 GB)
Large Page size = 2048 KB
***********************************************************

Não recomendo fazer como sugerido na MOS# 361468.1, que após definir no sysctl.conf já pede um reboot. Acho por bem testar a alocação de cada instância e depois sim fazer o reboot. Faço isso, pois caímos no mesmo caso de antes, após o reboot todas as instâncias irão subir e caso elas não estejam usando Hugepages não teremos memória suficiente.

Mesmo assim nem tudo são flores, dependendo a versão que você esteja rodando do seu banco (em específico a 11.2.0.2) podemos cair em um BUG de do CRS, especificamente o #9084067. Esse não é nenhum bug presente nos binários ou que precise de patch aplicado, é um simples erro de ulimit não configurado. Para corrigir basta editar o arquivo ohasd do init e trocar a linha:

start()
 {
   $ECHO -n $"Starting $PROG: "

Por:

start()
 {
   $ECHO -n $"Starting $PROG: "
   ulimit -n 65536
   ulimit -l "valor levemente inferior ao tamanho da memória em bytes"

Isso é necessário, pois como ohasd é chamado como um usuário diferente do Oracle ele pode não ter permissões para alocar tanta memória quanto o que foi definido para Hugepages. Caso você tenha acesso ao MOS, esse bug já foi corrigido no PSU2 do Oracle versão 11.2.0.2. Mas caso não o tenha fique atento a isso. Isso está listado nas notas MOS# 983715.1 e para Exadata na MOS# 1284261.1.

Acredito que para o uso de Hugepages no Oracle sejam estes os detalhes. Se você observar os passos que listei acima diferem do que são encontrados na nota MOS# 361468.1, procurei além de listar os passos explicar cada um deles. Nem todos essas passos são simples ou claros na primeira leitura, mas tentei explicar em detalhes cada um deles.

Quando você observa as vantagens de ter um banco de dados que tem custo mínimo na alocação de memória, que roda sempre em memória sem nunca ir para a swap e que o seu mapeamento de memória está no cache do processador as vantagens de Hugepages sobre a alocação tradicional são interessantes.

One Response to Oracle e Hugepages

  1. Gabriel Andrade says:

    Ótimo artigo, agora foi esclarecedor!

Leave a Reply

Your email address will not be published. Required fields are marked *