SlideShare a Scribd company logo
НАРОДНЫЕ СРЕДСТВА
ОПТИМИЗАЦИИ ЗАПРОСОВ
В PostgreSQL
Писарев Николай
МЫ РАССМОТРИМ
● Основные моменты работы с РБД
● Что такое EXPLAIN и как с ним работать
● Индексы и их особенности
● Лайфхаки и хитрости
Основы работы с РБД
● Запись в таблице должна соответствовать объекту
● Правильно выбрать уровень нормализации
● Уделить немало времени на проектирование
● Структура БД должна быть гибкой
● Простота в поддержке жизненого цикла БД
● Правильно расставлять индексы,
не создавать бардак
● Строить запросы не выбирая все подряд (*),
только необходимое
● Приступать к оптимизации, когда действительно это
требуется
ORM vs SQl
●
ORM — удобно?
●
ORM — быстро?
Total runtime: (~)117.092 ms*
JAVA
@ManyToOne
@JoinColumn(name = "CATEGORY_ID")
public Category getCategory() {
return category;
}
PHP
$customers = Customer::find()
->where(['status' => TRUE])
->orderBy('id')
->limit(100, 10000);
* Подробный пример мы разберем ниже
☑
ORM vs SQL
●
SQL — удобно?
●
SQL — быстро?
Total runtime: (~)11.926 ms
SELECT * FROM (
SELECT * FROM posts
WHERE author = 'nick'
ORDER BY posts.publish
DESC LIMIT 10
) as posts
JOIN post_category
ON posts.category_id = category.id
ORDER BY posts.publish DESC LIMIT 10;
☒
* Подробный пример мы разберем ниже
Еще об ORM
● Позволяет представить запись в БД в виде объекта
● Удобнее понимать и легче писать чем SQL
● Поддержка и изменения не вызывают трудностей
● Сложные запросы иногда невозможно написать
● Трудно оптимизировать
● Иногда строится запрос, который не использует
индексы
А что с SQL?
● Сложные выборки, запросы
● Возможности оптимизации
● Функции и различные возможности SQL
● HighLoad — однозначно SQL (узкое место)
● Запрос с использованием индексов не всегда быстрый
● Замусоривает код (JAVA и многострочные литералы)
● Нет проверок на этапе компиляции
Народные средства оптимизации PostgreSQL
ANALYZE
ANALYZE считывается определённое количество
строк таблицы в базе данных, выбранных случайным
образом, и сохраняет результаты в системном
каталоге pg_statistic.
Затем планировщик запросов будет использовать эту
статистику для выбора эффективных планов
запросов.
=> ANALYZE VERBOSE;
WARNING: skipping "pg_statistic" --- only superuser or database owner can analyze it
WARNING: skipping "pg_type" --- only superuser or database owner can analyze it
INFO: analyzing "public.test"
INFO: "test": scanned 16669 of 16669 pages, containing 2000200 live rows
and 0 dead rows; 30000 rows in sample, 2000200 estimated total rows
VACUUM
● Очистка места (помечание), занимаемое
«мертвыми» кортежами
● По-умолчанию очищает все таблицы доступные
пользователю
● Без опции FULL может работать параллельно,
т. к. не требует исключительной блокировки
● With FULL работает медленно, требует блокировки
и возвращает освобожденное место операционной
системе.
● Autovacuum — демон очистки (VACUUM+ANALYZE)
● ! table bloating
Народные средства оптимизации PostgreSQL
Таблица TEST
=> CREATE TABLE test (id integer, text text);
=> INSERT INTO test
SELECT id, md5(random()::text)
FROM generate_series(1, 1000000) AS id;
=> d test
Table "public.test"
Column | Type | Modifiers
----------------+------------+-----------
column_1 | integer |
column_2 | text |
=> EXPLAIN SELECT * FROM test;
● Cost — у.е. для оценки затратности
операции.
1ое значение — затраты доступа к 1й строке
2ое значение — затраты для доступа ко
всем строкам.
● Rows — (~) количество строк при вызове
Seq Scan к этой таблице
● With — длина строки в байтах
Всё, что мы видели выше в выводе команды EXPLAIN —
ожидания планировщика.
=> EXPLAIN (ANALYZE) SELECT * FROM test;
● Actual time — реальное время в миллисекундах для
1ой и всех строк
● Rows — реальное количество строк полученных
● Loops — количество выполнений данной операции
● Plannig time — время выполнения EXPLAIN
● Execution time — общее время выполнения
● Heap Fetches — число реальных обращений к таблице
!!! DANGER !!!
EXPLAIN (ANALYZE) исполняет команды
=> EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM test;
● Buffers: shared read — количество блоков
считанных с диска;
● Buffers: shared hit — количество блоков,
считанных из кэша PostgreSQL.
CACHE
WHERE
=> EXPLAIN (ANALYZE)
SELECT * FROM test WHERE id
between 1500 and 1550;
● Индексов нет поэтому Seq Scan
● Каждая строка сравнивается с:
Filter: ((id >= 1500) AND (id <= 1550))
● Cost увеличилось
● Rows уменьшилось до ождаемого количества
Execution time: 222.979 ms
Index Scan
=> CREATE INDEX ON test(id);
=> EXPLAIN (ANALYZE)
SELECT * FROM test WHERE id
between 1500 and 1550;
● Теперь Index Scan using test_id_idx on test;
● Index Cond: ((id >= 1500) AND (id <= 1550))
Execution time: 0.127 ms
Seq Scan
A C D
1) С < 10
2) С < 10
3) С < 10
С < 10
Index Scan
A C D
С < 10
C
ИНДЕКСЫ
● Но что будет, если поменять условие
=> EXPLAIN (ANALYZE)
SELECT * FROM test WHERE id > 1550;
● Теперь Seq Scan on test
● Пришлось прочитать все строки, кроме
первых 1500..
● Время увеличилось, что не удивительно
Execution time: 678.852 ms
ИНДЕКСЫ
● А если выключить Seq Scan
=> SET enable_seqscan TO off;
● Теперь Index Scan using test_id_idx on test
● Но время запроса стало еще больше
Execution time: 749.952 ms
● И стоимость cost также увеличилась
Планировщик не дурак =)
Про индексы
● Индекс это дополнительная структура
данных (не SQL)
● Индексы требуют затраты на поддержание
● Замедляют обновление
● Замедляют репликацию
● Малая селективность — неэфективно
Индексы не панацея!
Index Only Scan
● EXPLAIN (ANALYZE)
SELECT id FROM test WHERE id < 450;
● Index Only Scan using test_id_idx on test
● Выбираем только поле id,
чтобы включить IOS
● Скорость очень большая
Execution time: 0.659 ms
Index Only Scan
A C D
С < 10
C
Visability
MAP
ИНДЕКСЫ ПО ТЕКСТУ
EXPLAIN (ANALYZE)
SELECT * FROM test WHERE text LIKE 'ab%';
● Seq Scan
After CREATE INDEX ON test(text);
● Также Seq Scan (211 ms), потому что UTF-8!
● Нужно использовать класс оператора
text_pattern_ops
CREATE INDEX ON test(text text_pattern_ops);
ИНДЕКСЫ ПО ТЕКСТУ
EXPLAIN (ANALYZE)
SELECT * FROM test WHERE text LIKE 'ab%';
● Bitmap Index Scan on test_text_idx1
● Сравниваем
Index Cond: (
(text ~>=~ 'ab'::text) AND (text ~<~ 'ac'::text)
)
● Далее Bitmap Heap Scan on test
проверяет существуют ли записи на самом
деле
Bitmap Index Scan
A C D
С < 10
C
1
1 1
1
1 0
Создание индексов
● Требуют блокировки при создании
● CONCURRENTLY создает в фоне, но долго (требует
2 прохода)
● Можно и нужно мониторить неиспользуемые
индексы (расходуются рессурсы и время)
● Можно находить дубликаты индексов
● Можно строить индексы по функциям, но
необходимо точное её повторение при запросе
● ! index bloating
Когда создавать индексы?
CREATE TABLE test5 (id integer PRIMARY KEY, v float8);
ACREATE INDEX test5_v_idx ON test5(v);
INSERT INTO test5
(SELECT id, random() FROM generate_series(1,1000000) id);
CREATE TABLE test5 (id integer PRIMARY KEY, v float8);
BINSERT INTO test5
(SELECT id, random() FROM generate_series(1,1000000) id);
CREATE INDEX test5_v_idx ON test5(v);
Когда создавать индексы?
CREATE TABLE a (id integer PRIMARY KEY, v float8); 1,991 ms
CREATE INDEX a_v_idx ON a(v); 0,506 ms
INSERT INTO a
(SELECT id, random() FROM
generate_series(1,1000000) id);
4909,127 ms
A = Total: 4911 ms
CREATE TABLE b (id integer PRIMARY KEY, v float8); 1,990 ms
INSERT INTO b
(SELECT id, random() FROM
generate_series(1,1000000) id);
938,852 ms
CREATE INDEX b_v_idx ON b(v); 1195,492 ms
B = Total: 2136 ms
Lifehack Show
Мониторим неиспользуемые индексы
SELECT schemaname || '.' || relname AS table, indexrelname AS index,
pg_size_pretty(pg_relation_size(i.indexrelid)) AS index_size, idx_scan as
index_scans
FROM pg_stat_user_indexes ui
JOIN pg_index i ON ui.indexrelid = i.indexrelid
WHERE NOT indisunique AND idx_scan < 50 AND pg_relation_size(relid) > 5 * 819
ORDER BY pg_relation_size(i.indexrelid) / nullif(idx_scan, 0) DESC NULLS FIRST,
pg_relation_size(i.indexrelid) DESC;
table | index | index_size | index_scans
---------------------+---------------------------+---------------+-------------
public.test | test_text_idx | 56 MB | 0
public.test5 | test5_v_idx | 28 MB | 0
public.test6 | test6_v_idx | 21 MB | 0
public.test | test_text_idx1 | 56 MB | 3
public.test | test_id_idx | 21 MB | 36
Ищем дубликаты индексов
lk=> SELECT pg_size_pretty(SUM(pg_relation_size(idx))::BIGINT) AS SIZE,
(array_agg(idx))[1] AS idx1, (array_agg(idx))[2] AS idx2,
(array_agg(idx))[3] AS idx3, (array_agg(idx))[4] AS idx4
FROM ( SELECT indexrelid::regclass AS idx,
(indrelid::text ||E'n'|| indclass::text ||E'n'|| indkey::text ||E'n'||
COALESCE(indexprs::text,'')||E'n' || COALESCE(indpred::text,'')) AS KEY
FROM pg_index) sub
GROUP BY KEY HAVING COUNT(*)>1
ORDER BY SUM(pg_relation_size(idx)) DESC;
size | idx1 | idx2 | idx3 | idx4
---------+-----------------------+----------------------+------+------
32 kB | blocks_id_idx | blocks_id_idx1 | |
32 kB | blocks_type_idx1 | blocks_type_idx | |
32 kB | primary_key | ids | |
Оптимизация OFFSET
Ситуация:
SELECT …
FROM table1
JOIN table2 using (table2id)
JOIN table3 using (table3id)
WHERE
набор условий ТОЛЬКО по table1
Order by (набор полей table1) LIMIT ... OFFSET ...
Важно: сработает если соблюдается условие, что
логика выборки и offset реализуется в table1, а также
что присоединенные данные из таблиц table2 и table3
на запрос не влияют.
EXPLAIN ANALYZE
SELECT * FROM test
JOIN vals ON vals.test_id = test.id
WHERE val between 150 AND 9500
LIMIT 5 OFFSET 5000;
Execution time: 283.471 ms
EXPLAIN ANALYZE
SELECT * FROM ( SELECT * FROM test
WHERE val between 150 AND 9500
LIMIT 5 OFFSET 5000) AS test
JOIN vals ON vals.test_id = test.id;
Execution time: 4.079 ms
Оптимизация COUNT(*)
=> EXPLAIN ANALYZE SELECT count(*) FROM
cache_customers_rates;
Execution time: 18632.959 ms
=> EXPLAIN ANALYZE SELECT
(reltuples)::numeric FROM pg_class r WHERE
relkind='r' AND relname='cache_customers_rates';
Execution time: 0.079 ms
Получение строк в виде ROW
=> SELECT * FROM tasks;
id | type | status | params | out. | exc.
----+---------------+---------+------------------+------+-----
1 | refill_cache | new | {"threads":-1} | |
2 | refill_cache | new | {"threads":-1} | |
=> SELECT tasks FROM tasks;
tasks
--------------------------------------------------------------------
( 1, refill_cache, new, "{""threads"":-1}", "", "" )
( 2, refill_cache, new, "{""threads"":-1}", "", "" )
Получение строк в виде JSON
=> SELECT row_to_json(tasks) FROM tasks;
row_to_json
-----------------------------------------------------------------
{
"id":1,
"type":"refill_cache",
"status":"new",
"params":"{"threads":-1}",
"output":"",
"exception":""
}
Выбранные поля в виде JSON
=> SELECT row_to_json( t ) FROM ( SELECT id,
type FROM tasks) AS t;
row_to_json
------------------------------------------------
{ "id":1, "type":"refill_cache" }
{ "id":1, "type":"refill_cache" }
Данные в JSON ARRAY
=> SELECT
array_to_json( array_agg( row_to_json(tasks) ) )
FROM tasks;
[
{ "id":1, "type":"refill_cache", "status":"new",
"params":"{"threads":-1}", "output":"",
"exception":"" },
{ "id":2, "type":"refill_cache", "status":"new",
"params":"{"threads":-1}", "output":"",
"exception":"" }
]
OUTPUT to FILE
=> o tasks.json
=> SELECT array_to_json( array_agg( row_to_json(tasks) ) )
FROM tasks;
=> o
# cat tasks.json
array_to_json
-------------------------------------------------------------------------------
[{"id":1,"type":"refill_cache","status":"new","params":"{"thread
s":-1}","output":"","exception":""},
{"id":2,"type":"refill_cache","status":"new","params":"{"thread
s":-1}","output":"","exception":""}]
Народные средства оптимизации PostgreSQL
Полезная информация
●
https://wiki.postgresql.org/wiki/Index_Maintenance
Монторинг индексов
●
http://www.highload.ru/2013/abstracts/1170.html
Индексы
●
https://wiki.postgresql.org/wiki/Show_database_bloat
Table and index bloat
●
http://www.dalibo.org/_media/understanding_explain.pdf
EXPLAIN

