segunda-feira, 19 de novembro de 2012

Oracle 12c: Pluggable Databases

Antes de mais nada, preciso dizer que a informação deste post não é 100% confiável. O que vem a seguir é uma coletânea de informações de diversas fontes: palestras do último Oracle Open World, perguntas e respostas de participantes nestas mesmas palestras, perguntas que eu mesmo fiz a especialistas nos "Demo Grounds", posts do Fórum da Oracle, artigos já publicados na internet, entre outras. Questões sobre as quais ainda não encontrei informação alguma estão na última seção: Dúvidas.

Posto isso, vamos à novidade de maior impacto no Oracle Database desde... desde... bem, desde sempre, eu acho. Trabalho com Oracle desde a versão 7.1 e não consigo me lembrar de nada tão radical. Objetos, Java, Flashback, ASM, Editions, Replay... Nada mexeu tanto com a estrutura do software como este novo sistema de Containers e Plugged Databases. RAC, talvez ? Talvez, pois RAC trouxe o cenário "N instâncias e 1 database", e esta nova feature traz algo que poderia ser interpretado como "1 instância e N databases".

Introdução: O Problema


O que o mercado chama vulgarmente de "Banco Oracle" é tipicamente uma estrutura monolítica, criada para suportar aplicações grandes, enormes ou gigantescas. Você tem uma instância, que é um conjunto de processos no sistema operacional associado a algumas áreas de memória; você tem um database, que é um conjunto de diversos arquivos em disco. Os dois juntos formam um "Oracle Server", que é o nome correto. Vou manter esse nome em inglês para que não haja confusão com a palavra "servidor", que normalmente significa hardware. Uma variação dessa estrutura é a solução de alta disponibilidade RAC (Real Application Clusters), em que temos várias instâncias para um mesmo database.

Criar um Oracle Server é uma atividade planejada, que envolve diversas decisões; o cenário mais comum em produção é ele que ocupe totalmente o hardware que o abriga, e muitos sistemas baseados em Oracle foram projetados com essa premissa. Um Oracle Server precisa ser administrado. Precisa de políticas de backup, previsão de crescimento, acompanhamento de seus logs de alerta, atualização de estatísticas, entre dezenas de outras coisas.

Para aplicações pequenas e médias, existem dois cenários comuns:

  • Criar diversos Oracle Servers no mesmo hardware (com ou sem virtualização).
  • Colocar diversas aplicações no mesmo Oracle Server, uma em cada "schema/owner/usuário".

A primeira opção cria um desperdício enorme de recursos, tanto de hardware quanto humanos. É comum ver um DBA administrando 200 Oracle Servers, o que deixa todos mal administrados. É comum uma aplicação sofrer quedas de desempenho porque outra aplicação está processando algo pesado ou fazendo backup, e é difícil apurar isso porque as ferramentas de diagnóstico tratam de Oracle Servers, elas não conseguem olhar para fora desse escopo.

A segunda opção nem sempre é viável, pois muitas aplicações foram desenhadas para ocupar seu próprio Oracle Server. Quando é viável, existe a questão da segurança: não é trivial garantir que uma aplicação não terá acesso aos dados de outra (afinal de contas, privilégios "ANY" e roles como "DBA" dão acesso a todos os schemas). É comum que o departamento ou a empresa cujos dados estão em jogo simplesmente não aceite a consolidação.

Agora vamos pensar no cenário da TI corporativa nesta segunda década do século XXI: Exadata, Consolidação, Virtualização, Nuvem. O mundo mudou e todos os sistemas têm que compartilhar recursos. Dentro de uma Exadata, sistemas grandes são automaticamente rebaixados a médios ou pequenos. Como lidar então com o desperdício de recursos ?

Pluggable Databases


Com o Oracle 12c, entram em cena os conceitos de "Container" e "Pluggable" databases, uma estrutura hierárquica de dois níveis. Um "container database" (CDB) pode ter entre 0 e 250 "filhos", que são os "pluggable databases" (PDBs). Do ponto de vista das aplicações, cada PDB é um "Oracle Server" completo, totalmente compatível com as versões anteriores.

Olhando de fora, entretanto, a coisa é bem diferente: a partir do sistema operacional, a impressão que se tem é que existe apenas uma instância. Você verá apenas um processo SMON, um PMON, um CKPT. A mesma coisa acontece com a memória: SGA e PGA são dimensionadas no CDB, para atender a ele e a todos os seus PDBs. Undo e Redo ? Também. As mesmas tablespaces de UNDO e os mesmos redo logs online atendem ao CDB e a todos os seus PDBs.

