Desmisti cando o
Explain
Dickson S. Guedes
1º Beetup
Created: 2017-11-07 ter 09:41
Table of Contents
Epigramas, citações e falácias
Intensivão
Preparação do ambiente
Tipos de busca
Junções
Agregações
Obrigado :)
Epigramas, citações e falácias
É mais fácil escrever um programa incorreto do que entender um
corretamente. - Alan Perlis (1981)
Uma linguagem que não afeta o modo como você pensa sobre programação
não merece ser conhecida. - Alan Perlis (1982)
Não podemos esquecer que o nosso negócio não é escrever programas; o
nosso negócio é desenhar modelos computacionais que apresentarão um
comportamento desejado. - Edsger Dijkstra (1972)
"A banda é in nita e a latencia é sempre zero" - L. Peter Deutsch (1994)
Intensivão
os registros são também conhecidos como tuplas;
as tuplas são representadas por uma estrutura de dados chamada
Heap;
as heaps são distribuídas em páginas;
uma página tem várias heaps (tuplas/registros);
uma página é representada por um bloco em disco;
um bloco tem 8 Kilobytes (8192 bytes);
uma tabela está distribuída em um ou mais blocos.
Em outras palavras
imagine as tuplas como linhas ou parágrafos de um texto;
imagine que uma página tem vários parágrafos;
as paginas tem dimensões, imagine quantas letras cabem em uma
única folha A4?
na analogia, uma tabela seria o quê?
Preparação do ambiente
Criação do banco de dados
DROP DATABASE meetup;
CREATE DATABASE meetup;
ALTER SYSTEM SET autovacuum TO off;
SELECT pg_reload_conf();
Criação de uma tabela pessoa com dados
ctícios
DROP TABLE IF EXISTS pessoa;
CREATE TABLE pessoa AS
WITH candidatos AS (
SELECT
cast(regexp_replace(md5(cast(random() as text)), '[^0-9]+','','g') as numeric) as cpf,
upper(regexp_replace(md5(cast(random() as text)), '[0-9]+',' ','g')) as nome,
cast(random() * 30 as integer) + 18 as idade
FROM generate_series(1,1000000)
)
SELECT DISTINCT ON(cpf) *
FROM candidatos
ORDER BY cpf, idade;
DROP TABLE
SELECT 999777
Dados da tabela pessoa
SELECT * FROM pessoa LIMIT 10;
cpf nome idade
2057318 B F B ECBC D B E C DCD 18
2893810 D AFD F AB CF D F 25
4210178 BBFEC CB B E C F F D E 20
15939623 EA B AF D D ECA CCBD 19
24132985 F C EE B FCD B EF 45
28385656 F D F AC B D A B E C CCB F 40
31004208 F FA F E B B E D C 39
45399123 C FB E A EC F 27
48885965 BB A F AEC F FE D ECC 32
48976900 BE CFCF C B E DCC F 39
Dimensões da tabela pessoa
SELECT count(*) FROM pessoa;
count
999777
SELECT pg_size_pretty(pg_relation_size('pessoa'));
pg_size_pretty
67 MB
Criação de uma tabela conta com dados
ctícios
DROP TABLE IF EXISTS conta;
CREATE TABLE conta AS
WITH candidatos AS (
SELECT pessoa.cpf,
cast(random() * 10000000 + random() * 50 as integer) as numero_conta,
cast(random() * 1000000 as numeric(17,2)) as valor
FROM pessoa
CROSS JOIN generate_series(1, 3)
WHERE pessoa.idade > 10 + random() * 10
ORDER BY random()
)
SELECT DISTINCT ON(numero_conta) *
FROM candidatos
ORDER BY numero_conta, cpf;
DROP TABLE
SELECT 2576310
Dados da tabela conta
SELECT * FROM conta order by 1 limit 10;
cpf numero_conta valor
36695 946740 94673.51
36695 1140633 114062.68
36695 2773365 277335.07
595115 2572588 257257.54
595115 1121931 112192.55
595115 5638002 563797.41
1011570 7508314 750827.68
1011570 5848863 584883.35
1011570 9902002 990195.29
7944468 1944112 194410.26
Dimensões da tabela conta
SELECT count(*) FROM conta;
count
2576310
SELECT pg_size_pretty(pg_relation_size('conta'));
pg_size_pretty
149 MB
Tipos de busca
Sequencial Scan
Exemplo 1
EXPLAIN SELECT * FROM pessoa;
QUERY PLAN
Seq Scan on pessoa (cost=0.00..15934.05 rows=732105 width=68)
cost=0.00: custo para obter o primeiro registro
..15934.05: custo para obter todos os registros
rows=732105: número de linhas (estimadas)
width=68: média (em bytes) do tamanho dos registros retornados
Quantos bytes essa consulta retornará?
Exemplo 2
ANALYZE pessoa;
EXPLAIN SELECT * FROM pessoa;
QUERY PLAN
Seq Scan on pessoa (cost=0.00..18610.77 rows=999777 width=37)
Quantos bytes essa consulta retornará?
Sobre o ANALYZE
computa estatísticas de registros aleatórios da tabela
número de registros dados por 300*default_statistics_target
Sobre os custos
SELECT name,setting
FROM pg_settings
WHERE name ~ '_cost$';
name setting
cpu_index_tuple_cost 0.005
cpu_operator_cost 0.0025
cpu_tuple_cost 0.01
parallel_setup_cost 1000
parallel_tuple_cost 0.1
random_page_cost 4
seq_page_cost 1
Um pouco de matemática…
/*
(paginas * seq_page_cost)
+ (tuplas * cpu_tuple_cost)
*/
SELECT relpages * current_setting('seq_page_cost')::float4
+ reltuples * current_setting('cpu_tuple_cost')::float4 AS total_cost
FROM pg_class
WHERE relname='pessoa';
total_cost
18610.76953125
Exemplo 3
EXPLAIN SELECT * FROM pessoa WHERE idade < 40;
QUERY PLAN
Seq Scan on pessoa (cost=0.00..21110.21 rows=714407 width=37)
Filter: (idade < 40)
SELECT count(*) FROM pessoa WHERE idade < 40;
count
716130
Por que o custo foi de 18mil para 21mil se estamos trazendo menos
registros?
Mais um pouco de matemática
/*
(paginas * seq_page_cost)
+ (tuplas * cpu_tuple_cost)
+ (tuplas * cpu_operator_cost) <<<=====
*/
SELECT relpages * current_setting('seq_page_cost')::float4
+ reltuples * current_setting('cpu_tuple_cost')::float4
+ reltuples * current_setting('cpu_operator_cost')::float4 AS total_cost
FROM pg_class
WHERE relname='pessoa';
total_cost
21110.2119140625
IndexScan
ALTER TABLE pessoa ADD CONSTRAINT pk_pessoa PRIMARY KEY(cpf);
ALTER TABLE conta ADD CONSTRAINT pk_conta PRIMARY KEY(numero_conta);
CREATE INDEX IF NOT EXISTS ix_pessoa_idade ON pessoa(idade);
Exemplo 1
EXPLAIN SELECT * FROM pessoa WHERE idade < 40;
QUERY PLAN
Seq Scan on pessoa (cost=0.00..21110.21 rows=714407 width=37)
Filter: (idade < 40)
Por que o índice não foi usado?
Exemplo 1 explicado
EXPLAIN (ANALYZE) SELECT * FROM pessoa WHERE idade < 40;
QUERY PLAN
Seq Scan on pessoa (cost=0.00..21110.21 rows=714407 width=37)
(actual time=0.012..127.928 rows=716130 loops=1)
Filter: (idade < 40)
Rows Removed by Filter: 283647
Planning time: 0.212 ms
Execution time: 148.130 ms
De 1 milhão de registros, estamos lendo 71.4% e descartando 28.3%
As opções do EXPLAIN
A opção ANALYZE executa a consulta e retorna resultados adicionais, e a
opção BUFFERS mostra como a memória foi usada durante a execução.
Como usar EXPLAIN com ANALYZE e BUFFERS?
/** CUIDADO! Se fosse um DELETE ele seria realmente executado! */
EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM pessoa WHERE idade < 20;
QUERY PLAN
Bitmap Heap Scan on pessoa (cost=934.03..10168.98 rows=49756 width=37)
(actual time=5.459..44.119 rows=49624 loops=1)
Recheck Cond: (idade < 20)
Heap Blocks: exact=8587
Buffers: shared hit=2327 read=6398
-> Bitmap Index Scan on ix_pessoa_idade (cost=0.00..921.59 rows=49756 width=0)
(actual time=4.256..4.256 rows=49624 loops=1)
Index Cond: (idade < 20)
Buffers: shared read=138
Planning time: 0.208 ms
Execution time: 46.027 ms
actual time=x: quanto tempo levou para obter a primeira linha
..y: quanto tempo levou para obter todas as linhas
shared hit/reads: blocos lidos do cache ou do disco
E se limparmos os caches do SO?
# script para limpar os caches do S.O.
# deve ser executando como 'root'
pg_ctlcluster 9.6 main stop
sync
echo 3 > /proc/sys/vm/drop_caches
pg_ctlcluster 9.6 main start
/** CUIDADO! Se fosse um DELETE ele seria realmente executado! */
EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM pessoa WHERE idade < 20;
QUERY PLAN
Bitmap Heap Scan on pessoa (cost=934.03..10168.98 rows=49756 width=37)
(actual time=43.558..948.522 rows=49624 loops=1)
Recheck Cond: (idade < 20)
Heap Blocks: exact=8587
Buffers: shared read=8725
-> Bitmap Index Scan on ix_pessoa_idade (cost=0.00..921.59 rows=49756 width=0)
(actual time=35.317..35.317 rows=49624 loops=1)
Index Cond: (idade < 20)
Buffers: shared read=138
Planning time: 60.889 ms
Execution time: 951.930 ms
Exemplo 2
Vamos desabilitar o Sequencial Scan e ver se forçar o uso do índice é uma
boa idéia.
EXPLAIN SELECT * FROM pessoa WHERE idade < 40;
QUERY PLAN
Seq Scan on pessoa (cost=0.00..21110.21 rows=714407 width=37)
Filter: (idade < 40)
E desabilitando o Sequencial Scan
SET enable_seqscan TO off;
EXPLAIN (ANALYZE) SELECT * FROM pessoa WHERE idade < 40;
SET
QUERY PLAN
Bitmap Heap Scan on pessoa (cost=13381.08..30924.17 rows=714407 width=37) (actual time=210.36
Recheck Cond: (idade < 40)
Heap Blocks: exact=8613
-> Bitmap Index Scan on ix_pessoa_idade (cost=0.00..13202.48 rows=714407 width=0) (actual
Index Cond: (idade < 40)
Planning time: 0.192 ms
Execution time: 475.718 ms
O custo melhorou ou piorou?
IndexOnly Scan
-- SET enable_indexonlyscan TO off;
-- SET enable_indexscan TO off;
-- SET enable_bitmapscan TO off;
EXPLAIN (ANALYZE, BUFFERS) SELECT idade FROM pessoa WHERE idade <= 17;
QUERY PLAN
Index Only Scan using ix_pessoa_idade on pessoa (cost=0.29..8.31 rows=1 width=4) (actual time
Index Cond: (idade <= 17)
Heap Fetches: 0
Buffers: shared hit=2
Planning time: 0.188 ms
Execution time: 0.026 ms
Junções
Resuminho
Nested Loop: usado para tabelas pequenas, é rápido para começar,
porém é demorado para nalizar
Merge Join: ordena primeiro depois faz o merge, demora para iniciar
se não existir um índice, mas é o mais rápido em conjutos de dados
muito grandes
Hash Join: usado apenas em igualdades, muito rápido quando se tem
bastante memória, mas é lento para iniciar
Nested Loop
SET enable_hashjoin TO false;
SET enable_mergejoin TO false;
--SET effective_cache_size TO '2MB';
EXPLAIN
SELECT pessoa.cpf, pessoa.nome, conta.numero_conta
FROM pessoa
JOIN conta
ON (conta.cpf = pessoa.cpf)
SET
SET
QUERY PLAN
Nested Loop (cost=0.42..1260681.38 rows=2576310 width=37) (actual time=0.074..10457.272 rows=
Buffers: shared hit=10325234 read=19037
-> Seq Scan on conta (cost=0.00..44845.10 rows=2576310 width=36) (actual time=0.043..237.0
Buffers: shared hit=64 read=19018
-> Index Scan using pk_pessoa on pessoa (cost=0.42..0.46 rows=1 width=33) (actual time=0.0
Index Cond: (cpf = conta.cpf)
Buffers: shared hit=10325170 read=19
Planning time: 0.264 ms
Execution time: 10550.261 ms
Merge Join
SET enable_hashjoin TO false;
--SET enable_mergejoin TO false;
SET enable_nestloop TO false;
--SET effective_cache_size TO '2MB';
EXPLAIN
SELECT pessoa.cpf, pessoa.nome, conta.numero_conta
FROM pessoa
JOIN conta
ON (conta.cpf = pessoa.cpf)
SET
SET
QUERY PLAN
Merge Join (cost=460201.04..547014.14 rows=2576979 width=37)
Merge Cond: (pessoa.cpf = conta.cpf)
-> Index Scan using pk_pessoa on pessoa (cost=0.42..39216.98 rows=999770 width=33)
-> Materialize (cost=460200.61..473085.51 rows=2576979 width=36)
-> Sort (cost=460200.61..466643.06 rows=2576979 width=36)
Sort Key: conta.cpf
-> Seq Scan on conta (cost=0.00..44856.79 rows=2576979 width=36)
Hash Join
--SET enable_hashjoin TO false;
SET enable_mergejoin TO false;
SET enable_nestloop TO false;
--SET effective_cache_size TO '2MB';
EXPLAIN
SELECT pessoa.cpf, pessoa.nome, conta.numero_conta
FROM pessoa
JOIN conta
ON (conta.cpf = pessoa.cpf)
SET
SET
QUERY PLAN
Hash Join (cost=38918.82..167286.08 rows=2576979 width=37)
Hash Cond: (conta.cpf = pessoa.cpf)
-> Seq Scan on conta (cost=0.00..44856.79 rows=2576979 width=36)
-> Hash (cost=18610.70..18610.70 rows=999770 width=33)
-> Seq Scan on pessoa (cost=0.00..18610.70 rows=999770 width=33)
Agregações
Sem índice
DROP INDEX IF EXISTS ix_conta_cpf;
EXPLAIN (ANALYZE, BUFFERS)
SELECT pessoa.cpf, pessoa.nome, conta.numero_conta, avg(valor)
FROM pessoa
JOIN conta ON (conta.cpf = pessoa.cpf)
WHERE conta.cpf IN (112880952283029848196, 3857587214536686701033280,
416016733223615090434, 58852625213009237948, 119092641350631399)
GROUP BY pessoa.cpf,conta.numero_conta
QUERY PLAN
GroupAggregate (cost=117189.53..118639.07 rows=64424 width=69) (actual time=927.647..927.647
Group Key: pessoa.cpf, conta.numero_conta
Buffers: shared hit=2342 read=16751
-> Sort (cost=117189.53..117350.59 rows=64424 width=55) (actual time=927.646..927.646 rows
Sort Key: pessoa.cpf, conta.numero_conta
Sort Method: quicksort Memory: 25kB
Buffers: shared hit=2342 read=16751
-> Hash Join (cost=38918.82..109838.56 rows=64424 width=55) (actual time=927.625..92
Hash Cond: (conta.cpf = pessoa.cpf)
Buffers: shared hit=2336 read=16751
-> Seq Scan on conta (cost=0.00..60962.91 rows=64424 width=54) (actual time=92
Filter: (cpf = ANY ('{112880952283029848196,3857587214536686701033280,4160
Rows Removed by Filter: 2576979
Buffers: shared hit=2336 read=16751
-> Hash (cost=18610.70..18610.70 rows=999770 width=33) (never executed)
-> Seq Scan on pessoa (cost=0.00..18610.70 rows=999770 width=33) (never
Planning time: 0.314 ms
Execution time: 927.703 ms
Com índice
CREATE INDEX ix_conta_cpf ON conta(cpf);
EXPLAIN (ANALYZE, BUFFERS)
SELECT pessoa.cpf, pessoa.nome, conta.numero_conta, avg(valor)
FROM pessoa
JOIN conta ON (conta.cpf = pessoa.cpf)
WHERE conta.cpf IN (112880952283029848196, 3857587214536686701033280,
416016733223615090434, 58852625213009237948, 119092641350631399)
GROUP BY pessoa.cpf,conta.numero_conta
CREATE INDEX
QUERY PLAN
GroupAggregate (cost=77869.96..79319.50 rows=64424 width=69) (actual time=0.074..0.074 rows=0
Group Key: pessoa.cpf, conta.numero_conta
Buffers: shared hit=7 read=11
-> Sort (cost=77869.96..78031.02 rows=64424 width=55) (actual time=0.073..0.073 rows=0 loo
Sort Key: pessoa.cpf, conta.numero_conta
Sort Method: quicksort Memory: 25kB
Buffers: shared hit=7 read=11
-> Hash Join (cost=40428.27..70518.99 rows=64424 width=55) (actual time=0.065..0.065
Hash Cond: (conta.cpf = pessoa.cpf)
Buffers: shared hit=4 read=11
-> Bitmap Heap Scan on conta (cost=1509.44..21643.33 rows=64424 width=54) (act
Recheck Cond: (cpf = ANY ('{112880952283029848196,385758721453668670103328
Buffers: shared hit=4 read=11
-> Bitmap Index Scan on ix_conta_cpf (cost=0.00..1493.34 rows=64424 widt
Index Cond: (cpf = ANY ('{112880952283029848196,38575872145366867010
Buffers: shared hit=4 read=11
-> Hash (cost=18610.70..18610.70 rows=999770 width=33) (never executed)
-> Seq Scan on pessoa (cost=0.00..18610.70 rows=999770 width=33) (never
Planning time: 0.314 ms
Execution time: 0.119 ms
Obrigado :)

Destistificando o EXPLAIN

  • 1.
    Desmisti cando o Explain DicksonS. Guedes 1º Beetup Created: 2017-11-07 ter 09:41
  • 2.
    Table of Contents Epigramas,citações e falácias Intensivão Preparação do ambiente Tipos de busca Junções Agregações Obrigado :)
  • 3.
    Epigramas, citações efalácias É mais fácil escrever um programa incorreto do que entender um corretamente. - Alan Perlis (1981) Uma linguagem que não afeta o modo como você pensa sobre programação não merece ser conhecida. - Alan Perlis (1982) Não podemos esquecer que o nosso negócio não é escrever programas; o nosso negócio é desenhar modelos computacionais que apresentarão um comportamento desejado. - Edsger Dijkstra (1972) "A banda é in nita e a latencia é sempre zero" - L. Peter Deutsch (1994)
  • 4.
    Intensivão os registros sãotambém conhecidos como tuplas; as tuplas são representadas por uma estrutura de dados chamada Heap; as heaps são distribuídas em páginas; uma página tem várias heaps (tuplas/registros); uma página é representada por um bloco em disco; um bloco tem 8 Kilobytes (8192 bytes); uma tabela está distribuída em um ou mais blocos.
  • 5.
    Em outras palavras imagineas tuplas como linhas ou parágrafos de um texto; imagine que uma página tem vários parágrafos; as paginas tem dimensões, imagine quantas letras cabem em uma única folha A4? na analogia, uma tabela seria o quê?
  • 6.
  • 7.
    Criação do bancode dados DROP DATABASE meetup; CREATE DATABASE meetup; ALTER SYSTEM SET autovacuum TO off; SELECT pg_reload_conf();
  • 8.
    Criação de umatabela pessoa com dados ctícios DROP TABLE IF EXISTS pessoa; CREATE TABLE pessoa AS WITH candidatos AS ( SELECT cast(regexp_replace(md5(cast(random() as text)), '[^0-9]+','','g') as numeric) as cpf, upper(regexp_replace(md5(cast(random() as text)), '[0-9]+',' ','g')) as nome, cast(random() * 30 as integer) + 18 as idade FROM generate_series(1,1000000) ) SELECT DISTINCT ON(cpf) * FROM candidatos ORDER BY cpf, idade; DROP TABLE SELECT 999777
  • 9.
    Dados da tabelapessoa SELECT * FROM pessoa LIMIT 10; cpf nome idade 2057318 B F B ECBC D B E C DCD 18 2893810 D AFD F AB CF D F 25 4210178 BBFEC CB B E C F F D E 20 15939623 EA B AF D D ECA CCBD 19 24132985 F C EE B FCD B EF 45 28385656 F D F AC B D A B E C CCB F 40 31004208 F FA F E B B E D C 39 45399123 C FB E A EC F 27 48885965 BB A F AEC F FE D ECC 32 48976900 BE CFCF C B E DCC F 39
  • 10.
    Dimensões da tabelapessoa SELECT count(*) FROM pessoa; count 999777 SELECT pg_size_pretty(pg_relation_size('pessoa')); pg_size_pretty 67 MB
  • 11.
    Criação de umatabela conta com dados ctícios DROP TABLE IF EXISTS conta; CREATE TABLE conta AS WITH candidatos AS ( SELECT pessoa.cpf, cast(random() * 10000000 + random() * 50 as integer) as numero_conta, cast(random() * 1000000 as numeric(17,2)) as valor FROM pessoa CROSS JOIN generate_series(1, 3) WHERE pessoa.idade > 10 + random() * 10 ORDER BY random() ) SELECT DISTINCT ON(numero_conta) * FROM candidatos ORDER BY numero_conta, cpf; DROP TABLE SELECT 2576310
  • 12.
    Dados da tabelaconta SELECT * FROM conta order by 1 limit 10; cpf numero_conta valor 36695 946740 94673.51 36695 1140633 114062.68 36695 2773365 277335.07 595115 2572588 257257.54 595115 1121931 112192.55 595115 5638002 563797.41 1011570 7508314 750827.68 1011570 5848863 584883.35 1011570 9902002 990195.29 7944468 1944112 194410.26
  • 13.
    Dimensões da tabelaconta SELECT count(*) FROM conta; count 2576310 SELECT pg_size_pretty(pg_relation_size('conta')); pg_size_pretty 149 MB
  • 14.
  • 15.
  • 16.
    Exemplo 1 EXPLAIN SELECT* FROM pessoa; QUERY PLAN Seq Scan on pessoa (cost=0.00..15934.05 rows=732105 width=68) cost=0.00: custo para obter o primeiro registro ..15934.05: custo para obter todos os registros rows=732105: número de linhas (estimadas) width=68: média (em bytes) do tamanho dos registros retornados Quantos bytes essa consulta retornará?
  • 17.
    Exemplo 2 ANALYZE pessoa; EXPLAINSELECT * FROM pessoa; QUERY PLAN Seq Scan on pessoa (cost=0.00..18610.77 rows=999777 width=37) Quantos bytes essa consulta retornará?
  • 18.
    Sobre o ANALYZE computaestatísticas de registros aleatórios da tabela número de registros dados por 300*default_statistics_target
  • 19.
    Sobre os custos SELECTname,setting FROM pg_settings WHERE name ~ '_cost$'; name setting cpu_index_tuple_cost 0.005 cpu_operator_cost 0.0025 cpu_tuple_cost 0.01 parallel_setup_cost 1000 parallel_tuple_cost 0.1 random_page_cost 4 seq_page_cost 1
  • 20.
    Um pouco dematemática… /* (paginas * seq_page_cost) + (tuplas * cpu_tuple_cost) */ SELECT relpages * current_setting('seq_page_cost')::float4 + reltuples * current_setting('cpu_tuple_cost')::float4 AS total_cost FROM pg_class WHERE relname='pessoa'; total_cost 18610.76953125
  • 21.
    Exemplo 3 EXPLAIN SELECT* FROM pessoa WHERE idade < 40; QUERY PLAN Seq Scan on pessoa (cost=0.00..21110.21 rows=714407 width=37) Filter: (idade < 40) SELECT count(*) FROM pessoa WHERE idade < 40; count 716130 Por que o custo foi de 18mil para 21mil se estamos trazendo menos registros?
  • 22.
    Mais um poucode matemática /* (paginas * seq_page_cost) + (tuplas * cpu_tuple_cost) + (tuplas * cpu_operator_cost) <<<===== */ SELECT relpages * current_setting('seq_page_cost')::float4 + reltuples * current_setting('cpu_tuple_cost')::float4 + reltuples * current_setting('cpu_operator_cost')::float4 AS total_cost FROM pg_class WHERE relname='pessoa'; total_cost 21110.2119140625
  • 23.
    IndexScan ALTER TABLE pessoaADD CONSTRAINT pk_pessoa PRIMARY KEY(cpf); ALTER TABLE conta ADD CONSTRAINT pk_conta PRIMARY KEY(numero_conta); CREATE INDEX IF NOT EXISTS ix_pessoa_idade ON pessoa(idade);
  • 24.
    Exemplo 1 EXPLAIN SELECT* FROM pessoa WHERE idade < 40; QUERY PLAN Seq Scan on pessoa (cost=0.00..21110.21 rows=714407 width=37) Filter: (idade < 40) Por que o índice não foi usado?
  • 25.
    Exemplo 1 explicado EXPLAIN(ANALYZE) SELECT * FROM pessoa WHERE idade < 40; QUERY PLAN Seq Scan on pessoa (cost=0.00..21110.21 rows=714407 width=37) (actual time=0.012..127.928 rows=716130 loops=1) Filter: (idade < 40) Rows Removed by Filter: 283647 Planning time: 0.212 ms Execution time: 148.130 ms De 1 milhão de registros, estamos lendo 71.4% e descartando 28.3%
  • 26.
    As opções doEXPLAIN A opção ANALYZE executa a consulta e retorna resultados adicionais, e a opção BUFFERS mostra como a memória foi usada durante a execução.
  • 27.
    Como usar EXPLAINcom ANALYZE e BUFFERS? /** CUIDADO! Se fosse um DELETE ele seria realmente executado! */ EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM pessoa WHERE idade < 20; QUERY PLAN Bitmap Heap Scan on pessoa (cost=934.03..10168.98 rows=49756 width=37) (actual time=5.459..44.119 rows=49624 loops=1) Recheck Cond: (idade < 20) Heap Blocks: exact=8587 Buffers: shared hit=2327 read=6398 -> Bitmap Index Scan on ix_pessoa_idade (cost=0.00..921.59 rows=49756 width=0) (actual time=4.256..4.256 rows=49624 loops=1) Index Cond: (idade < 20) Buffers: shared read=138 Planning time: 0.208 ms Execution time: 46.027 ms actual time=x: quanto tempo levou para obter a primeira linha ..y: quanto tempo levou para obter todas as linhas shared hit/reads: blocos lidos do cache ou do disco
  • 28.
    E se limparmosos caches do SO? # script para limpar os caches do S.O. # deve ser executando como 'root' pg_ctlcluster 9.6 main stop sync echo 3 > /proc/sys/vm/drop_caches pg_ctlcluster 9.6 main start /** CUIDADO! Se fosse um DELETE ele seria realmente executado! */ EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM pessoa WHERE idade < 20; QUERY PLAN Bitmap Heap Scan on pessoa (cost=934.03..10168.98 rows=49756 width=37) (actual time=43.558..948.522 rows=49624 loops=1) Recheck Cond: (idade < 20) Heap Blocks: exact=8587 Buffers: shared read=8725 -> Bitmap Index Scan on ix_pessoa_idade (cost=0.00..921.59 rows=49756 width=0) (actual time=35.317..35.317 rows=49624 loops=1) Index Cond: (idade < 20) Buffers: shared read=138 Planning time: 60.889 ms Execution time: 951.930 ms
  • 29.
    Exemplo 2 Vamos desabilitaro Sequencial Scan e ver se forçar o uso do índice é uma boa idéia. EXPLAIN SELECT * FROM pessoa WHERE idade < 40; QUERY PLAN Seq Scan on pessoa (cost=0.00..21110.21 rows=714407 width=37) Filter: (idade < 40) E desabilitando o Sequencial Scan SET enable_seqscan TO off; EXPLAIN (ANALYZE) SELECT * FROM pessoa WHERE idade < 40; SET QUERY PLAN Bitmap Heap Scan on pessoa (cost=13381.08..30924.17 rows=714407 width=37) (actual time=210.36 Recheck Cond: (idade < 40) Heap Blocks: exact=8613 -> Bitmap Index Scan on ix_pessoa_idade (cost=0.00..13202.48 rows=714407 width=0) (actual Index Cond: (idade < 40) Planning time: 0.192 ms Execution time: 475.718 ms O custo melhorou ou piorou?
  • 30.
    IndexOnly Scan -- SETenable_indexonlyscan TO off; -- SET enable_indexscan TO off; -- SET enable_bitmapscan TO off; EXPLAIN (ANALYZE, BUFFERS) SELECT idade FROM pessoa WHERE idade <= 17; QUERY PLAN Index Only Scan using ix_pessoa_idade on pessoa (cost=0.29..8.31 rows=1 width=4) (actual time Index Cond: (idade <= 17) Heap Fetches: 0 Buffers: shared hit=2 Planning time: 0.188 ms Execution time: 0.026 ms
  • 31.
  • 32.
    Resuminho Nested Loop: usadopara tabelas pequenas, é rápido para começar, porém é demorado para nalizar Merge Join: ordena primeiro depois faz o merge, demora para iniciar se não existir um índice, mas é o mais rápido em conjutos de dados muito grandes Hash Join: usado apenas em igualdades, muito rápido quando se tem bastante memória, mas é lento para iniciar
  • 33.
    Nested Loop SET enable_hashjoinTO false; SET enable_mergejoin TO false; --SET effective_cache_size TO '2MB'; EXPLAIN SELECT pessoa.cpf, pessoa.nome, conta.numero_conta FROM pessoa JOIN conta ON (conta.cpf = pessoa.cpf) SET SET QUERY PLAN Nested Loop (cost=0.42..1260681.38 rows=2576310 width=37) (actual time=0.074..10457.272 rows= Buffers: shared hit=10325234 read=19037 -> Seq Scan on conta (cost=0.00..44845.10 rows=2576310 width=36) (actual time=0.043..237.0 Buffers: shared hit=64 read=19018 -> Index Scan using pk_pessoa on pessoa (cost=0.42..0.46 rows=1 width=33) (actual time=0.0 Index Cond: (cpf = conta.cpf) Buffers: shared hit=10325170 read=19 Planning time: 0.264 ms Execution time: 10550.261 ms
  • 34.
    Merge Join SET enable_hashjoinTO false; --SET enable_mergejoin TO false; SET enable_nestloop TO false; --SET effective_cache_size TO '2MB'; EXPLAIN SELECT pessoa.cpf, pessoa.nome, conta.numero_conta FROM pessoa JOIN conta ON (conta.cpf = pessoa.cpf) SET SET QUERY PLAN Merge Join (cost=460201.04..547014.14 rows=2576979 width=37) Merge Cond: (pessoa.cpf = conta.cpf) -> Index Scan using pk_pessoa on pessoa (cost=0.42..39216.98 rows=999770 width=33) -> Materialize (cost=460200.61..473085.51 rows=2576979 width=36) -> Sort (cost=460200.61..466643.06 rows=2576979 width=36) Sort Key: conta.cpf -> Seq Scan on conta (cost=0.00..44856.79 rows=2576979 width=36)
  • 35.
    Hash Join --SET enable_hashjoinTO false; SET enable_mergejoin TO false; SET enable_nestloop TO false; --SET effective_cache_size TO '2MB'; EXPLAIN SELECT pessoa.cpf, pessoa.nome, conta.numero_conta FROM pessoa JOIN conta ON (conta.cpf = pessoa.cpf) SET SET QUERY PLAN Hash Join (cost=38918.82..167286.08 rows=2576979 width=37) Hash Cond: (conta.cpf = pessoa.cpf) -> Seq Scan on conta (cost=0.00..44856.79 rows=2576979 width=36) -> Hash (cost=18610.70..18610.70 rows=999770 width=33) -> Seq Scan on pessoa (cost=0.00..18610.70 rows=999770 width=33)
  • 36.
  • 37.
    Sem índice DROP INDEXIF EXISTS ix_conta_cpf; EXPLAIN (ANALYZE, BUFFERS) SELECT pessoa.cpf, pessoa.nome, conta.numero_conta, avg(valor) FROM pessoa JOIN conta ON (conta.cpf = pessoa.cpf) WHERE conta.cpf IN (112880952283029848196, 3857587214536686701033280, 416016733223615090434, 58852625213009237948, 119092641350631399) GROUP BY pessoa.cpf,conta.numero_conta QUERY PLAN GroupAggregate (cost=117189.53..118639.07 rows=64424 width=69) (actual time=927.647..927.647 Group Key: pessoa.cpf, conta.numero_conta Buffers: shared hit=2342 read=16751 -> Sort (cost=117189.53..117350.59 rows=64424 width=55) (actual time=927.646..927.646 rows Sort Key: pessoa.cpf, conta.numero_conta Sort Method: quicksort Memory: 25kB Buffers: shared hit=2342 read=16751 -> Hash Join (cost=38918.82..109838.56 rows=64424 width=55) (actual time=927.625..92 Hash Cond: (conta.cpf = pessoa.cpf) Buffers: shared hit=2336 read=16751 -> Seq Scan on conta (cost=0.00..60962.91 rows=64424 width=54) (actual time=92 Filter: (cpf = ANY ('{112880952283029848196,3857587214536686701033280,4160 Rows Removed by Filter: 2576979 Buffers: shared hit=2336 read=16751 -> Hash (cost=18610.70..18610.70 rows=999770 width=33) (never executed) -> Seq Scan on pessoa (cost=0.00..18610.70 rows=999770 width=33) (never Planning time: 0.314 ms Execution time: 927.703 ms
  • 38.
    Com índice CREATE INDEXix_conta_cpf ON conta(cpf); EXPLAIN (ANALYZE, BUFFERS) SELECT pessoa.cpf, pessoa.nome, conta.numero_conta, avg(valor) FROM pessoa JOIN conta ON (conta.cpf = pessoa.cpf) WHERE conta.cpf IN (112880952283029848196, 3857587214536686701033280, 416016733223615090434, 58852625213009237948, 119092641350631399) GROUP BY pessoa.cpf,conta.numero_conta CREATE INDEX QUERY PLAN GroupAggregate (cost=77869.96..79319.50 rows=64424 width=69) (actual time=0.074..0.074 rows=0 Group Key: pessoa.cpf, conta.numero_conta Buffers: shared hit=7 read=11 -> Sort (cost=77869.96..78031.02 rows=64424 width=55) (actual time=0.073..0.073 rows=0 loo Sort Key: pessoa.cpf, conta.numero_conta Sort Method: quicksort Memory: 25kB Buffers: shared hit=7 read=11 -> Hash Join (cost=40428.27..70518.99 rows=64424 width=55) (actual time=0.065..0.065 Hash Cond: (conta.cpf = pessoa.cpf) Buffers: shared hit=4 read=11 -> Bitmap Heap Scan on conta (cost=1509.44..21643.33 rows=64424 width=54) (act Recheck Cond: (cpf = ANY ('{112880952283029848196,385758721453668670103328 Buffers: shared hit=4 read=11 -> Bitmap Index Scan on ix_conta_cpf (cost=0.00..1493.34 rows=64424 widt Index Cond: (cpf = ANY ('{112880952283029848196,38575872145366867010 Buffers: shared hit=4 read=11 -> Hash (cost=18610.70..18610.70 rows=999770 width=33) (never executed) -> Seq Scan on pessoa (cost=0.00..18610.70 rows=999770 width=33) (never Planning time: 0.314 ms Execution time: 0.119 ms
  • 39.