PostgreSQL యాంటీప్యాటర్న్‌లు: హానికరమైన చేరికలు మరియు ORలు

బఫర్‌లను తీసుకువచ్చే కార్యకలాపాల పట్ల జాగ్రత్త వహించండి...
చిన్న ప్రశ్నను ఉదాహరణగా ఉపయోగించి, PostgreSQLలో ప్రశ్నలను ఆప్టిమైజ్ చేయడానికి కొన్ని సార్వత్రిక విధానాలను చూద్దాం. మీరు వాటిని ఉపయోగించాలా వద్దా అనేది మీ ఇష్టం, కానీ వాటి గురించి తెలుసుకోవడం విలువైనదే.

PG యొక్క కొన్ని తదుపరి సంస్కరణల్లో, షెడ్యూలర్ తెలివిగా మారినప్పుడు పరిస్థితి మారవచ్చు, కానీ 9.4/9.6 కోసం ఇది ఇక్కడ ఉదాహరణలలో వలె దాదాపుగా అదే విధంగా కనిపిస్తుంది.

చాలా నిజమైన అభ్యర్థనను తీసుకుందాం:

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;

పట్టిక మరియు ఫీల్డ్ పేర్ల గురించిఫీల్డ్‌లు మరియు టేబుల్‌ల “రష్యన్” పేర్లను భిన్నంగా పరిగణించవచ్చు, కానీ ఇది రుచికి సంబంధించిన విషయం. ఎందుకంటే ఇక్కడ టెన్సర్ వద్ద విదేశీ డెవలపర్‌లు లేరు మరియు పోస్ట్‌గ్రెస్‌ఎస్‌క్యూఎల్ హైరోగ్లిఫ్‌లలో కూడా పేర్లను ఇవ్వడానికి అనుమతిస్తుంది. కోట్స్‌లో జతచేయబడింది, అప్పుడు మేము వస్తువులను నిస్సందేహంగా మరియు స్పష్టంగా పేరు పెట్టడానికి ఇష్టపడతాము, తద్వారా వ్యత్యాసాలు లేవు.
ఫలిత ప్రణాళికను చూద్దాం:
PostgreSQL యాంటీప్యాటర్న్‌లు: హానికరమైన చేరికలు మరియు ORలు
[explain.tensor.ru చూడండి]

144ms మరియు దాదాపు 53K బఫర్‌లు - అంటే, 400MB కంటే ఎక్కువ డేటా! మరియు మన అభ్యర్థన సమయానికి అవన్నీ కాష్‌లో ఉంటే మనం అదృష్టవంతులమవుతాము, లేకుంటే డిస్క్ నుండి చదివినప్పుడు చాలా రెట్లు ఎక్కువ సమయం పడుతుంది.

అల్గోరిథం చాలా ముఖ్యమైనది!

ఏదైనా అభ్యర్థనను ఎలాగైనా ఆప్టిమైజ్ చేయడానికి, అది ఏమి చేయాలో మీరు మొదట అర్థం చేసుకోవాలి.
ప్రస్తుతానికి ఈ కథనం యొక్క పరిధికి వెలుపల డేటాబేస్ నిర్మాణం యొక్క అభివృద్ధిని వదిలివేద్దాం మరియు మేము సాపేక్షంగా "చౌకగా" చేయగలమని అంగీకరిస్తున్నాము. అభ్యర్థనను తిరిగి వ్రాయండి మరియు/లేదా మనకు అవసరమైన కొన్ని వస్తువులను బేస్‌లోకి మార్చండి సూచికలు.

కాబట్టి అభ్యర్థన:
- కనీసం కొంత పత్రం ఉనికిని తనిఖీ చేస్తుంది
- మనకు అవసరమైన స్థితిలో మరియు ఒక నిర్దిష్ట రకం
- ఇక్కడ రచయిత లేదా ప్రదర్శకుడు మనకు అవసరమైన ఉద్యోగి

చేరండి + పరిమితి 1

