SlideShare a Scribd company logo
Как читать и интерпретировать
вывод команды EXPLAIN ?
Алексей Ермаков
alexey.ermakov@dataegret.com
2 Как ускорить запрос?
• “Очень долгий, очень пристальный и очень задумчивый взгляд” на
запрос
dataegret.com
2 Как ускорить запрос?
• “Очень долгий, очень пристальный и очень задумчивый взгляд” на
запрос
• EXPLAIN – основной инструмент анализа запросов
dataegret.com
3 Как ускорить запрос?
• EXPLAIN без параметров показывает план запроса, но не выполняет1
запрос и не показывает статистику выполнения запроса
1
на самом деле может выполнить immutable/stable хранимки, но ведь они у вас ничего
не меняют, правда?
dataegret.com
4 План запроса #1
explain select * from posts join categories on posts.category_id = categories.id where
posts.rating < 15;
--------------------------------------------------------------------------------------------
Nested Loop (cost=0.43..16.47 rows=1 width=32)
-> Index Scan using posts_rating_idx on posts (cost=0.29..8.30 rows=1 width=28)
Index Cond: (rating < 15)
-> Index Only Scan using categories_pkey on categories (cost=0.14..8.16 rows=1 width=4)
Index Cond: (id = posts.category_id)
dataegret.com
5 План запроса #2
explain select * from posts join categories on posts.category_id = categories.id where
posts.rating < 35;
Hash Join (cost=27.84..117.61 rows=601 width=32)
Hash Cond: (posts.category_id = categories.id)
-> Bitmap Heap Scan on posts (cost=12.94..94.46 rows=601 width=28)
Recheck Cond: (rating < 35)
-> Bitmap Index Scan on posts_rating_idx (cost=0.00..12.79 rows=601 width=0)
Index Cond: (rating < 35)
-> Hash (cost=13.64..13.64 rows=100 width=4)
-> Index Only Scan using categories_pkey on categories (cost=0.14..13.64 rows=100 width=4)
dataegret.com
6 План запроса #2
explain select * from posts join categories on posts.category_id = categories.id where
posts.rating < 35;
Hash Join (cost=27.84..117.61 rows=601 width=32)
Hash Cond: (posts.category_id = categories.id)
-> Bitmap Heap Scan on posts (cost=12.94..94.46 rows=601 width=28)
Recheck Cond: (rating < 35)
-> Bitmap Index Scan on posts_rating_idx (cost=0.00..12.79 rows=601 width=0)
Index Cond: (rating < 35)
-> Hash (cost=13.64..13.64 rows=100 width=4)
-> Index Only Scan using categories_pkey on categories (cost=0.14..13.64 rows=100 width=4)
dataegret.com
7 План запроса
• Это дерево
• Вышестоящие узлы запрашивают данные у нижестоящих
• План запроса не меняется по мере выполнения, даже если оказалось
что ожидания расходятся с реальностью на 6 порядков
• База не хранит историю неудачных или медленных планов запросов.
Так что, если если запрос получает неверный план - он будет
получать его воспроизводимо2
2
если не меняются настройки базы, распределение данных, размеры таблиц и
индексов, не используется geqo
dataegret.com
8 Мы любим ORM
select website0_.websiteId as websiteI1_304_0_,
website0_.websiteTitleType as websiteT2_304_0_,
website0_.websiteUrl as websiteU3_304_0_,
website0_.websiteLocalArticlePath as websiteL4_304_0_,
... (еще 14кб текста)
from Website website0_ where website0_.websiteId=$1
dataegret.com
8 Мы любим ORM
select website0_.websiteId as websiteI1_304_0_,
website0_.websiteTitleType as websiteT2_304_0_,
website0_.websiteUrl as websiteU3_304_0_,
website0_.websiteLocalArticlePath as websiteL4_304_0_,
... (еще 14кб текста)
from Website website0_ where website0_.websiteId=$1
• Иногда план запроса читать проще, нежели сам запрос
dataegret.com
9 Виды элементарных операций
Методы извлечения данных
• seq scan – последовательное чтение таблицы
• index scan – random io (чтение индекса + чтение таблицы)
• index only scan – random io (чтение только3 индекса)
• bitmap index scan + bitmap heap scan – компромисс между seq
scan/index scan, возможность использования нескольких индексов в
OR/AND условиях
• CTE scan – чтение из CTE (блок WITH)
• function scan – чтение строк из функции
3
Возможно и чтение таблицы, зависит от состояния visibility map
dataegret.com
10 Виды элементарных операций
Методы соединения данных
• nested loop – оптимален для небольших наборов данных
• hash join – оптимален для больших наборов данных
• merge join – оптимален для больших наборов данных, в случае, если
они отсортированы
dataegret.com
11 Nested loop
for each outer row:
for each inner row:
if join condition is true:
output combined row
dataegret.com
12 Виды элементарных операций
Методы обработки данных
• sort – сортирует данные в памяти или на диске, есть более быстрый
topN вариант
• limit – ограничивает выборку
• aggregate – используется в агрегирующих функциях
• hash aggregate – используется в группировках
• unique – отбрасывает дубликаты из отсортированных выборок
• gather – используется для объединения данных с различных
воркеров при одновременном выполнении
dataegret.com
13 Характеристики каждой операции
explain select * from pg_database;
QUERY PLAN
-----------------------------------------------------------
Seq Scan on pg_database (cost=0.00..0.16 rows=6 width=271)
(1 row)
Seq Scan тип операции
on pg_database объект, с которым проводится операция
cost=0.00..0.16 стоимость операции (startup..total cost)
rows=6 ожидаемое число строк
width=271 средняя ширина строки в байтах
dataegret.com
14 startup..total cost
explain select * from posts order by id limit 5;
QUERY PLAN
--------------------------------------------------------------------------------------
Limit (cost=0.29..0.46 rows=5 width=28)
-> Index Scan using posts_pkey on posts (cost=0.29..347.29 rows=10000 width=28)
(2 rows)
dataegret.com
14 startup..total cost
explain select * from posts order by id limit 5;
QUERY PLAN
--------------------------------------------------------------------------------------
Limit (cost=0.29..0.46 rows=5 width=28)
-> Index Scan using posts_pkey on posts (cost=0.29..347.29 rows=10000 width=28)
(2 rows)
• 0.29 + (347.29 - 0.29)*5/10000 = 0.4635
dataegret.com
15 rows*width
• rows*width у корневого узла дает примерный порядок объема
результата в байтах
• если запрос часто вызывается и при этом запрашивает большое
число данных – то легко можно забить сеть между базой и
приложением
• передача данных по сети может занимать большее время, чем время
выполнения запроса
• не всегда стоит запрашивать все поля, что есть, особенно если они
широкие и никак не используются
dataegret.com
16 Опции команды EXPLAIN
EXPLAIN (ANALYZE,VERBOSE,COSTS,BUFFERS,TIMING4) select * from t1;
QUERY PLAN
-------------------------------------------------------------------
Seq Scan on public.t1 (cost=0.00..104424.80 rows=10000000 width=8)
(actual time=0.218..2316.688 rows=10000000 loops=1)
Output: f1, f2
Buffers: shared read=44248
I/O Timings: read=322.7145
Planning time: 0.024 ms
Execution time: 3852.588 ms
4
COSTS и TIMING опции включены по-умолчанию
5
I/O Timings отображается, когда track_io_timing включен
dataegret.com
17 План запроса #3
Gather (cost=100.57..1338637.12 rows=2346550 width=1526) (actual time=4489.134..4855.942 rows=79061 loops=1)
Workers Planned: 4 Workers Launched: 4
-> Nested Loop (cost=0.57..1103882.12 rows=2346550 width=1526)
(actual time=4482.217..4810.097 rows=15812 loops=5)
-> Parallel Seq Scan on deferred_report r (cost=0.00..97294.45 rows=1345386 width=8)
(actual time=0.026..702.618 rows=1098890 loops=5)
Filter: (("timestamp" < ’2017-02-26 21:00:00’::timestamp without time zone) AND (proxy_id = 37) AND
Rows Removed by Filter: 2709566
-> Index Scan using transactions_pkey on transactions t (cost=0.57..0.74 rows=1 width=1526)
(actual time=0.004..0.004 rows=0 loops=5494451)
Index Cond: (id = r.tx_id)
Filter: (status = ’ok’::status)
Rows Removed by Filter: 1
Planning time: 0.736 ms
Execution time: 4861.460 ms
dataegret.com
18 Filter
-> Parallel Seq Scan on deferred_report r (cost=0.00..97294.45 rows=1345386 width=8)
(actual time=0.026..702.618 rows=1098890 loops=5)
Filter: (("timestamp" < ’2017-02-26 21:00:00’::timestamp without time zone) AND (proxy_id = 37
Rows Removed by Filter: 2709566
• Оправдано ли использование seq scan в данном случае?
dataegret.com
18 Filter
-> Parallel Seq Scan on deferred_report r (cost=0.00..97294.45 rows=1345386 width=8)
(actual time=0.026..702.618 rows=1098890 loops=5)
Filter: (("timestamp" < ’2017-02-26 21:00:00’::timestamp without time zone) AND (proxy_id = 37
Rows Removed by Filter: 2709566
• Оправдано ли использование seq scan в данном случае?
• worker извлек 1098890
1098890+2709566
≈ 28% строк
dataegret.com
18 Filter
-> Parallel Seq Scan on deferred_report r (cost=0.00..97294.45 rows=1345386 width=8)
(actual time=0.026..702.618 rows=1098890 loops=5)
Filter: (("timestamp" < ’2017-02-26 21:00:00’::timestamp without time zone) AND (proxy_id = 37
Rows Removed by Filter: 2709566
• Оправдано ли использование seq scan в данном случае?
• worker извлек 1098890
1098890+2709566
≈ 28% строк
• вероятно да, bitmap index scan может и был бы быстрее, но в 9.6 его еще нельзя
распараллелить по воркерам
dataegret.com
19 Filter
-> Index Scan using transactions_pkey on transactions t (cost=0.57..0.74 rows=1 width=1526)
(actual time=0.004..0.004 rows=0 loops=5494451)
Index Cond: (id = r.tx_id)
Filter: (status = ’ok’::status)
Rows Removed by Filter: 1
• Используется ли оптимальный индекс в данном случае?
dataegret.com
19 Filter
-> Index Scan using transactions_pkey on transactions t (cost=0.57..0.74 rows=1 width=1526)
(actual time=0.004..0.004 rows=0 loops=5494451)
Index Cond: (id = r.tx_id)
Filter: (status = ’ok’::status)
Rows Removed by Filter: 1
• Используется ли оптимальный индекс в данном случае?
• Большинство строк отбрасываются по условию status = ‘ok’::status
dataegret.com
19 Filter
-> Index Scan using transactions_pkey on transactions t (cost=0.57..0.74 rows=1 width=1526)
(actual time=0.004..0.004 rows=0 loops=5494451)
Index Cond: (id = r.tx_id)
Filter: (status = ’ok’::status)
Rows Removed by Filter: 1
• Используется ли оптимальный индекс в данном случае?
• Большинство строк отбрасываются по условию status = ‘ok’::status
• Вероятно нет, может быть тут более уместен индекс по (id, status) или частичный индекс по id
where status = ‘ok’
dataegret.com
20 Инструменты, позволяющие визуализировать план запроса
• https://explain.depesz.com/
• http://tatiyants.com/pev/
dataegret.com
21 explain.depesz.com
dataegret.com
22 Filter
WHERE dispatch_time + INTERVAL ’1 hour’ > now()
WHERE (DATE(o.created) >= ’2017-03-23’ AND DATE(o.created) <= ’2017-04-21’)
• конструкции такого вида не могут использовать обычный индекс по соответствующему полю
• необходимо привести их к виду индексируемое_поле индексируемый_оператор выражение
dataegret.com
23 Опция buffers
-> Index Only Scan using user_balance_id_type on user_balance ub (cost=0.00..0.56 rows=1 width=8)
(actual time=0.031..1.453 rows=1 loops=29721)
Index Cond: ((id = o.user_balance_id) AND (type = 1))
Heap Fetches: 7192482
Buffers: shared hit=7608576
• Что-то пошло не так...
dataegret.com
23 Опция buffers
-> Index Only Scan using user_balance_id_type on user_balance ub (cost=0.00..0.56 rows=1 width=8)
(actual time=0.031..1.453 rows=1 loops=29721)
Index Cond: ((id = o.user_balance_id) AND (type = 1))
Heap Fetches: 7192482
Buffers: shared hit=7608576
• Что-то пошло не так...
• Читаем 7608576
29721
= 256 страниц, чтобы извлечь одну строку
dataegret.com
23 Опция buffers
-> Index Only Scan using user_balance_id_type on user_balance ub (cost=0.00..0.56 rows=1 width=8)
(actual time=0.031..1.453 rows=1 loops=29721)
Index Cond: ((id = o.user_balance_id) AND (type = 1))
Heap Fetches: 7192482
Buffers: shared hit=7608576
• Что-то пошло не так...
• Читаем 7608576
29721
= 256 страниц, чтобы извлечь одну строку
• Вероятно, очень большой bloat в таблице/индексе
dataegret.com
23 Опция buffers
-> Index Only Scan using user_balance_id_type on user_balance ub (cost=0.00..0.56 rows=1 width=8)
(actual time=0.031..1.453 rows=1 loops=29721)
Index Cond: ((id = o.user_balance_id) AND (type = 1))
Heap Fetches: 7192482
Buffers: shared hit=7608576
• Что-то пошло не так...
• Читаем 7608576
29721
= 256 страниц, чтобы извлечь одну строку
• Вероятно, очень большой bloat в таблице/индексе
• Длинные транзакции? Не справляется автовакуум?
dataegret.com
23 Опция buffers
-> Index Only Scan using user_balance_id_type on user_balance ub (cost=0.00..0.56 rows=1 width=8)
(actual time=0.031..1.453 rows=1 loops=29721)
Index Cond: ((id = o.user_balance_id) AND (type = 1))
Heap Fetches: 7192482
Buffers: shared hit=7608576
• Что-то пошло не так...
• Читаем 7608576
29721
= 256 страниц, чтобы извлечь одну строку
• Вероятно, очень большой bloat в таблице/индексе
• Длинные транзакции? Не справляется автовакуум?
• Отстающая реплика с hot_standby_feedback = on !
dataegret.com
24 I/O timings
-> Bitmap Heap Scan on delivery fi (cost=872967.27..4196621.37 rows=32425255 width=36)
(actual time=30842.138..37818.082 rows=6674571 loops=1)
Recheck Cond: ((datetime_start >= ’2017-05-11 00:00:00’::timestamp without time zone) AND (datetime_end <= ’2
Filter: ((NOT is_deleted) OR (qty > 0))
Rows Removed by Filter: 60545
Heap Blocks: exact=667323
Buffers: shared hit=47584 read=827816 dirtied=1 written=2292
I/O Timings: read=30152.376 write=42.134
-> Bitmap Index Scan on delivery_datetime_start_datetime_end_idx (cost=0.00..864860.96 rows=32514577 width=
Index Cond: ((datetime_start >= ’2017-05-11 00:00:00’::timestamp without time zone) AND (datetime_end <
Buffers: shared hit=80 read=207997
I/O Timings: read=27315.687
dataegret.com
24 I/O timings
-> Bitmap Heap Scan on delivery fi (cost=872967.27..4196621.37 rows=32425255 width=36)
(actual time=30842.138..37818.082 rows=6674571 loops=1)
Recheck Cond: ((datetime_start >= ’2017-05-11 00:00:00’::timestamp without time zone) AND (datetime_end <= ’2
Filter: ((NOT is_deleted) OR (qty > 0))
Rows Removed by Filter: 60545
Heap Blocks: exact=667323
Buffers: shared hit=47584 read=827816 dirtied=1 written=2292
I/O Timings: read=30152.376 write=42.134
-> Bitmap Index Scan on delivery_datetime_start_datetime_end_idx (cost=0.00..864860.96 rows=32514577 width=
Index Cond: ((datetime_start >= ’2017-05-11 00:00:00’::timestamp without time zone) AND (datetime_end <
Buffers: shared hit=80 read=207997
I/O Timings: read=27315.687
• 30c из 37с ушло на чтение с диска
dataegret.com
24 I/O timings
-> Bitmap Heap Scan on delivery fi (cost=872967.27..4196621.37 rows=32425255 width=36)
(actual time=30842.138..37818.082 rows=6674571 loops=1)
Recheck Cond: ((datetime_start >= ’2017-05-11 00:00:00’::timestamp without time zone) AND (datetime_end <= ’2
Filter: ((NOT is_deleted) OR (qty > 0))
Rows Removed by Filter: 60545
Heap Blocks: exact=667323
Buffers: shared hit=47584 read=827816 dirtied=1 written=2292
I/O Timings: read=30152.376 write=42.134
-> Bitmap Index Scan on delivery_datetime_start_datetime_end_idx (cost=0.00..864860.96 rows=32514577 width=
Index Cond: ((datetime_start >= ’2017-05-11 00:00:00’::timestamp without time zone) AND (datetime_end <
Buffers: shared hit=80 read=207997
I/O Timings: read=27315.687
• 30c из 37с ушло на чтение с диска
• Отфильтровалось относительно мало строк
dataegret.com
25 I/O timings
• 30c из 37с ушло на чтение с диска
• Отфильтровалось относительно мало строк
• Происходит ли значительная фильтрация строк на более высоком
уровне ?
dataegret.com
25 I/O timings
• 30c из 37с ушло на чтение с диска
• Отфильтровалось относительно мало строк
• Происходит ли значительная фильтрация строк на более высоком
уровне ?
• Если да - возможно нужно соединять таблицы каким-то другим
способом, чтобы приходилось меньше читать данных
dataegret.com
26 Sort
explain analyze select distinct f1 from test_ndistinct ;
QUERY PLAN
-------------------------------------------------------------------------------------------
Unique (cost=1571431.43..1621431.49 rows=100000 width=4)
(actual time=4791.872..7551.150 rows=90020 loops=1)
-> Sort (cost=1571431.43..1596431.46 rows=10000012 width=4)
(actual time=4791.870..6893.413 rows=10000000 loops=1)
Sort Key: f1
Sort Method: external merge Disk: 101648kB
-> Seq Scan on test_ndistinct (cost=0.00..135314.12 rows=10000012 width=4)
(actual time=0.041..938.093 rows=10000000 loops=1)
Planning time: 0.099 ms
Execution time: 7714.701 ms
dataegret.com
27 HashAggregate
set work_mem = ’8MB’;
SET
explain analyze select distinct f1 from test_ndistinct ;
QUERY PLAN
-------------------------------------------------------------------------------------------
HashAggregate (cost=160314.15..161314.15 rows=100000 width=4)
(actual time=2371.902..2391.415 rows=90020 loops=1)
Group Key: f1
-> Seq Scan on test_ndistinct (cost=0.00..135314.12 rows=10000012 width=4)
(actual time=0.093..871.619 rows=10000000 loops=1)
Planning time: 0.048 ms
Execution time: 2396.186 ms
dataegret.com
28 Ошибки планировщика
explain analyze select account_id from account
where last_update <= ’2017-10-23’ and discarded = false limit 200;
-------------------------------------------------------------------------------------------
Limit (cost=0.08..26.15 rows=200 width=4) (actual time=0.584..18.087 rows=2 loops=1)
-> Index Scan using account_last_update_special1_key on account
(cost=0.08..18878.72 rows=144854 width=4) (actual time=0.582..18.084 rows=2 loops=1)
Index Cond: (last_update <= ’2017-10-23’::date)
dataegret.com
28 Ошибки планировщика
explain analyze select account_id from account
where last_update <= ’2017-10-23’ and discarded = false limit 200;
-------------------------------------------------------------------------------------------
Limit (cost=0.08..26.15 rows=200 width=4) (actual time=0.584..18.087 rows=2 loops=1)
-> Index Scan using account_last_update_special1_key on account
(cost=0.08..18878.72 rows=144854 width=4) (actual time=0.582..18.084 rows=2 loops=1)
Index Cond: (last_update <= ’2017-10-23’::date)
• Ошибки в оценке числа строк в несколько порядков могут привести в итоге к сильно
неоптимальному плану
dataegret.com
28 Ошибки планировщика
explain analyze select account_id from account
where last_update <= ’2017-10-23’ and discarded = false limit 200;
-------------------------------------------------------------------------------------------
Limit (cost=0.08..26.15 rows=200 width=4) (actual time=0.584..18.087 rows=2 loops=1)
-> Index Scan using account_last_update_special1_key on account
(cost=0.08..18878.72 rows=144854 width=4) (actual time=0.582..18.084 rows=2 loops=1)
Index Cond: (last_update <= ’2017-10-23’::date)
• Ошибки в оценке числа строк в несколько порядков могут привести в итоге к сильно
неоптимальному плану
• Можно посмотреть на собираемую autoanalyze статистику чтобы понять причину
dataegret.com
28 Ошибки планировщика
explain analyze select account_id from account
where last_update <= ’2017-10-23’ and discarded = false limit 200;
-------------------------------------------------------------------------------------------
Limit (cost=0.08..26.15 rows=200 width=4) (actual time=0.584..18.087 rows=2 loops=1)
-> Index Scan using account_last_update_special1_key on account
(cost=0.08..18878.72 rows=144854 width=4) (actual time=0.582..18.084 rows=2 loops=1)
Index Cond: (last_update <= ’2017-10-23’::date)
• Ошибки в оценке числа строк в несколько порядков могут привести в итоге к сильно
неоптимальному плану
• Можно посмотреть на собираемую autoanalyze статистику чтобы понять причину
• Можно посмотреть переписать запрос каким-то иным способом, заставив планировщик
соединять таблицы нужным образом и использовать нужные индексы
dataegret.com
29 Что можно делать ?
• Если запрос выполняется за разумное время, смотрим его explain
analyze
• Пытаемся найти узлы, на которые больше всего уходит времени
• Нет ли у нас где-то явно пропущенных индесов, в местах где много
строк фильтруется ?
• Нет ли проблем с bloat ? Смотрим explain (analyze, buffers)
• Нет ли проблем с дисками (track_io_timing)?
• Хватает ли work_mem ?
• Не ошибается ли планировщик в оценке числа строк на порядки?
• Не сильно ли велико время планирования?
dataegret.com
30 Что можно почитать?
• depesz: Explaining the unexplainable
• PostgreSQL Manual 14.1. Using EXPLAIN
• Bruce Momjian – Explaining the Postgres Query Optimizer
• www.slideshare.net/alexius2/
dataegret.com
31 Вопросы?
alexey.ermakov@dataegret.com
dataegret.com
Ad

More Related Content

What's hot (20)

Virtualization Support in ARMv8+
Virtualization Support in ARMv8+Virtualization Support in ARMv8+
Virtualization Support in ARMv8+
Aananth C N
 
Futex Scaling for Multi-core Systems
Futex Scaling for Multi-core SystemsFutex Scaling for Multi-core Systems
Futex Scaling for Multi-core Systems
Davidlohr Bueso
 
Big o notation
Big o notationBig o notation
Big o notation
hamza mushtaq
 
Android 逆向工程心得分享
Android 逆向工程心得分享Android 逆向工程心得分享
Android 逆向工程心得分享
EnPing Eric Hsieh
 
Kernel Recipes 2015: Kernel packet capture technologies
Kernel Recipes 2015: Kernel packet capture technologiesKernel Recipes 2015: Kernel packet capture technologies
Kernel Recipes 2015: Kernel packet capture technologies
Anne Nicolas
 
Divide and Conquer
Divide and ConquerDivide and Conquer
Divide and Conquer
Melaku Bayih Demessie
 
Flag Registers (Assembly Language)
Flag Registers (Assembly Language)Flag Registers (Assembly Language)
Flag Registers (Assembly Language)
Anwar Hasan Shuvo
 
8086 assembly
8086 assembly8086 assembly
8086 assembly
Shehrevar Davierwala
 
Test Driven Database Development
Test Driven Database DevelopmentTest Driven Database Development
Test Driven Database Development
David Wheeler
 
Ipc in linux
Ipc in linuxIpc in linux
Ipc in linux
Dr. C.V. Suresh Babu
 
Chapter 13 - I/O Systems
Chapter 13 - I/O SystemsChapter 13 - I/O Systems
Chapter 13 - I/O Systems
Wayne Jones Jnr
 
논문에 1인칭(i 와 we) 사용여부
논문에 1인칭(i 와 we) 사용여부논문에 1인칭(i 와 we) 사용여부
논문에 1인칭(i 와 we) 사용여부
에디티지(Editage Korea)
 
Memcache Injection (Hacktrick'15)
Memcache Injection (Hacktrick'15)Memcache Injection (Hacktrick'15)
Memcache Injection (Hacktrick'15)
Ömer Çıtak
 
Introduction to Return-Oriented Exploitation on ARM64 - Billy Ellis
Introduction to Return-Oriented Exploitation on ARM64 - Billy EllisIntroduction to Return-Oriented Exploitation on ARM64 - Billy Ellis
Introduction to Return-Oriented Exploitation on ARM64 - Billy Ellis
BillyEllis3
 
Finite automata
Finite automataFinite automata
Finite automata
ankitamakin
 
Linux Kernel Booting Process (1) - For NLKB
Linux Kernel Booting Process (1) - For NLKBLinux Kernel Booting Process (1) - For NLKB
Linux Kernel Booting Process (1) - For NLKB
shimosawa
 
Linux binary Exploitation - Basic knowledge
Linux binary Exploitation - Basic knowledgeLinux binary Exploitation - Basic knowledge
Linux binary Exploitation - Basic knowledge
Angel Boy
 
Address/Thread/Memory Sanitizer
Address/Thread/Memory SanitizerAddress/Thread/Memory Sanitizer
Address/Thread/Memory Sanitizer
Platonov Sergey
 
Vfs
VfsVfs
Vfs
Waqas !!!!
 
Cinderlib integration - deep dive slides
Cinderlib integration - deep dive slidesCinderlib integration - deep dive slides
Cinderlib integration - deep dive slides
Eyal Shenitzky
 
Virtualization Support in ARMv8+
Virtualization Support in ARMv8+Virtualization Support in ARMv8+
Virtualization Support in ARMv8+
Aananth C N
 
Futex Scaling for Multi-core Systems
Futex Scaling for Multi-core SystemsFutex Scaling for Multi-core Systems
Futex Scaling for Multi-core Systems
Davidlohr Bueso
 
Android 逆向工程心得分享
Android 逆向工程心得分享Android 逆向工程心得分享
Android 逆向工程心得分享
EnPing Eric Hsieh
 
Kernel Recipes 2015: Kernel packet capture technologies
Kernel Recipes 2015: Kernel packet capture technologiesKernel Recipes 2015: Kernel packet capture technologies
Kernel Recipes 2015: Kernel packet capture technologies
Anne Nicolas
 
Flag Registers (Assembly Language)
Flag Registers (Assembly Language)Flag Registers (Assembly Language)
Flag Registers (Assembly Language)
Anwar Hasan Shuvo
 
Test Driven Database Development
Test Driven Database DevelopmentTest Driven Database Development
Test Driven Database Development
David Wheeler
 
Chapter 13 - I/O Systems
Chapter 13 - I/O SystemsChapter 13 - I/O Systems
Chapter 13 - I/O Systems
Wayne Jones Jnr
 
Memcache Injection (Hacktrick'15)
Memcache Injection (Hacktrick'15)Memcache Injection (Hacktrick'15)
Memcache Injection (Hacktrick'15)
Ömer Çıtak
 
Introduction to Return-Oriented Exploitation on ARM64 - Billy Ellis
Introduction to Return-Oriented Exploitation on ARM64 - Billy EllisIntroduction to Return-Oriented Exploitation on ARM64 - Billy Ellis
Introduction to Return-Oriented Exploitation on ARM64 - Billy Ellis
BillyEllis3
 
Linux Kernel Booting Process (1) - For NLKB
Linux Kernel Booting Process (1) - For NLKBLinux Kernel Booting Process (1) - For NLKB
Linux Kernel Booting Process (1) - For NLKB
shimosawa
 
Linux binary Exploitation - Basic knowledge
Linux binary Exploitation - Basic knowledgeLinux binary Exploitation - Basic knowledge
Linux binary Exploitation - Basic knowledge
Angel Boy
 
Address/Thread/Memory Sanitizer
Address/Thread/Memory SanitizerAddress/Thread/Memory Sanitizer
Address/Thread/Memory Sanitizer
Platonov Sergey
 
Cinderlib integration - deep dive slides
Cinderlib integration - deep dive slidesCinderlib integration - deep dive slides
Cinderlib integration - deep dive slides
Eyal Shenitzky
 

Similar to Как читать и интерпретировать вывод команды EXPLAIN (20)

PostgreSQL performance recipes
PostgreSQL performance recipesPostgreSQL performance recipes
PostgreSQL performance recipes
Alexey Ermakov
 
Производительность параметрического поиска на основе опенсорс-платформы
Производительность параметрического поиска на основе опенсорс-платформыПроизводительность параметрического поиска на основе опенсорс-платформы
Производительность параметрического поиска на основе опенсорс-платформы
Yandex
 
Современному хайлоду - современные решения: MySQL 8.0 и улучшения Percona
Современному хайлоду - современные решения: MySQL 8.0 и улучшения PerconaСовременному хайлоду - современные решения: MySQL 8.0 и улучшения Percona
Современному хайлоду - современные решения: MySQL 8.0 и улучшения Percona
Sveta Smirnova
 
Производительность запросов в PostgreSQL - шаг за шагом / Илья Космодемьянски...
Производительность запросов в PostgreSQL - шаг за шагом / Илья Космодемьянски...Производительность запросов в PostgreSQL - шаг за шагом / Илья Космодемьянски...
Производительность запросов в PostgreSQL - шаг за шагом / Илья Космодемьянски...
Ontico
 
2014.12.23 Александр Андреев, Parallels
2014.12.23 Александр Андреев, Parallels2014.12.23 Александр Андреев, Parallels
2014.12.23 Александр Андреев, Parallels
Nikolay Samokhvalov
 
#PostgreSQLRussia в банке Тинькофф, доклад №1
#PostgreSQLRussia в банке Тинькофф, доклад №1#PostgreSQLRussia в банке Тинькофф, доклад №1
#PostgreSQLRussia в банке Тинькофф, доклад №1
Nikolay Samokhvalov
 
ObjectManager, или как работать с большим количеством объектов на карте, Мари...
ObjectManager, или как работать с большим количеством объектов на карте, Мари...ObjectManager, или как работать с большим количеством объектов на карте, Мари...
ObjectManager, или как работать с большим количеством объектов на карте, Мари...
Ontico
 
как из трех стоек сделать две.
как из трех стоек сделать две.как из трех стоек сделать две.
как из трех стоек сделать две.
Serguei Gitinsky
 
Anton Tsitou "Cycle ORM and Graphs"
Anton Tsitou "Cycle ORM and Graphs"Anton Tsitou "Cycle ORM and Graphs"
Anton Tsitou "Cycle ORM and Graphs"
Fwdays
 
Что такое Postgresql (Максим Богук)
Что такое Postgresql (Максим Богук)Что такое Postgresql (Максим Богук)
Что такое Postgresql (Максим Богук)
Ontico
 
Долгожданный релиз pg_pathman 1.0 / Александр Коротков, Дмитрий Иванов (Post...
Долгожданный релиз pg_pathman 1.0 / Александр Коротков,  Дмитрий Иванов (Post...Долгожданный релиз pg_pathman 1.0 / Александр Коротков,  Дмитрий Иванов (Post...
Долгожданный релиз pg_pathman 1.0 / Александр Коротков, Дмитрий Иванов (Post...
Ontico
 
django-and-postgresql
django-and-postgresqldjango-and-postgresql
django-and-postgresql
Oleg Churkin
 
Максим Богук. Postgres-XC
Максим Богук. Postgres-XCМаксим Богук. Postgres-XC
Максим Богук. Postgres-XC
PostgreSQL-Consulting
 
"Многомерные индексы в РСУБД с открытым кодом" Бородин Андрей , Октоника, УрФУ
"Многомерные индексы в РСУБД с открытым кодом" Бородин Андрей , Октоника, УрФУ"Многомерные индексы в РСУБД с открытым кодом" Бородин Андрей , Октоника, УрФУ
"Многомерные индексы в РСУБД с открытым кодом" Бородин Андрей , Октоника, УрФУ
it-people
 
Where is the space, Postgres?
Where is the space, Postgres?Where is the space, Postgres?
Where is the space, Postgres?
Alexey Ermakov
 
СУБД 2013 Лекция №7 "Оптимизация запросов и индексирование"
СУБД 2013 Лекция №7 "Оптимизация запросов и индексирование"СУБД 2013 Лекция №7 "Оптимизация запросов и индексирование"
СУБД 2013 Лекция №7 "Оптимизация запросов и индексирование"
Technopark
 
Дмитрий Куликовский, Алексей Лавренюк - Построение кластеров, нагрузочное тес...
Дмитрий Куликовский, Алексей Лавренюк - Построение кластеров, нагрузочное тес...Дмитрий Куликовский, Алексей Лавренюк - Построение кластеров, нагрузочное тес...
Дмитрий Куликовский, Алексей Лавренюк - Построение кластеров, нагрузочное тес...
Yandex
 
Дмитрий Куликовский - Построение кластеров, нагрузочное тестирование, capacit...
Дмитрий Куликовский - Построение кластеров, нагрузочное тестирование, capacit...Дмитрий Куликовский - Построение кластеров, нагрузочное тестирование, capacit...
Дмитрий Куликовский - Построение кластеров, нагрузочное тестирование, capacit...
Yandex
 
Оптимизации скорости выполнения запросов
Оптимизации скорости выполнения запросовОптимизации скорости выполнения запросов
Оптимизации скорости выполнения запросов
Alex.Kolonitsky
 
Иван Фролков
Иван ФролковИван Фролков
Иван Фролков
CodeFest
 
PostgreSQL performance recipes
PostgreSQL performance recipesPostgreSQL performance recipes
PostgreSQL performance recipes
Alexey Ermakov
 
Производительность параметрического поиска на основе опенсорс-платформы
Производительность параметрического поиска на основе опенсорс-платформыПроизводительность параметрического поиска на основе опенсорс-платформы
Производительность параметрического поиска на основе опенсорс-платформы
Yandex
 
Современному хайлоду - современные решения: MySQL 8.0 и улучшения Percona
Современному хайлоду - современные решения: MySQL 8.0 и улучшения PerconaСовременному хайлоду - современные решения: MySQL 8.0 и улучшения Percona
Современному хайлоду - современные решения: MySQL 8.0 и улучшения Percona
Sveta Smirnova
 
Производительность запросов в PostgreSQL - шаг за шагом / Илья Космодемьянски...
Производительность запросов в PostgreSQL - шаг за шагом / Илья Космодемьянски...Производительность запросов в PostgreSQL - шаг за шагом / Илья Космодемьянски...
Производительность запросов в PostgreSQL - шаг за шагом / Илья Космодемьянски...
Ontico
 
2014.12.23 Александр Андреев, Parallels
2014.12.23 Александр Андреев, Parallels2014.12.23 Александр Андреев, Parallels
2014.12.23 Александр Андреев, Parallels
Nikolay Samokhvalov
 
#PostgreSQLRussia в банке Тинькофф, доклад №1
#PostgreSQLRussia в банке Тинькофф, доклад №1#PostgreSQLRussia в банке Тинькофф, доклад №1
#PostgreSQLRussia в банке Тинькофф, доклад №1
Nikolay Samokhvalov
 
ObjectManager, или как работать с большим количеством объектов на карте, Мари...
ObjectManager, или как работать с большим количеством объектов на карте, Мари...ObjectManager, или как работать с большим количеством объектов на карте, Мари...
ObjectManager, или как работать с большим количеством объектов на карте, Мари...
Ontico
 
как из трех стоек сделать две.
как из трех стоек сделать две.как из трех стоек сделать две.
как из трех стоек сделать две.
Serguei Gitinsky
 
Anton Tsitou "Cycle ORM and Graphs"
Anton Tsitou "Cycle ORM and Graphs"Anton Tsitou "Cycle ORM and Graphs"
Anton Tsitou "Cycle ORM and Graphs"
Fwdays
 
Что такое Postgresql (Максим Богук)
Что такое Postgresql (Максим Богук)Что такое Postgresql (Максим Богук)
Что такое Postgresql (Максим Богук)
Ontico
 
Долгожданный релиз pg_pathman 1.0 / Александр Коротков, Дмитрий Иванов (Post...
Долгожданный релиз pg_pathman 1.0 / Александр Коротков,  Дмитрий Иванов (Post...Долгожданный релиз pg_pathman 1.0 / Александр Коротков,  Дмитрий Иванов (Post...
Долгожданный релиз pg_pathman 1.0 / Александр Коротков, Дмитрий Иванов (Post...
Ontico
 
django-and-postgresql
django-and-postgresqldjango-and-postgresql
django-and-postgresql
Oleg Churkin
 
Максим Богук. Postgres-XC
Максим Богук. Postgres-XCМаксим Богук. Postgres-XC
Максим Богук. Postgres-XC
PostgreSQL-Consulting
 
"Многомерные индексы в РСУБД с открытым кодом" Бородин Андрей , Октоника, УрФУ
"Многомерные индексы в РСУБД с открытым кодом" Бородин Андрей , Октоника, УрФУ"Многомерные индексы в РСУБД с открытым кодом" Бородин Андрей , Октоника, УрФУ
"Многомерные индексы в РСУБД с открытым кодом" Бородин Андрей , Октоника, УрФУ
it-people
 
Where is the space, Postgres?
Where is the space, Postgres?Where is the space, Postgres?
Where is the space, Postgres?
Alexey Ermakov
 
СУБД 2013 Лекция №7 "Оптимизация запросов и индексирование"
СУБД 2013 Лекция №7 "Оптимизация запросов и индексирование"СУБД 2013 Лекция №7 "Оптимизация запросов и индексирование"
СУБД 2013 Лекция №7 "Оптимизация запросов и индексирование"
Technopark
 
Дмитрий Куликовский, Алексей Лавренюк - Построение кластеров, нагрузочное тес...
Дмитрий Куликовский, Алексей Лавренюк - Построение кластеров, нагрузочное тес...Дмитрий Куликовский, Алексей Лавренюк - Построение кластеров, нагрузочное тес...
Дмитрий Куликовский, Алексей Лавренюк - Построение кластеров, нагрузочное тес...
Yandex
 
Дмитрий Куликовский - Построение кластеров, нагрузочное тестирование, capacit...
Дмитрий Куликовский - Построение кластеров, нагрузочное тестирование, capacit...Дмитрий Куликовский - Построение кластеров, нагрузочное тестирование, capacit...
Дмитрий Куликовский - Построение кластеров, нагрузочное тестирование, capacit...
Yandex
 
Оптимизации скорости выполнения запросов
Оптимизации скорости выполнения запросовОптимизации скорости выполнения запросов
Оптимизации скорости выполнения запросов
Alex.Kolonitsky
 
Иван Фролков
Иван ФролковИван Фролков
Иван Фролков
CodeFest
 
Ad

Как читать и интерпретировать вывод команды EXPLAIN

  • 1. Как читать и интерпретировать вывод команды EXPLAIN ? Алексей Ермаков [email protected]
  • 2. 2 Как ускорить запрос? • “Очень долгий, очень пристальный и очень задумчивый взгляд” на запрос dataegret.com
  • 3. 2 Как ускорить запрос? • “Очень долгий, очень пристальный и очень задумчивый взгляд” на запрос • EXPLAIN – основной инструмент анализа запросов dataegret.com
  • 4. 3 Как ускорить запрос? • EXPLAIN без параметров показывает план запроса, но не выполняет1 запрос и не показывает статистику выполнения запроса 1 на самом деле может выполнить immutable/stable хранимки, но ведь они у вас ничего не меняют, правда? dataegret.com
  • 5. 4 План запроса #1 explain select * from posts join categories on posts.category_id = categories.id where posts.rating < 15; -------------------------------------------------------------------------------------------- Nested Loop (cost=0.43..16.47 rows=1 width=32) -> Index Scan using posts_rating_idx on posts (cost=0.29..8.30 rows=1 width=28) Index Cond: (rating < 15) -> Index Only Scan using categories_pkey on categories (cost=0.14..8.16 rows=1 width=4) Index Cond: (id = posts.category_id) dataegret.com
  • 6. 5 План запроса #2 explain select * from posts join categories on posts.category_id = categories.id where posts.rating < 35; Hash Join (cost=27.84..117.61 rows=601 width=32) Hash Cond: (posts.category_id = categories.id) -> Bitmap Heap Scan on posts (cost=12.94..94.46 rows=601 width=28) Recheck Cond: (rating < 35) -> Bitmap Index Scan on posts_rating_idx (cost=0.00..12.79 rows=601 width=0) Index Cond: (rating < 35) -> Hash (cost=13.64..13.64 rows=100 width=4) -> Index Only Scan using categories_pkey on categories (cost=0.14..13.64 rows=100 width=4) dataegret.com
  • 7. 6 План запроса #2 explain select * from posts join categories on posts.category_id = categories.id where posts.rating < 35; Hash Join (cost=27.84..117.61 rows=601 width=32) Hash Cond: (posts.category_id = categories.id) -> Bitmap Heap Scan on posts (cost=12.94..94.46 rows=601 width=28) Recheck Cond: (rating < 35) -> Bitmap Index Scan on posts_rating_idx (cost=0.00..12.79 rows=601 width=0) Index Cond: (rating < 35) -> Hash (cost=13.64..13.64 rows=100 width=4) -> Index Only Scan using categories_pkey on categories (cost=0.14..13.64 rows=100 width=4) dataegret.com
  • 8. 7 План запроса • Это дерево • Вышестоящие узлы запрашивают данные у нижестоящих • План запроса не меняется по мере выполнения, даже если оказалось что ожидания расходятся с реальностью на 6 порядков • База не хранит историю неудачных или медленных планов запросов. Так что, если если запрос получает неверный план - он будет получать его воспроизводимо2 2 если не меняются настройки базы, распределение данных, размеры таблиц и индексов, не используется geqo dataegret.com
  • 9. 8 Мы любим ORM select website0_.websiteId as websiteI1_304_0_, website0_.websiteTitleType as websiteT2_304_0_, website0_.websiteUrl as websiteU3_304_0_, website0_.websiteLocalArticlePath as websiteL4_304_0_, ... (еще 14кб текста) from Website website0_ where website0_.websiteId=$1 dataegret.com
  • 10. 8 Мы любим ORM select website0_.websiteId as websiteI1_304_0_, website0_.websiteTitleType as websiteT2_304_0_, website0_.websiteUrl as websiteU3_304_0_, website0_.websiteLocalArticlePath as websiteL4_304_0_, ... (еще 14кб текста) from Website website0_ where website0_.websiteId=$1 • Иногда план запроса читать проще, нежели сам запрос dataegret.com
  • 11. 9 Виды элементарных операций Методы извлечения данных • seq scan – последовательное чтение таблицы • index scan – random io (чтение индекса + чтение таблицы) • index only scan – random io (чтение только3 индекса) • bitmap index scan + bitmap heap scan – компромисс между seq scan/index scan, возможность использования нескольких индексов в OR/AND условиях • CTE scan – чтение из CTE (блок WITH) • function scan – чтение строк из функции 3 Возможно и чтение таблицы, зависит от состояния visibility map dataegret.com
  • 12. 10 Виды элементарных операций Методы соединения данных • nested loop – оптимален для небольших наборов данных • hash join – оптимален для больших наборов данных • merge join – оптимален для больших наборов данных, в случае, если они отсортированы dataegret.com
  • 13. 11 Nested loop for each outer row: for each inner row: if join condition is true: output combined row dataegret.com
  • 14. 12 Виды элементарных операций Методы обработки данных • sort – сортирует данные в памяти или на диске, есть более быстрый topN вариант • limit – ограничивает выборку • aggregate – используется в агрегирующих функциях • hash aggregate – используется в группировках • unique – отбрасывает дубликаты из отсортированных выборок • gather – используется для объединения данных с различных воркеров при одновременном выполнении dataegret.com
  • 15. 13 Характеристики каждой операции explain select * from pg_database; QUERY PLAN ----------------------------------------------------------- Seq Scan on pg_database (cost=0.00..0.16 rows=6 width=271) (1 row) Seq Scan тип операции on pg_database объект, с которым проводится операция cost=0.00..0.16 стоимость операции (startup..total cost) rows=6 ожидаемое число строк width=271 средняя ширина строки в байтах dataegret.com
  • 16. 14 startup..total cost explain select * from posts order by id limit 5; QUERY PLAN -------------------------------------------------------------------------------------- Limit (cost=0.29..0.46 rows=5 width=28) -> Index Scan using posts_pkey on posts (cost=0.29..347.29 rows=10000 width=28) (2 rows) dataegret.com
  • 17. 14 startup..total cost explain select * from posts order by id limit 5; QUERY PLAN -------------------------------------------------------------------------------------- Limit (cost=0.29..0.46 rows=5 width=28) -> Index Scan using posts_pkey on posts (cost=0.29..347.29 rows=10000 width=28) (2 rows) • 0.29 + (347.29 - 0.29)*5/10000 = 0.4635 dataegret.com
  • 18. 15 rows*width • rows*width у корневого узла дает примерный порядок объема результата в байтах • если запрос часто вызывается и при этом запрашивает большое число данных – то легко можно забить сеть между базой и приложением • передача данных по сети может занимать большее время, чем время выполнения запроса • не всегда стоит запрашивать все поля, что есть, особенно если они широкие и никак не используются dataegret.com
  • 19. 16 Опции команды EXPLAIN EXPLAIN (ANALYZE,VERBOSE,COSTS,BUFFERS,TIMING4) select * from t1; QUERY PLAN ------------------------------------------------------------------- Seq Scan on public.t1 (cost=0.00..104424.80 rows=10000000 width=8) (actual time=0.218..2316.688 rows=10000000 loops=1) Output: f1, f2 Buffers: shared read=44248 I/O Timings: read=322.7145 Planning time: 0.024 ms Execution time: 3852.588 ms 4 COSTS и TIMING опции включены по-умолчанию 5 I/O Timings отображается, когда track_io_timing включен dataegret.com
  • 20. 17 План запроса #3 Gather (cost=100.57..1338637.12 rows=2346550 width=1526) (actual time=4489.134..4855.942 rows=79061 loops=1) Workers Planned: 4 Workers Launched: 4 -> Nested Loop (cost=0.57..1103882.12 rows=2346550 width=1526) (actual time=4482.217..4810.097 rows=15812 loops=5) -> Parallel Seq Scan on deferred_report r (cost=0.00..97294.45 rows=1345386 width=8) (actual time=0.026..702.618 rows=1098890 loops=5) Filter: (("timestamp" < ’2017-02-26 21:00:00’::timestamp without time zone) AND (proxy_id = 37) AND Rows Removed by Filter: 2709566 -> Index Scan using transactions_pkey on transactions t (cost=0.57..0.74 rows=1 width=1526) (actual time=0.004..0.004 rows=0 loops=5494451) Index Cond: (id = r.tx_id) Filter: (status = ’ok’::status) Rows Removed by Filter: 1 Planning time: 0.736 ms Execution time: 4861.460 ms dataegret.com
  • 21. 18 Filter -> Parallel Seq Scan on deferred_report r (cost=0.00..97294.45 rows=1345386 width=8) (actual time=0.026..702.618 rows=1098890 loops=5) Filter: (("timestamp" < ’2017-02-26 21:00:00’::timestamp without time zone) AND (proxy_id = 37 Rows Removed by Filter: 2709566 • Оправдано ли использование seq scan в данном случае? dataegret.com
  • 22. 18 Filter -> Parallel Seq Scan on deferred_report r (cost=0.00..97294.45 rows=1345386 width=8) (actual time=0.026..702.618 rows=1098890 loops=5) Filter: (("timestamp" < ’2017-02-26 21:00:00’::timestamp without time zone) AND (proxy_id = 37 Rows Removed by Filter: 2709566 • Оправдано ли использование seq scan в данном случае? • worker извлек 1098890 1098890+2709566 ≈ 28% строк dataegret.com
  • 23. 18 Filter -> Parallel Seq Scan on deferred_report r (cost=0.00..97294.45 rows=1345386 width=8) (actual time=0.026..702.618 rows=1098890 loops=5) Filter: (("timestamp" < ’2017-02-26 21:00:00’::timestamp without time zone) AND (proxy_id = 37 Rows Removed by Filter: 2709566 • Оправдано ли использование seq scan в данном случае? • worker извлек 1098890 1098890+2709566 ≈ 28% строк • вероятно да, bitmap index scan может и был бы быстрее, но в 9.6 его еще нельзя распараллелить по воркерам dataegret.com
  • 24. 19 Filter -> Index Scan using transactions_pkey on transactions t (cost=0.57..0.74 rows=1 width=1526) (actual time=0.004..0.004 rows=0 loops=5494451) Index Cond: (id = r.tx_id) Filter: (status = ’ok’::status) Rows Removed by Filter: 1 • Используется ли оптимальный индекс в данном случае? dataegret.com
  • 25. 19 Filter -> Index Scan using transactions_pkey on transactions t (cost=0.57..0.74 rows=1 width=1526) (actual time=0.004..0.004 rows=0 loops=5494451) Index Cond: (id = r.tx_id) Filter: (status = ’ok’::status) Rows Removed by Filter: 1 • Используется ли оптимальный индекс в данном случае? • Большинство строк отбрасываются по условию status = ‘ok’::status dataegret.com
  • 26. 19 Filter -> Index Scan using transactions_pkey on transactions t (cost=0.57..0.74 rows=1 width=1526) (actual time=0.004..0.004 rows=0 loops=5494451) Index Cond: (id = r.tx_id) Filter: (status = ’ok’::status) Rows Removed by Filter: 1 • Используется ли оптимальный индекс в данном случае? • Большинство строк отбрасываются по условию status = ‘ok’::status • Вероятно нет, может быть тут более уместен индекс по (id, status) или частичный индекс по id where status = ‘ok’ dataegret.com
  • 27. 20 Инструменты, позволяющие визуализировать план запроса • https://explain.depesz.com/ • http://tatiyants.com/pev/ dataegret.com
  • 29. 22 Filter WHERE dispatch_time + INTERVAL ’1 hour’ > now() WHERE (DATE(o.created) >= ’2017-03-23’ AND DATE(o.created) <= ’2017-04-21’) • конструкции такого вида не могут использовать обычный индекс по соответствующему полю • необходимо привести их к виду индексируемое_поле индексируемый_оператор выражение dataegret.com
  • 30. 23 Опция buffers -> Index Only Scan using user_balance_id_type on user_balance ub (cost=0.00..0.56 rows=1 width=8) (actual time=0.031..1.453 rows=1 loops=29721) Index Cond: ((id = o.user_balance_id) AND (type = 1)) Heap Fetches: 7192482 Buffers: shared hit=7608576 • Что-то пошло не так... dataegret.com
  • 31. 23 Опция buffers -> Index Only Scan using user_balance_id_type on user_balance ub (cost=0.00..0.56 rows=1 width=8) (actual time=0.031..1.453 rows=1 loops=29721) Index Cond: ((id = o.user_balance_id) AND (type = 1)) Heap Fetches: 7192482 Buffers: shared hit=7608576 • Что-то пошло не так... • Читаем 7608576 29721 = 256 страниц, чтобы извлечь одну строку dataegret.com
  • 32. 23 Опция buffers -> Index Only Scan using user_balance_id_type on user_balance ub (cost=0.00..0.56 rows=1 width=8) (actual time=0.031..1.453 rows=1 loops=29721) Index Cond: ((id = o.user_balance_id) AND (type = 1)) Heap Fetches: 7192482 Buffers: shared hit=7608576 • Что-то пошло не так... • Читаем 7608576 29721 = 256 страниц, чтобы извлечь одну строку • Вероятно, очень большой bloat в таблице/индексе dataegret.com
  • 33. 23 Опция buffers -> Index Only Scan using user_balance_id_type on user_balance ub (cost=0.00..0.56 rows=1 width=8) (actual time=0.031..1.453 rows=1 loops=29721) Index Cond: ((id = o.user_balance_id) AND (type = 1)) Heap Fetches: 7192482 Buffers: shared hit=7608576 • Что-то пошло не так... • Читаем 7608576 29721 = 256 страниц, чтобы извлечь одну строку • Вероятно, очень большой bloat в таблице/индексе • Длинные транзакции? Не справляется автовакуум? dataegret.com
  • 34. 23 Опция buffers -> Index Only Scan using user_balance_id_type on user_balance ub (cost=0.00..0.56 rows=1 width=8) (actual time=0.031..1.453 rows=1 loops=29721) Index Cond: ((id = o.user_balance_id) AND (type = 1)) Heap Fetches: 7192482 Buffers: shared hit=7608576 • Что-то пошло не так... • Читаем 7608576 29721 = 256 страниц, чтобы извлечь одну строку • Вероятно, очень большой bloat в таблице/индексе • Длинные транзакции? Не справляется автовакуум? • Отстающая реплика с hot_standby_feedback = on ! dataegret.com
  • 35. 24 I/O timings -> Bitmap Heap Scan on delivery fi (cost=872967.27..4196621.37 rows=32425255 width=36) (actual time=30842.138..37818.082 rows=6674571 loops=1) Recheck Cond: ((datetime_start >= ’2017-05-11 00:00:00’::timestamp without time zone) AND (datetime_end <= ’2 Filter: ((NOT is_deleted) OR (qty > 0)) Rows Removed by Filter: 60545 Heap Blocks: exact=667323 Buffers: shared hit=47584 read=827816 dirtied=1 written=2292 I/O Timings: read=30152.376 write=42.134 -> Bitmap Index Scan on delivery_datetime_start_datetime_end_idx (cost=0.00..864860.96 rows=32514577 width= Index Cond: ((datetime_start >= ’2017-05-11 00:00:00’::timestamp without time zone) AND (datetime_end < Buffers: shared hit=80 read=207997 I/O Timings: read=27315.687 dataegret.com
  • 36. 24 I/O timings -> Bitmap Heap Scan on delivery fi (cost=872967.27..4196621.37 rows=32425255 width=36) (actual time=30842.138..37818.082 rows=6674571 loops=1) Recheck Cond: ((datetime_start >= ’2017-05-11 00:00:00’::timestamp without time zone) AND (datetime_end <= ’2 Filter: ((NOT is_deleted) OR (qty > 0)) Rows Removed by Filter: 60545 Heap Blocks: exact=667323 Buffers: shared hit=47584 read=827816 dirtied=1 written=2292 I/O Timings: read=30152.376 write=42.134 -> Bitmap Index Scan on delivery_datetime_start_datetime_end_idx (cost=0.00..864860.96 rows=32514577 width= Index Cond: ((datetime_start >= ’2017-05-11 00:00:00’::timestamp without time zone) AND (datetime_end < Buffers: shared hit=80 read=207997 I/O Timings: read=27315.687 • 30c из 37с ушло на чтение с диска dataegret.com
  • 37. 24 I/O timings -> Bitmap Heap Scan on delivery fi (cost=872967.27..4196621.37 rows=32425255 width=36) (actual time=30842.138..37818.082 rows=6674571 loops=1) Recheck Cond: ((datetime_start >= ’2017-05-11 00:00:00’::timestamp without time zone) AND (datetime_end <= ’2 Filter: ((NOT is_deleted) OR (qty > 0)) Rows Removed by Filter: 60545 Heap Blocks: exact=667323 Buffers: shared hit=47584 read=827816 dirtied=1 written=2292 I/O Timings: read=30152.376 write=42.134 -> Bitmap Index Scan on delivery_datetime_start_datetime_end_idx (cost=0.00..864860.96 rows=32514577 width= Index Cond: ((datetime_start >= ’2017-05-11 00:00:00’::timestamp without time zone) AND (datetime_end < Buffers: shared hit=80 read=207997 I/O Timings: read=27315.687 • 30c из 37с ушло на чтение с диска • Отфильтровалось относительно мало строк dataegret.com
  • 38. 25 I/O timings • 30c из 37с ушло на чтение с диска • Отфильтровалось относительно мало строк • Происходит ли значительная фильтрация строк на более высоком уровне ? dataegret.com
  • 39. 25 I/O timings • 30c из 37с ушло на чтение с диска • Отфильтровалось относительно мало строк • Происходит ли значительная фильтрация строк на более высоком уровне ? • Если да - возможно нужно соединять таблицы каким-то другим способом, чтобы приходилось меньше читать данных dataegret.com
  • 40. 26 Sort explain analyze select distinct f1 from test_ndistinct ; QUERY PLAN ------------------------------------------------------------------------------------------- Unique (cost=1571431.43..1621431.49 rows=100000 width=4) (actual time=4791.872..7551.150 rows=90020 loops=1) -> Sort (cost=1571431.43..1596431.46 rows=10000012 width=4) (actual time=4791.870..6893.413 rows=10000000 loops=1) Sort Key: f1 Sort Method: external merge Disk: 101648kB -> Seq Scan on test_ndistinct (cost=0.00..135314.12 rows=10000012 width=4) (actual time=0.041..938.093 rows=10000000 loops=1) Planning time: 0.099 ms Execution time: 7714.701 ms dataegret.com
  • 41. 27 HashAggregate set work_mem = ’8MB’; SET explain analyze select distinct f1 from test_ndistinct ; QUERY PLAN ------------------------------------------------------------------------------------------- HashAggregate (cost=160314.15..161314.15 rows=100000 width=4) (actual time=2371.902..2391.415 rows=90020 loops=1) Group Key: f1 -> Seq Scan on test_ndistinct (cost=0.00..135314.12 rows=10000012 width=4) (actual time=0.093..871.619 rows=10000000 loops=1) Planning time: 0.048 ms Execution time: 2396.186 ms dataegret.com
  • 42. 28 Ошибки планировщика explain analyze select account_id from account where last_update <= ’2017-10-23’ and discarded = false limit 200; ------------------------------------------------------------------------------------------- Limit (cost=0.08..26.15 rows=200 width=4) (actual time=0.584..18.087 rows=2 loops=1) -> Index Scan using account_last_update_special1_key on account (cost=0.08..18878.72 rows=144854 width=4) (actual time=0.582..18.084 rows=2 loops=1) Index Cond: (last_update <= ’2017-10-23’::date) dataegret.com
  • 43. 28 Ошибки планировщика explain analyze select account_id from account where last_update <= ’2017-10-23’ and discarded = false limit 200; ------------------------------------------------------------------------------------------- Limit (cost=0.08..26.15 rows=200 width=4) (actual time=0.584..18.087 rows=2 loops=1) -> Index Scan using account_last_update_special1_key on account (cost=0.08..18878.72 rows=144854 width=4) (actual time=0.582..18.084 rows=2 loops=1) Index Cond: (last_update <= ’2017-10-23’::date) • Ошибки в оценке числа строк в несколько порядков могут привести в итоге к сильно неоптимальному плану dataegret.com
  • 44. 28 Ошибки планировщика explain analyze select account_id from account where last_update <= ’2017-10-23’ and discarded = false limit 200; ------------------------------------------------------------------------------------------- Limit (cost=0.08..26.15 rows=200 width=4) (actual time=0.584..18.087 rows=2 loops=1) -> Index Scan using account_last_update_special1_key on account (cost=0.08..18878.72 rows=144854 width=4) (actual time=0.582..18.084 rows=2 loops=1) Index Cond: (last_update <= ’2017-10-23’::date) • Ошибки в оценке числа строк в несколько порядков могут привести в итоге к сильно неоптимальному плану • Можно посмотреть на собираемую autoanalyze статистику чтобы понять причину dataegret.com
  • 45. 28 Ошибки планировщика explain analyze select account_id from account where last_update <= ’2017-10-23’ and discarded = false limit 200; ------------------------------------------------------------------------------------------- Limit (cost=0.08..26.15 rows=200 width=4) (actual time=0.584..18.087 rows=2 loops=1) -> Index Scan using account_last_update_special1_key on account (cost=0.08..18878.72 rows=144854 width=4) (actual time=0.582..18.084 rows=2 loops=1) Index Cond: (last_update <= ’2017-10-23’::date) • Ошибки в оценке числа строк в несколько порядков могут привести в итоге к сильно неоптимальному плану • Можно посмотреть на собираемую autoanalyze статистику чтобы понять причину • Можно посмотреть переписать запрос каким-то иным способом, заставив планировщик соединять таблицы нужным образом и использовать нужные индексы dataegret.com
  • 46. 29 Что можно делать ? • Если запрос выполняется за разумное время, смотрим его explain analyze • Пытаемся найти узлы, на которые больше всего уходит времени • Нет ли у нас где-то явно пропущенных индесов, в местах где много строк фильтруется ? • Нет ли проблем с bloat ? Смотрим explain (analyze, buffers) • Нет ли проблем с дисками (track_io_timing)? • Хватает ли work_mem ? • Не ошибается ли планировщик в оценке числа строк на порядки? • Не сильно ли велико время планирования? dataegret.com
  • 47. 30 Что можно почитать? • depesz: Explaining the unexplainable • PostgreSQL Manual 14.1. Using EXPLAIN • Bruce Momjian – Explaining the Postgres Query Optimizer • www.slideshare.net/alexius2/ dataegret.com