PostgreSQL antipatternid: CTE x CTE

Tegevuse iseloomust tulenevalt tuleb ette olukordi, kus arendaja kirjutab pÀringu ja mÔtleb: "tabel on nutikas, ise lahendab kÔik!"«

MÔnel juhul (osaliselt teadmatusest andmebaasi vÔimaluste suhtes, osaliselt enneaegsetest optimeerimistest) viib selline lÀhenemine "frankensteini" loomisele.

Alustuseks toon nÀite sellisest pÀringust:

-- iga vÔtme paari jaoks leidke seotud 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Ôti jaoks 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 pÔhjal 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 objektiivseks hindamiseks loome mingisuguse meelevaldse andmestiku komplekti:

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Ă€itmise ajast: ĐČŃ‹ĐżĐŸĐ»ĐœĐ”ĐœĐžŃ Đ·Đ°ĐżŃ€ĐŸŃĐ°:

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

LÀbime pÔhjalikult

Vaata tÀhelepanelikult pÀringut ja mÔtleme:

  1. Miks on siin WITH RECURSIVE, kui mingeid rekursiivseid CTE-sid pole?
  2. Miks gruppeerida min/max vÀÀrtused eraldi CTE-s, kui need hiljem ikkagi seotakse originaalvalikuga?
    +25% aega
  3. Miks kasutada lĂ”pus korduvat lugemist eelmisest CTE-st tingimusteta ‘SELECT * FROM’ kaudu?
    +14% aega

Selles olukorras vedas meid, et ĂŒhendamiseks valiti Hash Join, mitte Nested Loop, sest muidu oleksime saanud mitte ĂŒhe ainsa CTE skaneerimise, vaid 10K!

veidi CTE skaneerimisestSiin on oluline meeles pidada, et CTE skaneerimine on sama mis Seq Scan — see tĂ€hendab, et ei mingit indekseerimist, vaid ainult tĂ€ielik otsimine, mis nĂ”uaks 10K x 0.3ms = 3000ms cte_max tsĂŒklite puhul vĂ”i 1K x 1.5ms = 1500ms cte_bind tsĂŒklite puhul!
Kuidas, aga mida soovisime lĂ”puks saada? Ah, tavaliselt tekib selline kĂŒsimus kuskil viienda minuti jooksul «kolmeastmeliste» pĂ€ringute analĂŒĂŒsi kĂ€igus.

Soovisime iga unikaalse vĂ”tme paari jaoks vĂ€lja tuua min/max rĂŒhmas key_a jĂ€rgi.
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, on kogu meie ajavĂ”it -32% — puhas koormus, mis on eemaldatud CPU pĂ”hilt, kui selline pĂ€ring toimub piisavalt tihti.

Üldiselt ei tohiks anda andmebaasi ĂŒlesanne "ĂŒmmargune — kanda, nurgeline — veeretada".

Allikas: habr.com

Osta usaldusvÀÀrne veebihosting DDoS kaitsega, VPS VDS serverid đŸ”„ Osta usaldusvÀÀrne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster