Oracle Database 12.1.0.2: Introdução ao Full Database Caching

Por Deiby Gómez Oracle ACE Director e Alex Zaballa Oracle ACE Director
Postado em Outubro 2015

Há vários meses foi lançada a versão do banco de dados Oracle 12.1.0.2, que contém diversas novas características relativas ao tratamento de dados em memória. Com o passar do tempo fomos observando as alterações sofridas pelo banco de dados Oracle em relação à gestão de dados e como estes são armazenados no Buffer Cache. Em versões anteriores, um algoritmo chamado LRU ou Last Recent Used, era apenas uma lista, que quando estava cheia desalocava os blocos que estavam a mais tempo ​​sem serem utilizados. Logo, começamos a ver mudanças ao longo das novas versões que surgiram e sabemos que este algoritmo mudou um pouco, foi melhorado e agora desalocar os blocos no Buffer Cache é baseado em um contador e no tempo. O problema com o velho LRU é que cada vez que um bloco que já estava no Buffer Cache era usado, ele se movia para o topo da lista (nos referimos ao topo da lista os blocos ​mais recentemente usados). Quando era necessário desalocar blocos para acomodar outros, o que o Oracle fazia era remover os últimos blocos da lista (último recentemente usado) e acomodar os novos na parte mais alta. Como você pode ver a parte “alta“ se torna popular e muito solicitada.

O problema com isto é que, em um ambiente OLTP onde os blocos de diferentes objetos são utilizados ao mesmo tempo, é gerada uma contenção nesta lista. Isto ocorre porque o Oracle tenta colocar muitos blocos no topo e ao mesmo tempo. Para evitar esta contenção na parte superior da lista, o Oracle adicionou um contador e também um campo onde o tempo é salvo quando o bloco é usado. Sempre que um bloco é usado o contador é incrementado (o tempo também é atualizado). Em seguida, o contador nos dirá quanto tempo se passou desde que ele foi usado. Diz-se que o bloco é muito utilizado se o contador é alto e pouco usado se o contador é baixo, mas não apenas o contador é levado em conta quando se desloca blocos para o início da lista, também o momento em que o último bloco foi utilizado é levado em consideração.

Você pode ter um bloco que foi lido milhares de vezes que nos dará como resultado um contador alto, mas foi utilizado pela última vez a uma hora atrás ou mais. Deveríamos move-lo para o topo da lista? Devemos catalogar como um bloco muito usado? É aí que não só o contador é levado em conta, mas também a data que acompanha este contador. Tais perguntas são as que os algoritmos do Oracle tentam responder para tomar a melhor decisão possível. É bom esclarecer que não devemos basear 100% sobre o contador, pois foi demostrado que um bloco lido milhares de vezes ao longo de um período de tempo muito pequeno pode mostrar um contador menor que um bloco que foi lido muitas vezes (não milhares), mas em um tempo muito mais longo (Poderíamos filosofar sobre isso, mas temos as páginas limitadas neste artigo).

Então agora você sabe brevemente como os objetos são desalocados, e isso deve ser levado em conta já que não possuímos memória ilimitada. Nós temos um limite de GBs e devemos administrar bem.

O gerenciamento de tabelas muito grandes é um problema para essa quantidade de GBs que estão alocados para o Buffer Cache, pois se efetuarmos a leitura de um objeto grande poderíamos desalocar quase todos os blocos dos objetos mais usados que já estão no Buffer Cache. Todos os blocos da tabela grande seriam alojados e seriam marcados como usados mais recentemente. Isso estaria certo? Poderíamos pensar que sim, mas agora, suponha que esta tabela grande seja lida uma vez por semana, então por que deixar os blocos que são verdadeiramente utilizados no dia a dia pelas nossas aplicações substituídos por blocos que são lidos uma vez por semana? Não vale a pena e possivelmente iríamos obter um baixo desempenho, porque os blocos que foram realmente utilizados e que agora não existem mais na memória devem ser lidos novamente do disco. Para resolver este problema o Oracle aplica os seguintes critérios para tabelas grandes:

  • As tabelas pequenas são armazenadas na memória apenas se o tamanho total da tabela é menor que 2% do tamanho total do Buffer Cache.
  • Para tabelas médias (Alguns dizem que seu tamanho é entre 2% e 10% do tamanho do Buffer Cache, mas não é confirmado), o Oracle analisa a data em que a tabela foi lida pela última vez, a data da última utilização dos blocos que já estão no Buffer Cache, o tamanho da tabela e o espaço livre no Buffer Cache; Com base nessas informações o Oracle decide se irá colocar os blocos desta tabela no Buffer Cache ou não.
  • Tabelas grandes não são colocadas no Buffer Cache, a menos que seja indicado explicitamente que estes blocos devem ser colocados no Buffer Cache através da utilização da cláusula KEEP. Poderíamos começar a ver alguns blocos destas tabelas mas são apenas metadados.

