O Oracle não está usando um índice exclusivo para uma chave longa

16

Eu tenho uma tabela com 250K linhas no meu banco de dados de teste. (Existem algumas centenas de milhões em produção, podemos observar o mesmo problema.) A tabela possui um identificador de seqüência de caracteres nvarchar2 (50), não nulo, com um índice exclusivo (não é o PK).

Os identificadores são compostos de uma primeira parte que possui 8 valores diferentes no meu banco de dados de teste (e cerca de mil em produção), depois um sinal @ e, finalmente, um número com 1 a 6 dígitos. Por exemplo, pode haver 50 mil linhas que começam com 'ABCD_BGX1741F_2006_13_20110808.xml @' e é seguido por 50 mil números diferentes.

Quando eu procuro uma única linha com base em seu identificador, a cardinalidade é estimada em 1, o custo é muito baixo e funciona bem. Quando eu procuro por mais de uma linha com vários identificadores em uma expressão IN ou OR, as estimativas para o índice estão completamente erradas, portanto, uma varredura de tabela completa é usada. Se eu forçar o índice com uma dica, é muito rápido, a verificação completa da tabela é realmente executada uma ordem de magnitude mais lenta (e muito mais lenta na produção). Portanto, é um problema do otimizador.

Como teste, dupliquei a tabela (no mesmo esquema + espaço de tabela) com exatamente o mesmo DDL e exatamente o mesmo conteúdo. Recriei o índice exclusivo na primeira tabela para uma boa medida e criei exatamente o mesmo índice na tabela de clones. Eu fiz um DBMS_STATS.GATHER_SCHEMA_STATS('schemaname',estimate_percent=>100,cascade=>true);. Você pode até ver que os nomes dos índices são consecutivos. Portanto, agora a única diferença entre as duas tabelas é que a primeira foi carregada em ordem aleatória por um longo período de tempo, com blocos espalhados no disco (em um espaço de tabela juntamente com várias outras grandes tabelas), e a segunda foi carregada como um lote INSERIR-SELECIONAR. Fora isso, não consigo imaginar nenhuma diferença. (A tabela original foi reduzida desde a última grande exclusão e não houve uma única exclusão depois disso.)

Aqui estão os planos de consulta para a tabela de doentes e clones (as strings sob o pincel preto são as mesmas em toda a imagem e também sob o pincel cinza.):

planos de consulta

(Neste exemplo, existem 1867 linhas que começam com o identificador escovado em preto. Uma consulta de 2 linhas produz uma cardinalidade de 1867 * 2, enquanto uma consulta de 3 linhas produz uma cardinalidade de 1867 * 3, etc. por coincidência, a Oracle parece não se importar com o fim dos identificadores.)

O que poderia causar esse comportamento? Obviamente, seria muito caro recriar a tabela em produção.

USER_TABLES: http://i.stack.imgur.com/nDWze.jpg USER_INDEXES: http://i.stack.imgur.com/DG9um.jpg Eu só mudou o nome do esquema e tabela. Você pode ver que os nomes da tabela e do índice são os mesmos da captura de tela do plano de consulta.

fejesjoco
fonte

Respostas:

7

(Isso responde à outra pergunta sobre por que os histogramas são diferentes.)

Os histogramas são criados por padrão com base na inclinação da coluna e se a coluna foi usada em um predicado relevante. Copiar o DDL e os dados não é suficiente, as informações da carga de trabalho também são importantes.

De acordo com o Guia de ajuste de desempenho :

Quando você solta uma tabela, as informações de carga de trabalho usadas pelo recurso de coleta de histograma automático e o histórico de estatísticas salvas usadas pelos procedimentos RESTORE _ * _ STATS são perdidos. Sem esses dados, esses recursos não funcionam corretamente.

Por exemplo, aqui está uma tabela com dados inclinados, mas sem histograma:

drop table test1;
create table test1(a date);
insert into test1 select date '2000-01-01'+level from dual connect by level <= 10;
insert into test1 select date '2000-01-01' from dual connect by level <= 1000;
begin
    dbms_stats.gather_table_stats(user, 'TEST1');
end;
/
select histogram from user_tab_columns where table_name = 'TEST1';

HISTOGRAM
---------
NONE

Executar a mesma coisa, mas com uma consulta antes da coleta das estatísticas, gerará um histograma.

drop table test1;
create table test1(a date);
insert into test1 select date '2000-01-01'+level from dual connect by level <= 10;
insert into test1 select date '2000-01-01' from dual connect by level <= 1000;
select count(*) from test1 where a = sysdate; --Only new line
begin
    dbms_stats.gather_table_stats(user, 'TEST1');
end;
/
select histogram from user_tab_columns where table_name = 'TEST1';

