Nagamit mo na ito nang higit sa 6000 beses, ngunit ang isang madaling gamiting tampok na maaaring hindi napansin ay mga pahiwatig sa istruktura, na mukhang ganito:
Makinig sa kanila, at ang iyong mga kahilingan ay βmagiging makinis at malasutla.β π
Ngunit seryoso, maraming mga sitwasyon na gumagawa ng isang kahilingan na mabagal at gutom sa mapagkukunan ay tipikal at maaaring makilala ng istraktura at data ng plano.
Sa kasong ito, ang bawat indibidwal na developer ay hindi kailangang maghanap ng isang pagpipilian sa pag-optimize sa kanyang sarili, umaasa lamang sa kanyang karanasan - maaari naming sabihin sa kanya kung ano ang nangyayari dito, kung ano ang maaaring maging dahilan, at kung paano lumapit sa isang solusyon. Iyon ang ginawa namin.
Tingnan natin ang mga kasong ito - kung paano tinukoy ang mga ito at kung anong mga rekomendasyon ang hahantong sa mga ito.
Upang mas mahusay na isawsaw ang iyong sarili sa paksa, maaari mo munang makinig sa kaukulang bloke mula sa ang aking ulat sa PGConf.Russia 2020, at pagkatapos lamang magpatuloy sa isang detalyadong pagsusuri ng bawat halimbawa:
#1: index "undersorting"
Kapag umusbong
Ipakita ang pinakabagong invoice para sa kliyente na "LLC Kolokolchik".
Mapapansin mo kaagad na higit sa 100 mga tala ang ibinawas mula sa index, na pagkatapos ay lahat ay pinagsunod-sunod, at pagkatapos ay ang isa lamang ang naiwan.
Pagwawasto:
DROP INDEX tbl_fk_cli_idx;
CREATE INDEX ON tbl(fk_cli, pk DESC); -- Π΄ΠΎΠ±Π°Π²ΠΈΠ»ΠΈ ΠΊΠ»ΡΡ ΡΠΎΡΡΠΈΡΠΎΠ²ΠΊΠΈ
Kahit na sa gayong primitive na sample - 8.5 beses na mas mabilis at 33 beses na mas kaunting pagbabasa. Ang mas maraming "katotohanan" na mayroon ka para sa bawat halaga, mas malinaw ang epekto fk.
Tandaan ko na ang naturang index ay gagana bilang isang "prefix" na index na hindi mas masahol kaysa dati para sa iba pang mga query na may fk, kung saan ayusin ayon sa pk wala at wala (maaari kang magbasa ng higit pa tungkol dito sa aking artikulo tungkol sa paghahanap ng mga hindi epektibong index). Kasama, ito ay magbibigay ng normal tahasang suporta sa foreign key sa larangang ito.
#2: index intersection (BitmapAnd)
Kapag umusbong
Ipakita ang lahat ng mga kasunduan para sa kliyente na "LLC Kolokolchik", natapos sa ngalan ng "NAO Buttercup".
Paano makilala
-> BitmapAnd
-> Bitmap Index Scan
-> Bitmap Index Scan
Rekomendasyon
lumikha pinagsama-samang index sa pamamagitan ng mga field mula sa parehong orihinal o palawakin ang isa sa mga umiiral nang may mga field mula sa pangalawa.
Halimbawa:
CREATE TABLE tbl AS
SELECT
generate_series(1, 100000) pk -- 100K "ΡΠ°ΠΊΡΠΎΠ²"
, (random() * 100)::integer fk_org -- 100 ΡΠ°Π·Π½ΡΡ Π²Π½Π΅ΡΠ½ΠΈΡ ΠΊΠ»ΡΡΠ΅ΠΉ
, (random() * 1000)::integer fk_cli; -- 1K ΡΠ°Π·Π½ΡΡ Π²Π½Π΅ΡΠ½ΠΈΡ ΠΊΠ»ΡΡΠ΅ΠΉ
CREATE INDEX ON tbl(fk_org); -- ΠΈΠ½Π΄Π΅ΠΊΡ Π΄Π»Ρ foreign key
CREATE INDEX ON tbl(fk_cli); -- ΠΈΠ½Π΄Π΅ΠΊΡ Π΄Π»Ρ foreign key
SELECT
*
FROM
tbl
WHERE
(fk_org, fk_cli) = (1, 999); -- ΠΎΡΠ±ΠΎΡ ΠΏΠΎ ΠΊΠΎΠ½ΠΊΡΠ΅ΡΠ½ΠΎΠΉ ΠΏΠ°ΡΠ΅
Ang kabayaran dito ay mas maliit, dahil ang Bitmap Heap Scan ay lubos na epektibo sa sarili nitong. Pero kahit na 7 beses na mas mabilis at 2.5 beses na mas kaunting pagbabasa.
#3: Pagsamahin ang mga index (BitmapOr)
Kapag umusbong
Ipakita ang unang 20 pinakamatandang "kami" o hindi nakatalagang mga kahilingan para sa pagproseso, na ang sa iyo ang priyoridad.
Paano makilala
-> BitmapOr
-> Bitmap Index Scan
-> Bitmap Index Scan
Rekomendasyon
Gumamit UNION [LAHAT] upang pagsamahin ang mga subquery para sa bawat isa sa OR-block ng mga kundisyon.
Halimbawa:
CREATE TABLE tbl AS
SELECT
generate_series(1, 100000) pk -- 100K "ΡΠ°ΠΊΡΠΎΠ²"
, CASE
WHEN random() < 1::real/16 THEN NULL -- Ρ Π²Π΅ΡΠΎΡΡΠ½ΠΎΡΡΡΡ 1:16 Π·Π°ΠΏΠΈΡΡ "Π½ΠΈΡΡΡ"
ELSE (random() * 100)::integer -- 100 ΡΠ°Π·Π½ΡΡ Π²Π½Π΅ΡΠ½ΠΈΡ ΠΊΠ»ΡΡΠ΅ΠΉ
END fk_own;
CREATE INDEX ON tbl(fk_own, pk); -- ΠΈΠ½Π΄Π΅ΠΊΡ Ρ "Π²ΡΠΎΠ΄Π΅ ΠΊΠ°ΠΊ ΠΏΠΎΠ΄Ρ ΠΎΠ΄ΡΡΠ΅ΠΉ" ΡΠΎΡΡΠΈΡΠΎΠ²ΠΊΠΎΠΉ
SELECT
*
FROM
tbl
WHERE
fk_own = 1 OR -- ΡΠ²ΠΎΠΈ
fk_own IS NULL -- ... ΠΈΠ»ΠΈ "Π½ΠΈΡΡΠΈ"
ORDER BY
pk
, (fk_own = 1) DESC -- ΡΠ½Π°ΡΠ°Π»Π° "ΡΠ²ΠΎΠΈ"
LIMIT 20;
(
SELECT
*
FROM
tbl
WHERE
fk_own = 1 -- ΡΠ½Π°ΡΠ°Π»Π° "ΡΠ²ΠΎΠΈ" 20
ORDER BY
pk
LIMIT 20
)
UNION ALL
(
SELECT
*
FROM
tbl
WHERE
fk_own IS NULL -- ΠΏΠΎΡΠΎΠΌ "Π½ΠΈΡΡΠΈ" 20
ORDER BY
pk
LIMIT 20
)
LIMIT 20; -- Π½ΠΎ Π²ΡΠ΅Π³ΠΎ - 20, Π±ΠΎΠ»ΡΡΠ΅ ΠΈ Π½Π΅ Π½Π°Π΄ΠΎ
Sinamantala namin ang katotohanan na ang lahat ng 20 kinakailangang rekord ay agad na natanggap sa unang bloke, kaya ang pangalawa, na may mas "mahal" na Bitmap Heap Scan, ay hindi man lang naisakatuparan - sa huli 22x na mas mabilis, 44x na mas kaunting mga pagbabasa!
Bilang isang panuntunan, ito ay nangyayari kapag gusto mong "maglakip ng isa pang filter" sa isang umiiral nang kahilingan.
"At wala kang pareho, ngunit na may mga pindutan ng ina-ng-perlas? " pelikulang "The Diamond Arm"
Halimbawa, ang pagbabago sa gawain sa itaas, ipakita ang unang 20 pinakalumang "kritikal" na kahilingan para sa pagproseso, anuman ang layunin ng mga ito.
Lumikha ng [mas] dalubhasa index na may kondisyon na WHERE o magsama ng mga karagdagang field sa index.
Kung ang kundisyon ng filter ay "static" para sa iyong mga layunin - iyon ay ay hindi nagpapahiwatig ng pagpapalawak listahan ng mga halaga sa hinaharap - mas mahusay na gumamit ng WHERE index. Ang iba't ibang boolean/enum status ay akma sa kategoryang ito.
Kung ang kondisyon ng pagsasala maaaring magkaroon ng iba't ibang kahulugan, pagkatapos ay mas mahusay na palawakin ang index sa mga patlang na ito - tulad ng sa sitwasyon sa BitmapAt sa itaas.
Halimbawa:
CREATE TABLE tbl AS
SELECT
generate_series(1, 100000) pk -- 100K "ΡΠ°ΠΊΡΠΎΠ²"
, CASE
WHEN random() < 1::real/16 THEN NULL
ELSE (random() * 100)::integer -- 100 ΡΠ°Π·Π½ΡΡ Π²Π½Π΅ΡΠ½ΠΈΡ ΠΊΠ»ΡΡΠ΅ΠΉ
END fk_own
, (random() < 1::real/50) critical; -- 1:50, ΡΡΠΎ Π·Π°ΡΠ²ΠΊΠ° "ΠΊΡΠΈΡΠΈΡΠ½Π°Ρ"
CREATE INDEX ON tbl(pk);
CREATE INDEX ON tbl(fk_own, pk);
SELECT
*
FROM
tbl
WHERE
critical
ORDER BY
pk
LIMIT 20;
Tulad ng nakikita mo, ang pag-filter ay ganap na nawala mula sa plano, at ang kahilingan ay naging 5 beses na mas mabilis.
#5: kalat-kalat na mesa
Kapag umusbong
Iba't ibang mga pagtatangka upang lumikha ng iyong sariling pila sa pagproseso ng gawain, kapag ang isang malaking bilang ng mga pag-update/pagtanggal ng mga tala sa talahanayan ay humantong sa isang sitwasyon ng isang malaking bilang ng mga "patay" na mga tala.
Isagawa nang manu-mano nang regular VACUUM [FULL] o makamit ang sapat na madalas na pagsasanay autovacuum sa pamamagitan ng pag-fine-tune ng mga parameter nito, kabilang ang para sa isang tiyak na talahanayan.
Mukhang nagbasa kami ng kaunti, at na-index ang lahat, at hindi namin na-filter ang sinuman nang labis - ngunit nagbabasa pa rin kami ng mas maraming mga pahina kaysa sa gusto namin.
Tingnang mabuti ang istraktura ng index na ginamit at ang mga pangunahing field na tinukoy sa query - malamang bahagi ng index ay hindi tinukoy. Malamang na kailangan mong lumikha ng isang katulad na index, ngunit walang mga patlang ng prefix o matutong ulitin ang kanilang mga halaga.
Mukhang maayos ang lahat, kahit na ayon sa index, ngunit kahit papaano ay kahina-hinala - para sa bawat isa sa 20 talaang nabasa, kinailangan naming ibawas ang 4 na pahina ng data, 32KB bawat tala - hindi ba matapang iyon? At ang pangalan ng index tbl_fk_org_fk_cli_idx nakakapukaw ng pag-iisip.
Ang isang beses na pagpoproseso (pag-uuri o pag-uuri) ng isang malaking bilang ng mga tala ay hindi akma sa memorya na inilaan para dito.
Paano makilala
-> *
&& temp written > 0
Rekomendasyon
Kung ang halaga ng memorya na ginamit ng operasyon ay hindi lubos na lumampas sa tinukoy na halaga ng parameter work_mem, ito ay nagkakahalaga ng pagwawasto nito. Pwede ka agad sa config para sa lahat, o kaya mo SET [LOCAL] para sa isang partikular na kahilingan/transaksyon.
Halimbawa:
SHOW work_mem;
-- "16MB"
SELECT
random()
FROM
generate_series(1, 1000000)
ORDER BY
1;
Para sa mga malinaw na kadahilanan, kung memorya lamang ang ginagamit at hindi disk, kung gayon ang query ay isasagawa nang mas mabilis. Kasabay nito, ang bahagi ng pag-load mula sa HDD ay tinanggal din.
Ngunit kailangan mong maunawaan na hindi ka palaging makakapaglaan ng maraming at maraming memorya - hindi ito magiging sapat para sa lahat.
#9: walang kaugnayang istatistika
Kapag umusbong
Nagbuhos sila ng marami sa database nang sabay-sabay, ngunit walang oras upang itaboy ito ANALYZE.
Paano makilala
-> Seq Scan | Bitmap Heap Scan | Index [Only] Scan [Backward]
&& ratio >> 10
May paghihintay para sa isang lock na ipinataw ng isang nakikipagkumpitensyang kahilingan, o walang sapat na mapagkukunan ng CPU/hypervisor hardware.
Paano makilala
-> *
&& (shared hit / 8K) + (shared read / 1K) < time / 1000
-- RAM hit = 64MB/s, HDD read = 8MB/s
&& time > 100ms -- ΡΠΈΡΠ°Π»ΠΈ ΠΌΠ°Π»ΠΎ, Π½ΠΎ ΡΠ»ΠΈΡΠΊΠΎΠΌ Π΄ΠΎΠ»Π³ΠΎ
Rekomendasyon
Gumamit ng panlabas systemang pang-monitor server para sa pagharang o abnormal na pagkonsumo ng mapagkukunan. Napag-usapan na namin ang tungkol sa aming bersyon ng pag-aayos ng prosesong ito para sa daan-daang mga server dito ΠΈ dito.