Agora vamos falar sobre o outro lado da moeda, onde temos uma grande quantidade de GBs para o nosso Buffer Cache e esse montante é muito maior que o tamanho do nosso banco de dados. Agora que temos memória suficiente para armazenar todos os objetos no banco de dados no Buffer Cache, por que deveríamos continuar utilizando os algoritmos para desalocar blocos do Buffer Cache? Se tudo cabe na memória, não há razão para desalocar. A Oracle pensou nisto e introduziu um novo recurso na versão 12.1.0.2 chamado Full Database Caching.

O Full Database Caching trata de colocar dentro do Buffer Cache todos os blocos de todas as tabelas que forem lidos, o Oracle assume que o tamanho do Buffer Cache é grande o suficiente para acomodar toda a base de dados. Os critérios que já foram explicados para as tabelas pequenas, médias e grandes já não se aplicam ao banco de dados e já não interessa mais esta informação, pois ele irá colocar essas tabelas diretamente na memória.

Quando este recurso estiver ativado, o Oracle não irá carregar imediatamente todos os blocos de todos os objetos na memória, mas sim durante a leitura dos blocos. Então ative e permita que os aplicativos leiam os dados e façam a “mágica” acontecer.

Se o Full Database Caching estiver ativado em uma base de dados do tipo CDB, então esta característica é aplicada a nível do CDB e todos os objetos de todas as bases de dados Pluggable (PDB) que se encontram no CDB serão colocados em memória.

Com esse recurso, todos os blocos são colocados na memória, mesmo aqueles objetos que têm a propriedade NOCACHE, como por exemplo objetos LOB.

Para um banco de dados não-RAC, o tamanho lógico do nosso banco de dados deve ser menor do que o tamanho total do Buffer Cache.

Para os ambientes do tipo Real Application Cluster (RAC), o tamanho lógico do banco de dados deve ser menor do que o tamanho do Buffer Cache de cada instância que compõem a configuração do RAC e deve ser menor do que 80% do tamanho total do Buffer Cache de cada uma das instâncias. Para uma configuração RAC não é possível ter esse recurso ativado em alguns nós e em outros não, este recurso é aplicado ao banco de dados e consequentemente a todas as instâncias.

Para saber o tamanho do Buffer Cache para uma instância específica, podemos executar a seguinte consulta:

SQL> SELECT NAME, BYTES/1024/1024/1024 GB  
     FROM V$SGAINFO WHERE NAME = 'Buffer Cache Size';

NAME                 GB
-------------------- ----------
Buffer  Cache Size   3.21875


O tamanho lógico de nosso banco de dados deve ser inferior a este montante.

Para descobrir se o Full Database Caching está ativo, devemos executar a seguinte consulta:

SQL>  SELECT FORCE_FULL_DB_CACHING FROM V$DATABASE;

FORCE_FULL_DB_CACHING
---------------------
NO


Agora vamos ver o comportamento de um banco de dados quando o recurso Full Database Caching não está habilitado. Para isso, serão utilizadas três tabelas e faremos a leitura utilizando o Full Table Scan:

SQL> select table_name, blocks,  trunc((blocks*8192)/1024/1024,3) MB 
     from dba_tables where table_name in  ('BIG_TABLE','OTHER_BIG_TABLE','LITTLE_TABLE') order by 1;

TABLE_NAME           BLOCKS     MB
-------------------- ---------- ----------
BIG_TABLE            147473     1152.132
LITTLE_TABLE         143        1.117
OTHER_BIG_TABLE      147471     1152.117

 

Full Database Caching desativado

Limpando a SGA:

SQL>  shutdown immediate;
SQL> startup;


Nota:
Você também pode fazer um Flush.

Agora vamos ver quantos blocos de nossas tabelas estão no buffer cache:

SELECT o.object_name, COUNT(*) blocks,  trunc((COUNT(*)*8192)/1024/1024,3) size_MB 
FROM DBA_OBJECTS o, V$BH bh
WHERE o.data_object_id = bh.OBJD
AND o.owner ='DGOMEZ'
GROUP BY o.object_Name
ORDER BY COUNT(*);   

no rows selected


Nota:
A partir de agora vamos nos referir a esta consulta como: dgomez_cache_blocks.sql

Como se pode ver anteriormente, não há blocos de dados no Buffer Cache de nenhuma das nossa três tabelas. Agora vamos fazer uma varredura completa da tabela:

SQL> select count(*)  from dgomez.big_table;

COUNT(*)
----------
10000000