Segundo a Oracle, a economia de recursos permite um ganho de 5x ou mais em comparação com múltiplos Oracle Servers. Em outras palavras, esse mesmo hardware que hoje no 11g suporta 10 instâncias poderia suportar 50 delas no 12c. Digo "instâncias" porque esse fator se aplica obviamente aos recursos de memória. O storage ainda precisará ser cinco vezes maior.

Administração


A administração promete ser facilitada. Por exemplo, o RMAN poderá ser configurado para fazer o backup do CDB e tudo o que estiver abaixo em uma só operação, mas este backup servirá para recuperar qualquer PDB individualmente.

Outro exemplo, muito mais interessante: PDBs podem ser desplugados de um container e plugados em outro container !!! Isso facilitará a migração de databases para outra plataforma, para outro hardware e mesmo para novas versões. Imagino o que processo de upgrade para o 12cR2 será assim: instalar o novo software, criar um novo container e então trazer cada um de seus PDBs para plugar neste novo container.

Cópias de databases também serão mais simples. Veja como fica este cenário típico na vida de qualquer DBA: existe um Oracle Server chamado "app01", onde roda uma aplicação específica. A equipe de QA precisa de uma cópia por alguns dias, para fazer testes de uma nova funcionalidade. Hoje em dia precisamos criar um novo server "app02" e utilizar RMAN ou "data pump" para levar os dados de um para outro. Se "app01" fosse um PDB, o processo seria assim: conectado ao container, executar:

alter pluggable database app01 close;

alter pluggable database app01 open read only;

create pluggable database app02 from app01 
file_name_convert = ('/app01', '/app02');

alter pluggable database app02 open;

Feito. Tablespaces da aplicação, tabelas, objetos, roles, usuários, diretórios e tudo mais foram copiados e estão prontos para o uso. A cláusula "file_name_convert" faz a tradução dos caminhos dos datafiles; se os originais estavam em "/u04/oradata/app01" os novos estarão em "/u04/oradata/app02".

Outros Detalhes


Sistema de Arquivos


Na instalação default, os datafiles dos PDBs ficam em diretórios abaixo de seu CDB:

CDB1: /u04/oracle/oradata/cdb1/system01.dbf
PDB1: /u04/oracle/oradata/cdb1/pdb1/system01.dbf
PDB2: /u04/oracle/oradata/cdb1/pdb2/system01.dbf 
PDB3: /u04/oracle/oradata/cdb1/pdb3/system01.dbf 

Processos de Background


Como já vimos, os processos tradicionais no sistema operacional (pmon, smon, ckpt) aparentam ser todos do CDB. É muito provável que tenhamos novos processos para cuidar de aspectos que esta nova dimensão apresenta.

Segurança


Os privilégios dentro de um PDB se aplicam somente a este PDB. Aparentemente, o privilégio de SYSDBA em um CDB dá poder total sobre os PDBs. Podemos esperar novos privilégios, mais restritivos, no nível do container.

Dúvidas

  • Como serão divididos os recursos ? Será que teremos um novo Resource Manager que permita distribuir a carga entre os PDBs ?
  • Como ficam os parâmetros ? Se existe apenas um conjunto de processos, é razoável supor que haverá parâmetros que não podem ser alterados nos PDBs e assumem o valor de seu container ? Mais ainda, será que teremos parâmetros que só existem em CDBs ou em PDBs ?
  • Será que o padrão será deixar o Container Database vazio, sem dados de nenhuma aplicação ?
  • Como será o RAC 12c ? Clusters de CDBs, de PDBs ou ambos ?

Veja Também:

terça-feira, 6 de novembro de 2012

Chave Primária - "Natural" ou "Surrogate" ?

Sempre que se inicia a modelagem de um sistema, existe (ou pelo menos deveria existir) uma discussão sobre a questão das chaves primárias, baseada na seguinte pergunta: Devemos usar campos com significado de negócio - "Natural Keys" (RG, CPF, Matrícula, Apólice) ou números gerados artificialmente, sem significado fora do sistema - "Surrogate Keys" ?

Já trabalhei com sistemas nas duas abordagens e hoje defendo a segunda opção: chaves artificiais, sem relação alguma com o negócio; apresento aqui os prós e contras que me levaram a essa decisão.

