Oracle 11.2.0.4 no OEL 6.5
Nosso administrador de aplicativos executa algum script de atualização do fornecedor do aplicativo, e esse script está em execução há várias horas e ninguém sabe quando terminará. Além disso, o banco de dados agora gera muitos archives. Iniciei o sql trace por 15 minutos e descobri que há quase a única consulta.
INSERT INTO house_t
VALUES
(:1, :2, :3, :4, :5, :6, :7, :8, :9, :10, :11, :12, :13, :14, :15, :16,
:17, :18, :19, :20, :21, :22, :23)
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 70 0.00 0.00 0 0 0 0
Execute 71 23.86 633.03 83564 41375 4597750 355735
Fetch 0 0.00 0.00 0 0 0 0
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 141 23.87 633.03 83564 41375 4597750 355735
Por que uma simples inserção precisa usar essa enorme quantidade de blocos no modo atual 64757=4597750/71 ???
Misses in library cache during parse: 1
Misses in library cache during execute: 1
Optimizer mode: ALL_ROWS
Parsing user id: 103
Number of plan statistics captured: 3
Rows (1st) Rows (avg) Rows (max) Row Source Operation
---------- ---------- ---------- ---------------------------------------------------
0 0 0 LOAD TABLE CONVENTIONAL (cr=584 pr=1443 pw=0 time=11388214 us)
Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
SQL*Net more data from client 9523 0.00 0.08
db file sequential read 82252 0.60 603.94
SQL*Net message to client 70 0.00 0.00
SQL*Net message from client 70 0.00 0.15
log file switch completion 4 0.08 0.18
undo segment extension 4 0.00 0.01
log file sync 1 0.00 0.00
A tabela tem estrutura simples:
SQL> desc house_t
Name Null? Type
-------------------- -------- --------------
POSTALCODE VARCHAR2(6)
IFNSFL VARCHAR2(4)
TERRIFNSFL VARCHAR2(4)
IFNSUL VARCHAR2(4)
TERRIFNSUL VARCHAR2(4)
OKATO VARCHAR2(11)
OKTMO VARCHAR2(11)
UPDATEDATE DATE
HOUSENUM VARCHAR2(20)
ESTSTATUS NUMBER(5)
BUILDNUM VARCHAR2(10)
STRUCNUM VARCHAR2(10)
STRSTATUS NUMBER(5)
HOUSEID VARCHAR2(36)
HOUSEGUID VARCHAR2(36)
AOGUID VARCHAR2(36)
STARTDATE DATE
ENDDATE DATE
STATSTATUS NUMBER(5)
NORMDOC VARCHAR2(36)
COUNTER NUMBER(10)
CADNUM VARCHAR2(100)
DIVTYPE NUMBER(5)
Não há gatilhos nesta tabela, sem mviews, sem políticas fga definidas, sem políticas fgac.
Também a tabela tem 3 índices
CREATE UNIQUE INDEX HOUSE_T_IDX0 ON HOUSE_T(HOUSEID);
CREATE INDEX HOUSE_T_IDX1 ON HOUSE_T (T_HOUSEGUID);
CREATE INDEX HOUSE_T_IDX2 ON HOUSE_T (T_AOGUID);
O arquivo de rastreamento bruto está cheio de esperas de 'leitura sequencial de arquivo db' nesses 3 índices e blocos de desfazer.
A tabela agora tem cerca de 50 milhões de registros, o tamanho é de cerca de 12 Gb. Os índices têm cerca de 3-4 GB e têm blevel=3 em dba_indexes.
Espero que essa instrução de inserção possa levar de 20 a 30 blocos para ser executada, mas por que milhares?
Passos para reproduzir:
Dados de amostra:
Agora para simular seu script:
E a saída do tkprof:
E uma quantidade enorme de
leaf node splits
:1) Estou usando um SSD aqui, por isso minha demonstração demorou muito menos, devido ao tempo de resposta do disco muito menor. Você deve observar a quantidade de
current
blocos, não o tempo gasto com isso.2) Estou inserindo dados em ordem aleatória, isso causa
leaf block splits
.leaf node 90-10 splits
são causados pela inserção de dados na parte mais à direita do índice, em ordem crescente sequencial.leaf node 90-10 splits
pode ser feito simplesmente adicionando novos blocos de índice à parte "mais à direita" do índice e, em seguida, copie o valor mais alto neles. Às vezes, é oficialmente referido como uma99-1
divisão em vez de90-10
.leaf block splits
significa adicionar novos blocos no "meio" do índice, copiar e reorganizar dados em vários blocos, isso dá muito mais trabalho.3) Em termos de desempenho, acho uma péssima ideia usar GUIDs como identificadores. Eles consomem várias vezes mais espaço de armazenamento em comparação com
NUMBER
identificadores simples.Isso é o mesmo, mas com tipos NUMBER:
Além disso: Limitações do Oracle Cost Based Optimizer (Doc ID 212809.1)
Isso causou algumas estimativas incorretas de cardinalidade com histogramas em colunas GUID.
É assim que se comporta quando os valores são inseridos em ordem crescente (removido
order by dbms_random.value
):Como você pode ver, é menos de 1 bloco atual por linha.