PostgreSQL antipatternid: CTE x CTE

Tegevuse iseloomu tÔttu tuleb ette olukordi, kus arendaja kirjutab pÀringu ja mÔtleb: "andmebaas on nutikas, lahendab kÔik ise!"«

MÔnel juhul (osaliselt teadmatusest andmebaasi vÔimalustest, osaliselt liigsete varajaste optimeerimiste tÔttu) viib selline lÀhenemine "frankensteini" loomisele.

Alustuseks toome nÀite sellisest pÀringust:

-- iga vÔtme paari jaoks leiame seotud vÀljade vÀÀrtused
WITH RECURSIVE cte_bind AS (
  SELECT DISTINCT ON (key_a, key_b)
    key_a a
  , key_b b
  , fld1 bind_fld1
  , fld2 bind_fld2
  FROM
    tbl
)
-- leiame iga esimese vÔtme min/max vÀÀrtused
, cte_max AS (
  SELECT
    a
  , max(bind_fld1) bind_fld1
  , min(bind_fld2) bind_fld2
  FROM
    cte_bind
  GROUP BY
    a
)
-- seome esimese vÔtme jÀrgi vÔtme paarid ja min/max vÀÀrtused
, cte_a_bind AS (
  SELECT
    cte_bind.a
  , cte_bind.b
  , cte_max.bind_fld1
  , cte_max.bind_fld2
  FROM
    cte_bind
  INNER JOIN
    cte_max
      ON cte_max.a = cte_bind.a
)
SELECT * FROM cte_a_bind;

Kvaliteedi hindamiseks loome tĂŒĂŒpilise andmestiku kogumi:

CREATE TABLE tbl AS
SELECT
  (random() * 1000)::integer key_a
, (random() * 1000)::integer key_b
, (random() * 10000)::integer fld1
, (random() * 10000)::integer fld2
FROM
  generate_series(1, 10000);
CREATE INDEX ON tbl(key_a, key_b);

Selgub, et andmete lugemine vÔttis vÀhem kui veerand kogu pÀringu tÀitmisest: Eurekala:

PostgreSQL antipatternid: CTE x CTE[vaata explain.tensor.ru]

LÀbime pÔhjalikult

Vaadates pÀringut lÀhemalt, jÀÀme mÔtlema:

  1. Miks on siin WITH RECURSIVE, kui mingit rekurssiivset CTE-d — ei ole?
  2. Miks grupeerida min/max vÀÀrtused eraldi CTE-s, kui nad siis ikkagi seotakse originaalvalikuga?
    +25% aega
  3. Miks kasutada lĂ”pus uuesti eelmist CTE-d tingimusteta ‘SELECT * FROM’ kaudu?
    +14% aega

Meil oli sel juhul eriti vedanud, et ĂŒhenduseks valiti Hash Join, mitte Nested Loop, sest muidu saaksime mitte ĂŒhte, vaid 10K lĂ€bitud CTE skaneeringut!

natuke CTE skaneerimisestSiin tuleb meeles pidada, et CTE skaneerimine on Seq skaneerimise analoog — see tĂ€hendab, et mingit indekseerimist ei ole, vaid tĂ€ielik lĂ€bitöötamine, mis nĂ”uab 10K x 0.3ms = 3000ms cte_max-i silmade kaudu vĂ”i 1K x 1.5ms = 1500ms cte_bind-i silmade kaudu!
Tegelikult, mida me tulemuseks tahtsime? Jah, tavaliselt selline kĂŒsimus tekib kusagil viienda minuti jooksul "kolmeastmeliste" pĂ€ringute analĂŒĂŒsimisel.

Soovisime igale unikaalsele vÔtme paarile esitada min/max grupi jÀrgi key_a.
Nii et kasutame selleks akenfunktsioone:

SELECT DISTINCT ON(key_a, key_b)
	key_a a
,	key_b b
,	max(fld1) OVER(w) bind_fld1
,	min(fld2) OVER(w) bind_fld2
FROM
	tbl
WINDOW
	w AS (PARTITION BY key_a);

PostgreSQL antipatternid: CTE x CTE
[vaata explain.tensor.ru]

Kuna andmete lugemine mĂ”lemas variandis kestab umbes 4-5 ms, siis on kogu meie aja sÀÀst -32% — puhtalt koormus, mis on eemaldatud CPU pĂ”hilt, kui sellist pĂ€ringut tehakse piisavalt tihti.

Üldiselt ei tasu baasi sundida "ĂŒmmargust kandma, kandilist veeretama".

Allikas: habr.com

Osta usaldusvÀÀrne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid đŸ”„ Osta usaldusvÀÀrne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid | ProHoster