SlideShare une entreprise Scribd logo
1  sur  69
Télécharger pour lire hors ligne
PostgreSQL
Разработка приложений
Часть третья.
Возможности Postgres
l Views
l Массивы
l CTE
l Индексы
l Prepared statements
l Курсоры
l Разное
Пользователи
l Реально активно обычно 5-15% всех
пользователей.
l Обмен с диском идет постранично.
l Один пользователь — одна страница
l При сохранении в кеше тысячи пользователей
они будут занимать тысячу страниц; конкретно
строка пользователя будет занимать 1/8 — 1/200
страницы.
Пользователи
l Таким образом, 1000 пользователей
потребует 8М RAM, из которых будет реально
использоваться только 1000*200байт ≈ 20КБ
RAM.
l Кроме того, RAM будет использоваться еще и под
индекс.
− Кстати, Postgres не работает мимо кеша ОС;
следовательно, надо выделять или разумное
количество памяти, или почти всю.
l Сложим всех активных в секцию usr_active, а
неактивных — в usr_dormant
Union all
l select * from usr_active
union all
select * from usr_dormant
l От неактивных пользователей в кеше останется
только индекс.
l Можно еще лучше — создать функцию.
create function usr_all(ns integer[]) returns table(n integer) as
$code$
declare
v integer;
begin
foreach v in array ns loop
select a.n into usr_all.n from usr_active a where a.n=v;
if found then
return next;
else
select d.n into usr_all.n from usr_dormant d where d.n=v;
if found then
return next;
end if;
end if;
end loop;
end;
$code$
language plpgsql
create function usr_all(p_usr_id int) returns table(n integer) as
$code$
begin
select a.n into usr_all.n from usr_active a where a.n=p_usr_id;
if found then
return next;
else
select d.n into usr_all.n from usr_dormant d where d.n=p_usr_id;
if found then
return next;
end if;
end if;
end;
$code$
language plpgsql
Сравнение
l View — удобно; накладные расходы на индекс
l Функция —удобно (относительно); отсутствие
накладных расходов на индекс.
l Функция для LATERAL – удобно, отсутствие
накладных расходов на индекс, наличие
накладных расходов на вызов функции.
l Так ли уж это все надо?
− Скорее всего, нет. Но про страничный обмен забывать
все же не стоит.
− pg_repack, pg_reorg
LATERAL
• Что это такое?
select n, t
from generate_series(1,5) as n,
lateral(select max(t) as t
from generate_series(n*2,n*3) as gs1(t)
) as t
n | t
---+----
1 | 3
2 | 6
3 | 9
4 | 12
5 | 15
LATERAL
• select n, t
from generate_series(1,2) as n,
lateral(select t as t
from generate_series(n*2,n*3) as gs1(t)
) as t
n | t
---+--
1 | 2
1 | 3
2 | 4
2 | 5
2 | 6
LATERAL – зачем это надо?
• VIEW с параметрами
• Функции теперь first-class citizens
• Очевидное применение:
СЕКЦИОНИРОВАНИЕ
• В примере выше – неактивных пользователей
можно хранить в массивах, построив индекс
по range по их id.
• Не ограниченно только встроенными
средствами – можно делать произвольно.
View с параметрами
l Часто хотят view с параметрами
l Например, для пользователей показывать,
кто друг просматривающему; параметр — id
просматривающего пользователя
l Что делать?
l Декартово произведение. Да, именно декартово
произведение.
l N*1=N. Таблица параметров должна возвращать
единственную строку.
View с параметрами
create or replace view usr_cart as
select p.id as viewer_id,
u.*,
exists(select * from friend f
where f.usr_id=viewer_id
and f.friend_id=u.id)
as isfriend
from usr u, usr p
Используем:
select *
from usr_cart
where id in(...)
and viewer_id=$1
View с параметрами
l А что делать, если надо и без viewer_id?
Подставим несуществующий:
l create or replace view usr_cart as
select p.id as viewer_id,
u.*,
exists(select * from friend f
where f.usr_id=viewer_id
and f.friend_id=u.id)
as isfriend
from usr u, (select 0 as id
union all
select id from usr) as p
l Используем:
select *
from usr_cart
where id in(...)
and viewer_id=coalesce($1,0)
В отличие от CTE не создает
временной таблицы и
оптимизатор знает как это
оптимизировать
Top N для M
l Например, 10 последних постов из самых
популярных тем. Или 10 последних покупок в
категориях.
l Очевидный запрос
select p.*
from post p, topic t
where p.id in (select p1.id
from post p1
where p1.topic_id=t.id
order by added desc
limit 10
)
Top N для M
l К сожалению, не работает. Точнее, работает, но
для больших объемов неприемлемо медленно.
Обманываем оптимизатор
select p.*
from post p, topic t
where p.id=any(array(select p1.id
from post p1
where p1.topic_id=t.id
order by added desc
limit 10)
)
Top N для M
l Насколько эффективно это работает?
l Вообще говоря, вполне эффективно
l Если миллионы элементов, то все не так здорово
Common Table Expression
with query_name as(
select * from tbl
)
select * from query_name
Зачем надо?
l Удобно: эдакое маленькое view в запросе
l Как и всякое view, позволяет отдельно описывать
отдельные логические понятия и не дублировать
код.
l Результаты живут в work_mem (или вытесняются
на диск); по сути — временные таблицы.
l И, наконец, рекурсия.
Recursive CTE
with recursive tq as(
select 1 as n --1
union all
select n+1 from tq where n<10 --2
)
select * from tq
Что получилось
• N
----
1
2
3
4
5
6
7
8
9
10
(10 строк)
Разворачиваем дерево
Способ №1 или линейная
рекурсия
•with recursive
tree(id, parent_id, name) as
(values(1,null,'Адам'),
(2,1,'Авель'),
(3,1,'Каин'),
(4,2,'Енох')
),
with_parent as(
select *, null as parent_name
from tree
where parent_id is null
union all
select tree.*, wp.name
from tree, with_parent wp
where tree.parent_id=wp.id
)
select * from with_parent
id | parent_id | name | parent_name
----+-----------+-------+-------------
1 | | Адам |
3 | 1 | Каин | Адам
2 | 1 | Авель | Адам
4 | 2 | Енох | Авель
(4 строки)
Ну и в чем разница?
with recursive tree(id,
parent_id, name) as
(
values(1,null,'Адам'),
(2,1,'Авель'),
(3,1,'Каин'),
(4,2,'Енох')
)
select *,
(select t.name
from tree t
where tree.parent_id=t.id
) as parent_name
from tree
id | parent_id | name | parent_name
----+-----------+-------+-------------
1 | | Адам |
3 | 1 | Каин | Адам
2 | 1 | Авель | Адам
4 | 2 | Енох | Авель
(4 строки)
А вот в чем
with recursive tree(id, parent_id, name)
as(
values(1,null,'Адам'),
(2,1,'Авель'),
(3,1,'Каин'),
(4,2,'Енох')),
with_parent as(
select *, 'Бог' as ancestors_names
from tree
where parent_id is null
union all
select tree.*,
wp.ancestors_names||','||wp.name
from tree, with_parent wp
where tree.parent_id=wp.id)
•select * from with_parent
id | parent_id | name | ancestors_names
----+-----------+-------+-----------------
1 | | Адам | Бог
3 | 1 | Каин | Бог,Адам
2 | 1 | Авель | Бог,Адам
4 | 2 | Енох | Бог,Адам,Авель
(4 строки)
Нелинейная рекурсия
with recursive tree(id, parent_id, name) as(
values(1,null,'Адам'),
(2,1,'Авель'),(3,1,'Каин'),
(4,2,'Енох'), (5,null,'Ева'),
(6,5,'Дочь Евы'),(7,4,'Ирад')),
expand_tree as(
select id,parent_id,name from tree
union(
with expand_tree as(
select * from expand_tree
)
select e1.id, e1.parent_id, e2.name
from expand_tree e1,
expand_tree e2
where e2.parent_id=e1.id
)
)
select * from expand_tree e where id=1
Нелинейная рекурсия-2
id | parent_id | name
----+-----------+-------
1 | | Адам
1 | | Каин
1 | | Авель
1 | | Енох
1 | | Ирад
(5 строк)
Нелинейная рекурсия-3
Как это, собственно, работает
...where name='Ирад'
id | parent_id | name
----+-----------+------
7 | 4 | Ирад
4 | 2 | Ирад
2 | 1 | Ирад
1 | | Ирад
(4 строки)
Путь в циклическом графе
l Создаем граф.
− Создаем вершины:
create table vertex as
select n
from generate_series(1,10000) as gs(n);
alter table vertex add constraint vertex_pk
primary key (n);
− Создаем вершины:
create table edge(f,t) as select distinct (random()
*10000)::integer as from, (random()*10000)::integer as to from
generate_series(1,100000) as gs(n);
create index edge_ft on edge(f,t);
Ищем путь
l Весьма просто:
with recursive tq as(
select 1 as pass, n, array[n] as path
from vertex where n=1
union all
(with t as (select * from tq)
select pass+1, v.n, t.path||v.n
from vertex v, t, edge e
where v.n=e.t and t.n=e.f
and not (e.t=any(t.path))
and pass<8
and not exists(select * from t where n=5000)
)
)
select * from tq where n=5000
Ищем путь-2
pass | n | path
------+------+------------------------
5 | 5000 | {1,7000,6204,922,5000}
(1 строка)
•"CTE Scan on tq (cost=1314.97..1321.74 rows=2 width=40)
(actual time=115.405..132.329 rows=1 loops=1)"
Что дальше
l CTE как конечный автомат. Со стеком! Разбор
JSON'а.
− Как?
l Сделать одним запросом? Реально, но громоздко.
l plpgsql
l C-libraries
− Уже встроенный тип
l All art is quite useless
l Разбираем JSON. JSONPath навыворот
− Пример: $.users.[100].id
Разбор JSON
with recursive
tokens as(
select s[1] as token, row_number() over() as n
from regexp_matches($JS$
{
"data": [
{"name": "User1 User1", "id": "768709679"},
{"name": "User2 User2","id": "10604454123"}
]
"paging": {
"next": "https://graph.facebook.com/10000223"
}
}
$JS$,
Разбор JSON
$RE$(
(?:[][}{])
|
(?:" (?:"|[^"])+ ")
|
w+
|
s+
|
[:,]
)$RE$,
'gx') as g(s)
where s[1] !~ $RE$^[s:,s]+$$RE$
),
Разбор JSON. Промежуточный результат.
token | n
---------------------------------------+----
{ | 1
"data" | 2
[ | 3
{ | 4
"name" | 5
"User1 User1" | 6
"id" | 7
"768709679" | 8
} | 9
{ | 10
"name" | 11
"User2 User2" | 12
"id" | 13
"10604454123" | 14
} | 15
] | 16
"paging" | 17
{ | 18
"next" | 19
"https://graph.facebook.com/10000223" | 20
} | 21
} | 22
(22 строки)
Разбор JSON.parsed as(
select n,
token as token,
array[token] as stack,
array['$']::text[] as path,
'' as jsp,
array[0]::integer[] as poses
from tokens t where n=1
union all
select t.n,
t.token as token,
case when t.token in (']','}') then p.stack[1:array_upper(p.stack,1)-1]
when t.token in ('[','{') then p.stack || t.token
else p.stack
end,
case when t.token in ('[','{') then p.path ||
case when p.stack[array_upper(p.stack,1)]='{'
then regexp_replace(p.token,'^"|"$','','g')
else '[' || (p.poses[array_upper(p.poses,1)]+1)::text || ']'
end
when t.token in (']','}') then p.path[1:array_upper(p.path,1)-1]
else p.path
end,
case when p.stack[array_upper(p.stack,1)]='{' then p.token
when p.stack[array_upper(p.stack,1)]='[' then '[' ||
(p.poses[array_upper(p.poses,1)]+1)::text || ']'
else ''
end,
case when t.token in ('[','{') then p.poses[1:array_upper(p.poses,1)-1]||
(p.poses[array_upper(p.poses,1)]+1)||0
when t.token in (']','}') then p.poses[1:array_upper(p.poses,1)-1]
else p.poses[1:array_upper(p.poses,1)-1]||
(p.poses[array_upper(p.poses,1)]+1)
end
from parsed p, tokens t where t.n=p.n+1),
Разбор JSON.
Промежуточный результат.
n | token | stack | path | jsp | poses
----+---------------------------------------+-------------+--------------+---------------------------------------+---------
1 | { | {"{"} | {$} | | {0}
2 | "data" | {"{"} | {$} | { | {1}
3 | [ | {"{",[} | {$,data} | "data" | {2,0}
4 | { | {"{",[,"{"} | {$,data,[1]} | [1] | {2,1,0}
5 | "name" | {"{",[,"{"} | {$,data,[1]} | { | {2,1,1}
6 | "User1 User1" | {"{",[,"{"} | {$,data,[1]} | "name" | {2,1,2}
7 | "id" | {"{",[,"{"} | {$,data,[1]} | "User1 User1" | {2,1,3}
8 | "768709679" | {"{",[,"{"} | {$,data,[1]} | "id" | {2,1,4}
9 | } | {"{",[} | {$,data} | "768709679" | {2,1}
10 | { | {"{",[,"{"} | {$,data,[2]} | [2] | {2,2,0}
11 | "name" | {"{",[,"{"} | {$,data,[2]} | { | {2,2,1}
12 | "User2 User2" | {"{",[,"{"} | {$,data,[2]} | "name" | {2,2,2}
13 | "id" | {"{",[,"{"} | {$,data,[2]} | "User2 User2" | {2,2,3}
14 | "10604454123" | {"{",[,"{"} | {$,data,[2]} | "id" | {2,2,4}
15 | } | {"{",[} | {$,data} | "10604454123" | {2,2}
16 | ] | {"{"} | {$} | [3] | {2}
17 | "paging" | {"{"} | {$} | ] | {3}
18 | { | {"{","{"} | {$,paging} | "paging" | {4,0}
19 | "next" | {"{","{"} | {$,paging} | { | {4,1}
20 | "https://graph.facebook.com/10000223" | {"{","{"} | {$,paging} | "next" | {4,2}
21 | } | {"{"} | {$} | "https://graph.facebook.com/10000223" | {4}
22 | } | {} | {} | } | {}
(22 строки)
Разбор JSON.
res as(
select *
from parsed
where (stack[array_upper(stack,1)]='['
or (stack[array_upper(stack,1)]='{'
and poses[array_upper(poses,1)]%2=0)
)
and token not in ('{','[','}',']')
)
select array_to_string(path,'.')||'.'||
regexp_replace(jsp,'^"|"$','','g')
as json_path,
regexp_replace(token,'^"|"$','','g')
as json_value
from res
Результат
json_path | json_value
-----------------+-------------------------------------
$.data.[1].name | User1 User1
$.data.[1].id | 768709679
$.data.[2].name | User2 User2
$.data.[2].id | 10604454123
$.paging.next | https://graph.facebook.com/10000223
(5 строк)
А зачем это надо?
l Вообще полезно знать.
l Иногда нет возможности установить что-то
дополнительно
l Не только JSON — что угодно с относительно
несложным синтаксисом.
l Можно предложить еще способ — например,
строить дерево со ссылками на родителя,
рекурсивно же разбирать аналог JSONPath и
добираться до элементов.
− И не только JSON — пути в дереве вообще.
with recursive fs(id,pid,name) as(
values
(1,null,null),
(2,1,'usr'),
(3,2,'local'),
(4,3,'postgres'),
(5,4,'bin')
),
path as(
select p, row_number() over() as n
from regexp_split_to_table('/usr/local/postgres/bin','/')
as v(p)
),
parsed as(
select id, name,p,n
from fs, path where fs.pid is null and path.p=''
union all
select fs.id, fs.name, path.p, path.n
from fs, path, parsed
where fs.pid=parsed.id and fs.name=path.p
and path.n=parsed.n+1
)
select * from parsed order by n desc limit 1
Что получилось
• id | name | p | n
•----+------+-----+---
• 5 | bin | bin | 5
•(1 строка)
Prepared statements
l Увеличивают производительность — не требуется
каждый раз
− разбирать запрос
− Нагружать каталог на предмет существования таблиц,
индексов и т. п.
− Проверять права доступа к объектам
− А в случае широкого использования view все может
оказаться еще интереснее
l prepare <sthname> as <real query>
l execute <sthname>(par1,par2...parN)
Prepared statements & plpgsql
l В plpgsql отсутствует аналог пакета DBMS_SQL —
prepared statements недоступны.
− Каждый статический запрос компилируется один
раз при обращении или перекомпилируется при
изменении объектов
l Внимание: раньше была проблема, если объекты, на которые
ссылается statement, в момент начала его выполнения еще
существовали, но до обращения к ним были удалены.
Теперь ее больше нет.
l Типичный пример: переименование таблицы при высокой
OLAP-нагрузке
l Что делать?
Advisory locks
l Решение 1: рекомендательные блокировки
− pg_advisory_lock, pg_advisory_xact_lock, _shared,
pg_try_
l Решение 2: обычные блокировки. Таблица
блокировок. select … for share.
Prepared statements
l А точно ли недоступны prepared statements в
plpgsql?
l Конечно доступны: execute '….':
execute 'prepare sth1 for select 1';
execute 'execute sth1' into res;
l Execute 'execute' — а вот параметры, увы,
действительно недоступны. quote_literal(),
quote_ident()
Prepared statements
l Насколько это быстро?
− Кстати: функции времени: now() возвращает
дату и время начала транзакции. Используйте
clock_timestamp()
− Кстати - анонимные блоки:
l do $$ code $$
− Кстати: $$-quoting:
l $$, $RE$, $Im$, $ANYTHING_ELSE$
− select $Re$^("|[^"])+$Re$
Код
do $code$
declare
i integer;
ts1 timestamp with time zone;
ts2 timestamp with time zone;
res integer;
begin
execute 'prepare sth1 as select 1';
ts1:=clock_timestamp();
for i in 1..100000 loop
-- execute 'execute sth1' into res;
-- select 1 into res;
-– execute 'select 1' into res;
–- Реальная таблица: select 1 as n into stht
–- execute 'prepare sth1 as select n from stht';
–- select n into res from stht
–- execute 'select n from stht' into res
end loop;
ts2:=clock_timestamp();
raise notice 'diff=%', (ts2-ts1)::text;
end;
$code$
Результаты
А зачем это, собственно,
надо?
l Разбор
l Построение плана
l Выполнение
l Нередко построение плана > выполнение
l С динамическим выполнением все идет каждый
раз по новой
l С prepared — только один раз
l pg_prepared_statements
Prepared statements
l Возвращаем резалтсет
− return query execute
− только обернув в функцию
l Что еще?
− save_record('table_name',
'column1_name','column2_value',
'column2_name','column2_value'...
)
l plpgsql — функции с переменным числом параметров
Кстати: upsert и вставка в
несколько таблиц
Подход в лоб:
with
input as
(select n from generate_series(1,10) as gs(n)),
upd as(
update ups set val=i.n from input i
where ups.n=i.n returning i.n
),
ins as(
insert into ups select * from input i
where not exists(select * from upd u
where i.n=u.n)
returning ups.n
)
select (select count(*) from upd) as updates,
(select count(*) from ins) as inserts
Upsert
l Подход в лоб не работает — race condition: после
обновления другой процесс может добавить
новые строки, и при вставке возникнет ошибка.
l Как правильно:
− Для каждой вставляемой строки
l Попытаться обновить
l Обновилась — хорошо, не обновилось — добавляем
l При нарушении уникальности повторяем попытку добавления
строки с самого начала
Кстати: plpgsql, savepoint и
исключения
l В plpgsql нет явных savepoint
l В plpgsql при использовании исключения в блоке
на входе в блок ставится неявный savepoint
l При возникновении исключения в блоке
происходит откат к нему
l Пример:
Кстати: plpgsql, savepoint и
исключения
•do $code$
begin
create temporary table
excdemo as select 1 as n;
begin
update excdemo set n=10;
raise notice
'BEFORE exception n=%',
(select n from excdemo);
raise sqlstate
'EX001';
exception
when sqlstate 'EX001' then
raise notice
'AFTER exception n=%',
(select n from excdemo);
end;
end;
$code$
•ЗАМЕЧАНИЕ: BEFORE exception
n=10
•ЗАМЕЧАНИЕ: AFTER exception
n=1
Кстати: plpgsql, savepoint и
исключения
l Позволяет делать логгирование
− Не нужны автономные транзакции (ну, почти не
нужны...)
l Позволяет выполнять потенциально
некорректный код!
− Это хорошо, а не плохо! Можно в живой боевой
сервер на ходу добавлять код, не обваливая все по
ошибке.
l «Можно» - разумеется, не значит «нужно».
Курсоры
l Задача — resultset из фунции
− funcname(..) returns table(colname coltype...)
− select * from funcname(...)
l Задача расширилась — несколько resultset'ов из
функции
l Задача еще более расширилась — МНОГО
resultset'ов
l Причем переменное число
l Что делать?
Курсоры
l Курсоры существуют только в пределах
транзакции
− Начало транзакции
− Вызов фунции
− Fetch all from <cursor name1>
− Fetch all from <cursor name2>
− …
− Fetch all from <cursor nameN>
− commit
Курсоры
l
refcursor
В сущности, курсор — это просто его имя; имя
курсора — строка
declare
cursname1 refcursor;
...
begin
...
open cursname1 for select ...
...
return array[cursname1,cursname2...]
Зачем надо
Курсоры
l Требуется поддержка на клиенте
l Для курсоров она реализуется несложно — в perl
или php достаточно рекурсивно обойти resultset и
посмотреть типы колонок.
l В Java — используя декоратор, создать класс,
возвращающий ResultSet (как именно его
создавать — отдельная история)
l Для json можно поступить аналогично
− Отличть json от text на клиенте малореально. Хак:
можно сделать domain для text и смотреть длину.
generic_cursor
CREATE OR REPLACE FUNCTION generic_cursor(IN sql text, VARIADIC param text[] DEFAULT
ARRAY[]::text[])
RETURNS refcursor AS $BODY$
declare rv refcursor;
sthhash text := 'GENCURS'||md5(sql);
exec text := 'execute ' || sthhash ||
case when array_length(param,1) is null then ''
else '('||(select string_agg(coalesce(quote_literal(pv),'NULL'),',') from
unnest(param) as u(pv)) ||')' end;
begin
begin
open rv for execute exec;
exception
when sqlstate '26000' -- prepared statement does not exist
or sqlstate '0A000' -- table definition had changed
or sqlstate '42703' --- -/-
or sqlstate '42P11'
then
if sqlstate='0A000' or sqlstate='42703' then
execute 'deallocate '||sthhash;
end if;
execute 'prepare '||sthhash|| coalesce((select '('|| string_agg('text',',') |
| ')' from unnest(param) as u(pv)),'') ||' as '||sql;
open rv for execute exec;
end;
return rv;
end; $BODY$ LANGUAGE plpgsql
generic_exec
CREATE OR REPLACE FUNCTION generic_exec(
IN sql text,
VARIADIC param text[] DEFAULT
ARRAY[]::text[])
RETURNS SETOF record AS $BODY$
declare
cr refcursor:=
generic_cursor(sql, variadic param);
begin
return query execute 'fetch all from '||
quote_ident(cr::text); end;
$BODY$ LANGUAGE plpgsql
Используем
select *
from
generic_exec('select n from
generate_series(1,$1::integer)
as gs(n)',
20::text) as ge(n integer)
А оно надо? Да!
do $code$
declare
rv tt;
s1 timestamp;
s2 timestamp;
s3 timestamp;
i integer;
begin
s1:=clock_timestamp();
for i in 1..1000 loop
execute 'select t1.* from tt t1, tt t2, tt t3, tt t4, tt t5, tt t7, tt t8
where t1.id=t2.id and t2.id=t3.id and t3.id=t4.id and t1.id=t5.id and t5.id=t7.id and
t5.id=t8.id and t8.id=9999
and t1.id between 1 and 10000 and t4.id between 9999 and 200000 and t3.id>9000 and
exists(select * from tt t6 where t6.id=t5.id)
and t4.cnt=49 order by t4.cnt' into rv;
end loop;
s2:=clock_timestamp();
for i in 1..1000 loop
select * into rv from generic_exec('select t1.* from tt t1, tt t2, tt t3, tt t4, tt t5,
tt t7, tt t8
where t1.id=t2.id and t2.id=t3.id and t3.id=t4.id and t1.id=t5.id and t5.id=t7.id and
t5.id=t8.id and t8.id=9999
and t1.id between 1 and 10000 and t4.id between 9999 and 200000 and t3.id>9000 and
exists(select * from tt t6 where t6.id=t5.id)
and t4.cnt=49 order by t4.cnt') as ge(n integer, cnt bigint);
end loop;
s3:=clock_timestamp();
raise notice 'Run 1=%',s2-s1;
raise notice 'Run 2=%',s3-s2;
end; $code$
Результат
•ЗАМЕЧАНИЕ: Run 1=00:00:04.867
•ЗАМЕЧАНИЕ: Run 2=00:00:00.207
То же самое, но многопоточно
•$ pgbench -M prepared -U postgres -t 500 -n -j 2 -c 10 -f
generic_exec.pgb work
number of clients: 10
number of threads: 2
number of transactions per client: 500
number of transactions actually processed: 5000/5000
tps = 2340.001464 (including connections establishing)
tps = 2508.521577 (excluding connections establishing)
•$ pgbench -M prepared -U postgres -t 500 -n -j 2 -c 10 -f
plain_execute.pgb work
query mode: prepared
number of clients: 10
number of threads: 2
number of transactions per client: 500
number of transactions actually processed: 5000/5000
tps = 146.373869 (including connections establishing)
tps = 147.008750 (excluding connections establishing)
Альтернатива
l
JSON — в 9.2 row_to_json, array_to_json
− select
array_to_json(
array(
select row_to_json(c.*)
from pg_class c
)
)
l
В предыдущих версиях легко можно написать и
самостоятельно, используя, например, hstore
l
Можно просто возвращать массив hstore и
разбирать на клиенте
Заключение
l И снова: а зачем все это, собственно, надо?
l Производительность
l Переносимость
l Вычурность
l Пробуйте!
l Вопросы?

Contenu connexe

Tendances

Why Every Language Needs Its Underscore
Why Every Language Needs Its UnderscoreWhy Every Language Needs Its Underscore
Why Every Language Needs Its UnderscoreAlexander Schepanovski
 
Лекция 6. Фибоначчиевы кучи (Fibonacci heaps)
Лекция 6. Фибоначчиевы кучи (Fibonacci heaps)Лекция 6. Фибоначчиевы кучи (Fibonacci heaps)
Лекция 6. Фибоначчиевы кучи (Fibonacci heaps)Mikhail Kurnosov
 
Лекция 7. Бинарные кучи. Пирамидальная сортировка
Лекция 7. Бинарные кучи. Пирамидальная сортировкаЛекция 7. Бинарные кучи. Пирамидальная сортировка
Лекция 7. Бинарные кучи. Пирамидальная сортировкаMikhail Kurnosov
 
«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНС
«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНС«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНС
«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНСit-people
 
Лекция 4: Стек. Очередь
Лекция 4: Стек. ОчередьЛекция 4: Стек. Очередь
Лекция 4: Стек. ОчередьMikhail Kurnosov
 
Haskell Type System with Dzmitry Ivashnev.
Haskell Type System with Dzmitry Ivashnev.Haskell Type System with Dzmitry Ivashnev.
Haskell Type System with Dzmitry Ivashnev.Sergey Tihon
 
Python&Printer / Андрей Пучко / penta.by
Python&Printer / Андрей Пучко / penta.byPython&Printer / Андрей Пучко / penta.by
Python&Printer / Андрей Пучко / penta.byPython Meetup
 
Лекция 10. Графы. Остовные деревья минимальной стоимости
Лекция 10. Графы. Остовные деревья минимальной стоимостиЛекция 10. Графы. Остовные деревья минимальной стоимости
Лекция 10. Графы. Остовные деревья минимальной стоимостиMikhail Kurnosov
 
Лекция 2. Красно-чёрные деревья (Red-black trees). Скошенные деревья (Splay t...
Лекция 2. Красно-чёрные деревья (Red-black trees). Скошенные деревья (Splay t...Лекция 2. Красно-чёрные деревья (Red-black trees). Скошенные деревья (Splay t...
Лекция 2. Красно-чёрные деревья (Red-black trees). Скошенные деревья (Splay t...Mikhail Kurnosov
 
Лекция 1. Анализ эффективности алгоритмов
Лекция 1. Анализ эффективности алгоритмовЛекция 1. Анализ эффективности алгоритмов
Лекция 1. Анализ эффективности алгоритмовMikhail Kurnosov
 
Алгоритмы и структуры данных осень 2013 лекция 4
Алгоритмы и структуры данных осень 2013 лекция 4Алгоритмы и структуры данных осень 2013 лекция 4
Алгоритмы и структуры данных осень 2013 лекция 4Technopark
 
Лекция 7: Очереди с приоритетами. Бинарные кучи (пирамиды)
Лекция 7: Очереди с приоритетами. Бинарные кучи (пирамиды)Лекция 7: Очереди с приоритетами. Бинарные кучи (пирамиды)
Лекция 7: Очереди с приоритетами. Бинарные кучи (пирамиды)Mikhail Kurnosov
 
Алгоритмы и структуры данных осень 2013 лекция 1
Алгоритмы и структуры данных осень 2013 лекция 1Алгоритмы и структуры данных осень 2013 лекция 1
Алгоритмы и структуры данных осень 2013 лекция 1Technopark
 
CPU Performance in Java.
CPU Performance in Java.CPU Performance in Java.
CPU Performance in Java.Dzmitry Hil
 
Алгоритмы и структуры данных весна 2014 лекция 1
Алгоритмы и структуры данных весна 2014 лекция 1Алгоритмы и структуры данных весна 2014 лекция 1
Алгоритмы и структуры данных весна 2014 лекция 1Technopark
 
Семинар 2. Многопоточное программирование на OpenMP (часть 2)
Семинар 2. Многопоточное программирование на OpenMP (часть 2)Семинар 2. Многопоточное программирование на OpenMP (часть 2)
Семинар 2. Многопоточное программирование на OpenMP (часть 2)Mikhail Kurnosov
 
Лекция 8: Многопоточное программирование: Intel Threading Building Blocks
Лекция 8: Многопоточное программирование: Intel Threading Building BlocksЛекция 8: Многопоточное программирование: Intel Threading Building Blocks
Лекция 8: Многопоточное программирование: Intel Threading Building BlocksMikhail Kurnosov
 
Алгоритмы и структуры данных осень 2013 лекция 2
Алгоритмы и структуры данных осень 2013 лекция 2Алгоритмы и структуры данных осень 2013 лекция 2
Алгоритмы и структуры данных осень 2013 лекция 2Technopark
 
Алексей Куканов — Параллелизм в C++: управляйте приложением, а не потоками!
Алексей Куканов — Параллелизм в C++: управляйте приложением, а не потоками!Алексей Куканов — Параллелизм в C++: управляйте приложением, а не потоками!
Алексей Куканов — Параллелизм в C++: управляйте приложением, а не потоками!Yandex
 
Лекция 7. Стандарт OpenMP (подолжение)
Лекция 7. Стандарт OpenMP (подолжение)Лекция 7. Стандарт OpenMP (подолжение)
Лекция 7. Стандарт OpenMP (подолжение)Mikhail Kurnosov
 

Tendances (20)

Why Every Language Needs Its Underscore
Why Every Language Needs Its UnderscoreWhy Every Language Needs Its Underscore
Why Every Language Needs Its Underscore
 
Лекция 6. Фибоначчиевы кучи (Fibonacci heaps)
Лекция 6. Фибоначчиевы кучи (Fibonacci heaps)Лекция 6. Фибоначчиевы кучи (Fibonacci heaps)
Лекция 6. Фибоначчиевы кучи (Fibonacci heaps)
 
Лекция 7. Бинарные кучи. Пирамидальная сортировка
Лекция 7. Бинарные кучи. Пирамидальная сортировкаЛекция 7. Бинарные кучи. Пирамидальная сортировка
Лекция 7. Бинарные кучи. Пирамидальная сортировка
 
«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНС
«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНС«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНС
«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНС
 
Лекция 4: Стек. Очередь
Лекция 4: Стек. ОчередьЛекция 4: Стек. Очередь
Лекция 4: Стек. Очередь
 
Haskell Type System with Dzmitry Ivashnev.
Haskell Type System with Dzmitry Ivashnev.Haskell Type System with Dzmitry Ivashnev.
Haskell Type System with Dzmitry Ivashnev.
 
Python&Printer / Андрей Пучко / penta.by
Python&Printer / Андрей Пучко / penta.byPython&Printer / Андрей Пучко / penta.by
Python&Printer / Андрей Пучко / penta.by
 
Лекция 10. Графы. Остовные деревья минимальной стоимости
Лекция 10. Графы. Остовные деревья минимальной стоимостиЛекция 10. Графы. Остовные деревья минимальной стоимости
Лекция 10. Графы. Остовные деревья минимальной стоимости
 
Лекция 2. Красно-чёрные деревья (Red-black trees). Скошенные деревья (Splay t...
Лекция 2. Красно-чёрные деревья (Red-black trees). Скошенные деревья (Splay t...Лекция 2. Красно-чёрные деревья (Red-black trees). Скошенные деревья (Splay t...
Лекция 2. Красно-чёрные деревья (Red-black trees). Скошенные деревья (Splay t...
 
Лекция 1. Анализ эффективности алгоритмов
Лекция 1. Анализ эффективности алгоритмовЛекция 1. Анализ эффективности алгоритмов
Лекция 1. Анализ эффективности алгоритмов
 
Алгоритмы и структуры данных осень 2013 лекция 4
Алгоритмы и структуры данных осень 2013 лекция 4Алгоритмы и структуры данных осень 2013 лекция 4
Алгоритмы и структуры данных осень 2013 лекция 4
 
Лекция 7: Очереди с приоритетами. Бинарные кучи (пирамиды)
Лекция 7: Очереди с приоритетами. Бинарные кучи (пирамиды)Лекция 7: Очереди с приоритетами. Бинарные кучи (пирамиды)
Лекция 7: Очереди с приоритетами. Бинарные кучи (пирамиды)
 
Алгоритмы и структуры данных осень 2013 лекция 1
Алгоритмы и структуры данных осень 2013 лекция 1Алгоритмы и структуры данных осень 2013 лекция 1
Алгоритмы и структуры данных осень 2013 лекция 1
 
CPU Performance in Java.
CPU Performance in Java.CPU Performance in Java.
CPU Performance in Java.
 
Алгоритмы и структуры данных весна 2014 лекция 1
Алгоритмы и структуры данных весна 2014 лекция 1Алгоритмы и структуры данных весна 2014 лекция 1
Алгоритмы и структуры данных весна 2014 лекция 1
 
Семинар 2. Многопоточное программирование на OpenMP (часть 2)
Семинар 2. Многопоточное программирование на OpenMP (часть 2)Семинар 2. Многопоточное программирование на OpenMP (часть 2)
Семинар 2. Многопоточное программирование на OpenMP (часть 2)
 
Лекция 8: Многопоточное программирование: Intel Threading Building Blocks
Лекция 8: Многопоточное программирование: Intel Threading Building BlocksЛекция 8: Многопоточное программирование: Intel Threading Building Blocks
Лекция 8: Многопоточное программирование: Intel Threading Building Blocks
 
Алгоритмы и структуры данных осень 2013 лекция 2
Алгоритмы и структуры данных осень 2013 лекция 2Алгоритмы и структуры данных осень 2013 лекция 2
Алгоритмы и структуры данных осень 2013 лекция 2
 
Алексей Куканов — Параллелизм в C++: управляйте приложением, а не потоками!
Алексей Куканов — Параллелизм в C++: управляйте приложением, а не потоками!Алексей Куканов — Параллелизм в C++: управляйте приложением, а не потоками!
Алексей Куканов — Параллелизм в C++: управляйте приложением, а не потоками!
 
Лекция 7. Стандарт OpenMP (подолжение)
Лекция 7. Стандарт OpenMP (подолжение)Лекция 7. Стандарт OpenMP (подолжение)
Лекция 7. Стандарт OpenMP (подолжение)
 

En vedette

Siyer-i Nebi 7. Sayı
Siyer-i Nebi 7. SayıSiyer-i Nebi 7. Sayı
Siyer-i Nebi 7. Sayısiyerinebi
 
TRiLOC GPS LOCATOR
TRiLOC GPS LOCATORTRiLOC GPS LOCATOR
TRiLOC GPS LOCATORLou Giuffre
 
PG Day'14 Russia, Социальная сеть, которая просто работает, Владислав Коваль
PG Day'14 Russia, Социальная сеть, которая просто работает, Владислав КовальPG Day'14 Russia, Социальная сеть, которая просто работает, Владислав Коваль
PG Day'14 Russia, Социальная сеть, которая просто работает, Владислав Ковальpgdayrussia
 
This is What Happens When You Automate Stories for the AP
This is What Happens When You Automate Stories for the APThis is What Happens When You Automate Stories for the AP
This is What Happens When You Automate Stories for the APAutomated Insights
 
Kertaskerjaharikeluargadansukaneka 120520050502-phpapp01
Kertaskerjaharikeluargadansukaneka 120520050502-phpapp01Kertaskerjaharikeluargadansukaneka 120520050502-phpapp01
Kertaskerjaharikeluargadansukaneka 120520050502-phpapp01nurfazlina87
 
Transmongoliano
TransmongolianoTransmongoliano
Transmongolianoarrute10
 
少女のメガホン - マニュアル②キャンペーンを応援
少女のメガホン - マニュアル②キャンペーンを応援少女のメガホン - マニュアル②キャンペーンを応援
少女のメガホン - マニュアル②キャンペーンを応援chucklellc
 
Siyer-i Nebi 32. Sayı
Siyer-i Nebi 32. SayıSiyer-i Nebi 32. Sayı
Siyer-i Nebi 32. Sayısiyerinebi
 

En vedette (8)

Siyer-i Nebi 7. Sayı
Siyer-i Nebi 7. SayıSiyer-i Nebi 7. Sayı
Siyer-i Nebi 7. Sayı
 
TRiLOC GPS LOCATOR
TRiLOC GPS LOCATORTRiLOC GPS LOCATOR
TRiLOC GPS LOCATOR
 
PG Day'14 Russia, Социальная сеть, которая просто работает, Владислав Коваль
PG Day'14 Russia, Социальная сеть, которая просто работает, Владислав КовальPG Day'14 Russia, Социальная сеть, которая просто работает, Владислав Коваль
PG Day'14 Russia, Социальная сеть, которая просто работает, Владислав Коваль
 
This is What Happens When You Automate Stories for the AP
This is What Happens When You Automate Stories for the APThis is What Happens When You Automate Stories for the AP
This is What Happens When You Automate Stories for the AP
 
Kertaskerjaharikeluargadansukaneka 120520050502-phpapp01
Kertaskerjaharikeluargadansukaneka 120520050502-phpapp01Kertaskerjaharikeluargadansukaneka 120520050502-phpapp01
Kertaskerjaharikeluargadansukaneka 120520050502-phpapp01
 
Transmongoliano
TransmongolianoTransmongoliano
Transmongoliano
 
少女のメガホン - マニュアル②キャンペーンを応援
少女のメガホン - マニュアル②キャンペーンを応援少女のメガホン - マニュアル②キャンペーンを応援
少女のメガホン - マニュアル②キャンペーンを応援
 
Siyer-i Nebi 32. Sayı
Siyer-i Nebi 32. SayıSiyer-i Nebi 32. Sayı
Siyer-i Nebi 32. Sayı
 

Similaire à PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 3, Иван Фролков

Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"Yandex
 
Функциональное программирование на F#
Функциональное программирование на F#Функциональное программирование на F#
Функциональное программирование на F#akrakovetsky
 
Tech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU: Как приручить дракона: введение в LLVMTech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU: Как приручить дракона: введение в LLVMTech Talks @NSU
 
Как приручить дракона: введение в LLVM
Как приручить дракона: введение в LLVMКак приручить дракона: введение в LLVM
Как приручить дракона: введение в LLVMTech Talks @NSU
 
Pyton – пробуем функциональный стиль
Pyton – пробуем функциональный стильPyton – пробуем функциональный стиль
Pyton – пробуем функциональный стильPython Meetup
 
Внешние языки DSL на funcparserlib
Внешние языки DSL на funcparserlibВнешние языки DSL на funcparserlib
Внешние языки DSL на funcparserlibAndrey Vlasovskikh
 
Введение в машинное обучение. Кластеризация (Bitworks Software, Кирилл Жданов)
Введение в машинное обучение. Кластеризация (Bitworks Software, Кирилл Жданов)Введение в машинное обучение. Кластеризация (Bitworks Software, Кирилл Жданов)
Введение в машинное обучение. Кластеризация (Bitworks Software, Кирилл Жданов)Dmitry Kornev
 
Введение в машинное обучение. Кластеризация (Bitworks Software, Кирилл Жданов)
Введение в машинное обучение. Кластеризация (Bitworks Software, Кирилл Жданов)Введение в машинное обучение. Кластеризация (Bitworks Software, Кирилл Жданов)
Введение в машинное обучение. Кластеризация (Bitworks Software, Кирилл Жданов)Bitworks Software
 
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 2...
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 2...PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 2...
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 2...pgdayrussia
 
Tarantool: как обрабатывать 
1,5 млрд запросов в сутки?
Tarantool: как обрабатывать 
1,5 млрд запросов в сутки?Tarantool: как обрабатывать 
1,5 млрд запросов в сутки?
Tarantool: как обрабатывать 
1,5 млрд запросов в сутки?tfmailru
 
Back to the future: Функциональное программирование вчера и сегодня
Back to the future: Функциональное программирование вчера и сегодняBack to the future: Функциональное программирование вчера и сегодня
Back to the future: Функциональное программирование вчера и сегодняAlexander Granin
 
Python и его тормоза
Python и его тормозаPython и его тормоза
Python и его тормозаAlexander Shigin
 
PostgreSQL: практические примеры оптимизации SQL-запросов / Иван Фролков (Po...
PostgreSQL: практические примеры оптимизации SQL-запросов /  Иван Фролков (Po...PostgreSQL: практические примеры оптимизации SQL-запросов /  Иван Фролков (Po...
PostgreSQL: практические примеры оптимизации SQL-запросов / Иван Фролков (Po...Ontico
 
Дмитрий Прокопцев — R-ссылки в С++11
Дмитрий Прокопцев — R-ссылки в С++11Дмитрий Прокопцев — R-ссылки в С++11
Дмитрий Прокопцев — R-ссылки в С++11Yandex
 
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014Python Meetup
 
Лекция 8. Intel Threading Building Blocks
Лекция 8. Intel Threading Building BlocksЛекция 8. Intel Threading Building Blocks
Лекция 8. Intel Threading Building BlocksMikhail Kurnosov
 
Функциональное программирование с использованием библиотеки fp-ts | Odessa Fr...
Функциональное программирование с использованием библиотеки fp-ts | Odessa Fr...Функциональное программирование с использованием библиотеки fp-ts | Odessa Fr...
Функциональное программирование с использованием библиотеки fp-ts | Odessa Fr...OdessaFrontend
 

Similaire à PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 3, Иван Фролков (20)

Иван Фролков. Tricky SQL
Иван Фролков. Tricky SQLИван Фролков. Tricky SQL
Иван Фролков. Tricky SQL
 
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"
 
Функциональное программирование на F#
Функциональное программирование на F#Функциональное программирование на F#
Функциональное программирование на F#
 
Tech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU: Как приручить дракона: введение в LLVMTech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU: Как приручить дракона: введение в LLVM
 
Как приручить дракона: введение в LLVM
Как приручить дракона: введение в LLVMКак приручить дракона: введение в LLVM
Как приручить дракона: введение в LLVM
 
Основы SciPy
Основы SciPyОсновы SciPy
Основы SciPy
 
Pyton – пробуем функциональный стиль
Pyton – пробуем функциональный стильPyton – пробуем функциональный стиль
Pyton – пробуем функциональный стиль
 
Внешние языки DSL на funcparserlib
Внешние языки DSL на funcparserlibВнешние языки DSL на funcparserlib
Внешние языки DSL на funcparserlib
 
Введение в машинное обучение. Кластеризация (Bitworks Software, Кирилл Жданов)
Введение в машинное обучение. Кластеризация (Bitworks Software, Кирилл Жданов)Введение в машинное обучение. Кластеризация (Bitworks Software, Кирилл Жданов)
Введение в машинное обучение. Кластеризация (Bitworks Software, Кирилл Жданов)
 
Введение в машинное обучение. Кластеризация (Bitworks Software, Кирилл Жданов)
Введение в машинное обучение. Кластеризация (Bitworks Software, Кирилл Жданов)Введение в машинное обучение. Кластеризация (Bitworks Software, Кирилл Жданов)
Введение в машинное обучение. Кластеризация (Bitworks Software, Кирилл Жданов)
 
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 2...
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 2...PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 2...
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 2...
 
Tarantool: как обрабатывать 
1,5 млрд запросов в сутки?
Tarantool: как обрабатывать 
1,5 млрд запросов в сутки?Tarantool: как обрабатывать 
1,5 млрд запросов в сутки?
Tarantool: как обрабатывать 
1,5 млрд запросов в сутки?
 
Back to the future: Функциональное программирование вчера и сегодня
Back to the future: Функциональное программирование вчера и сегодняBack to the future: Функциональное программирование вчера и сегодня
Back to the future: Функциональное программирование вчера и сегодня
 
Python и его тормоза
Python и его тормозаPython и его тормоза
Python и его тормоза
 
PostgreSQL: практические примеры оптимизации SQL-запросов / Иван Фролков (Po...
PostgreSQL: практические примеры оптимизации SQL-запросов /  Иван Фролков (Po...PostgreSQL: практические примеры оптимизации SQL-запросов /  Иван Фролков (Po...
PostgreSQL: практические примеры оптимизации SQL-запросов / Иван Фролков (Po...
 
Дмитрий Прокопцев — R-ссылки в С++11
Дмитрий Прокопцев — R-ссылки в С++11Дмитрий Прокопцев — R-ссылки в С++11
Дмитрий Прокопцев — R-ссылки в С++11
 
Efficiency vvv
Efficiency vvvEfficiency vvv
Efficiency vvv
 
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
 
Лекция 8. Intel Threading Building Blocks
Лекция 8. Intel Threading Building BlocksЛекция 8. Intel Threading Building Blocks
Лекция 8. Intel Threading Building Blocks
 
Функциональное программирование с использованием библиотеки fp-ts | Odessa Fr...
Функциональное программирование с использованием библиотеки fp-ts | Odessa Fr...Функциональное программирование с использованием библиотеки fp-ts | Odessa Fr...
Функциональное программирование с использованием библиотеки fp-ts | Odessa Fr...
 

Plus de pgdayrussia

PG Day'14 Russia, Secure PostgreSQL Deployment, Magnus Hagander
PG Day'14 Russia, Secure PostgreSQL Deployment, Magnus HaganderPG Day'14 Russia, Secure PostgreSQL Deployment, Magnus Hagander
PG Day'14 Russia, Secure PostgreSQL Deployment, Magnus Haganderpgdayrussia
 
PG Day'14 Russia, Работа со слабо-структурированными данными в PostgreSQL, Ол...
PG Day'14 Russia, Работа со слабо-структурированными данными в PostgreSQL, Ол...PG Day'14 Russia, Работа со слабо-структурированными данными в PostgreSQL, Ол...
PG Day'14 Russia, Работа со слабо-структурированными данными в PostgreSQL, Ол...pgdayrussia
 
PG Day'14 Russia, PostgreSQL в avito.ru, Михаил Тюрин
PG Day'14 Russia, PostgreSQL в avito.ru, Михаил ТюринPG Day'14 Russia, PostgreSQL в avito.ru, Михаил Тюрин
PG Day'14 Russia, PostgreSQL в avito.ru, Михаил Тюринpgdayrussia
 
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 1...
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 1...PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 1...
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 1...pgdayrussia
 
PG Day'14 Russia, PostgreSQL System Architecture, Heikki Linnakangas
PG Day'14 Russia, PostgreSQL System Architecture, Heikki LinnakangasPG Day'14 Russia, PostgreSQL System Architecture, Heikki Linnakangas
PG Day'14 Russia, PostgreSQL System Architecture, Heikki Linnakangaspgdayrussia
 
PG Day'14 Russia, PostgreSQL: архитектура, настройка и оптимизация, Илья Косм...
PG Day'14 Russia, PostgreSQL: архитектура, настройка и оптимизация, Илья Косм...PG Day'14 Russia, PostgreSQL: архитектура, настройка и оптимизация, Илья Косм...
PG Day'14 Russia, PostgreSQL: архитектура, настройка и оптимизация, Илья Косм...pgdayrussia
 
PG Day'14 Russia, Индексный поиск по регулярным выражениям, Александр Коротков
PG Day'14 Russia, Индексный поиск по регулярным выражениям, Александр КоротковPG Day'14 Russia, Индексный поиск по регулярным выражениям, Александр Коротков
PG Day'14 Russia, Индексный поиск по регулярным выражениям, Александр Коротковpgdayrussia
 
PG Day'14 Russia, GIN — Stronger than ever in 9.4 and further, Александр Коро...
PG Day'14 Russia, GIN — Stronger than ever in 9.4 and further, Александр Коро...PG Day'14 Russia, GIN — Stronger than ever in 9.4 and further, Александр Коро...
PG Day'14 Russia, GIN — Stronger than ever in 9.4 and further, Александр Коро...pgdayrussia
 
PG Day'14 Russia, Нетрадиционный PostgreSQL: хранение бинарных данных в БД, А...
PG Day'14 Russia, Нетрадиционный PostgreSQL: хранение бинарных данных в БД, А...PG Day'14 Russia, Нетрадиционный PostgreSQL: хранение бинарных данных в БД, А...
PG Day'14 Russia, Нетрадиционный PostgreSQL: хранение бинарных данных в БД, А...pgdayrussia
 
PG Day'14 Russia, Модуль anyarray, Федор Сигаев
PG Day'14 Russia, Модуль anyarray, Федор СигаевPG Day'14 Russia, Модуль anyarray, Федор Сигаев
PG Day'14 Russia, Модуль anyarray, Федор Сигаевpgdayrussia
 

Plus de pgdayrussia (10)

PG Day'14 Russia, Secure PostgreSQL Deployment, Magnus Hagander
PG Day'14 Russia, Secure PostgreSQL Deployment, Magnus HaganderPG Day'14 Russia, Secure PostgreSQL Deployment, Magnus Hagander
PG Day'14 Russia, Secure PostgreSQL Deployment, Magnus Hagander
 
PG Day'14 Russia, Работа со слабо-структурированными данными в PostgreSQL, Ол...
PG Day'14 Russia, Работа со слабо-структурированными данными в PostgreSQL, Ол...PG Day'14 Russia, Работа со слабо-структурированными данными в PostgreSQL, Ол...
PG Day'14 Russia, Работа со слабо-структурированными данными в PostgreSQL, Ол...
 
PG Day'14 Russia, PostgreSQL в avito.ru, Михаил Тюрин
PG Day'14 Russia, PostgreSQL в avito.ru, Михаил ТюринPG Day'14 Russia, PostgreSQL в avito.ru, Михаил Тюрин
PG Day'14 Russia, PostgreSQL в avito.ru, Михаил Тюрин
 
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 1...
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 1...PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 1...
PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 1...
 
PG Day'14 Russia, PostgreSQL System Architecture, Heikki Linnakangas
PG Day'14 Russia, PostgreSQL System Architecture, Heikki LinnakangasPG Day'14 Russia, PostgreSQL System Architecture, Heikki Linnakangas
PG Day'14 Russia, PostgreSQL System Architecture, Heikki Linnakangas
 
PG Day'14 Russia, PostgreSQL: архитектура, настройка и оптимизация, Илья Косм...
PG Day'14 Russia, PostgreSQL: архитектура, настройка и оптимизация, Илья Косм...PG Day'14 Russia, PostgreSQL: архитектура, настройка и оптимизация, Илья Косм...
PG Day'14 Russia, PostgreSQL: архитектура, настройка и оптимизация, Илья Косм...
 
PG Day'14 Russia, Индексный поиск по регулярным выражениям, Александр Коротков
PG Day'14 Russia, Индексный поиск по регулярным выражениям, Александр КоротковPG Day'14 Russia, Индексный поиск по регулярным выражениям, Александр Коротков
PG Day'14 Russia, Индексный поиск по регулярным выражениям, Александр Коротков
 
PG Day'14 Russia, GIN — Stronger than ever in 9.4 and further, Александр Коро...
PG Day'14 Russia, GIN — Stronger than ever in 9.4 and further, Александр Коро...PG Day'14 Russia, GIN — Stronger than ever in 9.4 and further, Александр Коро...
PG Day'14 Russia, GIN — Stronger than ever in 9.4 and further, Александр Коро...
 
PG Day'14 Russia, Нетрадиционный PostgreSQL: хранение бинарных данных в БД, А...
PG Day'14 Russia, Нетрадиционный PostgreSQL: хранение бинарных данных в БД, А...PG Day'14 Russia, Нетрадиционный PostgreSQL: хранение бинарных данных в БД, А...
PG Day'14 Russia, Нетрадиционный PostgreSQL: хранение бинарных данных в БД, А...
 
PG Day'14 Russia, Модуль anyarray, Федор Сигаев
PG Day'14 Russia, Модуль anyarray, Федор СигаевPG Day'14 Russia, Модуль anyarray, Федор Сигаев
PG Day'14 Russia, Модуль anyarray, Федор Сигаев
 

PG Day'14 Russia, PostgreSQL как платформа для разработки приложений, часть 3, Иван Фролков

  • 2. Возможности Postgres l Views l Массивы l CTE l Индексы l Prepared statements l Курсоры l Разное
  • 3. Пользователи l Реально активно обычно 5-15% всех пользователей. l Обмен с диском идет постранично. l Один пользователь — одна страница l При сохранении в кеше тысячи пользователей они будут занимать тысячу страниц; конкретно строка пользователя будет занимать 1/8 — 1/200 страницы.
  • 4. Пользователи l Таким образом, 1000 пользователей потребует 8М RAM, из которых будет реально использоваться только 1000*200байт ≈ 20КБ RAM. l Кроме того, RAM будет использоваться еще и под индекс. − Кстати, Postgres не работает мимо кеша ОС; следовательно, надо выделять или разумное количество памяти, или почти всю. l Сложим всех активных в секцию usr_active, а неактивных — в usr_dormant
  • 5. Union all l select * from usr_active union all select * from usr_dormant l От неактивных пользователей в кеше останется только индекс. l Можно еще лучше — создать функцию.
  • 6. create function usr_all(ns integer[]) returns table(n integer) as $code$ declare v integer; begin foreach v in array ns loop select a.n into usr_all.n from usr_active a where a.n=v; if found then return next; else select d.n into usr_all.n from usr_dormant d where d.n=v; if found then return next; end if; end if; end loop; end; $code$ language plpgsql
  • 7. create function usr_all(p_usr_id int) returns table(n integer) as $code$ begin select a.n into usr_all.n from usr_active a where a.n=p_usr_id; if found then return next; else select d.n into usr_all.n from usr_dormant d where d.n=p_usr_id; if found then return next; end if; end if; end; $code$ language plpgsql
  • 8. Сравнение l View — удобно; накладные расходы на индекс l Функция —удобно (относительно); отсутствие накладных расходов на индекс. l Функция для LATERAL – удобно, отсутствие накладных расходов на индекс, наличие накладных расходов на вызов функции. l Так ли уж это все надо? − Скорее всего, нет. Но про страничный обмен забывать все же не стоит. − pg_repack, pg_reorg
  • 9. LATERAL • Что это такое? select n, t from generate_series(1,5) as n, lateral(select max(t) as t from generate_series(n*2,n*3) as gs1(t) ) as t n | t ---+---- 1 | 3 2 | 6 3 | 9 4 | 12 5 | 15
  • 10. LATERAL • select n, t from generate_series(1,2) as n, lateral(select t as t from generate_series(n*2,n*3) as gs1(t) ) as t n | t ---+-- 1 | 2 1 | 3 2 | 4 2 | 5 2 | 6
  • 11. LATERAL – зачем это надо? • VIEW с параметрами • Функции теперь first-class citizens • Очевидное применение: СЕКЦИОНИРОВАНИЕ • В примере выше – неактивных пользователей можно хранить в массивах, построив индекс по range по их id. • Не ограниченно только встроенными средствами – можно делать произвольно.
  • 12. View с параметрами l Часто хотят view с параметрами l Например, для пользователей показывать, кто друг просматривающему; параметр — id просматривающего пользователя l Что делать? l Декартово произведение. Да, именно декартово произведение. l N*1=N. Таблица параметров должна возвращать единственную строку.
  • 13. View с параметрами create or replace view usr_cart as select p.id as viewer_id, u.*, exists(select * from friend f where f.usr_id=viewer_id and f.friend_id=u.id) as isfriend from usr u, usr p Используем: select * from usr_cart where id in(...) and viewer_id=$1
  • 14. View с параметрами l А что делать, если надо и без viewer_id? Подставим несуществующий: l create or replace view usr_cart as select p.id as viewer_id, u.*, exists(select * from friend f where f.usr_id=viewer_id and f.friend_id=u.id) as isfriend from usr u, (select 0 as id union all select id from usr) as p l Используем: select * from usr_cart where id in(...) and viewer_id=coalesce($1,0) В отличие от CTE не создает временной таблицы и оптимизатор знает как это оптимизировать
  • 15. Top N для M l Например, 10 последних постов из самых популярных тем. Или 10 последних покупок в категориях. l Очевидный запрос select p.* from post p, topic t where p.id in (select p1.id from post p1 where p1.topic_id=t.id order by added desc limit 10 )
  • 16. Top N для M l К сожалению, не работает. Точнее, работает, но для больших объемов неприемлемо медленно. Обманываем оптимизатор select p.* from post p, topic t where p.id=any(array(select p1.id from post p1 where p1.topic_id=t.id order by added desc limit 10) )
  • 17. Top N для M l Насколько эффективно это работает? l Вообще говоря, вполне эффективно l Если миллионы элементов, то все не так здорово
  • 18. Common Table Expression with query_name as( select * from tbl ) select * from query_name
  • 19. Зачем надо? l Удобно: эдакое маленькое view в запросе l Как и всякое view, позволяет отдельно описывать отдельные логические понятия и не дублировать код. l Результаты живут в work_mem (или вытесняются на диск); по сути — временные таблицы. l И, наконец, рекурсия.
  • 20. Recursive CTE with recursive tq as( select 1 as n --1 union all select n+1 from tq where n<10 --2 ) select * from tq
  • 23. Способ №1 или линейная рекурсия •with recursive tree(id, parent_id, name) as (values(1,null,'Адам'), (2,1,'Авель'), (3,1,'Каин'), (4,2,'Енох') ), with_parent as( select *, null as parent_name from tree where parent_id is null union all select tree.*, wp.name from tree, with_parent wp where tree.parent_id=wp.id ) select * from with_parent id | parent_id | name | parent_name ----+-----------+-------+------------- 1 | | Адам | 3 | 1 | Каин | Адам 2 | 1 | Авель | Адам 4 | 2 | Енох | Авель (4 строки)
  • 24. Ну и в чем разница? with recursive tree(id, parent_id, name) as ( values(1,null,'Адам'), (2,1,'Авель'), (3,1,'Каин'), (4,2,'Енох') ) select *, (select t.name from tree t where tree.parent_id=t.id ) as parent_name from tree id | parent_id | name | parent_name ----+-----------+-------+------------- 1 | | Адам | 3 | 1 | Каин | Адам 2 | 1 | Авель | Адам 4 | 2 | Енох | Авель (4 строки)
  • 25. А вот в чем with recursive tree(id, parent_id, name) as( values(1,null,'Адам'), (2,1,'Авель'), (3,1,'Каин'), (4,2,'Енох')), with_parent as( select *, 'Бог' as ancestors_names from tree where parent_id is null union all select tree.*, wp.ancestors_names||','||wp.name from tree, with_parent wp where tree.parent_id=wp.id) •select * from with_parent id | parent_id | name | ancestors_names ----+-----------+-------+----------------- 1 | | Адам | Бог 3 | 1 | Каин | Бог,Адам 2 | 1 | Авель | Бог,Адам 4 | 2 | Енох | Бог,Адам,Авель (4 строки)
  • 26. Нелинейная рекурсия with recursive tree(id, parent_id, name) as( values(1,null,'Адам'), (2,1,'Авель'),(3,1,'Каин'), (4,2,'Енох'), (5,null,'Ева'), (6,5,'Дочь Евы'),(7,4,'Ирад')), expand_tree as( select id,parent_id,name from tree union( with expand_tree as( select * from expand_tree ) select e1.id, e1.parent_id, e2.name from expand_tree e1, expand_tree e2 where e2.parent_id=e1.id ) ) select * from expand_tree e where id=1
  • 27. Нелинейная рекурсия-2 id | parent_id | name ----+-----------+------- 1 | | Адам 1 | | Каин 1 | | Авель 1 | | Енох 1 | | Ирад (5 строк)
  • 28. Нелинейная рекурсия-3 Как это, собственно, работает ...where name='Ирад' id | parent_id | name ----+-----------+------ 7 | 4 | Ирад 4 | 2 | Ирад 2 | 1 | Ирад 1 | | Ирад (4 строки)
  • 29. Путь в циклическом графе l Создаем граф. − Создаем вершины: create table vertex as select n from generate_series(1,10000) as gs(n); alter table vertex add constraint vertex_pk primary key (n); − Создаем вершины: create table edge(f,t) as select distinct (random() *10000)::integer as from, (random()*10000)::integer as to from generate_series(1,100000) as gs(n); create index edge_ft on edge(f,t);
  • 30. Ищем путь l Весьма просто: with recursive tq as( select 1 as pass, n, array[n] as path from vertex where n=1 union all (with t as (select * from tq) select pass+1, v.n, t.path||v.n from vertex v, t, edge e where v.n=e.t and t.n=e.f and not (e.t=any(t.path)) and pass<8 and not exists(select * from t where n=5000) ) ) select * from tq where n=5000
  • 31. Ищем путь-2 pass | n | path ------+------+------------------------ 5 | 5000 | {1,7000,6204,922,5000} (1 строка) •"CTE Scan on tq (cost=1314.97..1321.74 rows=2 width=40) (actual time=115.405..132.329 rows=1 loops=1)"
  • 32. Что дальше l CTE как конечный автомат. Со стеком! Разбор JSON'а. − Как? l Сделать одним запросом? Реально, но громоздко. l plpgsql l C-libraries − Уже встроенный тип l All art is quite useless l Разбираем JSON. JSONPath навыворот − Пример: $.users.[100].id
  • 33. Разбор JSON with recursive tokens as( select s[1] as token, row_number() over() as n from regexp_matches($JS$ { "data": [ {"name": "User1 User1", "id": "768709679"}, {"name": "User2 User2","id": "10604454123"} ] "paging": { "next": "https://graph.facebook.com/10000223" } } $JS$,
  • 34. Разбор JSON $RE$( (?:[][}{]) | (?:" (?:"|[^"])+ ") | w+ | s+ | [:,] )$RE$, 'gx') as g(s) where s[1] !~ $RE$^[s:,s]+$$RE$ ),
  • 35. Разбор JSON. Промежуточный результат. token | n ---------------------------------------+---- { | 1 "data" | 2 [ | 3 { | 4 "name" | 5 "User1 User1" | 6 "id" | 7 "768709679" | 8 } | 9 { | 10 "name" | 11 "User2 User2" | 12 "id" | 13 "10604454123" | 14 } | 15 ] | 16 "paging" | 17 { | 18 "next" | 19 "https://graph.facebook.com/10000223" | 20 } | 21 } | 22 (22 строки)
  • 36. Разбор JSON.parsed as( select n, token as token, array[token] as stack, array['$']::text[] as path, '' as jsp, array[0]::integer[] as poses from tokens t where n=1 union all select t.n, t.token as token, case when t.token in (']','}') then p.stack[1:array_upper(p.stack,1)-1] when t.token in ('[','{') then p.stack || t.token else p.stack end, case when t.token in ('[','{') then p.path || case when p.stack[array_upper(p.stack,1)]='{' then regexp_replace(p.token,'^"|"$','','g') else '[' || (p.poses[array_upper(p.poses,1)]+1)::text || ']' end when t.token in (']','}') then p.path[1:array_upper(p.path,1)-1] else p.path end, case when p.stack[array_upper(p.stack,1)]='{' then p.token when p.stack[array_upper(p.stack,1)]='[' then '[' || (p.poses[array_upper(p.poses,1)]+1)::text || ']' else '' end, case when t.token in ('[','{') then p.poses[1:array_upper(p.poses,1)-1]|| (p.poses[array_upper(p.poses,1)]+1)||0 when t.token in (']','}') then p.poses[1:array_upper(p.poses,1)-1] else p.poses[1:array_upper(p.poses,1)-1]|| (p.poses[array_upper(p.poses,1)]+1) end from parsed p, tokens t where t.n=p.n+1),
  • 37. Разбор JSON. Промежуточный результат. n | token | stack | path | jsp | poses ----+---------------------------------------+-------------+--------------+---------------------------------------+--------- 1 | { | {"{"} | {$} | | {0} 2 | "data" | {"{"} | {$} | { | {1} 3 | [ | {"{",[} | {$,data} | "data" | {2,0} 4 | { | {"{",[,"{"} | {$,data,[1]} | [1] | {2,1,0} 5 | "name" | {"{",[,"{"} | {$,data,[1]} | { | {2,1,1} 6 | "User1 User1" | {"{",[,"{"} | {$,data,[1]} | "name" | {2,1,2} 7 | "id" | {"{",[,"{"} | {$,data,[1]} | "User1 User1" | {2,1,3} 8 | "768709679" | {"{",[,"{"} | {$,data,[1]} | "id" | {2,1,4} 9 | } | {"{",[} | {$,data} | "768709679" | {2,1} 10 | { | {"{",[,"{"} | {$,data,[2]} | [2] | {2,2,0} 11 | "name" | {"{",[,"{"} | {$,data,[2]} | { | {2,2,1} 12 | "User2 User2" | {"{",[,"{"} | {$,data,[2]} | "name" | {2,2,2} 13 | "id" | {"{",[,"{"} | {$,data,[2]} | "User2 User2" | {2,2,3} 14 | "10604454123" | {"{",[,"{"} | {$,data,[2]} | "id" | {2,2,4} 15 | } | {"{",[} | {$,data} | "10604454123" | {2,2} 16 | ] | {"{"} | {$} | [3] | {2} 17 | "paging" | {"{"} | {$} | ] | {3} 18 | { | {"{","{"} | {$,paging} | "paging" | {4,0} 19 | "next" | {"{","{"} | {$,paging} | { | {4,1} 20 | "https://graph.facebook.com/10000223" | {"{","{"} | {$,paging} | "next" | {4,2} 21 | } | {"{"} | {$} | "https://graph.facebook.com/10000223" | {4} 22 | } | {} | {} | } | {} (22 строки)
  • 38. Разбор JSON. res as( select * from parsed where (stack[array_upper(stack,1)]='[' or (stack[array_upper(stack,1)]='{' and poses[array_upper(poses,1)]%2=0) ) and token not in ('{','[','}',']') ) select array_to_string(path,'.')||'.'|| regexp_replace(jsp,'^"|"$','','g') as json_path, regexp_replace(token,'^"|"$','','g') as json_value from res
  • 39. Результат json_path | json_value -----------------+------------------------------------- $.data.[1].name | User1 User1 $.data.[1].id | 768709679 $.data.[2].name | User2 User2 $.data.[2].id | 10604454123 $.paging.next | https://graph.facebook.com/10000223 (5 строк)
  • 40. А зачем это надо? l Вообще полезно знать. l Иногда нет возможности установить что-то дополнительно l Не только JSON — что угодно с относительно несложным синтаксисом. l Можно предложить еще способ — например, строить дерево со ссылками на родителя, рекурсивно же разбирать аналог JSONPath и добираться до элементов. − И не только JSON — пути в дереве вообще.
  • 41. with recursive fs(id,pid,name) as( values (1,null,null), (2,1,'usr'), (3,2,'local'), (4,3,'postgres'), (5,4,'bin') ), path as( select p, row_number() over() as n from regexp_split_to_table('/usr/local/postgres/bin','/') as v(p) ), parsed as( select id, name,p,n from fs, path where fs.pid is null and path.p='' union all select fs.id, fs.name, path.p, path.n from fs, path, parsed where fs.pid=parsed.id and fs.name=path.p and path.n=parsed.n+1 ) select * from parsed order by n desc limit 1
  • 42. Что получилось • id | name | p | n •----+------+-----+--- • 5 | bin | bin | 5 •(1 строка)
  • 43. Prepared statements l Увеличивают производительность — не требуется каждый раз − разбирать запрос − Нагружать каталог на предмет существования таблиц, индексов и т. п. − Проверять права доступа к объектам − А в случае широкого использования view все может оказаться еще интереснее l prepare <sthname> as <real query> l execute <sthname>(par1,par2...parN)
  • 44. Prepared statements & plpgsql l В plpgsql отсутствует аналог пакета DBMS_SQL — prepared statements недоступны. − Каждый статический запрос компилируется один раз при обращении или перекомпилируется при изменении объектов l Внимание: раньше была проблема, если объекты, на которые ссылается statement, в момент начала его выполнения еще существовали, но до обращения к ним были удалены. Теперь ее больше нет. l Типичный пример: переименование таблицы при высокой OLAP-нагрузке l Что делать?
  • 45. Advisory locks l Решение 1: рекомендательные блокировки − pg_advisory_lock, pg_advisory_xact_lock, _shared, pg_try_ l Решение 2: обычные блокировки. Таблица блокировок. select … for share.
  • 46. Prepared statements l А точно ли недоступны prepared statements в plpgsql? l Конечно доступны: execute '….': execute 'prepare sth1 for select 1'; execute 'execute sth1' into res; l Execute 'execute' — а вот параметры, увы, действительно недоступны. quote_literal(), quote_ident()
  • 47. Prepared statements l Насколько это быстро? − Кстати: функции времени: now() возвращает дату и время начала транзакции. Используйте clock_timestamp() − Кстати - анонимные блоки: l do $$ code $$ − Кстати: $$-quoting: l $$, $RE$, $Im$, $ANYTHING_ELSE$ − select $Re$^("|[^"])+$Re$
  • 48. Код do $code$ declare i integer; ts1 timestamp with time zone; ts2 timestamp with time zone; res integer; begin execute 'prepare sth1 as select 1'; ts1:=clock_timestamp(); for i in 1..100000 loop -- execute 'execute sth1' into res; -- select 1 into res; -– execute 'select 1' into res; –- Реальная таблица: select 1 as n into stht –- execute 'prepare sth1 as select n from stht'; –- select n into res from stht –- execute 'select n from stht' into res end loop; ts2:=clock_timestamp(); raise notice 'diff=%', (ts2-ts1)::text; end; $code$
  • 50. А зачем это, собственно, надо? l Разбор l Построение плана l Выполнение l Нередко построение плана > выполнение l С динамическим выполнением все идет каждый раз по новой l С prepared — только один раз l pg_prepared_statements
  • 51. Prepared statements l Возвращаем резалтсет − return query execute − только обернув в функцию l Что еще? − save_record('table_name', 'column1_name','column2_value', 'column2_name','column2_value'... ) l plpgsql — функции с переменным числом параметров
  • 52. Кстати: upsert и вставка в несколько таблиц Подход в лоб: with input as (select n from generate_series(1,10) as gs(n)), upd as( update ups set val=i.n from input i where ups.n=i.n returning i.n ), ins as( insert into ups select * from input i where not exists(select * from upd u where i.n=u.n) returning ups.n ) select (select count(*) from upd) as updates, (select count(*) from ins) as inserts
  • 53. Upsert l Подход в лоб не работает — race condition: после обновления другой процесс может добавить новые строки, и при вставке возникнет ошибка. l Как правильно: − Для каждой вставляемой строки l Попытаться обновить l Обновилась — хорошо, не обновилось — добавляем l При нарушении уникальности повторяем попытку добавления строки с самого начала
  • 54. Кстати: plpgsql, savepoint и исключения l В plpgsql нет явных savepoint l В plpgsql при использовании исключения в блоке на входе в блок ставится неявный savepoint l При возникновении исключения в блоке происходит откат к нему l Пример:
  • 55. Кстати: plpgsql, savepoint и исключения •do $code$ begin create temporary table excdemo as select 1 as n; begin update excdemo set n=10; raise notice 'BEFORE exception n=%', (select n from excdemo); raise sqlstate 'EX001'; exception when sqlstate 'EX001' then raise notice 'AFTER exception n=%', (select n from excdemo); end; end; $code$ •ЗАМЕЧАНИЕ: BEFORE exception n=10 •ЗАМЕЧАНИЕ: AFTER exception n=1
  • 56. Кстати: plpgsql, savepoint и исключения l Позволяет делать логгирование − Не нужны автономные транзакции (ну, почти не нужны...) l Позволяет выполнять потенциально некорректный код! − Это хорошо, а не плохо! Можно в живой боевой сервер на ходу добавлять код, не обваливая все по ошибке. l «Можно» - разумеется, не значит «нужно».
  • 57. Курсоры l Задача — resultset из фунции − funcname(..) returns table(colname coltype...) − select * from funcname(...) l Задача расширилась — несколько resultset'ов из функции l Задача еще более расширилась — МНОГО resultset'ов l Причем переменное число l Что делать?
  • 58. Курсоры l Курсоры существуют только в пределах транзакции − Начало транзакции − Вызов фунции − Fetch all from <cursor name1> − Fetch all from <cursor name2> − … − Fetch all from <cursor nameN> − commit
  • 59. Курсоры l refcursor В сущности, курсор — это просто его имя; имя курсора — строка declare cursname1 refcursor; ... begin ... open cursname1 for select ... ... return array[cursname1,cursname2...]
  • 61. Курсоры l Требуется поддержка на клиенте l Для курсоров она реализуется несложно — в perl или php достаточно рекурсивно обойти resultset и посмотреть типы колонок. l В Java — используя декоратор, создать класс, возвращающий ResultSet (как именно его создавать — отдельная история) l Для json можно поступить аналогично − Отличть json от text на клиенте малореально. Хак: можно сделать domain для text и смотреть длину.
  • 62. generic_cursor CREATE OR REPLACE FUNCTION generic_cursor(IN sql text, VARIADIC param text[] DEFAULT ARRAY[]::text[]) RETURNS refcursor AS $BODY$ declare rv refcursor; sthhash text := 'GENCURS'||md5(sql); exec text := 'execute ' || sthhash || case when array_length(param,1) is null then '' else '('||(select string_agg(coalesce(quote_literal(pv),'NULL'),',') from unnest(param) as u(pv)) ||')' end; begin begin open rv for execute exec; exception when sqlstate '26000' -- prepared statement does not exist or sqlstate '0A000' -- table definition had changed or sqlstate '42703' --- -/- or sqlstate '42P11' then if sqlstate='0A000' or sqlstate='42703' then execute 'deallocate '||sthhash; end if; execute 'prepare '||sthhash|| coalesce((select '('|| string_agg('text',',') | | ')' from unnest(param) as u(pv)),'') ||' as '||sql; open rv for execute exec; end; return rv; end; $BODY$ LANGUAGE plpgsql
  • 63. generic_exec CREATE OR REPLACE FUNCTION generic_exec( IN sql text, VARIADIC param text[] DEFAULT ARRAY[]::text[]) RETURNS SETOF record AS $BODY$ declare cr refcursor:= generic_cursor(sql, variadic param); begin return query execute 'fetch all from '|| quote_ident(cr::text); end; $BODY$ LANGUAGE plpgsql
  • 64. Используем select * from generic_exec('select n from generate_series(1,$1::integer) as gs(n)', 20::text) as ge(n integer)
  • 65. А оно надо? Да! do $code$ declare rv tt; s1 timestamp; s2 timestamp; s3 timestamp; i integer; begin s1:=clock_timestamp(); for i in 1..1000 loop execute 'select t1.* from tt t1, tt t2, tt t3, tt t4, tt t5, tt t7, tt t8 where t1.id=t2.id and t2.id=t3.id and t3.id=t4.id and t1.id=t5.id and t5.id=t7.id and t5.id=t8.id and t8.id=9999 and t1.id between 1 and 10000 and t4.id between 9999 and 200000 and t3.id>9000 and exists(select * from tt t6 where t6.id=t5.id) and t4.cnt=49 order by t4.cnt' into rv; end loop; s2:=clock_timestamp(); for i in 1..1000 loop select * into rv from generic_exec('select t1.* from tt t1, tt t2, tt t3, tt t4, tt t5, tt t7, tt t8 where t1.id=t2.id and t2.id=t3.id and t3.id=t4.id and t1.id=t5.id and t5.id=t7.id and t5.id=t8.id and t8.id=9999 and t1.id between 1 and 10000 and t4.id between 9999 and 200000 and t3.id>9000 and exists(select * from tt t6 where t6.id=t5.id) and t4.cnt=49 order by t4.cnt') as ge(n integer, cnt bigint); end loop; s3:=clock_timestamp(); raise notice 'Run 1=%',s2-s1; raise notice 'Run 2=%',s3-s2; end; $code$
  • 67. То же самое, но многопоточно •$ pgbench -M prepared -U postgres -t 500 -n -j 2 -c 10 -f generic_exec.pgb work number of clients: 10 number of threads: 2 number of transactions per client: 500 number of transactions actually processed: 5000/5000 tps = 2340.001464 (including connections establishing) tps = 2508.521577 (excluding connections establishing) •$ pgbench -M prepared -U postgres -t 500 -n -j 2 -c 10 -f plain_execute.pgb work query mode: prepared number of clients: 10 number of threads: 2 number of transactions per client: 500 number of transactions actually processed: 5000/5000 tps = 146.373869 (including connections establishing) tps = 147.008750 (excluding connections establishing)
  • 68. Альтернатива l JSON — в 9.2 row_to_json, array_to_json − select array_to_json( array( select row_to_json(c.*) from pg_class c ) ) l В предыдущих версиях легко можно написать и самостоятельно, используя, например, hstore l Можно просто возвращать массив hstore и разбирать на клиенте
  • 69. Заключение l И снова: а зачем все это, собственно, надо? l Производительность l Переносимость l Вычурность l Пробуйте! l Вопросы?