SlideShare a Scribd company logo
Учим слона танцевать рок’н’ролл.
Часть 1: игры с индексами
Maxim Boguk
dataegret.com
Data Egret (в прошло PostgreSQL-Consulting.com) вот уже много
лет занимается поддержкой PostgreSQL в России и за рубежом.
Лично я работаю с PostgreSQL больше 18 лет (еще с версии 7.3).
27/7 поддержка
консультации по работе базой и ее настройкам
Аудит состояния и настроек базы
Мониторинг
Обучение
О DataEgret
dataegret.com
Умеют ли слоны танцевать rock’n’roll?
dataegret.com
Некоторые типы запросов PostgreSQL не умеет выполнять
эффективно (пока что?)
Альтернативный подход.
Ручная реализация быстрого алгоритма может дать
большое ускорение запросов
Это выглядит как магия в начале.
dataegret.com
Знание как работают следующие фичи базы:
WITH [RECURSIVE]
[JOIN] LATERAL
UNNEST [WITH ORDINALITY]
И так далее
Навыки требуемые для понимания материала
dataegret.com
PostgreSQL 9.6
Будет работать на 9.5 и 9.4 без (серьезных) правок
Миграция на 9.3 (и даже на 8.4) возможна, но надо
будет реализовывать руками отсуствующие в этих
версиях возможности.
Версия PostgreSQL
dataegret.com
Описание проблемы
Подготовка данных
Стандартная реализация на SQL
EXPLAIN (ANALYZE, TIMING false, COSTS false)
Альтернативное решение
EXPLAIN (ANALYZE, TIMING false, COSTS false)
У нея внутри неонка! (или смотрим под капот)
Структура
01
IOS для
запросов с
большим
OFFSET
dataegret.com
Описание проблемы01
Queries with large ofset are slow
To produce 1.000.001’st row, database frst going to iterate through
1.000.000 rows
Alternatve: use fast Index Only Scan to skip the frst 1.000.000 rows
dataegret.com
Подготовка данных01
CREATE TABLE t AS select
id,
'some very boring payload text ...'::text AS payload
FROM generate_series(1, 1000000) AS g(id);
ALTER TABLE t ADD PRIMARY KEY(id);
VACUUM ANALYZE t;
dataegret.com
Стандартная реализация на SQL01
SELECT
*
FROM t
ORDER BY id
OFFSET 1000000
LIMIT 5
dataegret.com
EXPLAIN ANALYZE01
Limit (actual time=728..1728 rows=5 loops=1)
-> Index Scan using t_pkey on t
(actual time=0..530 rows=1000005 loops=1)
Execution time: 728 ms
dataegret.com
Альтернативное решение01
WITH _start AS (
--find a starting id using IOS to skip OFFSET rows
SELECT id FROM t
ORDER BY id OFFSET 999999 LIMIT 1
)
--return result using normal index scan
SELECT * FROM t WHERE id>(SELECT id FROM _start)
ORDER BY id LIMIT 5;
dataegret.com
EXPLAIN ANALYZE01
Limit (actual rows=5 loops=1)
CTE _start
-> Limit (actual rows=1 loops=1)
-> Index Only Scan using t_pkey on t t_1
(actual rows=1000000 loops=1)
InitPlan 2 (returns $1)
-> CTE Scan on _start (actual rows=1 loops=1)
-> Index Scan using t_pkey on t (actual rows=5 loops=1)
Index Cond: (id > $1)
Execution time: 80 ms
dataegret.com
Под капотом01
SELECT * FROM t WHERE id>999998 ORDER BY id LIMIT 9;
id | payload
---------+---------
999999 | payload
1000000 | payload
1000001 | payload
1000002 | payload
1000003 | payload
1000004 | payload
1000005 | payload
1000006 | payload
dataegret.com
Под капотом01
SELECT * FROM t ORDER BY id OFFSET 1000000 LIMIT 5;
пропускаем 1000000 строк из TABLE
id | payload
---------+---------
...
999999 | payload
1000000 | payload
1000001 | payload
1000002 | payload
1000003 | payload
1000004 | payload
1000005 | payload
1000006 | payload
dataegret.com
Под капотом01
_start AS (SELECT id FROM t ORDER BY id OFFSET 999999 LIMIT 1)
= 1000000, пропускаем 999999 строк из INDEX ONLY
id | payload
---------+---------
...
999999 | payload
1000000 | payload
1000001 | payload
1000002 | payload
1000003 | payload
1000004 | payload
1000005 | payload
1000006 | payload
dataegret.com
Под капотом01
SELECT * FROM t WHERE id>(SELECT id FROM _start)
ORDER BY id LIMIT 5; --читаем 5 строк из TABLE
id | payload
---------+---------
999999 | payload
1000000 | payload
1000001 | payload
1000002 | payload
1000003 | payload
1000004 | payload
1000005 | payload
1000006 | payload
02
Использование
индекса (f1) для
запросов
ORDER BY f1,f2
LIMIT N
dataegret.com
Описание проблемы02
Есть запрос ORDER BY f1,f2 LIMIT N
Но есть только INDEX ON (f1)
По умолчанию – база будет делать seq scan + sort
А если подумать?
dataegret.com
Подготовка данных02
CREATE TABLE t AS SELECT
id,
(Random()*100000)::integer AS f1,
(Random()*100000)::integer AS f2,
'some very boring payload text ...'::text AS payload
FROM generate_series(1, 1000000) AS g(id);
CREATE INDEX t_f1_key ON t(f1);
VACUUM ANALYZE t;
dataegret.com
Стандартная реализация на SQL02
SELECT
*
FROM t
ORDER BY f1,f2
LIMIT 5
dataegret.com
EXPLAIN ANALYZE02
Limit (actual rows=5 loops=1)
-> Sort (actual rows=5 loops=1)
Sort Key: f1, f2
Sort Method: top-N heapsort Memory: 45kB
-> Seq Scan on t (actual rows=1000000 loops=1)
Execution time: 518.492 ms
dataegret.com
Альтернативное решение02
WITH _t1 AS (
--lastN order by f1
SELECT * FROM t ORDER BY f1 LIMIT 5
), _max AS (
--max f1 value from lastN
SELECT max(f1) AS f1 FROM _t1
), _t2 AS (
--all lastN with f1 < max
SELECT * FROM _t1 WHERE f1<(SELECT _max.f1 FROM _max)
)
SELECT * FROM _t2
UNION ALL (
--grab required amount of additional values from table
SELECT * FROM t WHERE f1=(SELECT _max.f1 FROM _max)
ORDER BY f1, f2 LIMIT (5 - (SELECT COUNT(*) FROM _t2))
) ORDER BY f1,f2;
dataegret.com
EXPLAIN ANALYZE02
Sort (actual rows=5 loops=1)
Sort Key: _t2.f1, _t2.f2
Sort Method: quicksort Memory: 45kB
CTE _t1
-> Limit (actual rows=10 loops=1)
-> Index Scan using t_f1_key on t t_1 (actual rows=5 loops=1)
CTE _max
-> Aggregate (actual rows=1 loops=1)
-> CTE Scan on _t1 (actual rows=5 loops=1)
CTE _t2
-> CTE Scan on _t1 _t1_1 (actual rows=2 loops=1)
Filter: (f1 < $2)
Rows Removed by Filter: 3
InitPlan 3 (returns $2)
-> CTE Scan on _max (actual rows=1 loops=1)
-> Append (actual rows=5 loops=1)
-> CTE Scan on _t2 (actual rows=2 loops=1)
-> Limit (actual rows=3 loops=1)
InitPlan 5 (returns $4)
-> CTE Scan on _max _max_1 (actual rows=1 loops=1)
InitPlan 6 (returns $5)
-> Aggregate (actual rows=1 loops=1)
-> CTE Scan on _t2 _t2_1 (actual rows=3 loops=1)
-> Sort (actual rows=3 loops=1)
Sort Key: t.f2
Sort Method: top-N heapsort Memory: 31kB
-> Index Scan using t_f1_key on t (actual rows=3 loops=1)
Index Cond: (f1 = $4)
Execution time: 0.133 ms
dataegret.com
Под капотом02
SELECT row_number() over (ORDER BY f1, f2), f1, f2 FROM t
ORDER BY f1, f2 LIMIT 10;
row_number | f1 | f2
------------+----+-------
1 | 0 | 39213
2 | 0 | 86085
3 | 1 | 4750
4 | 1 | 10391
5 | 1 | 13418
6 | 1 | 13960
7 | 1 | 15210
8 | 1 | 54074
9 | 1 | 63252
10 | 2 | 11866
dataegret.com
Под капотом02
SELECT f1,f2 FROM t ORDER BY f1,f2 LIMIT 5
row_number | f1 | f2
------------+----+-------
1 | 0 | 39213
2 | 0 | 86085
3 | 1 | 4750
4 | 1 | 10391
5 | 1 | 13418
6 | 1 | 13960
7 | 1 | 15210
8 | 1 | 54074
9 | 1 | 63252
10 | 2 | 11866
dataegret.com
Под капотом02
t1 AS (SELECT * FROM t ORDER BY f1 LIMIT 5)
2 RED and ANY 3 of GREEN
row_number | f1 | f2
------------+----+-------
1 | 0 | 39213
2 | 0 | 86085
3 | 1 | 4750
4 | 1 | 10391
5 | 1 | 13418
6 | 1 | 13960
7 | 1 | 15210
8 | 1 | 54074
9 | 1 | 63252
10 | 2 | 11866
dataegret.com
Под капотом02
_max AS (SELECT max(f1) AS f1 FROM _t1)
= 1
row_number | f1 | f2
------------+----+-------
1 | 0 | 39213
2 | 0 | 86085
3 | 1 | 4750
4 | 1 | 10391
5 | 1 | 13418
6 | 1 | 13960
7 | 1 | 15210
8 | 1 | 54074
9 | 1 | 63252
10 | 2 | 11866
dataegret.com
Под капотом02
_t2 AS (SELECT * FROM _t1 WHERE f1<(SELECT _max.f1 FROM _max))
(f1<1)
row_number | f1 | f2
------------+----+-------
1 | 0 | 39213
2 | 0 | 86085
3 | 1 | 4750
4 | 1 | 10391
5 | 1 | 13418
6 | 1 | 13960
7 | 1 | 15210
8 | 1 | 54074
9 | 1 | 63252
10 | 2 | 11866
dataegret.com
Под капотом02
5-(SELECT COUNT(*) FROM _t2)
5-2 = 3 еще надо 3 строки с f1=1
row_number | f1 | f2
------------+----+-------
1 | 0 | 39213
2 | 0 | 86085
3 | 1 | 4750
4 | 1 | 10391
5 | 1 | 13418
6 | 1 | 13960
7 | 1 | 15210
8 | 1 | 54074
9 | 1 | 63252
10 | 2 | 11866
dataegret.com
Под капотом02
SELECT * FROM t WHERE f1=(SELECT _max.f1 FROM _max)
f1 = 1, всего 7 строк
row_number | f1 | f2
------------+----+-------
1 | 0 | 39213
2 | 0 | 86085
3 | 1 | 4750
4 | 1 | 10391
5 | 1 | 13418
6 | 1 | 13960
7 | 1 | 15210
8 | 1 | 54074
9 | 1 | 63252
10 | 2 | 11866
dataegret.com
Под капотом02
SELECT * FROM t WHERE f1=(SELECT _max.f1 FROM _max)
ORDER BY f1, f2 LIMIT (5 - (SELECT COUNT(*) FROM _t2))
f1 = 1, 5-2=3 строки
row_number | f1 | f2
------------+----+-------
1 | 0 | 39213
2 | 0 | 86085
3 | 1 | 4750
4 | 1 | 10391
5 | 1 | 13418
6 | 1 | 13960
7 | 1 | 15210
8 | 1 | 54074
9 | 1 | 63252
10 | 2 | 11866
dataegret.com
Под капотом02
SELECT * FROM _t2
UNION ALL (
SELECT * FROM t WHERE f1=(SELECT _max.f1 FROM _max)
ORDER BY f1, f2 LIMIT (5 - (SELECT COUNT(*) FROM _t2))
) ORDER BY f1,f2;
row_number | f1 | f2
------------+----+-------
1 | 0 | 39213
2 | 0 | 86085
3 | 1 | 4750
4 | 1 | 10391
5 | 1 | 13418
6 | 1 | 13960
7 | 1 | 15210
8 | 1 | 54074
03
Использование
индекса (f1, f2)
для запросов с
ORDER BY f2, f1
LIMIT N
dataegret.com
Описание проблемы03
Query with ORDER BY f2, f1 LIMIT N
But only INDEX ON (f1, f2) available with low distnct f1
By default – seq scan + sort
Lets try creatve approach to get beter results
dataegret.com
Подготовка данных03
CREATE TABLE t AS SELECT
id,
(Random()*1000)::integer AS f1,
(Random()*100000)::integer AS f2,
'some very boring payload text ...'::text AS payload
FROM generate_series(1,1000000) AS g(id);
CREATE INDEX t_f1_key ON t(f1, f2);
VACUUM ANALYZE t;
dataegret.com
Стандартная реализация на SQL03
SELECT
*
FROM t
ORDER BY f2, f1
LIMIT 2
dataegret.com
EXPLAIN ANALYZE03
Limit (actual rows=2 loops=1)
-> Sort (actual rows=2 loops=1)
Sort Key: f2, f1
Sort Method: top-N heapsort Memory: 35kB
-> Seq Scan on t (actual rows=1000000 loops=1)
Execution time: 574.482 ms
dataegret.com
Альтернативная реализация03
WITH RECURSIVE
--iterating over possible f1 values
rec AS (
--start from minimal f1
SELECT min(f1) AS f1 FROM t
UNION ALL
--looking for next f1
SELECT (SELECT min(f1) FROM t WHERE t.f1>rec.f1)
FROM rec WHERE rec.f1 IS NOT NULL
)
--return found values
SELECT _t1.* FROM rec,
LATERAL (SELECT * FROM t WHERE f1=rec.f1 ORDER BY f2 LIMIT 2) AS _t1
ORDER BY _t1.f2, _t1.f1 LIMIT 2;
dataegret.com
EXPLAIN ANALYZE03
Limit (actual rows=2 loops=1)
CTE rec
-> Recursive Union (actual rows=1002 loops=1)
-> Result (actual rows=1 loops=1)
InitPlan 2 (returns $1)
-> Limit (actual rows=1 loops=1)
-> Index Only Scan using t_f1_key on t t_2 (actual rows=1 loops=1)
Index Cond: (f1 IS NOT NULL)
-> WorkTable Scan on rec rec_1 (actual rows=1 loops=1002)
Filter: (f1 IS NOT NULL)
-> Limit (actual rows=1 loops=1001)
-> Index Only Scan using t_f1_key on t t_1 (actual rows=1 loops=1001)
Index Cond: (f1 > rec_1.f1)
-> Sort (actual rows=2 loops=1)
Sort Key: t.f2, t.f1
-> Nested Loop (actual rows=2002 loops=1)
-> CTE Scan on rec (actual rows=1002 loops=1)
-> Limit (actual rows=2 loops=1002)
-> Index Scan using t_f1_key on t (actual rows=2 loops=1002)
Index Cond: (f1 = rec.f1)
Execution time: 18.790 ms
dataegret.com
Под капотом03
SELECT f1, f2 FROM t ORDER BY f2, f1 LIMIT 2;
f1 | f2
-----+----
117 | 3
841 | 3
dataegret.com
Под капотом03
SELECT DISTINCT f1 FROM t ORDER BY f1;
f1
------
0
1
2
…
1000
dataegret.com
Под капотом03
--start from minimal f1
SELECT min(f1) AS f1 FROM t
f1=0
f1
------
0
1
2
3
…
1000
dataegret.com
Под капотом03
--смотрим следующее уникальное значение f1
SELECT (SELECT min(f1) FROM t WHERE t.f1>rec.f1) AS f1
FROM rec WHERE rec.f1 IS NOT NULL
предыдущий f1=0, получили f1=1
f1
------
0
1
2
…
1000
dataegret.com
Под капотом03
--смотрим следующее уникальное значение f1
SELECT (SELECT min(f1) FROM t WHERE t.f1>rec.f1 ) AS f1 FROM
rec WHERE rec.f1 IS NOT NULL
предыдущее f1=1, получили f1=2
f1
------
0
1
2
…
1000
dataegret.com
Под капотом03
--rinse and repeat 1000 раз
SELECT (SELECT min(f1) FROM t WHERE t.f1>rec.f1) AS f1 FROM
rec WHERE rec.f1 IS NOT NULL
предыдущий f1=999, получили f1=1000
f1
------
0
1
2
…
1000
dataegret.com
Под капотом03
– последний проход цикла
SELECT (SELECT min(f1) FROM t WHERE t.f1>rec.f1) AS f1 FROM
rec WHERE rec.f1 IS NOT NULL
previous f1=1000, now we have f1=NULL
f1
------
0
1
…
1000
Nothing
dataegret.com
Под капотом03
--выход
SELECT (SELECT min(f1) FROM t WHERE t.f1>rec.f1) AS f1 FROM
rec WHERE rec.f1 IS NOT NULL
previous f1=NULL, end loop
f1
------
0
1
…
1000
Nothing
dataegret.com
Под капотом03
--перебиваем все значения f1
f1 FROM rec
f1
------
0
1
…
1000
dataegret.com
Под капотом03
--и выбираем для них нужные f2
LATERAL (SELECT * FROM t WHERE f1=rec.f1 ORDER BY f2 LIMIT 2)
rec.f1=0
f1 | f2
----+------
0 | 422
0 | 1321
0 | 1891
0 | 3216
0 | 3533
dataegret.com
Под капотом03
--и выбираем для них нужные f2
LATERAL (SELECT * FROM t WHERE f1=rec.f1 ORDER BY f2 LIMIT 2)
rec.f1=1 и так далее до окончания списка f1
f1 | f2
----+------
1 | 2475
1 | 2490
1 | 2722
1 | 2921
1 | 7123
dataegret.com
Под капотом03
--loop over all DISTINCT f1 values from rec
LATERAL (SELECT * FROM t WHERE f1=rec.f1 ORDER BY f2 LIMIT 2)
rec.f1=1000
f1 | f2
------+-------
1000 | 1313
1000 | 1530
1000 | 6125
1000 | 7108
1000 | 13199
dataegret.com
Под капотом03
–- результат 2000 строк… 2 минимальных f2 для каждого
уникального значения f1:
f1 | f2
------+-------
0 | 422
0 | 1321
1 | 2475
1 | 2490
…
1000 | 1313
1000 | 1530
dataegret.com
Под капотом03
--сортируем что получилось и возвращаем итог
SELECT _t1.* FROM rec,
LATERAL (SELECT * FROM t WHERE f1=rec.f1 ORDER BY f2 LIMIT 2) AS _t1
ORDER BY _t1.f2, _t1.f1 LIMIT 2;
f1 | f2
------+-------
117 | 3
841 | 3
dataegret.com
Под капотом03
Оценочная сложность: COUNT(DISTINCT f1)*(requested LIMIT)
В нашем случае 1.000*2=2.000 vs 1.000.000.
Производительность с разными LIMIT:
LIMIT | NORMAL | CUSTOM
-----------------------------------
1 | 311ms | 21ms
10 | 325ms | 22ms
100 | 339ms | 60ms
1000 | 350ms | 533ms
04
Заключение
dataegret.com
Выводы04
Самый короткий запрос на SQL – далеко не всегда самый быстрый
Зачастую проще оказывается переписать вдумчиво запрос чем
начинать созавать новый индекс на 1Tb+ таблице (и смотреть где
можно купить еще пару дисков Intel Optane)
Sql очень гибкий язык с почти неограниченной гибкостью,
пользуйтейсь этим.
dataegret.com
Вопросы?04
dataegret.com Maxim Boguk
Дякую!