Prós:
  • Chaves artificiais permitem alterações de negócio com menos impacto. Este é provavelmente o argumento mais importante. Chaves naturais estão sempre sujeitas a mudanças. Empresas se fundem, o governo adiciona mais um dígito, a lei muda. Sempre desconfie de um analista que diz que "esse campo não vai mudar nunca". Eu já tive que alterar uma informação (número de apólice) que era PK de uma tabela de 600 mil registros, com mais de 100 "descendentes" distribuídos em 11 níveis de dependência. Acredite, não é legal. Ou você acha impossível que amanhã ou depois o governo brasileiro resolva adicionar um dígito ou dois ao CPF ?
  • Chaves artificiais resultam em menos colunas nas tabelas. A opção das chaves naturais vem sempre acompanhada do uso de chaves compostas, o que leva à redundância da informação e ao desperdício de espaço.
  • Não ocorre o problema das FKs inúteis. Como não há chaves compostas, não existe a possibilidade de um dos campos ser NULL e outro não.
  • Não ocorrem updates nos campos da chave primária, portanto há menos chance de locks problemáticos. Aliás, updates em campos de chave primária violam o modelo relacional bom senso e não deveriam mesmo acontecer em hipótese alguma.
  • Como as chaves são de apenas um campo, e este campo é NUMBER, os joins são mais simples (para o desenvolvedor e para o otimizador) e mais rápidos.
  • O uso de chaves com apenas uma coluna facilita os cálculos para o otimizador de custo (que só sabe mesmo lidar com seletividade de múltiplas colunas no Oracle 11g release 2). Isso resulta em melhores decisões de planos de execução e portanto em melhor desempenho do sistema como um todo.
Contras:
  • Mais operações de join são necessárias para montar a informação para o usuário final. Isso pode ser resolvido de várias maneiras, sendo que "snapshots" ou "materialized views" são a opção mais popular hoje em dia.
  • Uma constraint a mais é necessária (do tipo "unique" nos campos da chave natural). 
  • Parte da integridade de negócio deixa de ser validada pelas FKs. Este é o maior ponto de atenção, pois o banco de dados não detectará anomalias como "pedido de um cliente com itens de outro cliente". Na prática, é difícil uma aplicação estável criar esse tipo de registro, mas processos de carga e migração podem fazê-lo facilmente. 

Veja Também:

O Problema da FK Inútil


Gostaria de esclarecer um comportamento não-intuitivo do Oracle (e de todos os SGBDs aderentes ao padrão ANSI/ISO SQL92), que pode levar a problemas de modelagem de dados e de consistência destes dados.

Imagine uma tabela XPTO, cuja PK é composta de dois campos: ID_XPTO e ID_EMPRESA. Agora imagine outra tabela ACME: (ID_ACME [PK], ID_XPTO [FK] e ID_EMPRESA [FK]). Em ACME, filha de XPTO, ID_XPTO e ID_EMPRESA estão ligados a XPTO por uma constraint do tipo FK, mas permitem valores NULL. Suponha que XPTO tem apenas um registro (1,100) e são inseridas em ACME as seguintes linhas:

1. ACME ( 1, 1, 100 );
2. ACME ( 2, 1, NULL );
3. ACME ( 3, NULL, 100 );
4. ACME ( 4, NULL, NULL );
5. ACME ( 5, 666, NULL );
6. ACME ( 6, NULL, 666 );

Quantas destas linhas serão aceitas ? TODAS ELAS. A presença do valor NULL desobriga o banco da validação da chave estrangeira, portanto devemos tomar o cuidado de adicionar uma constraint de check que garante que ou todos os valores da FK são nulos, ou nenhum é nulo:

ALTER TABLE ACME ADD CONSTRAINT CK_ACME_XXXX CHECK
  ((ID_XPTO IS NULL AND ID_EMPRESA IS NULL) OR (ID_XPTO IS NOT NULL AND ID_EMPRESA IS NOT NULL))

Atenção: Claro que a melhor opção seria deixar os campos em ACME como NOT NULL. A solução acima deve ser utilizada apenas se o requisito de negócio exigir que os campos permitam NULL.

terça-feira, 9 de outubro de 2012

Novas Features do Oracle 12c

Estive na última quinta-feira na famosa palestra que Tom Kyte (asktom.oracle.com) faz a cada lançamento de versão do banco de dados Oracle ("My X favorite things about Oracle X"), agora na versão 12c. Foi uma apresentação bem corrida, mas reproduzo aqui as novidades que achei mais interessantes. Deixo de fora a maior de todas elas, "Pluggable Databases", que terá um post exclusivo no futuro.

