Você provavelmente não quer ouvir isso, mas a melhor opção para acelerar SELECT DISTINCT é evitarDISTINCT começar. Em muitos casos (nem todos!), Isso pode ser evitado com um melhor design do banco de dados ou com melhores consultas.
Às vezes, GROUP BYé mais rápido, porque leva um caminho de código diferente.
No seu caso particular , não parece que você possa se livrar deDISTINCT . Mas você pode dar suporte à consulta com um índice especializado se tiver muitas consultas desse tipo:
CREATEINDEX foo ON events (project_id,"time", user_id);
A adição user_idé útil apenas se você conseguir verificações somente de índice . Siga o link para detalhes. Removeria o caro verificação de heap de bitmap do seu plano de consulta, que consome 90% do tempo da consulta.
Sua EXPLAINsaída me diz que a consulta precisa condensar 2.491 usuários distintos em meio milhão de linhas correspondentes. Isso não se tornará super-rápido, não importa o que você faça, mas pode ser substancialmente mais rápido.
Se os intervalos de tempo em suas consultas forem sempre os mesmos, uma MATERIALIIZED VIEWdobra user_idpor(project_id, <fixed time intervall>) percorrerá um longo caminho. Não há nenhuma chance com intervalos de tempo variados. Talvez você possa pelo menos dobrar os usuários por hora ou alguma outra unidade de tempo mínima, e isso compraria desempenho suficiente para garantir a sobrecarga considerável.
Nitpick:
Muito provavelmente, os predicados "time"devem realmente ser:
Eu li um pouco sobre varreduras de índice, vou tentar.
Sam
Infelizmente, o intervalo de tempo não é fixo.
Sam
@ Sam: Então, quanto mais rápido sua consulta de exemplo ficou com o índice sugerido?
Erwin Brandstetter
3
@edwin: Ainda não experimentou a produção. No entanto, executei a consulta original no meu local (com os mesmos dados) e demorou 3678.780 ms. Então eu adicionei o índice e ele acelerou até 170.156 ms. O plano agora contém 'Verificação apenas do índice usando foo em eventos'.
Sam
1
@ Sam: Legal! Era para isso que eu estava buscando.
Erwin Brandstetter
2
Aqui está o meu teste no caso de Sam e a resposta de Erwin
droptable t1
createtable t1 (id int, user_id int, project_id int, date_time timestamp without time zone);insertinto t1 -- 10 million row - size="498 MB"select row_number()over(), round(row_number()over()/1000), round(row_number()over()/100000), date
from generate_series('2015-01-01'::date,'2016-12-01'::date,'6 seconds'::interval
) date
limit 10000000-- before indexing - 10000000 row - output=100 row - time=2900msSELECTDISTINCT user_id
FROM t1
WHERE project_id =1AND date_time >'2015-01-01 8:00:00'AND date_time <'2016-12-01 8:00:00';CREATEINDEX foo ON t1 (project_id, date_time, user_id);-- time process=51.2 secs -- size="387 MB" -- after indexing - 10000000 row - output=100 row - time= 75ms (reduce ~ 38 times)SELECTDISTINCT user_id
FROM t1
WHERE project_id =1AND date_time >'2015-01-01 00:00:00'AND date_time <'2016-12-01 00:00:00';
Erwin disse: "Você provavelmente não quer ouvir isso, mas a melhor opção para acelerar o SELECT DISTINCT é evitar o DISTINCT para começar. Em muitos casos (nem todos!), Isso pode ser evitado com um melhor design do banco de dados ou com melhores consultas. " Acho que ele está certo, devemos evitar usar "distinto, agrupar por, ordenar por" (se houver).
Eu conheci uma situação como o caso de Sam e acho que Sam pode usar partição na tabela de eventos por mês. Reduzirá o tamanho dos dados quando você consultar, mas você precisa de uma função (pl / pgsql) para executar em vez da consulta acima. A função encontrará partições apropriadas (depende das condições) para executar a consulta.
> Acho que ele está certo, devemos evitar usar "distinto, agrupar por, ordenar por" - e também SELECT, INSERT e UPDATE. Se evitarmos essas construções, nosso banco de dados será muito rápido!
Aqui está o meu teste no caso de Sam e a resposta de Erwin
Erwin disse: "Você provavelmente não quer ouvir isso, mas a melhor opção para acelerar o SELECT DISTINCT é evitar o DISTINCT para começar. Em muitos casos (nem todos!), Isso pode ser evitado com um melhor design do banco de dados ou com melhores consultas. " Acho que ele está certo, devemos evitar usar "distinto, agrupar por, ordenar por" (se houver).
Eu conheci uma situação como o caso de Sam e acho que Sam pode usar partição na tabela de eventos por mês. Reduzirá o tamanho dos dados quando você consultar, mas você precisa de uma função (pl / pgsql) para executar em vez da consulta acima. A função encontrará partições apropriadas (depende das condições) para executar a consulta.
fonte