More Related Content

Similar to Учим слона танцевать рок'н'ролл #RuPostgres #DataEgret #MaxBoguk

Программирование на PySpark
Программирование на PySparkПрограммирование на PySpark
Программирование на PySpark
RamblerML
 
Новые возможности отладки MySQL 5.7 на практике
Новые возможности отладки MySQL 5.7 на практикеНовые возможности отладки MySQL 5.7 на практике
Новые возможности отладки MySQL 5.7 на практикеSveta Smirnova
 
Поиск? Sphinx!
Поиск? Sphinx!Поиск? Sphinx!
Поиск? Sphinx!
Roman Zaiev
 
Aleksei Milovidov "Let's optimize one aggregate function in ClickHouse"
Aleksei Milovidov "Let's optimize one aggregate function in ClickHouse"Aleksei Milovidov "Let's optimize one aggregate function in ClickHouse"
Aleksei Milovidov "Let's optimize one aggregate function in ClickHouse"
Fwdays
 
Современному хайлоду - современные решения: MySQL 8.0 и улучшения Percona
Современному хайлоду - современные решения: MySQL 8.0 и улучшения PerconaСовременному хайлоду - современные решения: MySQL 8.0 и улучшения Percona
Современному хайлоду - современные решения: MySQL 8.0 и улучшения Percona
Sveta Smirnova
 