Adaptive Execution Plans


Hoje em dia, o plano de execução de um SQL qualquer é definido antes da execução, com base nas estatísticas. Se estas estiverem desatualizadas ou erradas, um plano de execução ruim será gerado, e a execução consumirá mais recursos do que o necessário.

No Oracle 12c, o otimizador será capaz de detectar más escolhas durante a execução, e mudar o plano ao invés de insistir no erro. Se, por exemplo, as estatísticas indicavam que a tabela EMP tinha 10 registros mas na hora da execução ele perceber que eram 10 milhões, ele poderá decidir mudar a operação de NESTED LOOPS para um HASH JOIN.

Sim, eu sei o que você está pensando: e quando houver um problema de desempenho, como eu vou saber qual foi o plano utilizado ? Essa informação sairá apenas em traces, então vamos torcer para que o otimizador decida corretamente.

Enhanced Statistics


Diversas melhorias relacionadas à coleta e utilização de estatísticas:

  • A coleta dinâmica (dynamic sampling) agora pode ser configurada com o valor 11, o que faz com que a coleta seja persisitida no banco. Não ficou claro se essa persistência será no mesmo local das estatísticas tradicionais, coletadas com DBMS_STATS.
  • Temos agora um novo tipo de histograma (hybrid) que é utilizado automaticamente em colunas com mais de 254 valores distintos.
  • Estatísticas agora serão coletadas automaticamente durante cargas de dados.
  • Para tabelas temporárias (Global Temporary Tables) as estatísticas serão privadas para cada sessão de usuário.

Top-N and Pagination Queries


Agora será possível em Oracle solicitar diretamente em SQL "os primeiros 10 registros" ou "os próximos 20 registros" sem necessidade do velho truque "subselect + order by + rownum".

Improved Defaults


Temos diversas melhorias relacionadas a valores default:

  • Default to Sequence: o valor default agora pode ser o NEXTVAL de uma sequence.
  • Default to Null Inserted: este valor default será utilizado mesmo que seja especificado NULL durante o insert. 
  • Metadata Defaults: a adição de uma nova coluna com um valor default passa a ser instantânea, para qualquer tamanho de tabela.

Temporary Undo


Hoje, por estranho que possa parecer, operações temporárias geram REDO. Isso é porque elas precisam gerar UNDO para fins de recovery, e essa geração de UNDO gera REDO. Na versão 12c, o parâmetro TEMP_UNDO_ENABLED faz com que a geração de UNDO das operações temporárias seja também uma operação temporária, eliminando a geração de REDO. Isso deve causar melhorias espontâneas de desempenho em diversas aplicações pelo mundo afora.

Increased Limits for Varchar


O limite de tamanho para colunas VARCHAR2, NVARCHAR2 ou RAW agora é de 32K. Este já era o tamanho permitido para os tipos de dado homônimos em PL/SQL.


quarta-feira, 7 de março de 2012

Erro ORA-04030

O erro "ORA-04030" significa que um processo de usuário, que é a contraparte da sessão Oracle no sistema operacional, não conseguiu alocar memória suficiente. Pode ser falta de memória disponível ou excesso de alocação (memory leak). 
_
O primeiro passo na investigação é verificar os limites definidos no sistema operacional. Para isso é necessário logar com o usuário dono dos binários do Oracle (geralmente é o usuário "oracle") e executar o comando "ulimit -a". Os valores destacados em vermelho devem estar "unilimited" segundo a documentação (Linux 64 bits). Na realidade, este "unilimited" significa 4GB na maioria das plataformas.
_
[oracle@dbserver17 ~]$ ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 139264
max locked memory       (kbytes, -l) 32
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 10240
cpu time               (seconds, -t) unlimited
max user processes              (-u) 139264
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
_
Se os parâmetros estiverem corretos, é necessário extrair um trace específico para o processo que apresenta o problema. Este trace pode ser obtido com o código abaixo. Insira a chamada do processo conforme indicado (entre "Begin call" e "End call"). Após a execução, procure no diretório de traces um arquivo contendo no nome a string "ERRO_4030". Este arquivo contém detalhes sobre a execução e ajudará a identificar a origem do problema.
_
select name, value from v$parameter where name = 'user_dump_dest';

alter session set tracefile_identifier='ERRO_4030';
_
alter session set events '4030 trace name heapdump level 536870917 ; name errorstack level 3';
_
-- Begin call
-- End call
_
alter session set events '4030 trace name heapdump off ; name errorstack off';

