Вопрос

у меня есть стол items со следующей схемой (в 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

Когда я выполняю запрос, он очень долго зависает:

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

После VACUUM ANALYZE выполнение запросов значительно улучшилось, но все еще не идеально.

# 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)

Странно то, что когда я выполняю

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

он возвращает 0 строк, и я могу сделать это в своем коде, чтобы оптимизировать производительность моего веб-приложения, но почему это может сделать сам Postgres?Я пришел в Postgres из MySQL и никогда не видел там таких странных вещей.

=====

Я обнаружил, что он использует другой план запроса, другой индекс, но почему?

# 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)
Это было полезно?

Решение 2

Чтобы оптимизировать запрос

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

Я сделал следующее

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

Помогло без добавления индекса (object_id asc, id desc) который предложил @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)

Другие советы

Попытка объяснить, почему существует разница в производительности между двумя запросами.

Вот этот: SELECT * FROM "items" WHERE "object_id" = '123' LIMIT 1 удовлетворен любой одна строка с соответствием object_id, поэтому индекс на object_id это естественный выбор.Запрос требует минимального ввода-вывода:сканирование индекса для поиска первого совпадающего значения плюс одно чтение кучи для извлечения всей строки.

Альтернатива: SELECT * FROM "items" WHERE "object_id" = '123' ORDER BY "id" DESC LIMIT 1 требует все строки с соответствием object_id сортироваться по другому столбцу, id, то строка с максимальным значением id быть возвращено.Если бы вы использовали индекс на object_id вам потребуется выполнить следующие операции:просканируйте индекс, чтобы найти каждый соответствие object_id;для каждого совпадения выберите фактическую строку;затем отсортируйте все выбранные строки по id и верните тот, у которого самый большой id.

Альтернатива, выбранная оптимизатором, предположительно основанная на object_id гистограмма, это:сканировать индекс на id назад, целиком;для каждого значения выберите строку и проверьте, соответствует ли значение object_id Матчи;вернуть первую совпадающую строку, которая будет иметь максимально возможное значение id ценить.Эта альтернатива позволяет избежать сортировки строк, поэтому я думаю, что оптимизатор предпочитает использовать индекс в object_id.

Наличие индекса на (object_id asc, id desc) допускает еще одну альтернативу:просканируйте этот новый индекс на предмет первой записи, соответствующей предоставленному object_id значение, которое по определению будет иметь наибольшую id ценить;возьмите одну подходящую строку и вернитесь.Очевидно, что это наиболее эффективный подход.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с dba.stackexchange
scroll top