User Defined Materials in LS-DYNA
User Defined Materials in LS-DYNAUser Defined Materials in LS-DYNA
User Defined Materials in LS-DYNA
Yury Novozhilov
 
#RuPostgresLive 4: как писать и читать сложные SQL-запросы
#RuPostgresLive 4: как писать и читать сложные SQL-запросы#RuPostgresLive 4: как писать и читать сложные SQL-запросы
#RuPostgresLive 4: как писать и читать сложные SQL-запросы
Nikolay Samokhvalov
 
DSLs in Lisp and Clojure
DSLs in Lisp and ClojureDSLs in Lisp and Clojure
DSLs in Lisp and Clojure
Vasil Remeniuk
 
Cергей Голубчик, Monty Program AB
Cергей Голубчик, Monty Program ABCергей Голубчик, Monty Program AB
Cергей Голубчик, Monty Program AB
Ontico
 
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 3...
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 3...PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 3...
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 3...
pgdayrussia
 
Расширения для PostgreSQL
Расширения для PostgreSQLРасширения для PostgreSQL
Расширения для PostgreSQL
Anastasia Lubennikova
 
Hacking PostgreSQL. Физическое представление данных
Hacking PostgreSQL. Физическое представление данныхHacking PostgreSQL. Физическое представление данных
Hacking PostgreSQL. Физическое представление данных
Anastasia Lubennikova
 