More Related Content

What's hot (19)

WinDbg в руках .NET разработчика
WinDbg в руках .NET разработчикаWinDbg в руках .NET разработчика
WinDbg в руках .NET разработчика
Mikhail Shcherbakov
 
Where is the space, Postgres?
Where is the space, Postgres?Where is the space, Postgres?
Where is the space, Postgres?
Alexey Ermakov
 
Cache2012 administrationbasics
Cache2012 administrationbasicsCache2012 administrationbasics
Cache2012 administrationbasics
Denis Pavlov
 
Web весна 2013 лекция 9
Web весна 2013 лекция 9Web весна 2013 лекция 9
Web весна 2013 лекция 9
Technopark
 
Formal verification of C code
Formal verification of C codeFormal verification of C code
Formal verification of C code
Denis Efremov
 
Долгожданный релиз pg_pathman 1.0 / Александр Коротков, Дмитрий Иванов (Post...
Долгожданный релиз pg_pathman 1.0 / Александр Коротков,  Дмитрий Иванов (Post...Долгожданный релиз pg_pathman 1.0 / Александр Коротков,  Дмитрий Иванов (Post...
Долгожданный релиз pg_pathman 1.0 / Александр Коротков, Дмитрий Иванов (Post...
Ontico
 
MariaDB 10.4 - что нового
MariaDB 10.4 - что новогоMariaDB 10.4 - что нового
MariaDB 10.4 - что нового
Sergey Petrunya
 
[JAM 1.1] Clean Code (Paul Malikov)
[JAM 1.1] Clean Code (Paul Malikov)[JAM 1.1] Clean Code (Paul Malikov)
[JAM 1.1] Clean Code (Paul Malikov)
Evgeny Kaziak
 
Статический анализ кода
Статический анализ кода Статический анализ кода
Статический анализ кода
Pavel Tsukanov
 
Улучшение качества открытого программного обеспечения с помощью инструментов ...
Улучшение качества открытого программного обеспечения с помощью инструментов ...Улучшение качества открытого программного обеспечения с помощью инструментов ...
Улучшение качества открытого программного обеспечения с помощью инструментов ...
Andrey Karpov
 
PostgreSQL performance recipes
PostgreSQL performance recipesPostgreSQL performance recipes
PostgreSQL performance recipes
Alexey Ermakov
 
Лекция о языке программирования Haskell
Лекция о языке программирования HaskellЛекция о языке программирования Haskell
Лекция о языке программирования Haskell
husniyarova
 
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++ Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Sergey Platonov
 
Lift, play, akka, rails part1
Lift, play, akka, rails part1Lift, play, akka, rails part1
Lift, play, akka, rails part1
Eduard Antsupov
 
Scala for android
Scala for androidScala for android
Scala for android
Alexander Rusin
 
Мифы и легенды Java Stream API
Мифы и легенды Java Stream APIМифы и легенды Java Stream API
Мифы и легенды Java Stream API
CEE-SEC(R)
 
Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)
Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)
Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)
Ontico
 
Юлия Ковалёва. Fscheck — альтернативный путь для unit тестов
Юлия Ковалёва. Fscheck — альтернативный путь для unit тестовЮлия Ковалёва. Fscheck — альтернативный путь для unit тестов
Юлия Ковалёва. Fscheck — альтернативный путь для unit тестов
MskDotNet Community
 
WinDbg в руках .NET разработчика
WinDbg в руках .NET разработчикаWinDbg в руках .NET разработчика
WinDbg в руках .NET разработчика
Mikhail Shcherbakov
 
Where is the space, Postgres?
Where is the space, Postgres?Where is the space, Postgres?
Where is the space, Postgres?
Alexey Ermakov
 
Cache2012 administrationbasics
Cache2012 administrationbasicsCache2012 administrationbasics
Cache2012 administrationbasics
Denis Pavlov
 
Web весна 2013 лекция 9
Web весна 2013 лекция 9Web весна 2013 лекция 9
Web весна 2013 лекция 9
Technopark
 
Formal verification of C code
Formal verification of C codeFormal verification of C code
Formal verification of C code
Denis Efremov
 
Долгожданный релиз pg_pathman 1.0 / Александр Коротков, Дмитрий Иванов (Post...
Долгожданный релиз pg_pathman 1.0 / Александр Коротков,  Дмитрий Иванов (Post...Долгожданный релиз pg_pathman 1.0 / Александр Коротков,  Дмитрий Иванов (Post...
Долгожданный релиз pg_pathman 1.0 / Александр Коротков, Дмитрий Иванов (Post...
Ontico
 
MariaDB 10.4 - что нового
MariaDB 10.4 - что новогоMariaDB 10.4 - что нового
MariaDB 10.4 - что нового
Sergey Petrunya
 
[JAM 1.1] Clean Code (Paul Malikov)
[JAM 1.1] Clean Code (Paul Malikov)[JAM 1.1] Clean Code (Paul Malikov)
[JAM 1.1] Clean Code (Paul Malikov)
Evgeny Kaziak
 
Статический анализ кода
Статический анализ кода Статический анализ кода
Статический анализ кода
Pavel Tsukanov
 
Улучшение качества открытого программного обеспечения с помощью инструментов ...
Улучшение качества открытого программного обеспечения с помощью инструментов ...Улучшение качества открытого программного обеспечения с помощью инструментов ...
Улучшение качества открытого программного обеспечения с помощью инструментов ...
Andrey Karpov
 
PostgreSQL performance recipes
PostgreSQL performance recipesPostgreSQL performance recipes
PostgreSQL performance recipes
Alexey Ermakov
 
Лекция о языке программирования Haskell
Лекция о языке программирования HaskellЛекция о языке программирования Haskell
Лекция о языке программирования Haskell
husniyarova
 
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++ Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Sergey Platonov
 
Lift, play, akka, rails part1
Lift, play, akka, rails part1Lift, play, akka, rails part1
Lift, play, akka, rails part1
Eduard Antsupov
 
Мифы и легенды Java Stream API
Мифы и легенды Java Stream APIМифы и легенды Java Stream API
Мифы и легенды Java Stream API
CEE-SEC(R)
 
Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)
Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)
Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)
Ontico
 
Юлия Ковалёва. Fscheck — альтернативный путь для unit тестов
Юлия Ковалёва. Fscheck — альтернативный путь для unit тестовЮлия Ковалёва. Fscheck — альтернативный путь для unit тестов
Юлия Ковалёва. Fscheck — альтернативный путь для unit тестов
MskDotNet Community
 

Similar to Народные средства оптимизации PostgreSQL (20)

Query perfomance tuning
Query perfomance tuningQuery perfomance tuning
Query perfomance tuning
collabock
 
#RuPostgresLive 4: как писать и читать сложные SQL-запросы
#RuPostgresLive 4: как писать и читать сложные SQL-запросы#RuPostgresLive 4: как писать и читать сложные SQL-запросы
#RuPostgresLive 4: как писать и читать сложные SQL-запросы
Nikolay Samokhvalov
 
Как читать и интерпретировать вывод команды EXPLAIN
Как читать и интерпретировать вывод команды EXPLAINКак читать и интерпретировать вывод команды EXPLAIN
Как читать и интерпретировать вывод команды EXPLAIN
Alexey Ermakov
 
Оптимизации скорости выполнения запросов
Оптимизации скорости выполнения запросовОптимизации скорости выполнения запросов
Оптимизации скорости выполнения запросов
Alex.Kolonitsky
 
Архитектура и новые возможности B-tree
Архитектура и новые возможности B-treeАрхитектура и новые возможности B-tree
Архитектура и новые возможности B-tree
Anastasia Lubennikova
 
Производительность запросов в PostgreSQL - шаг за шагом / Илья Космодемьянски...
Производительность запросов в PostgreSQL - шаг за шагом / Илья Космодемьянски...Производительность запросов в PostgreSQL - шаг за шагом / Илья Космодемьянски...
Производительность запросов в PostgreSQL - шаг за шагом / Илья Космодемьянски...
Ontico
 
СУБД 2013 Лекция №5 "Определение узких мест"
СУБД 2013 Лекция №5 "Определение узких мест"СУБД 2013 Лекция №5 "Определение узких мест"
СУБД 2013 Лекция №5 "Определение узких мест"
Technopark
 
SAMag2007 Conference: PostgreSQL 8.3 presentation
SAMag2007 Conference: PostgreSQL 8.3 presentationSAMag2007 Conference: PostgreSQL 8.3 presentation
SAMag2007 Conference: PostgreSQL 8.3 presentation
Nikolay Samokhvalov
 
Основы индексирования и расширенные возможности EXPLAIN в MySQL / Василий Лук...
Основы индексирования и расширенные возможности EXPLAIN в MySQL / Василий Лук...Основы индексирования и расширенные возможности EXPLAIN в MySQL / Василий Лук...
Основы индексирования и расширенные возможности EXPLAIN в MySQL / Василий Лук...
Ontico
 
PostgreSQL: практические примеры оптимизации SQL-запросов / Иван Фролков (Po...
PostgreSQL: практические примеры оптимизации SQL-запросов /  Иван Фролков (Po...PostgreSQL: практические примеры оптимизации SQL-запросов /  Иван Фролков (Po...
PostgreSQL: практические примеры оптимизации SQL-запросов / Иван Фролков (Po...
Ontico
 
Современному хайлоду - современные решения: MySQL 8.0 и улучшения Percona
Современному хайлоду - современные решения: MySQL 8.0 и улучшения PerconaСовременному хайлоду - современные решения: MySQL 8.0 и улучшения Percona
Современному хайлоду - современные решения: MySQL 8.0 и улучшения Percona
Sveta Smirnova
 
СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-зап...
СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-зап...СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-зап...
СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-зап...
Technopark
 
Индексы в MySQL
Индексы в MySQLИндексы в MySQL
Индексы в MySQL
Pavel Zyukin
 
поиск узких мест в производительности My sql ботанический определитель. г. ру...
поиск узких мест в производительности My sql ботанический определитель. г. ру...поиск узких мест в производительности My sql ботанический определитель. г. ру...
поиск узких мест в производительности My sql ботанический определитель. г. ру...
rit2011
 
Troubleshooting my sql_performance_addons
Troubleshooting my sql_performance_addonsTroubleshooting my sql_performance_addons
Troubleshooting my sql_performance_addons
Sveta Smirnova
 
SQL________________________________.pptx
SQL________________________________.pptxSQL________________________________.pptx
SQL________________________________.pptx
DenisMoscvin3
 
Сергей Аверин, То, что вы хотели знать о HandlerSocket, но не смогли нагуглить
Сергей Аверин, То, что вы хотели знать о HandlerSocket, но не смогли нагуглитьСергей Аверин, То, что вы хотели знать о HandlerSocket, но не смогли нагуглить
Сергей Аверин, То, что вы хотели знать о HandlerSocket, но не смогли нагуглить
Tanya Denisyuk
 
SQL-ник DevDay. Каменский. Расширенный SQL в MySQL и PostgreSQL. Сравнение во...
SQL-ник DevDay. Каменский. Расширенный SQL в MySQL и PostgreSQL. Сравнение во...SQL-ник DevDay. Каменский. Расширенный SQL в MySQL и PostgreSQL. Сравнение во...
SQL-ник DevDay. Каменский. Расширенный SQL в MySQL и PostgreSQL. Сравнение во...
DevDay
 
Расширяемость PostgreSQL для хакеров и архитекторов / Олег Бартунов, Александ...
Расширяемость PostgreSQL для хакеров и архитекторов / Олег Бартунов, Александ...Расширяемость PostgreSQL для хакеров и архитекторов / Олег Бартунов, Александ...
Расширяемость PostgreSQL для хакеров и архитекторов / Олег Бартунов, Александ...
Ontico
 
Поиск? Sphinx!
Поиск? Sphinx!Поиск? Sphinx!
Поиск? Sphinx!
Roman Zaiev
 
Query perfomance tuning
Query perfomance tuningQuery perfomance tuning
Query perfomance tuning
collabock
 
#RuPostgresLive 4: как писать и читать сложные SQL-запросы
#RuPostgresLive 4: как писать и читать сложные SQL-запросы#RuPostgresLive 4: как писать и читать сложные SQL-запросы
#RuPostgresLive 4: как писать и читать сложные SQL-запросы
Nikolay Samokhvalov
 
Как читать и интерпретировать вывод команды EXPLAIN
Как читать и интерпретировать вывод команды EXPLAINКак читать и интерпретировать вывод команды EXPLAIN
Как читать и интерпретировать вывод команды EXPLAIN
Alexey Ermakov
 
Оптимизации скорости выполнения запросов
Оптимизации скорости выполнения запросовОптимизации скорости выполнения запросов
Оптимизации скорости выполнения запросов
Alex.Kolonitsky
 
Архитектура и новые возможности B-tree
Архитектура и новые возможности B-treeАрхитектура и новые возможности B-tree
Архитектура и новые возможности B-tree
Anastasia Lubennikova
 
Производительность запросов в PostgreSQL - шаг за шагом / Илья Космодемьянски...
Производительность запросов в PostgreSQL - шаг за шагом / Илья Космодемьянски...Производительность запросов в PostgreSQL - шаг за шагом / Илья Космодемьянски...
Производительность запросов в PostgreSQL - шаг за шагом / Илья Космодемьянски...
Ontico
 
СУБД 2013 Лекция №5 "Определение узких мест"
СУБД 2013 Лекция №5 "Определение узких мест"СУБД 2013 Лекция №5 "Определение узких мест"
СУБД 2013 Лекция №5 "Определение узких мест"
Technopark
 
SAMag2007 Conference: PostgreSQL 8.3 presentation
SAMag2007 Conference: PostgreSQL 8.3 presentationSAMag2007 Conference: PostgreSQL 8.3 presentation
SAMag2007 Conference: PostgreSQL 8.3 presentation
Nikolay Samokhvalov
 
Основы индексирования и расширенные возможности EXPLAIN в MySQL / Василий Лук...
Основы индексирования и расширенные возможности EXPLAIN в MySQL / Василий Лук...Основы индексирования и расширенные возможности EXPLAIN в MySQL / Василий Лук...
Основы индексирования и расширенные возможности EXPLAIN в MySQL / Василий Лук...
Ontico
 
PostgreSQL: практические примеры оптимизации SQL-запросов / Иван Фролков (Po...
PostgreSQL: практические примеры оптимизации SQL-запросов /  Иван Фролков (Po...PostgreSQL: практические примеры оптимизации SQL-запросов /  Иван Фролков (Po...
PostgreSQL: практические примеры оптимизации SQL-запросов / Иван Фролков (Po...
Ontico
 
Современному хайлоду - современные решения: MySQL 8.0 и улучшения Percona
Современному хайлоду - современные решения: MySQL 8.0 и улучшения PerconaСовременному хайлоду - современные решения: MySQL 8.0 и улучшения Percona
Современному хайлоду - современные решения: MySQL 8.0 и улучшения Percona
Sveta Smirnova
 
СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-зап...
СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-зап...СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-зап...
СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-зап...
Technopark
 
Индексы в MySQL
Индексы в MySQLИндексы в MySQL
Индексы в MySQL
Pavel Zyukin
 
поиск узких мест в производительности My sql ботанический определитель. г. ру...
поиск узких мест в производительности My sql ботанический определитель. г. ру...поиск узких мест в производительности My sql ботанический определитель. г. ру...
поиск узких мест в производительности My sql ботанический определитель. г. ру...
rit2011
 
Troubleshooting my sql_performance_addons
Troubleshooting my sql_performance_addonsTroubleshooting my sql_performance_addons
Troubleshooting my sql_performance_addons
Sveta Smirnova
 
SQL________________________________.pptx
SQL________________________________.pptxSQL________________________________.pptx
SQL________________________________.pptx
DenisMoscvin3
 
Сергей Аверин, То, что вы хотели знать о HandlerSocket, но не смогли нагуглить
Сергей Аверин, То, что вы хотели знать о HandlerSocket, но не смогли нагуглитьСергей Аверин, То, что вы хотели знать о HandlerSocket, но не смогли нагуглить
Сергей Аверин, То, что вы хотели знать о HandlerSocket, но не смогли нагуглить
Tanya Denisyuk
 
SQL-ник DevDay. Каменский. Расширенный SQL в MySQL и PostgreSQL. Сравнение во...
SQL-ник DevDay. Каменский. Расширенный SQL в MySQL и PostgreSQL. Сравнение во...SQL-ник DevDay. Каменский. Расширенный SQL в MySQL и PostgreSQL. Сравнение во...
SQL-ник DevDay. Каменский. Расширенный SQL в MySQL и PostgreSQL. Сравнение во...
DevDay
 
Расширяемость PostgreSQL для хакеров и архитекторов / Олег Бартунов, Александ...
Расширяемость PostgreSQL для хакеров и архитекторов / Олег Бартунов, Александ...Расширяемость PostgreSQL для хакеров и архитекторов / Олег Бартунов, Александ...
Расширяемость PostgreSQL для хакеров и архитекторов / Олег Бартунов, Александ...
Ontico
 
Поиск? Sphinx!
Поиск? Sphinx!Поиск? Sphinx!
Поиск? Sphinx!
Roman Zaiev
 

Народные средства оптимизации PostgreSQL

  • 2. МЫ РАССМОТРИМ ● Основные моменты работы с РБД ● Что такое EXPLAIN и как с ним работать ● Индексы и их особенности ● Лайфхаки и хитрости
  • 3. Основы работы с РБД ● Запись в таблице должна соответствовать объекту ● Правильно выбрать уровень нормализации ● Уделить немало времени на проектирование ● Структура БД должна быть гибкой ● Простота в поддержке жизненого цикла БД ● Правильно расставлять индексы, не создавать бардак ● Строить запросы не выбирая все подряд (*), только необходимое ● Приступать к оптимизации, когда действительно это требуется
  • 4. ORM vs SQl ● ORM — удобно? ● ORM — быстро? Total runtime: (~)117.092 ms* JAVA @ManyToOne @JoinColumn(name = "CATEGORY_ID") public Category getCategory() { return category; } PHP $customers = Customer::find() ->where(['status' => TRUE]) ->orderBy('id') ->limit(100, 10000); * Подробный пример мы разберем ниже ☑
  • 5. ORM vs SQL ● SQL — удобно? ● SQL — быстро? Total runtime: (~)11.926 ms SELECT * FROM ( SELECT * FROM posts WHERE author = 'nick' ORDER BY posts.publish DESC LIMIT 10 ) as posts JOIN post_category ON posts.category_id = category.id ORDER BY posts.publish DESC LIMIT 10; ☒ * Подробный пример мы разберем ниже
  • 6. Еще об ORM ● Позволяет представить запись в БД в виде объекта ● Удобнее понимать и легче писать чем SQL ● Поддержка и изменения не вызывают трудностей ● Сложные запросы иногда невозможно написать ● Трудно оптимизировать ● Иногда строится запрос, который не использует индексы
  • 7. А что с SQL? ● Сложные выборки, запросы ● Возможности оптимизации ● Функции и различные возможности SQL ● HighLoad — однозначно SQL (узкое место) ● Запрос с использованием индексов не всегда быстрый ● Замусоривает код (JAVA и многострочные литералы) ● Нет проверок на этапе компиляции
  • 9. ANALYZE ANALYZE считывается определённое количество строк таблицы в базе данных, выбранных случайным образом, и сохраняет результаты в системном каталоге pg_statistic. Затем планировщик запросов будет использовать эту статистику для выбора эффективных планов запросов. => ANALYZE VERBOSE; WARNING: skipping "pg_statistic" --- only superuser or database owner can analyze it WARNING: skipping "pg_type" --- only superuser or database owner can analyze it INFO: analyzing "public.test" INFO: "test": scanned 16669 of 16669 pages, containing 2000200 live rows and 0 dead rows; 30000 rows in sample, 2000200 estimated total rows
  • 10. VACUUM ● Очистка места (помечание), занимаемое «мертвыми» кортежами ● По-умолчанию очищает все таблицы доступные пользователю ● Без опции FULL может работать параллельно, т. к. не требует исключительной блокировки ● With FULL работает медленно, требует блокировки и возвращает освобожденное место операционной системе. ● Autovacuum — демон очистки (VACUUM+ANALYZE) ● ! table bloating
  • 12. Таблица TEST => CREATE TABLE test (id integer, text text); => INSERT INTO test SELECT id, md5(random()::text) FROM generate_series(1, 1000000) AS id; => d test Table "public.test" Column | Type | Modifiers ----------------+------------+----------- column_1 | integer | column_2 | text |
  • 13. => EXPLAIN SELECT * FROM test; ● Cost — у.е. для оценки затратности операции. 1ое значение — затраты доступа к 1й строке 2ое значение — затраты для доступа ко всем строкам. ● Rows — (~) количество строк при вызове Seq Scan к этой таблице ● With — длина строки в байтах
  • 14. Всё, что мы видели выше в выводе команды EXPLAIN — ожидания планировщика. => EXPLAIN (ANALYZE) SELECT * FROM test; ● Actual time — реальное время в миллисекундах для 1ой и всех строк ● Rows — реальное количество строк полученных ● Loops — количество выполнений данной операции ● Plannig time — время выполнения EXPLAIN ● Execution time — общее время выполнения ● Heap Fetches — число реальных обращений к таблице !!! DANGER !!! EXPLAIN (ANALYZE) исполняет команды
  • 15. => EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM test; ● Buffers: shared read — количество блоков считанных с диска; ● Buffers: shared hit — количество блоков, считанных из кэша PostgreSQL. CACHE
  • 16. WHERE => EXPLAIN (ANALYZE) SELECT * FROM test WHERE id between 1500 and 1550; ● Индексов нет поэтому Seq Scan ● Каждая строка сравнивается с: Filter: ((id >= 1500) AND (id <= 1550)) ● Cost увеличилось ● Rows уменьшилось до ождаемого количества Execution time: 222.979 ms
  • 17. Index Scan => CREATE INDEX ON test(id); => EXPLAIN (ANALYZE) SELECT * FROM test WHERE id between 1500 and 1550; ● Теперь Index Scan using test_id_idx on test; ● Index Cond: ((id >= 1500) AND (id <= 1550)) Execution time: 0.127 ms
  • 18. Seq Scan A C D 1) С < 10 2) С < 10 3) С < 10 С < 10
  • 19. Index Scan A C D С < 10 C
  • 20. ИНДЕКСЫ ● Но что будет, если поменять условие => EXPLAIN (ANALYZE) SELECT * FROM test WHERE id > 1550; ● Теперь Seq Scan on test ● Пришлось прочитать все строки, кроме первых 1500.. ● Время увеличилось, что не удивительно Execution time: 678.852 ms
  • 21. ИНДЕКСЫ ● А если выключить Seq Scan => SET enable_seqscan TO off; ● Теперь Index Scan using test_id_idx on test ● Но время запроса стало еще больше Execution time: 749.952 ms ● И стоимость cost также увеличилась Планировщик не дурак =)
  • 22. Про индексы ● Индекс это дополнительная структура данных (не SQL) ● Индексы требуют затраты на поддержание ● Замедляют обновление ● Замедляют репликацию ● Малая селективность — неэфективно Индексы не панацея!
  • 23. Index Only Scan ● EXPLAIN (ANALYZE) SELECT id FROM test WHERE id < 450; ● Index Only Scan using test_id_idx on test ● Выбираем только поле id, чтобы включить IOS ● Скорость очень большая Execution time: 0.659 ms
  • 24. Index Only Scan A C D С < 10 C Visability MAP
  • 25. ИНДЕКСЫ ПО ТЕКСТУ EXPLAIN (ANALYZE) SELECT * FROM test WHERE text LIKE 'ab%'; ● Seq Scan After CREATE INDEX ON test(text); ● Также Seq Scan (211 ms), потому что UTF-8! ● Нужно использовать класс оператора text_pattern_ops CREATE INDEX ON test(text text_pattern_ops);
  • 26. ИНДЕКСЫ ПО ТЕКСТУ EXPLAIN (ANALYZE) SELECT * FROM test WHERE text LIKE 'ab%'; ● Bitmap Index Scan on test_text_idx1 ● Сравниваем Index Cond: ( (text ~>=~ 'ab'::text) AND (text ~<~ 'ac'::text) ) ● Далее Bitmap Heap Scan on test проверяет существуют ли записи на самом деле
  • 27. Bitmap Index Scan A C D С < 10 C 1 1 1 1 1 0
  • 28. Создание индексов ● Требуют блокировки при создании ● CONCURRENTLY создает в фоне, но долго (требует 2 прохода) ● Можно и нужно мониторить неиспользуемые индексы (расходуются рессурсы и время) ● Можно находить дубликаты индексов ● Можно строить индексы по функциям, но необходимо точное её повторение при запросе ● ! index bloating
  • 29. Когда создавать индексы? CREATE TABLE test5 (id integer PRIMARY KEY, v float8); ACREATE INDEX test5_v_idx ON test5(v); INSERT INTO test5 (SELECT id, random() FROM generate_series(1,1000000) id); CREATE TABLE test5 (id integer PRIMARY KEY, v float8); BINSERT INTO test5 (SELECT id, random() FROM generate_series(1,1000000) id); CREATE INDEX test5_v_idx ON test5(v);
  • 30. Когда создавать индексы? CREATE TABLE a (id integer PRIMARY KEY, v float8); 1,991 ms CREATE INDEX a_v_idx ON a(v); 0,506 ms INSERT INTO a (SELECT id, random() FROM generate_series(1,1000000) id); 4909,127 ms A = Total: 4911 ms CREATE TABLE b (id integer PRIMARY KEY, v float8); 1,990 ms INSERT INTO b (SELECT id, random() FROM generate_series(1,1000000) id); 938,852 ms CREATE INDEX b_v_idx ON b(v); 1195,492 ms B = Total: 2136 ms
  • 32. Мониторим неиспользуемые индексы SELECT schemaname || '.' || relname AS table, indexrelname AS index, pg_size_pretty(pg_relation_size(i.indexrelid)) AS index_size, idx_scan as index_scans FROM pg_stat_user_indexes ui JOIN pg_index i ON ui.indexrelid = i.indexrelid WHERE NOT indisunique AND idx_scan < 50 AND pg_relation_size(relid) > 5 * 819 ORDER BY pg_relation_size(i.indexrelid) / nullif(idx_scan, 0) DESC NULLS FIRST, pg_relation_size(i.indexrelid) DESC; table | index | index_size | index_scans ---------------------+---------------------------+---------------+------------- public.test | test_text_idx | 56 MB | 0 public.test5 | test5_v_idx | 28 MB | 0 public.test6 | test6_v_idx | 21 MB | 0 public.test | test_text_idx1 | 56 MB | 3 public.test | test_id_idx | 21 MB | 36
  • 33. Ищем дубликаты индексов lk=> SELECT pg_size_pretty(SUM(pg_relation_size(idx))::BIGINT) AS SIZE, (array_agg(idx))[1] AS idx1, (array_agg(idx))[2] AS idx2, (array_agg(idx))[3] AS idx3, (array_agg(idx))[4] AS idx4 FROM ( SELECT indexrelid::regclass AS idx, (indrelid::text ||E'n'|| indclass::text ||E'n'|| indkey::text ||E'n'|| COALESCE(indexprs::text,'')||E'n' || COALESCE(indpred::text,'')) AS KEY FROM pg_index) sub GROUP BY KEY HAVING COUNT(*)>1 ORDER BY SUM(pg_relation_size(idx)) DESC; size | idx1 | idx2 | idx3 | idx4 ---------+-----------------------+----------------------+------+------ 32 kB | blocks_id_idx | blocks_id_idx1 | | 32 kB | blocks_type_idx1 | blocks_type_idx | | 32 kB | primary_key | ids | |
  • 34. Оптимизация OFFSET Ситуация: SELECT … FROM table1 JOIN table2 using (table2id) JOIN table3 using (table3id) WHERE набор условий ТОЛЬКО по table1 Order by (набор полей table1) LIMIT ... OFFSET ... Важно: сработает если соблюдается условие, что логика выборки и offset реализуется в table1, а также что присоединенные данные из таблиц table2 и table3 на запрос не влияют.
  • 35. EXPLAIN ANALYZE SELECT * FROM test JOIN vals ON vals.test_id = test.id WHERE val between 150 AND 9500 LIMIT 5 OFFSET 5000; Execution time: 283.471 ms EXPLAIN ANALYZE SELECT * FROM ( SELECT * FROM test WHERE val between 150 AND 9500 LIMIT 5 OFFSET 5000) AS test JOIN vals ON vals.test_id = test.id; Execution time: 4.079 ms
  • 36. Оптимизация COUNT(*) => EXPLAIN ANALYZE SELECT count(*) FROM cache_customers_rates; Execution time: 18632.959 ms => EXPLAIN ANALYZE SELECT (reltuples)::numeric FROM pg_class r WHERE relkind='r' AND relname='cache_customers_rates'; Execution time: 0.079 ms
  • 37. Получение строк в виде ROW => SELECT * FROM tasks; id | type | status | params | out. | exc. ----+---------------+---------+------------------+------+----- 1 | refill_cache | new | {"threads":-1} | | 2 | refill_cache | new | {"threads":-1} | | => SELECT tasks FROM tasks; tasks -------------------------------------------------------------------- ( 1, refill_cache, new, "{""threads"":-1}", "", "" ) ( 2, refill_cache, new, "{""threads"":-1}", "", "" )
  • 38. Получение строк в виде JSON => SELECT row_to_json(tasks) FROM tasks; row_to_json ----------------------------------------------------------------- { "id":1, "type":"refill_cache", "status":"new", "params":"{"threads":-1}", "output":"", "exception":"" }
  • 39. Выбранные поля в виде JSON => SELECT row_to_json( t ) FROM ( SELECT id, type FROM tasks) AS t; row_to_json ------------------------------------------------ { "id":1, "type":"refill_cache" } { "id":1, "type":"refill_cache" }
  • 40. Данные в JSON ARRAY => SELECT array_to_json( array_agg( row_to_json(tasks) ) ) FROM tasks; [ { "id":1, "type":"refill_cache", "status":"new", "params":"{"threads":-1}", "output":"", "exception":"" }, { "id":2, "type":"refill_cache", "status":"new", "params":"{"threads":-1}", "output":"", "exception":"" } ]
  • 41. OUTPUT to FILE => o tasks.json => SELECT array_to_json( array_agg( row_to_json(tasks) ) ) FROM tasks; => o # cat tasks.json array_to_json ------------------------------------------------------------------------------- [{"id":1,"type":"refill_cache","status":"new","params":"{"thread s":-1}","output":"","exception":""}, {"id":2,"type":"refill_cache","status":"new","params":"{"thread s":-1}","output":"","exception":""}]