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 и многострочные литералы)
● Нет проверок на этапе компиляции
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
Таблица 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":""}]
Полезная информация
●
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

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 administrationbasicsDenis Pavlov
 
Web весна 2013 лекция 9
Web весна 2013 лекция 9Web весна 2013 лекция 9
Web весна 2013 лекция 9Technopark
 
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 part1Eduard 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
 

What's hot (19)

WinDbg в руках .NET разработчика
WinDbg в руках .NET разработчикаWinDbg в руках .NET разработчика
WinDbg в руках .NET разработчика
 
Where is the space, Postgres?
Where is the space, Postgres?Where is the space, Postgres?
Where is the space, Postgres?
 
Cache2012 administrationbasics
Cache2012 administrationbasicsCache2012 administrationbasics
Cache2012 administrationbasics
 
Web весна 2013 лекция 9
Web весна 2013 лекция 9Web весна 2013 лекция 9
Web весна 2013 лекция 9
 
Formal verification of C code
Formal verification of C codeFormal verification of C code
Formal verification of C code
 
Долгожданный релиз pg_pathman 1.0 / Александр Коротков, Дмитрий Иванов (Post...
Долгожданный релиз pg_pathman 1.0 / Александр Коротков,  Дмитрий Иванов (Post...Долгожданный релиз pg_pathman 1.0 / Александр Коротков,  Дмитрий Иванов (Post...
Долгожданный релиз pg_pathman 1.0 / Александр Коротков, Дмитрий Иванов (Post...
 
MariaDB 10.4 - что нового
MariaDB 10.4 - что новогоMariaDB 10.4 - что нового
MariaDB 10.4 - что нового
 
[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)
 
Статический анализ кода
Статический анализ кода Статический анализ кода
Статический анализ кода
 
Улучшение качества открытого программного обеспечения с помощью инструментов ...
Улучшение качества открытого программного обеспечения с помощью инструментов ...Улучшение качества открытого программного обеспечения с помощью инструментов ...
Улучшение качества открытого программного обеспечения с помощью инструментов ...
 
PostgreSQL performance recipes
PostgreSQL performance recipesPostgreSQL performance recipes
PostgreSQL performance recipes
 
Лекция о языке программирования Haskell
Лекция о языке программирования HaskellЛекция о языке программирования Haskell
Лекция о языке программирования Haskell
 
Zagursky
ZagurskyZagursky
Zagursky
 
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++ Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
 
Lift, play, akka, rails part1
Lift, play, akka, rails part1Lift, play, akka, rails part1
Lift, play, akka, rails part1
 
Scala for android
Scala for androidScala for android
Scala for android
 
Мифы и легенды Java Stream API
Мифы и легенды Java Stream APIМифы и легенды Java Stream API
Мифы и легенды Java Stream API
 
Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)
Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)
Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)
 
Юлия Ковалёва. Fscheck — альтернативный путь для unit тестов
Юлия Ковалёва. Fscheck — альтернативный путь для unit тестовЮлия Ковалёва. Fscheck — альтернативный путь для unit тестов
Юлия Ковалёва. Fscheck — альтернативный путь для unit тестов
 

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

Как не сделать врагами архитектуру и оптимизацию, Кирилл Березин, Mail.ru Group
Как не сделать врагами архитектуру и оптимизацию, Кирилл Березин, Mail.ru GroupКак не сделать врагами архитектуру и оптимизацию, Кирилл Березин, Mail.ru Group
Как не сделать врагами архитектуру и оптимизацию, Кирилл Березин, Mail.ru Group
Mail.ru Group
 
Tech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU: Как приручить дракона: введение в LLVMTech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU
 
Как приручить дракона: введение в LLVM
Как приручить дракона: введение в LLVMКак приручить дракона: введение в LLVM
Как приручить дракона: введение в LLVM
Tech Talks @NSU
 
SAMag2007 Conference: PostgreSQL 8.3 presentation
SAMag2007 Conference: PostgreSQL 8.3 presentationSAMag2007 Conference: PostgreSQL 8.3 presentation
SAMag2007 Conference: PostgreSQL 8.3 presentationNikolay Samokhvalov
 
PostgreSQL: практические примеры оптимизации SQL-запросов / Иван Фролков (Po...
PostgreSQL: практические примеры оптимизации SQL-запросов /  Иван Фролков (Po...PostgreSQL: практические примеры оптимизации SQL-запросов /  Иван Фролков (Po...
PostgreSQL: практические примеры оптимизации SQL-запросов / Иван Фролков (Po...
Ontico
 
django-and-postgresql
django-and-postgresqldjango-and-postgresql
django-and-postgresqlOleg Churkin
 
статический анализ кода
статический анализ кодастатический анализ кода
статический анализ кодаAndrey Karpov
 
Новые возможности языка SQL в Firebird 3.0
Новые возможности языка SQL в Firebird 3.0Новые возможности языка SQL в Firebird 3.0
Новые возможности языка SQL в Firebird 3.0
Alexey Kovyazin
 
Alasql.js - SQL база данных на JavaScript / Андрей Гершун (МАГ КОНСАЛТИНГ)
Alasql.js - SQL база данных на JavaScript / Андрей Гершун (МАГ КОНСАЛТИНГ)Alasql.js - SQL база данных на JavaScript / Андрей Гершун (МАГ КОНСАЛТИНГ)
Alasql.js - SQL база данных на JavaScript / Андрей Гершун (МАГ КОНСАЛТИНГ)
Ontico
 
AlaSQL библиотека для обработки JavaScript данных (презентация для ForntEnd 2...
AlaSQL библиотека для обработки JavaScript данных (презентация для ForntEnd 2...AlaSQL библиотека для обработки JavaScript данных (презентация для ForntEnd 2...
AlaSQL библиотека для обработки JavaScript данных (презентация для ForntEnd 2...
Andrey Gershun
 
AlaSQL - SQL библиотека на JavaScript (выступление на PiterJS)
AlaSQL - SQL библиотека на JavaScript (выступление на PiterJS)AlaSQL - SQL библиотека на JavaScript (выступление на PiterJS)
AlaSQL - SQL библиотека на JavaScript (выступление на PiterJS)
Andrey Gershun
 
Современный статический анализ кода: что умеет он, чего не умели линтеры
Современный статический анализ кода: что умеет он, чего не умели линтерыСовременный статический анализ кода: что умеет он, чего не умели линтеры
Современный статический анализ кода: что умеет он, чего не умели линтеры
corehard_by
 
Расширение библиотеки Slick
Расширение библиотеки SlickРасширение библиотеки Slick
Расширение библиотеки Slick
Арсений Жижелев
 
Опыт разработки статического анализатора кода
Опыт разработки статического анализатора кодаОпыт разработки статического анализатора кода
Опыт разработки статического анализатора кода
Andrey Karpov
 
Формальная верификация кода на языке Си
Формальная верификация кода на языке СиФормальная верификация кода на языке Си
Формальная верификация кода на языке Си
Positive Hack Days
 
Формальная верификация кода на языке Си
Формальная верификация кода на языке СиФормальная верификация кода на языке Си
Формальная верификация кода на языке Си
Positive Development User Group
 
My sql 5.6-new-stable-mmug
My sql 5.6-new-stable-mmugMy sql 5.6-new-stable-mmug
My sql 5.6-new-stable-mmug
Andrey Tokarchuk
 
Web осень 2013 лекция 6
Web осень 2013 лекция 6Web осень 2013 лекция 6
Web осень 2013 лекция 6Technopark
 
DSLs in Lisp and Clojure
DSLs in Lisp and ClojureDSLs in Lisp and Clojure
DSLs in Lisp and Clojure
Vasil Remeniuk
 
OpenACC short review
OpenACC short reviewOpenACC short review
OpenACC short review
Andrei Poliakov
 

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

Как не сделать врагами архитектуру и оптимизацию, Кирилл Березин, Mail.ru Group
Как не сделать врагами архитектуру и оптимизацию, Кирилл Березин, Mail.ru GroupКак не сделать врагами архитектуру и оптимизацию, Кирилл Березин, Mail.ru Group
Как не сделать врагами архитектуру и оптимизацию, Кирилл Березин, Mail.ru Group
 
Tech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU: Как приручить дракона: введение в LLVMTech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU: Как приручить дракона: введение в LLVM
 
Как приручить дракона: введение в LLVM
Как приручить дракона: введение в LLVMКак приручить дракона: введение в LLVM
Как приручить дракона: введение в LLVM
 
SAMag2007 Conference: PostgreSQL 8.3 presentation
SAMag2007 Conference: PostgreSQL 8.3 presentationSAMag2007 Conference: PostgreSQL 8.3 presentation
SAMag2007 Conference: PostgreSQL 8.3 presentation
 
PostgreSQL: практические примеры оптимизации SQL-запросов / Иван Фролков (Po...
PostgreSQL: практические примеры оптимизации SQL-запросов /  Иван Фролков (Po...PostgreSQL: практические примеры оптимизации SQL-запросов /  Иван Фролков (Po...
PostgreSQL: практические примеры оптимизации SQL-запросов / Иван Фролков (Po...
 
django-and-postgresql
django-and-postgresqldjango-and-postgresql
django-and-postgresql
 
статический анализ кода
статический анализ кодастатический анализ кода
статический анализ кода
 
Новые возможности языка SQL в Firebird 3.0
Новые возможности языка SQL в Firebird 3.0Новые возможности языка SQL в Firebird 3.0
Новые возможности языка SQL в Firebird 3.0
 
Alasql.js - SQL база данных на JavaScript / Андрей Гершун (МАГ КОНСАЛТИНГ)
Alasql.js - SQL база данных на JavaScript / Андрей Гершун (МАГ КОНСАЛТИНГ)Alasql.js - SQL база данных на JavaScript / Андрей Гершун (МАГ КОНСАЛТИНГ)
Alasql.js - SQL база данных на JavaScript / Андрей Гершун (МАГ КОНСАЛТИНГ)
 
AlaSQL библиотека для обработки JavaScript данных (презентация для ForntEnd 2...
AlaSQL библиотека для обработки JavaScript данных (презентация для ForntEnd 2...AlaSQL библиотека для обработки JavaScript данных (презентация для ForntEnd 2...
AlaSQL библиотека для обработки JavaScript данных (презентация для ForntEnd 2...
 
AlaSQL - SQL библиотека на JavaScript (выступление на PiterJS)
AlaSQL - SQL библиотека на JavaScript (выступление на PiterJS)AlaSQL - SQL библиотека на JavaScript (выступление на PiterJS)
AlaSQL - SQL библиотека на JavaScript (выступление на PiterJS)
 
Современный статический анализ кода: что умеет он, чего не умели линтеры
Современный статический анализ кода: что умеет он, чего не умели линтерыСовременный статический анализ кода: что умеет он, чего не умели линтеры
Современный статический анализ кода: что умеет он, чего не умели линтеры
 
Расширение библиотеки Slick
Расширение библиотеки SlickРасширение библиотеки Slick
Расширение библиотеки Slick
 
Опыт разработки статического анализатора кода
Опыт разработки статического анализатора кодаОпыт разработки статического анализатора кода
Опыт разработки статического анализатора кода
 
Формальная верификация кода на языке Си
Формальная верификация кода на языке СиФормальная верификация кода на языке Си
Формальная верификация кода на языке Си
 
Формальная верификация кода на языке Си
Формальная верификация кода на языке СиФормальная верификация кода на языке Си
Формальная верификация кода на языке Си
 
My sql 5.6-new-stable-mmug
My sql 5.6-new-stable-mmugMy sql 5.6-new-stable-mmug
My sql 5.6-new-stable-mmug
 
Web осень 2013 лекция 6
Web осень 2013 лекция 6Web осень 2013 лекция 6
Web осень 2013 лекция 6
 
DSLs in Lisp and Clojure
DSLs in Lisp and ClojureDSLs in Lisp and Clojure
DSLs in Lisp and Clojure
 
OpenACC short review
OpenACC short reviewOpenACC short review
OpenACC short review
 

Народные средства оптимизации 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 и многострочные литералы) ● Нет проверок на этапе компиляции
  • 8.
  • 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
  • 11.
  • 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":""}]
  • 42.