Sergii Tsypanov "Performance 1001 Tips"
Sergii Tsypanov "Performance 1001 Tips"Sergii Tsypanov "Performance 1001 Tips"
Sergii Tsypanov "Performance 1001 Tips"
LogeekNightUkraine
 
OpenACC short review
OpenACC short reviewOpenACC short review
OpenACC short review
Andrei Poliakov
 

Similar to Учим слона танцевать рок'н'ролл #RuPostgres #DataEgret #MaxBoguk (14)

Программирование на PySpark
Программирование на PySparkПрограммирование на PySpark
Программирование на PySpark
 
Новые возможности отладки MySQL 5.7 на практике
Новые возможности отладки MySQL 5.7 на практикеНовые возможности отладки MySQL 5.7 на практике
Новые возможности отладки MySQL 5.7 на практике
 
Поиск? Sphinx!
Поиск? Sphinx!Поиск? Sphinx!
Поиск? Sphinx!
 
Aleksei Milovidov "Let's optimize one aggregate function in ClickHouse"
Aleksei Milovidov "Let's optimize one aggregate function in ClickHouse"Aleksei Milovidov "Let's optimize one aggregate function in ClickHouse"
Aleksei Milovidov "Let's optimize one aggregate function in ClickHouse"
 
Современному хайлоду - современные решения: MySQL 8.0 и улучшения Percona
Современному хайлоду - современные решения: MySQL 8.0 и улучшения PerconaСовременному хайлоду - современные решения: MySQL 8.0 и улучшения Percona
Современному хайлоду - современные решения: MySQL 8.0 и улучшения Percona
 