sexta-feira, 24 de fevereiro de 2012

Numeração Contínua com "Sequences" usando NOCACHE ?

Vamos lá: os objetos do tipo "sequence" em Oracle não garantem sequências numéricas sem "gaps". Nunca. Não existe nenhum parâmetro que possa ser setado, nehuma variação na sintaxe do comando CREATE SEQUENCE, nada que possa garantir isso.
_
Por quê ? Ora, porque existe uma coisa chamada "rollback", que acontece automaticamente quando ocorre um erro qualquer dentro de uma transação. Quando você pega um número da sequence (nextval), isso não tem volta. É como se fosse uma transação autônoma. A grosso modo, se você tem um processo qualquer que usa uma sequence para preencher um campo, você terá um buraco na sequence sempre que esse processo der erro.
_
Às vezes encontramos por aí sequences configuradas como NOCACHE, numa tentativa de conseguir esse efeito de numeração contínua. Isso não apenas não resolve um problema, mas cria outro pior. Sequences com NOCACHE prejudicam muito o desempenho, especialmente em processos batch e mais especialmente ainda em ambientes clusterizados (RAC). Se você tiver problemas de desempenho relacionados ao evento de wait "SQ - Sequence Number Enqueue", procure por sequences definidas como NOCACHE.

Duas Dicas Sobre Privilégios

Primeira dica: Uma procedure/function/package roda sempre* com os privilégios do owner, não do executor. Isso significa que, se o usuário SCOTT precisa rodar uma package do usuário FINANC que acessa 300 tabelas de FINANC, apenas o grant de EXECUTE na package para SCOTT é suficiente. Dentro da execução, valem os privilégios de FINANC.
_
Segunda dica: Dentro de uma procedure/function/package, grants recebidos através de uma role não valem. Se através da role ABC você consegue abrir um SQL*Plus e fazer select na tabela X, isso não lhe dá o direito de fazer o mesmo select dentro de uma procedure sua. Para uso dentro de procedures, o grant tem que ser direto e não via role.
_
Tenha sempre isso em mente quando estiver investigando problemas ligados a grants de privilégios. Você poupará muito esforço inútil.
_
* Exceto quando explicitamente definido pelo pragma AUTHID_CURRENT_USER

quarta-feira, 22 de fevereiro de 2012

Instance Caging: Limitando Recursos

Em ambientes de desenvolvimento e de homologação, é comum termos várias instâncias e vários bancos de dados compartilhando uma mesma máquina. Como uma instância Oracle é composta por vários processos no sistema operacional e cada sessão de usuário é também atrelada a um ou mais processos, pode ser bastante difícil impedir que um processo problemático em uma instância "afogue" a máquina e prejudique todas as outras que residem no mesmo hardware.
_
Para endereçar este problema, temos no Oracle 11g release 2 uma nova funcionalidade chamada "Instance Caging" (literalmente, "encarceramento"), que permite definir exatamente o número de CPUs às quais uma instância terá acesso.
_
A ativação é muito simples. Basta habilitar um "resource plan" e ajustar o número de CPUs. Suponhamos uma máquina com 16 CPUs e uma instância onde queremos que apenas duas dessas CPUs sejam utilizadas:
_
ALTER SYSTEM SET RESOURCE_MANAGER_PLAN=DEFAULT_PLAN;
_
ALTER SYSTEM SET CPU_COUNT=2;
_
Pronto. Instância devidamente enjaulada.
_

sexta-feira, 17 de fevereiro de 2012

Livro: Expert Oracle Exadata



Excelente livro para quem precisa começar a lidar com a "Oracle Exadata Database Machine". Ao contrário do que aparenta, a Exadata não é apenas uma caixa preta de 7 milhões de dólares que você pode comprar e colocar no data center. Existem diversos sabores, diversas opções de configuração e dezenas de parâmetros exclusivos. Embora a Exadata rode exatamente o mesmo software Oracle 11g Release 2, existem coisas que só funcionam nela. E são coisas incríveis. Comprei na Amazon. Tem versão para o Kindle.

Mudança no comportamento das roles "default"