HISTOGRAM
---------
FREQUENCY
Jon Heller
fonte
2
Exemplo brilhante e simples. Você tem alguma idéia de por que a CBO estava usando histogramas para estimativas de cardinalidade em uma varredura exclusiva, em vez de apenas assumir 1?
Jack Douglas
Obrigado! Fiz uma reprodução completa com meu tipo de dados e consultas no meu blog: joco.name/2014/01/05/…
fejesjoco
@ Jack Eu acho que é preguiça. Os engenheiros da Oracle devem ter imaginado que as estatísticas de um índice exclusivo terão o mesmo número de valores distintos que as linhas; portanto, a suposição de cardinalidade 1 não é conectada, mas simplesmente usada a partir das estatísticas, como em qualquer outro caso. Além disso, como um caso geral, os histogramas superam as estatísticas simples. Meu caso parece ser muito especial por causa apenas das teclas longas, mas acredito que isso funcione muito bem de outra maneira.
fejesjoco
@fejesjoco Acho que a explicação de JL é mais provável, pois os histogramas também teriam superado as estatísticas gerais no caso de uma única pesquisa (sem a in), não? Eu acho que o CBO faz a suposição de cardinalidade 1, mas apenas no caso mais simples. Suponho que você possa solucionar a coisa toda usando um valor grande, UNION ALLmas pode haver outros motivos para não fazer isso e JL menciona outras soluções possíveis na postagem do blog vinculado.
Jack Douglas
11
Um outro pequeno mistério a considerar - como esse histograma foi criado em primeiro lugar? O Oracle parece considerar uma coluna inclinada apenas se houver duplicatas, o que obviamente sua coluna exclusiva não pode ter. Alguém intencionalmente construiu esse histograma (improvável) ou alguém reuniu estatísticas com o não recomendado method_opt=>'for all indexed columns'?
precisa saber é o seguinte
8

Eu encontrei a solução! É tão bonito e eu realmente aprendi MUITO sobre o Oracle.

Em uma palavra: histogramas.

Comecei a ler muito sobre como o CBO da Oracle funciona e me deparei com histogramas. Como não entendi completamente, dei uma olhada na tabela USER_HISTOGRAMS e voilá. Havia várias linhas para a mesa doente e praticamente nada para a mesa clonada. Para a tabela doente, havia uma linha para cada uma das oito partes iniciais identificadoras diferentes. E esta é a chave: eles foram cortados em 32 caracteres, antes do sinal @. Como eu disse, a primeira parte das teclas é altamente repetitiva, elas se tornam diferentes após o sinal @.

Parece que os histogramas podem ser mais poderosos do que o simples fato de um índice único sempre ter uma cardinalidade de 0 ou 1 para um determinado valor. Quando eu estava consultando mais de duas linhas, a Oracle olhou para o histograma, e pensou que poderia haver dezenas de milhares de valores para essa parte inicial do identificador e tirou o CBO do caminho.

Apaguei os histogramas dessa coluna na tabela antiga e o problema desapareceu!

Mais informações: https://blogs.oracle.com/optimizer/entry/how_do_i_drop_an_existing_histogram_on_a_column_and_stop_the_auto_stats_gathering_job_from_creating

fejesjoco
fonte
2
Mencionei que na nossa sala de bate-papo :) chat.stackexchange.com/transcript/message/12987649#12987649
Philᵀᴹ
Eu não vi isso :). Portanto, a única coisa estranha é por que havia histogramas na primeira tabela e não no clone, pensei que o gather_schema_stats atualizou tudo, aparentemente não.
fejesjoco
6

Enviei um email para Jonathan Lewis sobre isso e recebi uma resposta muito útil:

A singularidade no cálculo é uma conseqüência dos limites dos histogramas baseados em caracteres, veja particularmente:

http://jonathanlewis.wordpress.com/2010/10/13/frequency-histogram-5/ http://jonathanlewis.wordpress.com/2010/10/19/frequency-histograms-6/

Olhando para o exemplo, a consulta é para uma lista IN, não para uma única linha; portanto, meu palpite inicial seria que o otimizador usou uma estratégia genérica para calcular a seletividade de várias linhas em vez de ter um trecho de código especial para um Lista IN em uma chave primária. Eu acho que não seria muito difícil para eles reconhecerem esse caso, mas os desenvolvedores provavelmente não consideraram que vale a pena o esforço.

Eu recomendo a leitura dos posts que ele vincula, eles descrevem em detalhes a limitação dos histogramas nos quais você está executando, por exemplo:

Conclusão : se você possui seqüências de caracteres longas e semelhantes em uma coluna que é uma boa candidata a um histograma de frequência (por exemplo, uma coluna de status muito descritiva), há um problema se um valor muito raro parecer idêntico a um muito popular. valor até os primeiros 32 caracteres. Você pode achar que a única solução é alterar a lista de valores legais (embora várias estratégias que envolvam colunas virtuais ou índices baseados em funções possam ignorar o problema).

Jack Douglas
fonte
Infelizmente, os histogramas parecem ser um recurso pouco conhecido, acho que é porque é muito profundo para um desenvolvedor SQL e, na maioria das vezes, eles simplesmente funcionam, mas é bom saber que existem muitos recursos sobre isso, mas eu não estava olhando no lugares certos :). É muito ruim que a Oracle corte em 32 bytes e tome decisões desastrosas com base nisso. Felizmente, não preciso de ajustes, soltar os histogramas é uma solução perfeita. Os valores-chave são únicos, sempre procuro 20 valores por vez, funciona bem apenas com um índice e é determinístico. Mas não usarei teclas longas da próxima vez, com certeza.
fejesjoco
Os histogramas são bem conhecidos entre os DBAs;) Adoro o fato de que você parece interessado em aprender coisas mais profundas e realmente acha que deveria ler o livro de JL , é muito, muito bom. O CBO geralmente faz um ótimo trabalho: sempre haverá casos que precisam ser investigados, mas vale lembrar que, mesmo sem o corte, as estimativas são sempre apenas estimativas.
Jack Douglas
11
Se você executar um trabalho de estatísticas regular (como o Oracle executa por padrão em uma instalação limpa), os histogramas reaparecem, talvez seja necessário procurar uma maneira de evitar isso (como LOCK_TABLE_STATS, talvez)
Jack Douglas
Mencionei um post do blog na minha resposta, há instruções sobre como evitar histogramas para uma coluna.
fejesjoco
11
@ Jack Douglas, obrigado por envolver J. Lewis e relatar de volta!
Dimitre Radoulov