User Defined Materials in LS-DYNA
User Defined Materials in LS-DYNAUser Defined Materials in LS-DYNA
User Defined Materials in LS-DYNA
 
#RuPostgresLive 4: как писать и читать сложные SQL-запросы
#RuPostgresLive 4: как писать и читать сложные SQL-запросы#RuPostgresLive 4: как писать и читать сложные SQL-запросы
#RuPostgresLive 4: как писать и читать сложные SQL-запросы
 
DSLs in Lisp and Clojure
DSLs in Lisp and ClojureDSLs in Lisp and Clojure
DSLs in Lisp and Clojure
 
Cергей Голубчик, Monty Program AB
Cергей Голубчик, Monty Program ABCергей Голубчик, Monty Program AB
Cергей Голубчик, Monty Program AB
 
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 3...
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 3...PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 3...
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 3...
 
Расширения для PostgreSQL
Расширения для PostgreSQLРасширения для PostgreSQL
Расширения для PostgreSQL
 
Hacking PostgreSQL. Физическое представление данных
Hacking PostgreSQL. Физическое представление данныхHacking PostgreSQL. Физическое представление данных
Hacking PostgreSQL. Физическое представление данных
 
Sergii Tsypanov "Performance 1001 Tips"
Sergii Tsypanov "Performance 1001 Tips"Sergii Tsypanov "Performance 1001 Tips"
Sergii Tsypanov "Performance 1001 Tips"
 
OpenACC short review
OpenACC short reviewOpenACC short review
OpenACC short review
 

Учим слона танцевать рок'н'ролл #RuPostgres #DataEgret #MaxBoguk