Até a versão 10.2.0.4 do Oracle Database, era possível o seguinte cenário: 
_
Uma determinada role "ROLE_XX", embora protegida por senha, era ativada automaticamente para usuários que a têm como role default. 
_
Em outras palavras: se foi dado apenas o grant da role para o usuário SCOTT ("grant ROLE_XX to SCOTT") e a role é protegida por senha, então SCOTT precisava rodar o seguinte comando para ter os privilégios dados pela role:
_
set role ROLE_XX identified by SENHA_DA_ROLE_XX;
_
Entretanto, se a role foi também associada como "role default" ("alter user SCOTT default role ROLE_XX"), não havia necessidade do "set role". Ao efetuar o login, SCOTT já ganhava os privilégios da role, mesmo sem saber a senha.
_
Isso mudou na versão 10.2.0.5 e posteriores. Mesmo roles associadas como "default" precisam do comando "set role" com a senha correta para serem ativadas.
_
É importante notar que a Oracle considerou o comportamento anterior um "bug" de segurança, portanto não disponibilizou nenhum parâmetro ou "workaround". Aplicações que se beneficiavam das roles default para, por exemplo, dar privilégios a um usuário de execução de processos batch, simplesmente param de funcionar.
_
O documento oficial sobre a mudança pode ser encontrado no documento número 745407.1 do Suporte Oracle.

quarta-feira, 15 de fevereiro de 2012

Mensagens de Erro Intrigantes

Algumas mensagens de erro Oracle são no mínimo estranhas. Compilei essa lista já faz um bom tempo e já sei quem é essa tal diana, mas ainda me pergunto que tipo de problema retornaria o erro "ORA-02846 - Servidor imortal" :-) 
_
Deixei a lista em inglês porque as mensagens são mais engraçadas. Quem quiser saber a versão em português, basta usar o código ao final do post e digitar o número do erro quando solicitado.
_
ORA-00982 missing plus sign
ORA-00996 the concatenate operator is ||, not |
ORA-01072 cannot stop ORACLE; ORACLE not running
ORA-01169 DATAFILE number 1 not found. Must be present
ORA-01177 data file does not match dictionary - probably old incarnation
ORA-01241 an external cache has died
ORA-01645 previous attempt to make read write is half complete
ORA-01712 you cannot grant a privilege which you do not have
ORA-01744 inappropriate INTO
ORA-01842 quarter must be between 1 and 4
ORA-01869 reserved for future use
ORA-02309 atomic NULL violation
ORA-02841 Server died on start up
ORA-02846 Unkillable server
ORA-03002 operator not implemented
ORA-03007 obsolete feature
ORA-03129 the next piece to be inserted is required
ORA-04012 object is not a sequence
ORA-04028 cannot generate diana for object
ORA-04046 results of compilation are too large to support
ORA-08114 can not alter a fake index

declare
   s varchar2(200);
   i pls_integer;
begin
   i:= utl_lms.get_message(&CODIGO, 'rdbms', 'ora', 'brazilian portuguese', s);
   dbms_output.put_line(s);
end;

Starvation ou "Meu Trace Sofreu um Lapso de Tempo"

Durante a análise de arquivos trace do Oracle, às vezes nos deparamos com a seguinte situação: o tempo de CPU foi X, o tempo decorrido (elapsed) foi bem maior que X, mas não aparece nenhum evento de espera que justifique a diferença. Por exemplo:
_
********************************************************************************
SQL ID: fcqbkkryrx6xh Plan Hash: 2798344373
_
SELECT MIN(OBJECT_ID)
FROM
 DBA_OBJECTS WHERE OBJECT_ID > :B1
_
call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute   7088      0.83       2.51          0          0          0           0
Fetch     7088   1396.10    4853.38          0   17149867          0        7088
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total    14177   1396.94    4855.90          0   17149867          0        7088
_
Elapsed times include waiting on following events:
  Event waited on                             Times   Max. Wait  Total Waited
  ----------------------------------------   Waited  ----------  ------------
  asynch descriptor resize                    28176        0.00          0.06
  latch: cache buffers chains                     7        0.07          0.24
  latch: row cache objects                        1        0.05          0.05
  latch free                                      1        0.06          0.06
********************************************************************************
_
Neste caso, a query executou durante 4856 segundos, mas gastou apenas 1397 deles em CPU. Isso é comum, porque processos de banco de dados geralmente passam um bom tempo esperando blocos serem trazidos do cache ou do disco. Mas vejam que não houve espera alguma por disco ("db file sequential read" ou "db file scattered read") nem por qualquer outro recurso (Soma de "Total Waited" não dá nem 1s). Então pra onde foram os outros 3459 segundos ?

