У мене відносно простий запит на таблиці з 1,5М рядками:
SELECT mtid FROM publication
WHERE mtid IN (9762715) OR last_modifier=21321
LIMIT 5000;
EXPLAIN ANALYZE
вихід:
Limit (cost=8.84..12.86 rows=1 width=8) (actual time=0.985..0.986 rows=1 loops=1) -> Bitmap Heap Scan on publication (cost=8.84..12.86 rows=1 width=8) (actual time=0.984..0.985 rows=1 loops=1) Recheck Cond: ((mtid = 9762715) OR (last_modifier = 21321)) -> BitmapOr (cost=8.84..8.84 rows=1 width=0) (actual time=0.971..0.971 rows=0 loops=1) -> Bitmap Index Scan on publication_pkey (cost=0.00..4.42 rows=1 width=0) (actual time=0.295..0.295 rows=1 loops=1) Index Cond: (mtid = 9762715) -> Bitmap Index Scan on publication_last_modifier_btree (cost=0.00..4.42 rows=1 width=0) (actual time=0.674..0.674 rows=0 loops=1) Index Cond: (last_modifier = 21321) Total runtime: 1.027 ms
Поки що добре, швидко і використовує доступні індекси.
Тепер, якщо я трохи змінити запит, результат буде таким:
SELECT mtid FROM publication
WHERE mtid IN (SELECT 9762715) OR last_modifier=21321
LIMIT 5000;
EXPLAIN ANALYZE
вихід:
Limit (cost=0.01..2347.74 rows=5000 width=8) (actual time=2735.891..2841.398 rows=1 loops=1) -> Seq Scan on publication (cost=0.01..349652.84 rows=744661 width=8) (actual time=2735.888..2841.393 rows=1 loops=1) Filter: ((hashed SubPlan 1) OR (last_modifier = 21321)) SubPlan 1 -> Result (cost=0.00..0.01 rows=1 width=0) (actual time=0.001..0.001 rows=1 loops=1) Total runtime: 2841.442 ms
Не так швидко, і за допомогою Seq-сканування ...
Звичайно, оригінальний запит, який виконується додатком, є дещо складнішим і навіть повільнішим, і, звичайно, оригінальний сплячий оригінал - ні (SELECT 9762715)
, але повільність існує навіть для цього (SELECT 9762715)
! Запит генерується в сплячому режимі, тому змінити їх є досить складним завданням, а деякі функції недоступні (наприклад UNION
, недоступно, що було б швидко).
Питання
- Чому індекс не можна використовувати у другому випадку? Як їх можна було використовувати?
- Чи можу я покращити ефективність запитів іншим способом?
Додаткові думки
Здається, що ми могли використати перший випадок, зробивши SELECT вручну, а потім поставивши отриманий список у запит. Навіть з 5000 цифрами у списку IN () це в чотири рази швидше, ніж друге рішення. Однак це просто здається НЕПОЗНАЧНИМ (також, це може бути в 100 разів швидше :)). Зовсім незрозуміло, чому планувальник запитів використовує зовсім інший метод для цих двох запитів, тому я хотів би знайти приємніше рішення цієї проблеми.
(SELECT 9762715)
.
(SELECT 9762715)
. До сплячого питання: це можна зробити, але потрібно серйозно переписати код, оскільки у нас є визначені користувачем критерії сплячого режиму, які перекладаються на ходу. Тож по суті ми б модифікували сплячку, яка є величезною справою з великою кількістю можливих побічних ефектів.
JOIN
замістьIN ()
? Такожpublication
проаналізовано нещодавно?