Pergunta

eu tenho mesa items com o seguinte esquema (no postgres v9.3.5):

  Column   | Type   |                         Modifiers                  | Storage  
-----------+--------+----------------------------------------------------+----------
 id        | bigint | not null default nextval('items_id_seq'::regclass) | plain    
 data      | text   | not null                                           | extended 
 object_id | bigint | not null                                           | plain    
Indexes:
    "items_pkey" PRIMARY KEY, btree (id)
    "items_object_id_idx" btree (object_id)
Has OIDs: no

Quando executo a consulta, ela trava por muito tempo:

SELECT * FROM "items" WHERE "object_id" = '123' ORDER BY "id" DESC LIMIT 1;

Após VACUUM ANALYZE a execução da consulta melhorou bastante, mas ainda não é perfeita.

# EXPLAIN ANALYZE SELECT * FROM "items" WHERE "object_id" = '123' ORDER BY "id" DESC LIMIT 1;
                                                                            QUERY PLAN                                  
------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=0.44..1269.14 rows=1 width=63) (actual time=873796.061..873796.061 rows=0 loops=1)
   ->  Index Scan Backward using items_pkey on items  (cost=0.44..1164670.11 rows=918 width=63) (actual time=873796.059..873796.059 rows=0 loops=1)
         Filter: (object_id = 123::bigint)
         Rows Removed by Filter: 27942522
 Total runtime: 873796.113 ms
(5 rows)

O estranho é que quando executo

SELECT * FROM "items" WHERE "object_id" = '123' LIMIT 1;

ele retorna 0 linhas e posso fazer isso em meu código para otimizar o desempenho do meu aplicativo web, mas por que isso pode ser feito pelo próprio Postgres?Vim do MySQL para o Postgres e nunca vi coisas tão estranhas lá.

=====

Descobri que ele usa planos de consulta diferentes, índices diferentes, mas por quê?

# EXPLAIN ANALYZE SELECT * FROM "items" WHERE "object_id" = '123' LIMIT 1;
                                                                          QUERY PLAN                                    
--------------------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=0.56..3.34 rows=1 width=63) (actual time=0.014..0.014 rows=0 loops=1)
   ->  Index Scan using items_object_id_operation_idx on items  (cost=0.56..2579.16 rows=929 width=63) (actual time=0.013..0.013 rows=0 loops=1)
         Index Cond: (object_id = 123::bigint)
 Total runtime: 0.029 ms
(4 rows)
Foi útil?

Solução 2

Para otimizar a consulta

SELECT * FROM "items" WHERE "object_id" = '123' ORDER BY "id" DESC LIMIT 1;

Eu fiz o seguinte

SELECT * FROM 
    (SELECT * FROM "items" 
     WHERE "object_id" = '123'
     ORDER BY "id" DESC) AS "items" 
ORDER BY "id" DESC LIMIT 1;

Ajudou sem adicionar índice (object_id asc, id desc) que foi sugerido por @mustaccio.

# EXPLAIN SELECT * FROM 
    (SELECT * FROM "items" 
     WHERE "object_id" = '123'
     ORDER BY "id" DESC) AS "items" 
ORDER BY "id" DESC LIMIT 1;
                                               QUERY PLAN
--------------------------------------------------------------------------------------------------------
 Limit  (cost=16629.84..16629.86 rows=1 width=59)
   ->  Sort  (cost=16629.84..16640.44 rows=4239 width=59)
         Sort Key: items.id
         ->  Bitmap Heap Scan on items  (cost=125.42..16374.45 rows=4239 width=59)
               Recheck Cond: (object_id = 123::bigint)
                   ->  Bitmap Index Scan on items_object_id_idx  (cost=0.00..124.36 rows=4239 width=0)
                     Index Cond: (object_id = 123::bigint)
(7 rows)

Outras dicas

Tentando explicar porque há diferença de desempenho entre as duas consultas.

Este: SELECT * FROM "items" WHERE "object_id" = '123' LIMIT 1 está satisfeito por qualquer uma linha com a correspondência object_id, então o índice em object_id é uma escolha natural.A consulta requer E/S mínima:varredura de índice para encontrar o primeiro valor correspondente mais uma leitura de heap para buscar a linha inteira.

A alternativa: SELECT * FROM "items" WHERE "object_id" = '123' ORDER BY "id" DESC LIMIT 1 requer todos linhas com a correspondência object_id ser classificado por outra coluna, id, então a linha com o valor máximo de id Ser devolvido.Se você usasse o índice em object_id você precisaria realizar as seguintes operações:escaneie o índice para encontrar todo Coincidindo object_id;para cada partida, busque a linha real;em seguida, classifique todas as linhas buscadas por id e devolva aquele com o maior id.

A alternativa escolhida pelo otimizador, presumivelmente baseada na object_id histograma, é:escaneie o índice em id para trás, na sua totalidade;para cada valor vá buscar a linha e verifique se o valor de object_id partidas;retornar a primeira linha correspondente, que terá o máximo possível id valor.Esta alternativa evita a classificação das linhas, então acho que o otimizador prefere usar o índice em object_id.

A presença de um índice (object_id asc, id desc) permite ainda outra alternativa:digitalize este novo índice para a primeira entrada que corresponda ao fornecido object_id valor, que por definição terá o maior id valor;vá buscar uma linha correspondente e retorne.Obviamente, esta é a abordagem mais eficiente.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a dba.stackexchange
scroll top