చాలా తరచుగా, డెవలపర్‌కు పెద్ద సంఖ్యలో పట్టికలు మొదట చేరిన ప్రశ్నను వ్రాయడం చాలా సులభం, ఆపై ఈ మొత్తం సెట్ నుండి ఒక రికార్డ్ మాత్రమే మిగిలి ఉంటుంది. కానీ డెవలపర్‌కు సులభంగా అంటే డేటాబేస్ కోసం మరింత సమర్థవంతమైనది కాదు.
మా విషయంలో కేవలం 3 పట్టికలు మాత్రమే ఉన్నాయి - మరియు ప్రభావం ఏమిటి ...

మొదట "డాక్యుమెంట్ టైప్" టేబుల్‌తో కనెక్షన్‌ని వదిలించుకుందాం మరియు అదే సమయంలో డేటాబేస్‌కు చెప్పండి మా టైప్ రికార్డ్ ప్రత్యేకమైనది (ఇది మాకు తెలుసు, కానీ షెడ్యూలర్‌కి ఇంకా ఆలోచన లేదు):

WITH T AS (
  SELECT
    "@ТипДокумента"
  FROM
    "ТипДокумента"
  WHERE
    "ТипДокумента" = 'ПланРабот'
  LIMIT 1
)
...
WHERE
  d."ТипДокумента" = (TABLE T)
...

అవును, టేబుల్/CTE ఒకే రికార్డ్ యొక్క ఒకే ఫీల్డ్‌ని కలిగి ఉంటే, PGలో మీరు ఇలా కూడా వ్రాయవచ్చు, బదులుగా

d."ТипДокумента" = (SELECT "@ТипДокумента" FROM T LIMIT 1)

PostgreSQL ప్రశ్నలలో లేజీ మూల్యాంకనం

BitmapOr vs UNION

కొన్ని సందర్భాల్లో, బిట్‌మ్యాప్ హీప్ స్కాన్ మాకు చాలా ఖర్చవుతుంది - ఉదాహరణకు, మా పరిస్థితిలో, చాలా ఎక్కువ రికార్డులు అవసరమైన పరిస్థితిని చేరుకున్నప్పుడు. ఎందుకంటే మాకు వచ్చింది OR పరిస్థితి BitmapOrగా మారింది- ప్రణాళికలో ఆపరేషన్.
అసలు సమస్యకు తిరిగి వెళ్దాం - మేము సంబంధిత రికార్డును కనుగొనాలి ఎవరైనా షరతుల నుండి - అంటే, రెండు షరతులలో మొత్తం 59K రికార్డుల కోసం వెతకవలసిన అవసరం లేదు. ఒక షరతుతో పని చేయడానికి ఒక మార్గం ఉంది, మరియు మొదటిదానిలో ఏమీ కనుగొనబడనప్పుడు మాత్రమే రెండవదానికి వెళ్లండి. కింది డిజైన్ మాకు సహాయం చేస్తుంది:

(
  SELECT
    ...
  LIMIT 1
)
UNION ALL
(
  SELECT
    ...
  LIMIT 1
)
LIMIT 1

"బాహ్య" పరిమితి 1 మొదటి రికార్డ్ కనుగొనబడినప్పుడు శోధన ముగుస్తుందని నిర్ధారిస్తుంది. మరియు ఇది ఇప్పటికే మొదటి బ్లాక్‌లో కనుగొనబడితే, రెండవ బ్లాక్ అమలు చేయబడదు (ఎప్పుడూ అమలు చేయలేదు సంబంధించిన).

"కేస్ కింద క్లిష్ట పరిస్థితులను దాచడం"

అసలు ప్రశ్నలో చాలా అసౌకర్య క్షణం ఉంది - సంబంధిత పట్టిక “డాక్యుమెంట్ ఎక్స్‌టెన్షన్”కి వ్యతిరేకంగా స్థితిని తనిఖీ చేస్తోంది. వ్యక్తీకరణలోని ఇతర పరిస్థితుల నిజంతో సంబంధం లేకుండా (ఉదాహరణకు, d.“తొలగించబడింది” నిజం కాదు), ఈ కనెక్షన్ ఎల్లప్పుడూ అమలు చేయబడుతుంది మరియు "వనరులను ఖర్చు చేస్తుంది". వాటిలో ఎక్కువ లేదా తక్కువ ఖర్చు చేయబడుతుంది - ఈ పట్టిక పరిమాణంపై ఆధారపడి ఉంటుంది.
కానీ మీరు ప్రశ్నను సవరించవచ్చు, తద్వారా సంబంధిత రికార్డు కోసం శోధన నిజంగా అవసరమైనప్పుడు మాత్రమే జరుగుతుంది:

SELECT
  ...
FROM
  "Документ" d
WHERE
  ... /*index cond*/ AND
  CASE
    WHEN "$Черновик" IS NULL AND "Удален" IS NOT TRUE THEN (
      SELECT
        "Состояние"[1] IS TRUE
      FROM
        "ДокументРасширение"
      WHERE
        "@Документ" = d."@Документ"
    )
  END

ఒకసారి మాకు లింక్ చేయబడిన పట్టిక నుండి ఫలితం కోసం ఫీల్డ్‌లు ఏవీ అవసరం లేదు, అప్పుడు సబ్‌క్వెరీలో JOINని షరతుగా మార్చడానికి మాకు అవకాశం ఉంది.
ఇండెక్స్ చేయబడిన ఫీల్డ్‌లను “CASE బ్రాకెట్‌ల వెలుపల” వదిలివేద్దాం, రికార్డ్ నుండి WHEN బ్లాక్‌కి సాధారణ షరతులను జోడిద్దాం - మరియు ఇప్పుడు “భారీ” ప్రశ్న ఆ తర్వాత వెళ్లినప్పుడు మాత్రమే అమలు చేయబడుతుంది.

నా చివరి పేరు "మొత్తం"

మేము పైన వివరించిన అన్ని మెకానిక్‌లతో ఫలిత ప్రశ్నను సేకరిస్తాము:

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;

సూచికలను సర్దుబాటు చేయడం

UNION సబ్‌బ్లాక్‌లలో ఇండెక్స్ చేయబడిన పరిస్థితులు కొద్దిగా భిన్నంగా ఉన్నాయని శిక్షణ పొందిన కన్ను గమనించింది - దీనికి కారణం మేము ఇప్పటికే పట్టికలో తగిన సూచికలను కలిగి ఉన్నాము. మరియు అవి ఉనికిలో లేకుంటే, అది సృష్టించడం విలువైనది: పత్రం(వ్యక్తి3, డాక్యుమెంట్ రకం) и పత్రం(పత్రం రకం, ఉద్యోగి).
ROW పరిస్థితుల్లో ఫీల్డ్‌ల క్రమం గురించిప్లానర్ పాయింట్ ఆఫ్ వ్యూ నుండి, మీరు వ్రాయవచ్చు (A, B) = (constA, constB)మరియు (B, A) = (constB, constA). కానీ రికార్డింగ్ చేసేటప్పుడు ఇండెక్స్‌లోని ఫీల్డ్‌ల క్రమంలో, అటువంటి అభ్యర్థన తర్వాత డీబగ్ చేయడానికి మరింత సౌకర్యవంతంగా ఉంటుంది.
ప్లాన్‌లో ఏముంది?
PostgreSQL యాంటీప్యాటర్న్‌లు: హానికరమైన చేరికలు మరియు ORలు
[explain.tensor.ru చూడండి]

దురదృష్టవశాత్తూ, మేము దురదృష్టవంతులం మరియు మొదటి UNION బ్లాక్‌లో ఏమీ కనుగొనబడలేదు, కాబట్టి రెండవది ఇప్పటికీ అమలు చేయబడింది. కానీ కూడా - మాత్రమే 0.037ms మరియు 11 బఫర్‌లు!
మేము అభ్యర్థనను వేగవంతం చేసాము మరియు మెమరీలో డేటా పంపింగ్‌ను తగ్గించాము అనేక వేల సార్లు, చాలా సరళమైన పద్ధతులను ఉపయోగించడం - కొద్దిగా కాపీ-పేస్ట్‌తో మంచి ఫలితం. 🙂

మూలం: www.habr.com

ఒక వ్యాఖ్యను జోడించండి