Resposta: eles foram gastos esperando por CPU. Durante o tempo de execução, outros processos estavam rodando na mesma máquina, e nosso processo só teve acesso aos ciclos da CPU durante 29% do tempo em que rodou. A espera por CPU não é registrada pelo trace do Oracle como um evento de "wait", portanto não aparece.

No exemplo acima eu sei que foi exatamente isso que aconteceu, porque foi um teste controlado: em uma máquina com 4 CPUs foram disparados 4 processos Oracle que executavam operações pesadas, mas praticamente sem acesso a disco. Em seguida, disparei um quinto processo idêntico, mas este sofreu um rebaixamento de prioridade no sistema operacional (comando "renice" em Linux). O trecho acima vem deste último processo.

Então em resumo é isso: quando cair em suas mãos um trace com lapsos de tempo, é porque outra(s) coisa(s) estava(m) rodando junto com seu processo no período registrado pelo trace. Para investigar mais a fundo um caso como esse, é necessário utilizar ferramentas que vejam a instância Oracle como um todo (e.g. AWR Report) ou até ferramentas de análise de carga do servidor, já que os processos que consumiram a CPU podem nem ter sido do Oracle.

segunda-feira, 13 de fevereiro de 2012

Cuidado com estes parâmetros no Oracle 11g

Para você que está migrando agora para o Oracle 11g, segue uma lista com alguns parâmetros que me causaram surpresas desagradáveis. Claro, o correto seria ter lido a documentação completa e o "Upgrade Guide" da nova versão, mas eu não tive tempo e duvido que você tenha, então vamos lá:
_
1. SEC_CASE_SENSITIVE_LOGON { TRUE | FALSE }
_
Este parâmetro torna "case sensitive" as senhas do Oracle, ou seja, passa a haver diferenciação de letras maiúsculas e minúsculas. Como o valor padrão é "TRUE", quaisquer sistemas ou interfaces que não façam esta diferenciação podem parar de funcionar por conta de erro na autenticação. Para evitar isso, ou para contornar o problema até que os sistemas sejam adaptados, altere o valor do parâmetro para FALSE.
_
2. JOB_QUEUE_PROCESSES { Integer }
_
Este parâmetro limita o número de jobs que podem executar simultaneamente. Jobs são processos agendados para execução imediata ou posterior, através das packages DBMS_JOB ou DBMS_SCHEDULER. Como muitas vezes os jobs são utilizados para execução de um grande número de tarefas em lote, é importante que este parâmetro seja definido com um número razoável para o ambiente. O problema é que o valor padrão no Oracle 10g era "10", mas passou para "1000" na versão 11g. "10" é um número de processos que a maioria dos servidores pode suportar, mas "1000" é coisa para uma Exadata. Ao migrar para 11g, configure este parâmetro de acordo com a capacidade do seu ambiente.
_
3. _PARTITION_LARGE_EXTENTS { TRUE | FALSE }
_
Quando este parâmetro está com o valor "TRUE", que é o padrão no 11g, quaisquer novas partições são criadas com tamanho de 8 MB, enquanto na versão anterior este valor era de 64 kB. Em sistemas com muitas tabelas particionadas, esta diferença pode causar uma ocupação de disco muito maior no 11g do que na versão anterior, sem razão aparente. Para evitar este problema, configure o parâmetro com o valor "FALSE", pelo menos durante a criação das partições.  

Descendentes de uma Tabela

O script SQL abaixo retorna, para uma tabela qualquer, todas as descendentes, ou seja, todas as tabelas que possuem constraints de integridade referencial (FKs) que apontem para ela em qualquer nível. É útil por exemplo em atividades de saneamento de dados, quando você precisa remover a pessoa com ID = 186413 mas existem dezenas de outras tabelas onde este ID é referenciado. Claro que o script só funciona se as FKs realmente existem na base de dados e estão ativas.


select nivel, parent_table_name, child_table_name, fkey_constraint
from   ( select max(level) nivel,
                parent_table_name,
                child_table_name,
                fkey_constraint
         from   ( select pk.table_name         parent_table_name,
                         pk.constraint_name    pkey_constraint,
                         fk.table_name         child_table_name,
                         fk.constraint_name    fkey_constraint,
                         fk.r_constraint_name
                  from   all_constraints pk,
                         all_constraints fk
                  where  pk.constraint_name = fk.r_constraint_name
                    and  pk.constraint_type in ('P','U')
                    and  fk.status = 'ENABLED'
                    and  fk.constraint_type = 'R'
                )
         start with parent_table_name = upper('&TABELA')
         connect by nocycle prior child_table_name = parent_table_name
         group by parent_table_name, child_table_name, fkey_constraint
       )
