Um dos grandes problemas de performances em bancos de dados são os indíces, ou mais precisamente, a falta deles.
Antes de mais nada, o que raios é um índice?
Um índice nada mais é do que um conjunto de tuplas ordenadas, vamos pegar como exemplo a seguinte tabela:
CREATE TABLE usuarios (
id INTEGER,
login VARCHAR(20),
senha VARCHAR(40)
);
Nessa tabela, nenhum índice foi criado, então, vamos inserir alguns registros:
INSERT INTO usuarios (id, login, senha) VALUES (1, 'usuario1', 'senha1');
INSERT INTO usuarios (id, login, senha) VALUES (2, 'usuario2', 'senha2');
INSERT INTO usuarios (id, login, senha) VALUES (3, 'usuario3', 'senha3');
...
INSERT INTO usuarios (id, login, senha) VALUES (300000, 'usuario300000', 'senha300000');
Agora temos 300,000 usuários no nosso banco de dados, uma quantidade pequena, mas já serve ao nosso proposito, vamos fazer um select que nos retorne um usuário qualquer:
=# SELECT login FROM usuarios WHERE login='usuario2';
login
----------
usuario2
(1 row)
Time: 179.047 ms
Se saiu bem….179ms pra encontrar um resultado, agora vamos fazer outro:
=# SELECT login FROM usuarios WHERE login='usuario223432';
login
---------------
usuario223432
(1 row)
Time: 191.386 ms
Hmmm….subiu pra 191, o que aconteceria se eu precisasse pegar o registro número mais alto, tipo 9999999? Teriamos um sério problema certo? Mas porque isso acontece?? Vamos entrar no cerne do PostgreSQL, vou mudar meu SQL:
=# EXPLAIN SELECT login FROM usuarios WHERE login='usuario223432';
QUERY PLAN
------------------------------------------------------------
Seq Scan on usuarios (cost=0.00..5910.00 rows=1 width=13)
Filter: ((login)::text = 'usuario223432'::text)
(2 rows)
Time: 0.792 ms
Repare na primeira linha do resultado, ele está fazendo uma busca sequencial até encontrar o resultado desejado. De forma resumida, isso significa que ele vai comparar cada linha que tiver na tabela até encontrar o que você, desde o primeiro até o último (ou até encontrar), se o seu resultado estiver nos primeiros registros legal, mas se for um dos ultimos bilhões….
Pra resolver esse problema, basta criar um índice na tabela:
=# CREATE INDEX idx_usuarios_login ON usuarios(login);
CREATE INDEX
Time: 4020.891 ms
Agora vamos repetir o mesmo SQL:
=# SELECT login FROM usuarios WHERE login='usuario223432';
login
---------------
usuario223432
(1 row)
Time: 1.528 ms
OMFG!!!! Caiu de 190 pra 1!!!! UM!!!! Preciso falar mais alguma coisa?
“Puxa vida Rafael, você resolveu todos os problemas da minha vida, vou criar indíces em todas os campos de todas as minhas tabelas e em todas as combinações possiveis!!”
Você ta loco??? Se você fizer isso sua performance em modificações (INSERT, UPDATE, DELETE) vai sofrer muito. Isso porque se sempre que você modificar os valores, o PostgreSQL vai precisar reorganizar os indíces pra garantir que a performance no SELECT seja alta.
“Carai, como infernos eu vou saber em que campos criar um índice então?”
Fácil, crie apenas em campos que você use WHERE e ORDER BY, dessa forma você vai manter o equilibrio na performance.
“Pow cara, mas e as chaves primárias e estrangeiras?”
Bom, uma chave primaria naturalmente já é um indíce, porém, uma chave estrangeira não, então, se você tiver um campo que seja uma chave estrangeira para um campo em outra tabela, crie um índice nele também.