Vamos verificar quantos blocos foram colocados na memória:

SQL>  @dgomez_cache_blocks.sql

OBJECT_NAME     BLOCKS    SIZE_MB
--------------- --------  ----------
BIG_TABLE       1         0.007


A tabela não foi carregada no Buffer Cache, você sabe por quê? Se agora você está dizendo que a razão é porque a tabela é muito grande (> 10% Buffer Cache), então você está entendendo o conceito.

Agora vamos tentar colocar outra tabela grande no Buffer Cache:

SQL>  select count(*) from dgomez.other_big_table;

COUNT(*)
----------
10000000

SQL>  @dgomez_cache_blocks.sql

OBJECT_NAME         BLOCKS    SIZE_MB
------------------  --------- ----------
BIG_TABLE           1         0.007
OTHER_BIG_TABLE     1         0.007


Esta tabela não foi colocada no Buffer Cache, porque ela também é classificada como "grande". Vamos tentar colocar uma tabela menor do que 2% do Buffer Cache, utilizando a nossa tabela LITTLE_TABLE que tem apenas 1 MB de tamanho:

SQL>  select count(*) from dgomez.little_table;

COUNT(*)
----------
10000

SQL>  @dgomez_cache_blocks.sql

OBJECT_NAME         BLOCKS    SIZE_MB
------------------  --------- ----------
BIG_TABLE           1         0.007
OTHER_BIG_TABLE     1         0.007
LITTLE_TABLE        132       1.031


Como a tabela é classificada como “pequena”, todos os blocos foram colocados no Buffer Cache. Parece que os conceitos estão sendo cumpridos.

Agora iremos mostrar o comportamento do banco de dados com o Full Database Caching ativado.

Full Database Caching ativado

Ativamos o recurso Full Database Caching:

SQL>  shutdown immediate;
      Database  closed.
      Database  dismounted.

ORACLE  instance shut down.

SQL>  startup mount;

ORACLE  instance started.

Total  System Global Area   4294967296 bytes
       Fixed  Size             2932632 bytes
       Variable  Size        939524200 bytes
       Database  Buffers    3338665984 bytes
       Redo  Buffers          13844480 bytes
       Database  mounted.

SQL>  ALTER DATABASE FORCE FULL DATABASE CACHING;
Database  altered.

SQL>  alter database open;
Database altered.

Podemos verificar que o recurso foi ativado corretamente:

SQL>  SELECT FORCE_FULL_DB_CACHING FROM V$DATABASE;  

FORCE_FULL_DB_CACHING
---------------------
YES


Podemos verificar os blocos de nossas tabelas que estão em memória:

SQL> @dgomez_cache_blocks.sql
no rows selected


Vamos realizar uma varredura completa em uma tabela grande:

SQL>  select count(*) from dgomez.big_table;

COUNT(*)
----------
10000000

SQL> @dgomez_cache_blocks.sql

OBJECT_NAME     BLOCKS     SIZE_MB
--------------- ---------- ----------
BIG_TABLE       146838     1147.171


Como você pode ver, o Oracle não está mais interessado se a tabela é pequena, média ou grande, simples e facilmente acomoda todos os blocos em memória.

Faremos a leitura de outra tabela grande para verificar se alguns dos blocos são desalocados. Em outras palavras confirmar se o algoritmo LRU é usado:

SQL>  select count(*) from dgomez.other_big_table;

COUNT(*)
----------
10000000  
   

OBJECT_NAME                BLOCKS     SIZE_MB
-------------------------  ---------- ----------
OTHER_BIG_TABLE            146836     1147.156
BIG_TABLE                  146838     1147.171


Podemos ver que a segunda tabela também foi alojada na memória em sua totalidade e os blocos da primeira tabela não foram tocados, ou seja, eles não foram desalocados. O que o Oracle está fazendo é simplesmente acumular todos os blocos que foram lidos até agora e continuará até que todo o banco de dados esteja na memória.

Finalmente faremos a leitura da nossa tabela pequena:

SQL> @dgomez_cache_blocks.sql

SQL> select count(*) from dgomez.little_table;

COUNT(*)
----------
10000 

OBJECT_NAME         BLOCKS     SIZE_MB
------------------  ---------- ----------
LITTLE_TABLE        132        1.031
OTHER_BIG_TABLE     146836     1147.156
BIG_TABLE           146838     1147.171


Novos blocos foram adicionados e nenhum foi desalocado.

Se você é um DBA curioso como nós, neste momento você está pensando sobre qual é o comportamento do banco de dados quando o Full Database Caching está ativo, mas o Buffer Cache é menor do que o tamanho total do banco de dados. Bem, “lamentos” informar que você não receberá um ORA-600. Para manter o bom hábito de apoiar a curiosidade, vamos mostrar com exemplos esse comportamento:

