ProHoster > Blog > Tsamaiso > PostgreSQL Antipatterns: ha re otle JOIN e boima ka bukantswe
PostgreSQL Antipatterns: ha re otle JOIN e boima ka bukantswe
Re tsoela pele ka letoto la lingoloa tse fanoeng boithutong ba mekhoa e sa tsejoeng haholo ea ho ntlafatsa ts'ebetso ea lipotso tse "bonahalang li le bonolo" tsa PostgreSQL:
Se ke oa nahana hore ha ke rate JOIN haholo ... :)
Empa hangata ntle le eona, kopo e bonahala e sebetsa haholo ho feta ka eona. Kahoo kajeno re tla leka tlosa ts'ebeliso e matla ea lisebelisoa - ho sebedisa bukantswe.
Ho qala ka PostgreSQL 12, a mang a maemo a hlalositsoeng ka tlase a ka hlahisoa hape ka tsela e fapaneng ka lebaka la CTE e sa sebetseng ea kamehla. Boitšoaro bona bo ka khutlisoa ka ho totobatsa senotlolo MATERIALIZED.
"Lintlha" tse ngata ka pokello ea mantsoe e fokolang
Ha re nkeng mosebetsi oa sebele oa kopo - re hloka ho hlahisa lenane melaetsa e kenang kapa mesebetsi e sebetsang le baromeli:
25.01 | Иванов И.И. | Подготовить описание нового алгоритма.
22.01 | Иванов И.И. | Написать статью на Хабр: жизнь без JOIN.
20.01 | Петров П.П. | Помочь оптимизировать запрос.
18.01 | Иванов И.И. | Написать статью на Хабр: JOIN с учетом распределения данных.
16.01 | Петров П.П. | Помочь оптимизировать запрос.
Lefatšeng le sa utloahaleng, bangoli ba mesebetsi ba lokela ho ajoa ka ho lekana har'a basebetsi bohle ba mokhatlo oa rona, empa bonneteng mesebetsi e tla, joalo ka molao, ho tsoa ho palo e fokolang ea batho - "ho tloha ho botsamaisi" ho ea holimo kapa "ho tsoa ho likonteraka tse nyane" ho tsoa mafapheng a boahelani (bahlahlobisisi, baqapi, ba mebaraka, ...).
Ha re amoheleng hore mokhatlong oa rona oa batho ba 1000, ke bangoli ba 20 feela (hangata le ka tlase ho moo) ba behang mesebetsi bakeng sa sebapali se seng le se seng. Ha re sebeliseng tsebo ea taba enaho potlakisa potso ea "setso".
Jenereithara ea mongolo
-- сотрудники
CREATE TABLE person AS
SELECT
id
, repeat(chr(ascii('a') + (id % 26)), (id % 32) + 1) "name"
, '2000-01-01'::date - (random() * 1e4)::integer birth_date
FROM
generate_series(1, 1000) id;
ALTER TABLE person ADD PRIMARY KEY(id);
-- задачи с указанным распределением
CREATE TABLE task AS
WITH aid AS (
SELECT
id
, array_agg((random() * 999)::integer + 1) aids
FROM
generate_series(1, 1000) id
, generate_series(1, 20)
GROUP BY
1
)
SELECT
*
FROM
(
SELECT
id
, '2020-01-01'::date - (random() * 1e3)::integer task_date
, (random() * 999)::integer + 1 owner_id
FROM
generate_series(1, 100000) id
) T
, LATERAL(
SELECT
aids[(random() * (array_length(aids, 1) - 1))::integer + 1] author_id
FROM
aid
WHERE
id = T.owner_id
LIMIT 1
) a;
ALTER TABLE task ADD PRIMARY KEY(id);
CREATE INDEX ON task(owner_id, task_date);
CREATE INDEX ON task(author_id);
Ha re bontše mesebetsi e 100 ea ho qetela bakeng sa mophethahatsi ea itseng:
SELECT
task.*
, person.name
FROM
task
LEFT JOIN
person
ON person.id = task.author_id
WHERE
owner_id = 777
ORDER BY
task_date DESC
LIMIT 100;
E hlaha joalo 1/3 kakaretso ea nako le ho baloa ha 3/4 maqephe a data a entsoe feela ho batla mongoli makhetlo a 100 - bakeng sa mosebetsi o mong le o mong oa tlhahiso. Empa rea tseba hore hara makholo ana tse 20 feela tse fapaneng - Na hoa khoneha ho sebelisa tsebo ee?
hstore-dictionary
Ha re nke monyetla mofuta oa hstore ho hlahisa "dictionary" bohlokoa ba bohlokoa:
CREATE EXTENSION hstore
Re hloka feela ho kenya ID ea mongoli le lebitso la hae ka har'a bukantswe e le hore re ka ntsha ka ho sebedisa senotlolo sena:
-- формируем целевую выборку
WITH T AS (
SELECT
*
FROM
task
WHERE
owner_id = 777
ORDER BY
task_date DESC
LIMIT 100
)
-- формируем словарь для уникальных значений
, dict AS (
SELECT
hstore( -- hstore(keys::text[], values::text[])
array_agg(id)::text[]
, array_agg(name)::text[]
)
FROM
person
WHERE
id = ANY(ARRAY(
SELECT DISTINCT
author_id
FROM
T
))
)
-- получаем связанные значения словаря
SELECT
*
, (TABLE dict) -> author_id::text -- hstore -> key
FROM
T;
E sebelisitsoe ho fumana tlhahisoleseling ka batho Nako ea 2 nako e nyane le makhetlo a 7 ho baloa ha data! Ho phaella ho "polelo ea mantsoe", se ileng sa boela sa re thusa ho finyella liphello tsena ke ho khutlisa rekoto ka bongata ho tloha tafoleng ka tsela e le 'ngoe ea ho sebelisa = ANY(ARRAY(...)).
Kenyelletso ea Lethathamo: Seriization le Deserialization
Empa ho thoe'ng haeba re hloka ho boloka eseng karolo e le 'ngoe feela ea mongolo, empa le keno eohle bukeng e hlalosang mantsoe? Tabeng ena, bokhoni ba PostgreSQL bo tla re thusa tšoara sekeno sa tafole joalo ka boleng bo le bong:
...
, dict AS (
SELECT
hstore(
array_agg(id)::text[]
, array_agg(p)::text[] -- магия #1
)
FROM
person p
WHERE
...
)
SELECT
*
, (((TABLE dict) -> author_id::text)::person).* -- магия #2
FROM
T;
Ha re shebe se neng se etsahala mona:
Re nkile p joalo ka lebitso la lebitso la motho ea felletseng ea kenang tafoleng mme a bokella bongata ba tsona.
sena letoto la lirekoto li ile tsa fetoleloa bocha ho letoto la likhoele tsa mongolo (motho[]::text[]) ho e beha bukeng ea bukantswe ya hstore e le letoto la boleng.
Ha re fumana tlaleho e amanang le eona, re ho ntšoa bukeng ka senotlolo joalo ka khoele ea mongolo.
Re hloka mongolo fetola boleng ba mofuta oa tafole motho (tafoleng e 'ngoe le e 'ngoe mofuta oa lebitso le le leng o iketselitse).
"Atolosa" rekoto e tlatsitsoeng ho likholomo u sebelisa (...).*.
bukantswe ya json
Empa leqheka le joalo leo re le sebelisitseng ka holimo le ke ke la sebetsa haeba ho se na mofuta oa tafole o tsamaisanang le "casting". Hantle-ntle boemo bo tšoanang bo tla hlaha, 'me haeba re leka ho sebelisa mola oa CTE, eseng tafole ea "sebele"..
...
, p AS ( -- это уже CTE
SELECT
*
FROM
person
WHERE
...
)
, dict AS (
SELECT
json_object( -- теперь это уже json
array_agg(id)::text[]
, array_agg(row_to_json(p))::text[] -- и внутри json для каждой строки
)
FROM
p
)
SELECT
*
FROM
T
, LATERAL(
SELECT
*
FROM
json_to_record(
((TABLE dict) ->> author_id::text)::json -- извлекли из словаря как json
) AS j(name text, birth_date date) -- заполнили нужную нам структуру
) j;
Hoa lokela ho hlokomeloa hore ha re hlalosa sebopeho sa sepheo, re ke ke ra thathamisa likarolo tsohle tsa mohala oa mohloli, empa ke feela tseo re li hlokang. Haeba re na le tafole ea "letsoalloa", joale ho molemo ho sebelisa mosebetsi json_populate_record.
Re ntse re fumana bukantswe hang, empa json-[de] litjeo tsa serialization li holimo haholo, ka hona, hoa utloahala ho sebelisa mokhoa ona feela maemong a mang ha CTE Scan "ea tšepahalang" e iponahatsa e le mpe le ho feta.
Tshebetso ea teko
Kahoo, re na le mekhoa e 'meli ea ho hlophisa data ho bukantswe - hstore/json_object. Ntle le moo, lihlopha tsa linotlolo le boleng ka botsona li ka hlahisoa ka mekhoa e 'meli, ka phetoho ea kahare kapa kantle ho mongolo: array_agg(i::text) / array_agg(i)::text[].
Ha re hlahlobeng katleho ea mefuta e fapaneng ea serialization re sebelisa mohlala oa maiketsetso - serialize linomoro tse fapaneng tsa linotlolo:
WITH dict AS (
SELECT
hstore(
array_agg(i::text)
, array_agg(i::text)
)
FROM
generate_series(1, ...) i
)
TABLE dict;
Mongolo oa tlhahlobo: serialization
WITH T AS (
SELECT
*
, (
SELECT
regexp_replace(ea[array_length(ea, 1)], '^Execution Time: (d+.d+) ms$', '1')::real et
FROM
(
SELECT
array_agg(el) ea
FROM
dblink('port= ' || current_setting('port') || ' dbname=' || current_database(), $$
explain analyze
WITH dict AS (
SELECT
hstore(
array_agg(i::text)
, array_agg(i::text)
)
FROM
generate_series(1, $$ || (1 << v) || $$) i
)
TABLE dict
$$) T(el text)
) T
) et
FROM
generate_series(0, 19) v
, LATERAL generate_series(1, 7) i
ORDER BY
1, 2
)
SELECT
v
, avg(et)::numeric(32,3)
FROM
T
GROUP BY
1
ORDER BY
1;
Ho PostgreSQL 11, hoo e ka bang boholo ba dikishinari ba linotlolo tsa 2^12 serialization ho json e nka nako e nyane. Tabeng ena, e sebetsang ka ho fetisisa ke motsoako oa json_object le phetoho ea mofuta oa "internal". array_agg(i::text).
Joale a re leke ho bala bohlokoa ba senotlolo ka seng ka makhetlo a 8 - ka mor'a moo, haeba u sa fumane buka e hlalosang mantsoe, ke hobane'ng ha e hlokahala?
Sengoloa sa tlhahlobo: ho baloa ho tsoa bukeng e hlalosang mantsoe
WITH T AS (
SELECT
*
, (
SELECT
regexp_replace(ea[array_length(ea, 1)], '^Execution Time: (d+.d+) ms$', '1')::real et
FROM
(
SELECT
array_agg(el) ea
FROM
dblink('port= ' || current_setting('port') || ' dbname=' || current_database(), $$
explain analyze
WITH dict AS (
SELECT
json_object(
array_agg(i::text)
, array_agg(i::text)
)
FROM
generate_series(1, $$ || (1 << v) || $$) i
)
SELECT
(TABLE dict) -> (i % ($$ || (1 << v) || $$) + 1)::text
FROM
generate_series(1, $$ || (1 << (v + 3)) || $$) i
$$) T(el text)
) T
) et
FROM
generate_series(0, 19) v
, LATERAL generate_series(1, 7) i
ORDER BY
1, 2
)
SELECT
v
, avg(et)::numeric(32,3)
FROM
T
GROUP BY
1
ORDER BY
1;
'Me ... e se e le hoo e ka bang ka 2^6 linotlolo, ho bala ho tsoa bukeng ea json ho qala ho lahleheloa ke makhetlo a mangata ho bala ho tsoa hstore, hobane jsonb ho etsahala se tšoanang ho 2^9.
Qetello ea ho qetela:
haeba u hloka ho e etsa KENA ka lirekoto tse ngata tse pheta-phetoang - ho molemo ho sebelisa "dictionary" ea tafole
haeba bukantswe ya hao e lebeletswe e nyane mme o ka se bale haholo ho yona - o ka sebelisa json[b]
maemong ohle a mang hstore + array_agg(i:: mongolo) e tla sebetsa haholoanyane