Varo toimintoja, jotka tuovat puskureita...
Tarkastellaanpa esimerkkinä pientä kyselyä yleisiä lähestymistapoja kyselyiden optimointiin PostgreSQL:ssä. Voit itse päättää, käytätkö niitä vai et, mutta niistä kannattaa tietää.
Joissakin myöhemmissä PG:n versioissa tilanne saattaa muuttua, kun ajastin tulee älykkäämmäksi, mutta versioille 9.4/9.6 se näyttää suunnilleen samalta kuin näissä esimerkeissä.
Otetaanpa hyvin todellinen pyyntö:
SELECT
TRUE
FROM
"Документ" d
INNER JOIN
"ДокументРасширение" doc_ex
USING("@Документ")
INNER JOIN
"ТипДокумента" t_doc ON
t_doc."@ТипДокумента" = d."ТипДокумента"
WHERE
(d."Лицо3" = 19091 or d."Сотрудник" = 19091) AND
d."$Черновик" IS NULL AND
d."Удален" IS NOT TRUE AND
doc_ex."Состояние"[1] IS TRUE AND
t_doc."ТипДокумента" = 'ПланРабот'
LIMIT 1;
taulukoiden ja kenttien nimistäKenttien ja taulukoiden "venäläisiä" nimiä voidaan käsitellä eri tavalla, mutta tämä on makuasia. Koska
Katsotaanpa tuloksena olevaa suunnitelmaa:
144ms ja lähes 53K puskurit - eli yli 400 Mt dataa! Ja olemme onnekkaita, jos ne kaikki ovat välimuistissa pyyntöömme mennessä, muuten levyltä luettaessa kestää monta kertaa kauemmin.
Algoritmi on tärkein!
Jotta voisit jotenkin optimoida minkä tahansa pyynnön, sinun on ensin ymmärrettävä, mitä sen pitäisi tehdä.
Jätetään itse tietokantarakenteen kehittäminen toistaiseksi tämän artikkelin ulkopuolelle ja sovitaan, että voimme suhteellisen "halvalla" kirjoita pyyntö uudelleen ja/tai rullata alustalle joitain asioita, joita tarvitsemme Indexes.
Joten pyyntö:
— tarkistaa ainakin jonkin asiakirjan olemassaolon
- sellaisessa kunnossa kuin tarvitsemme ja tietyn tyyppisiä
- jossa tekijä tai esittäjä on tarvitsemamme työntekijä
LIITTY + RAJA 1
Melko usein kehittäjän on helpompi kirjoittaa kysely, jossa ensin liitetään suuri määrä taulukoita, ja sitten tästä koko joukosta on jäljellä vain yksi tietue. Mutta helpompi kehittäjälle ei tarkoita tehokkaampaa tietokantaa.
Meidän tapauksessamme oli vain 3 pöytää - ja mikä on vaikutus...
Puretaan ensin yhteys "Dokumenttityyppi"-taulukkoon ja kerrotaan samalla tietokannalle, että tyyppitietueemme on ainutlaatuinen (Tiedämme tämän, mutta aikatauluttaja ei tiedä vielä):
WITH T AS (
SELECT
"@ТипДокумента"
FROM
"ТипДокумента"
WHERE
"ТипДокумента" = 'ПланРабот'
LIMIT 1
)
...
WHERE
d."ТипДокумента" = (TABLE T)
...
Kyllä, jos taulukko/CTE koostuu yhden tietueen yhdestä kentästä, PG:ssä voit kirjoittaa jopa näin, sen sijaan, että
d."ТипДокумента" = (SELECT "@ТипДокумента" FROM T LIMIT 1)
Laiska arviointi PostgreSQL-kyselyissä
BitmapOr vs UNION
Joissain tapauksissa Bitmap Heap Scan tulee meille kalliiksi - esimerkiksi meidän tilanteessamme, kun aika monet tietueet täyttävät vaaditun ehdon. Saimme sen, koska OR-ehto muuttui BitmapOr:ksi- toiminta suunnitelmassa.
Palataan alkuperäiseen ongelmaan - meidän on löydettävä vastaava tietue mille tahansa ehdoista - eli kaikkia 59K tietueita ei tarvitse etsiä molemmissa olosuhteissa. On olemassa tapa ratkaista yksi ehto, ja Siirry toiseen vasta, kun ensimmäisestä ei löytynyt mitään. Seuraava suunnittelu auttaa meitä:
(
SELECT
...
LIMIT 1
)
UNION ALL
(
SELECT
...
LIMIT 1
)
LIMIT 1
"Ulkoinen" RAJA 1 varmistaa, että haku päättyy, kun ensimmäinen tietue löytyy. Ja jos se löytyy jo ensimmäisestä lohkosta, toista lohkoa ei suoriteta (ei koskaan teloitettu suhteen).
"Vaikeiden olosuhteiden piilottaminen CASE:n alla"
Alkuperäisessä kyselyssä on äärimmäisen epämukava hetki - tilan tarkistaminen vastaavasta taulukosta "DocumentExtension". Riippumatta lausekkeen muiden ehtojen totuudesta (esim. d."Poistettu" EI OLE TOSI), tämä yhteys suoritetaan aina ja "maksaa resursseja". Enemmän tai vähemmän niistä käytetään - riippuu tämän pöydän koosta.
Voit kuitenkin muokata kyselyä niin, että liittyvää tietuetta etsitään vain silloin, kun se on todella tarpeen:
SELECT
...
FROM
"Документ" d
WHERE
... /*index cond*/ AND
CASE
WHEN "$Черновик" IS NULL AND "Удален" IS NOT TRUE THEN (
SELECT
"Состояние"[1] IS TRUE
FROM
"ДокументРасширение"
WHERE
"@Документ" = d."@Документ"
)
END
Kerran linkitetystä taulukosta meille mitään kenttiä ei tarvita tulokseen, niin meillä on mahdollisuus muuttaa JOIN alikyselyn ehdoksi.
Jätetään indeksoidut kentät "CASE-sulkujen ulkopuolelle", lisätään yksinkertaiset ehdot tietueesta WHEN-lohkoon - ja nyt "raskas" kysely suoritetaan vain siirrettäessä THEN:iin.
Sukunimeni on "Total"
Keräämme tuloksena olevan kyselyn kaikilla yllä kuvatuilla mekaniikkailla:
WITH T AS (
SELECT
"@ТипДокумента"
FROM
"ТипДокумента"
WHERE
"ТипДокумента" = 'ПланРабот'
)
(
SELECT
TRUE
FROM
"Документ" d
WHERE
("Лицо3", "ТипДокумента") = (19091, (TABLE T)) AND
CASE
WHEN "$Черновик" IS NULL AND "Удален" IS NOT TRUE THEN (
SELECT
"Состояние"[1] IS TRUE
FROM
"ДокументРасширение"
WHERE
"@Документ" = d."@Документ"
)
END
LIMIT 1
)
UNION ALL
(
SELECT
TRUE
FROM
"Документ" d
WHERE
("ТипДокумента", "Сотрудник") = ((TABLE T), 19091) AND
CASE
WHEN "$Черновик" IS NULL AND "Удален" IS NOT TRUE THEN (
SELECT
"Состояние"[1] IS TRUE
FROM
"ДокументРасширение"
WHERE
"@Документ" = d."@Документ"
)
END
LIMIT 1
)
LIMIT 1;
Säätö [to] indekseihin
Koulutettu silmä huomasi, että UNION-alilohkojen indeksoidut ehdot ovat hieman erilaisia - tämä johtuu siitä, että meillä on jo sopivat indeksit taulukossa. Ja jos niitä ei olisi olemassa, kannattaa luoda: Asiakirja (henkilö3, asiakirjatyyppi) и Asiakirja (asiakirjan tyyppi, työntekijä).
kenttien järjestyksestä ROW-ehdoissaSuunnittelijan näkökulmasta tietysti voit kirjoittaa (A, B) = (constA, constB)Ja (B, A) = (constB, constA). Mutta äänitettäessä hakemiston kenttien järjestyksessä, tällainen pyyntö on yksinkertaisesti helpompi korjata myöhemmin.
Mitä suunnitelmassa on?
Valitettavasti meillä oli epäonnea ja ensimmäisestä UNION-lohkosta ei löytynyt mitään, joten toinen teloitettiin silti. Mutta silti - vain 0.037 ms ja 11 puskuria!
Olemme nopeuttaneet pyyntöä ja vähentäneet tietojen pumppaamista muistiin useita tuhansia kertoja, käyttäen melko yksinkertaisia tekniikoita - hyvä tulos pienellä copy-pastella. 🙂
Lähde: will.com