Verificando se o Full Database Caching está ativado:

SQL> SELECT FORCE_FULL_DB_CACHING FROM V$DATABASE;  

FORCE_FULL_DB_CACHING
--------------------
YES


Comprovamos que o nosso Buffer Cache só comportaria uma de nossas tabelas grandes:

SQL> SELECT  NAME, BYTES/1024/1024/1024 GB 
     FROM V$SGAINFO WHERE NAME = 'Buffer Cache Size';

NAME                      GB
------------------------  ----------
Buffer Cache Size         1.53125


Podemos verificar os blocos de nossas tabelas que estão em memória:

SQL>  @dgomez_cache_blocks.sql
no rows selected


Vamos realizar uma varredura completa na primeira tabela grande:

SQL>  select count(*) from dgomez.big_table;

COUNT(*)
----------
10000000

Podemos verificar os blocos de nossas tabelas que estão em memória:

SQL>  @dgomez_cache_blocks.sql

OBJECT_NAME   BLOCKS   SIZE_MB
------------- -------- ----------
BIG_TABLE     146838   1147.171


Vamos realizar uma varredura completa na outra tabela grande:

SQL> select  count(*) from dgomez.other_big_table;

COUNT(*)
----------
10000000


Podemos verificar os blocos de nossas tabelas que estão em memória:

SQL>  @dgomez_cache_blocks.sql

OBJECT_NAME         BLOCKS     SIZE_MB
------------------  ---------- ----------
BIG_TABLE           42794      334.328
OTHER_BIG_TABLE     146836     1147.156


Aparentemente o Oracle continua utilizando do Full Database Caching, pois a segunda tabela grande foi colocada na memória completamente e não foi utilizado qualquer um dos critérios de carregamento em memória de tabelas grandes ([0%,<2%],[>=2%,<=10%+tiempo],[>10%,100%]), mas como é óbvio alguns blocos da primeira tabela grande (BIG_TABLE) foram desalocados, ou seja, os blocos que foram necessários para colocar na memória a segunda maior tabela (OTHER_BIG_TABLE).

Vamos ver o que acontece ao ler novamente a primeira tabela grande (BIG_TABLE):

SQL> SQL>  select count(*) from dgomez.big_table;

COUNT(*)
----------
10000000

SQL>  @dgomez_cache_blocks.sql

OBJECT_NAME           BLOCKS     SIZE_MB
--------------------  ---------- ----------
OTHER_BIG_TABLE       41916      327.468
BIG_TABLE             146838     1147.171


Agora alguns blocos da tabela OTHER_BIG_TABLE foram desalocados, ou seja, os blocos que foram necessários para colocar na memória a tabela BIG_TABLE.

Nossa recomendação é que se o Buffer Cache não tem memória suficiente para acomodar o banco de dados como um todo, você não deve ativar esse recurso, porque ao ativar você irá observar um grande número de blocos sendo desalocados, o que não é o ideal. Nós recomendamos que você deixe o Oracle decidir e implementar os seus critérios e algoritmos para alocar e desalocar os blocos.

Como último ponto vamos lhe mostrar como desativar o Full Database Caching, para isso você só precisa seguir estes passos:

SQL>  shutdown immediate;.
SQL>  startup mount;
ORACLE  instance started.

SQL>  ALTER DATABASE NO FORCE FULL DATABASE CACHING;
Database  altered.

SQL> alter database open;
Database altered.


Deiby Gomez é "Oracle ACE Director", "A Oracle Certified Master 11g" e "A Oracle 12c Certified Master". Ele tem sido um orador na Oracle Open World nos EUA e no Brasil; em Colaborar, Las Vegas e OTN Tour em vários países da América Latina. Vencedor do "SELECT Choice Award da revista Editor de 2016". Ele é um membro da "OraWorld-Team" foi "Beta Tester" da versão 12cR2. Deiby é Presidente o Grupo usuários Oracle de Guatemala (GOUG) e atualmente é Consultor bancos de dados Oracle em Nuvola Consulting Group (www.nuvolacg.com). Twitter @hdeiby.

Alex Zaballa, formado em Análise de Sistemas, é especialista em Banco de Dados Oracle com sólidos conhecimentos em Servidores de Aplicação e Sistemas Operacionais; trabalha com Oracle há 15 anos, é Oracle ACE Director, certificado OCM Database 11G/Cloud e conta com mais de 140 outras certificações em produtos da Oracle. Alex também é fundador do Grupo de Usuários Oracle de Angola (GUOA) e membro do time OraWorld.

Este artigo foi revisto pela equipe de produtos Oracle e está em conformidade com as normas e práticas para o uso de produtos Oracle.