order by nivel
/

domingo, 12 de fevereiro de 2012

Paralelismo Automático no Oracle 11g Release 2

Embora algum tipo de processamento paralelo esteja presente nos bancos de dados Oracle desde a versão 6.2, apenas agora na versão 11.2 este processamento pode ser configurado para atuar de forma automática, sem necessidade de "hints" ou de alterações no grau de paralelismo nas tabelas (campo "DEGREE" em "USER_TABLES"). 
_
Na versão 11.2, basta definir o valor de dois parâmetros dinâmicos e o otimizador passará a utilizar, se julgar necessário, planos de acesso com operações paralelas. Note que nem sempre o otimizador toma a decisão correta, então é necessário realizar testes antes de implementar esta técnica em sistemas de produção. Vamos aos parâmetros.
_
1. PARALLEL_SERVERS_TARGET { INTEGER }
_
Define o número de processos paralelos que se deseja ter executando simultaneamente. Deve ser configurado com um número que seja razoável para o hardware onde estamos executando. Por exemplo, para um servidor Intel com 2 CPUS quad-core, este valor poderia ser 8:
_
ALTER SYSTEM SET PARALLEL_SERVERS_TARGET = 8;
_
2. PARALLEL_DEGREE_POLICY { MANUAL | LIMITED | AUTO }
_
Define a política de paralelismo. Qualquer valor diferente de "MANUAL" ativará o paralelismo automático:
_
ALTER SYSTEM SET PARALLEL_DEGREE_POLICY = AUTO;

Geração de Trace sem Mestre

Na análise de problemas de desempenho em processos Oracle, ainda é comum a necessidade de geração e análise de "trace files", arquivos que registram todas as ações de uma sessão no banco de dados. O problema é que muitas vezes a investigação tem que ser feita diretamente no ambiente de produção, onde geralmente existe todo um processo até que um DBA identifique a sessão, ative a geração de trace e finalmente retorne o arquivo processado. 

Felizmente, existe uma maneira de ligar a geração de TRACE já no script, sem necessidade de terceiros. Vejam o código abaixo. Depois de terminada a execução, existirá um arquivo de trace no servidor de banco de dados com a string “TRACE_DO_MEU_PROCESSO” no nome do arquivo.

Além de ser bem mais simples do que pedir a alguém para encontrar sua sessão, ligar o trace, esperar acabar, desligar o trace e processar a saída, este método ainda permite que você trabalhe em lote: execute todos os processos necessários e posteriormente solicite aos responsáveis os arquivos resultantes: “Favor executar o comando TKPROF em todos os arquivos de trace gerados ontem com 'PROC_FATURAMENTO' no nome do arquivo e enviar para o e-mail X”.

declare
  str varchar2(127);
begin
  --
  -- Ligando o Trace: coloque um texto no "identifier" para que seu arquivo seja encontrado depois:
  --
  str := 'alter session set tracefile_identifier=''TRACE_DO_MEU_PROCESSO''';
  execute immediate str;
  str := 'alter session set events ''10046 trace name context forever, level 8''';
  execute immediate str;
  --
  -- Fazendo o que eu tenho que fazer: aqui você chama o seu processo
  --
  for i in ( select * from user_objects ) loop
      null;
  end loop;
  --
  -- Desligando o Trace
  --
  str:= 'alter session set events ''10046 trace name context off''';
  execute immediate str;
  -- 
end;
/

Posteriormente, seus arquivos serão facilmente identificados no servidor:

[oracle@dbserver01 udump]$ ls -l
total 51672
-rw-r----- 1 oracle oinstall      874 Nov 25 20:11 perf01_ora_27175.trc
-rw-r----- 1 oracle oinstall     1136 Dec  4 19:15 perf01_ora_30466.trc
-rw-r----- 1 oracle oinstall    44883 Dec  4 19:37 perf01_ora_31275_TRACE_DO_MEU_PROCESSO.trc
-rw-r----- 1 oracle oinstall     1100 Dec  1 18:41 perf01_ora_32359.trc
-rw-r----- 1 oracle oinstall      621 Dec  1 18:41 perf01_ora_32365.trc
-rw-r----- 1 oracle oinstall      874 Dec  1 18:41 perf01_ora_32393.trc
-rw-r----- 1 oracle oinstall     4864 Dec  1 19:17 perf01_ora_32449.trc