PostgreSQL Antipatterns: ื ื™ื•ื•ื˜ ื‘ืจื™ืฉื•ื

ื”ื™ื•ื ืœื ื™ื”ื™ื• ืžืงืจื™ื ืžื•ืจื›ื‘ื™ื ื•ืืœื’ื•ืจื™ืชืžื™ื ืžืชื•ื—ื›ืžื™ื ื‘-SQL. ื”ื›ืœ ื™ื”ื™ื” ืคืฉื•ื˜ ืžืื•ื“, ื‘ืจืžื” ืฉืœ ืงืคื˜ืŸ ื‘ืจื•ืจ - ื‘ื•ืื• ื ืขืฉื” ืืช ื–ื” ืฆืคื™ื™ื” ื‘ืžืจืฉื ื”ืื™ืจื•ืขื™ื ืžืžื•ื™ืŸ ืœืคื™ ื–ืžืŸ.

ื›ืœื•ืžืจ, ื™ืฉ ืฉืœื˜ ื‘ืžืกื“ ื”ื ืชื•ื ื™ื events, ื•ื™ืฉ ืœื” ืฉื“ื” ts - ื‘ื“ื™ื•ืง ื”ืฉืขื” ืฉื‘ื” ืื ื• ืจื•ืฆื™ื ืœื”ืฆื™ื’ ืืช ื”ืจืฉื•ืžื•ืช ื”ืœืœื• ื‘ืฆื•ืจื” ืžืกื•ื“ืจืช:

CREATE TABLE events(
  id
    serial
      PRIMARY KEY
, ts
    timestamp
, data
    json
);

CREATE INDEX ON events(ts DESC);

ื‘ืจื•ืจ ืฉืœื ื™ื”ื™ื• ืœื ื• ืฉื ืชืจื™ืกืจ ืจืฉื•ืžื•ืช, ืื– ื ืฆื˜ืจืš ืฆื•ืจื” ื›ืœืฉื”ื™ ืฉืœ ื ื™ื•ื•ื˜ ื‘ื“ืฃ.

#0. "ืื ื™ ื”ืคื•ื’ืจื•ื ืฉืœ ืืžื ืฉืœื™"

cur.execute("SELECT * FROM events;")
rows = cur.fetchall();
rows.sort(key=lambda row: row.ts, reverse=True);
limit = 26
print(rows[offset:offset+limit]);

ื–ื” ื›ืžืขื˜ ืœื ื‘ื“ื™ื—ื” - ื–ื” ื ื“ื™ืจ, ืื‘ืœ ื ืžืฆื ื‘ื˜ื‘ืข. ืœืคืขืžื™ื, ืœืื—ืจ ืขื‘ื•ื“ื” ืขื ORM, ื™ื›ื•ืœ ืœื”ื™ื•ืช ืงืฉื” ืœืขื‘ื•ืจ ืœืขื‘ื•ื“ื” "ื™ืฉื™ืจื”" ืขื SQL.

ืื‘ืœ ื‘ื•ืื• ื ืขื‘ื•ืจ ืœื‘ืขื™ื•ืช ื ืคื•ืฆื•ืช ื™ื•ืชืจ ื•ืคื—ื•ืช ื‘ืจื•ืจื•ืช.

#1. ืœึฐืงึทื–ึตื–

SELECT
  ...
FROM
  events
ORDER BY
  ts DESC
LIMIT 26 OFFSET $1; -- 26 - ะทะฐะฟะธัะตะน ะฝะฐ ัั‚ั€ะฐะฝะธั†ะต, $1 - ะฝะฐั‡ะฐะปะพ ัั‚ั€ะฐะฝะธั†ั‹

ืžืื™ืคื” ื”ื’ื™ืข ื”ืžืกืคืจ 26? ื–ื”ื• ื”ืžืกืคืจ ื”ืžืฉื•ืขืจ ืฉืœ ืขืจื›ื™ื ืœืžื™ืœื•ื™ ืžืกืš ืื—ื“. ืœื™ืชืจ ื“ื™ื•ืง, 25 ืจืฉื•ืžื•ืช ืฉื”ื•ืฆื’ื•, ืคืœื•ืก 1, ืžืื•ืชืชื•ืช ืฉืœืคื—ื•ืช ื™ืฉ ืขื•ื“ ืžืฉื”ื• ื‘ืžื“ื’ื ื•ื”ื’ื™ื•ื ื™ ืœื”ืžืฉื™ืš ื”ืœืื”.

ื›ืžื•ื‘ืŸ, ืœื ื ื™ืชืŸ "ืœืชืคื•ืจ" ืขืจืš ื–ื” ืœื’ื•ืฃ ื”ื‘ืงืฉื”, ืืœื ืœื”ืขื‘ื™ืจ ืื•ืชื• ื“ืจืš ืคืจืžื˜ืจ. ืื‘ืœ ื‘ืžืงืจื” ื–ื”, ืžืชื–ืžืŸ PostgreSQL ืœื ื™ื•ื›ืœ ืœื”ืกืชืžืš ืขืœ ื”ื™ื“ื™ืขื” ืฉืืžื•ืจื•ืช ืœื”ื™ื•ืช ืžืขื˜ ืจืฉื•ืžื•ืช ื™ื—ืกื™ืช โ€“ ื•ื™ื‘ื—ืจ ื‘ืงืœื•ืช ื‘ืชื•ื›ื ื™ืช ืœื ื™ืขื™ืœื”.

ื•ื‘ืขื•ื“ ืฉื‘ืžืžืฉืง ื”ืืคืœื™ืงืฆื™ื”, ื”ืฆืคื™ื™ื” ื‘ืจื™ืฉื•ื ืžื™ื•ืฉืžืช ื›ืžืขื‘ืจ ื‘ื™ืŸ "ื“ืคื™ื" ื—ื–ื•ืชื™ื™ื, ืืฃ ืื—ื“ ืœื ืฉื ืœื‘ ืœืžืฉื”ื• ื—ืฉื•ื“ ื‘ืžืฉืš ื–ืžืŸ ืจื‘. ื‘ื“ื™ื•ืง ืขื“ ืœืจื’ืข ืฉื‘ื•, ื‘ืžืื‘ืง ืขืœ ื”ื ื•ื—ื•ืช, UI/UX ืžื—ืœื™ื˜ ืœื”ืคื•ืš ืืช ื”ืžืžืฉืง ืœ"ื’ืœื™ืœื” ืื™ื ืกื•ืคื™ืช" - ื›ืœื•ืžืจ, ื›ืœ ืขืจื›ื™ ื”ืจื™ืฉื•ื ืžืฆื•ื™ืจื™ื ื‘ืจืฉื™ืžื” ืื—ืช ืฉื”ืžืฉืชืžืฉ ื™ื›ื•ืœ ืœื’ืœื•ืœ ืœืžืขืœื” ื•ืœืžื˜ื”.

ื•ื›ืš, ื‘ืžื”ืœืš ื”ื‘ื“ื™ืงื” ื”ื‘ืื”, ืืชื” ื ืชืคืก ืฉื›ืคื•ืœ ืจืฉื•ืžื•ืช ื‘ืจื™ืฉื•ื. ืœืžื”, ื›ื™ ืœื˜ื‘ืœื” ื™ืฉ ืื™ื ื“ืงืก ืจื’ื™ืœ (ts), ืฉืขืœื™ื• ืžืกืชืžื›ืช ื”ืฉืื™ืœืชื” ืฉืœืš?

ื‘ื“ื™ื•ืง ื‘ื’ืœืœ ืฉืœื ืœืงื—ืชื ืืช ื–ื” ื‘ื—ืฉื‘ื•ืŸ ts ืื™ื ื• ืžืคืชื— ื™ื™ื—ื•ื“ื™ ื‘ื˜ื‘ืœื” ื–ื•. ื‘ืขืฆื, ื• ื”ืขืจื›ื™ื ืฉืœื• ืื™ื ื ื™ื™ื—ื•ื“ื™ื™ื, ื›ืžื• ื›ืœ "ื–ืžืŸ" ื‘ืชื ืื™ื ืืžื™ืชื™ื™ื - ืœื›ืŸ, ืื•ืชื” ืจืฉื•ืžื” ื‘ืฉืชื™ ืฉืื™ืœืชื•ืช ืกืžื•ื›ื•ืช "ืงื•ืคืฆืช" ื‘ืงืœื•ืช ืžืขืžื•ื“ ืœืขืžื•ื“ ืขืงื‘ ืกื“ืจ ืกื•ืคื™ ืฉื•ื ื” ื‘ืžืกื’ืจืช ื”ืžื™ื•ืŸ ืฉืœ ืื•ืชื• ืขืจืš ืžืคืชื—.

ืœืžืขืฉื”, ืžืกืชืชืจืช ื›ืืŸ ื’ื ื‘ืขื™ื” ืฉื ื™ื™ื”, ืฉื”ืจื‘ื” ื™ื•ืชืจ ืงืฉื” ืœื”ื‘ื—ื™ืŸ ื‘ื” - ื—ืœืง ืžื”ืขืจื›ื™ื ืœื ื™ื•ืฆื’ื• ื‘ื›ืœืœ! ืื—ืจื™ ื”ื›ืœ, ื”ืจืฉื•ืžื•ืช ื”"ื›ืคื•ืœื•ืช" ืชืคืกื• ืืช ืžืงื•ืžื• ืฉืœ ืžื™ืฉื”ื• ืื—ืจ. ื ื™ืชืŸ ืœืžืฆื•ื ื”ืกื‘ืจ ืžืคื•ืจื˜ ืขื ืชืžื•ื ื•ืช ื™ืคื•ืช ืงืจื ื›ืืŸ.

ื”ืจื—ื‘ืช ื”ืื™ื ื“ืงืก

ืžืคืชื— ืขืจืžื•ืžื™ ืžื‘ื™ืŸ ืฉืฆืจื™ืš ืœื”ืคื•ืš ืืช ืžืคืชื— ื”ืื™ื ื“ืงืก ืœื™ื™ื—ื•ื“ื™, ื•ื”ื“ืจืš ื”ืงืœื” ื‘ื™ื•ืชืจ ื”ื™ื ืœื”ืจื—ื™ื‘ ืื•ืชื• ืขื ืฉื“ื” ื™ื™ื—ื•ื“ื™ ื‘ืขืœื™ืœ, ืฉ-PK ืžื•ืฉืœื ืขื‘ื•ืจื•:

CREATE UNIQUE INDEX ON events(ts DESC, id DESC);

ื•ื”ื‘ืงืฉื” ืžืฉืชื ื”:

SELECT
  ...
ORDER BY
  ts DESC, id DESC
LIMIT 26 OFFSET $1;

#2. ืขื‘ื•ืจ ืœ"ืกืžื ื™ื"

ะะตะบะพั‚ะพั€ะพะต ะฒั€ะตะผั ัะฟัƒัั‚ั ะบ ะฒะฐะผ ะฟั€ะธั…ะพะดะธั‚ DBA ะธ ยซั€ะฐะดัƒะตั‚ยป, ั‡ั‚ะพ ะฒะฐัˆะธ ะทะฐะฟั€ะพัั‹ ื”ื ื˜ื•ืขื ื™ื ืืช ื”ืฉืจืช ื›ืžื• ืœืขื–ืื–ืœ ืขื ื—ื•ืงื™ ื”OFFSET ืฉืœื”ื, ื•ื‘ื›ืœืœ, ื”ื’ื™ืข ื”ื–ืžืŸ ืœืขื‘ื•ืจ ืœ ื ื™ื•ื•ื˜ ืžื”ืขืจืš ื”ืื—ืจื•ืŸ ืฉื”ื•ืฆื’. ื”ืฉืื™ืœืชื” ืฉืœืš ืžืฉืชื ื™ืช ืฉื•ื‘:

SELECT
  ...
WHERE
  (ts, id) < ($1, $2) -- ะฟะพัะปะตะดะฝะธะต ะฟะพะปัƒั‡ะตะฝะฝั‹ะต ะฝะฐ ะฟั€ะตะดั‹ะดัƒั‰ะตะผ ัˆะฐะณะต ะทะฝะฐั‡ะตะฝะธั
ORDER BY
  ts DESC, id DESC
LIMIT 26;

ะ’ั‹ ะพะฑะปะตะณั‡ะตะฝะฝะพ ะฒะทะดะพั…ะฝัƒะปะธ, ะฟะพะบะฐ ะฝะต ะฝะฐัั‚ัƒะฟะธะปะฐโ€ฆ

#3. ื ื™ืงื•ื™ ืื™ื ื“ืงืกื™ื

ื›ื™ ื™ื•ื ืื—ื“ ืงืจื ืืช ื”-DBA ืฉืœืš ืžืืžืจ ืขืœ ืžืฆื™ืืช ืื™ื ื“ืงืกื™ื ืœื ื™ืขื™ืœื™ื ื•ื”ื‘ื™ืŸ ืืช ื–ื” ื—ื•ืชืžืช ื”ื–ืžืŸ "ืœื ื”ืื—ืจื•ื ื”" ืื™ื ื” ื˜ื•ื‘ื”. ื•ื”ื’ืขืชื™ ืืœื™ืš ืฉื•ื‘ - ืขื›ืฉื™ื• ื‘ืžื—ืฉื‘ื” ืฉื”ืžื“ื“ ื”ื–ื” ืขื“ื™ื™ืŸ ืฆืจื™ืš ืœื—ื–ื•ืจ (ts DESC).

ืื‘ืœ ืžื” ืœืขืฉื•ืช ืขื ื”ื‘ืขื™ื” ื”ืจืืฉื•ื ื™ืช ืฉืœ "ืงืคื™ืฆืช" ืจืฉื•ืžื•ืช ื‘ื™ืŸ ืขืžื•ื“ื™ื?.. ื•ื”ื›ืœ ืคืฉื•ื˜ - ืฆืจื™ืš ืœื‘ื—ื•ืจ ื‘ืœื•ืงื™ื ืขื ืžืกืคืจ ืœื ืงื‘ื•ืข ืฉืœ ืจืฉื•ืžื•ืช!

ื‘ื›ืœืœ, ืžื™ ืื•ืกืจ ืขืœื™ื ื• ืœืงืจื•ื ืœื "ื‘ื“ื™ื•ืง 26", ืืœื "ืœื ืคื—ื•ืช ืž-26"? ืœืžืฉืœ, ื›ื“ื™ ืฉื‘ื’ื•ืฉ ื”ื‘ื ื™ืฉ ืจืฉื•ืžื•ืช ืขื ืžืฉืžืขื•ื™ื•ืช ืฉื•ื ื•ืช ื‘ืจื•ืจื•ืช ts - ืื– ืœื ืชื”ื™ื” ื‘ืขื™ื” ืขื "ืงืคื™ืฆื”" ืฉืœ ืจืฉื•ืžื•ืช ื‘ื™ืŸ ื‘ืœื•ืงื™ื!

ื”ื ื” ืื™ืš ืœื”ืฉื™ื’ ื–ืืช:

SELECT
  ...
WHERE
  ts < $1 AND
  ts >= coalesce((
    SELECT
      ts
    FROM
      events
    WHERE
      ts < $1
    ORDER BY
      ts DESC
    LIMIT 1 OFFSET 25
  ), '-infinity')
ORDER BY
  ts DESC;

ืžื” ืงื•ืจื” ืคื”?

  1. ืื ื• ืžื“ืจื’ื™ื 25 ืจืฉื•ืžื•ืช "ืœืžื˜ื”" ื•ืžืงื‘ืœื™ื ืืช ืขืจืš ื”"ื’ื‘ื•ืœ". ts.
  2. ืื ืื™ืŸ ืฉื ื›ืœื•ื, ืื– ื”ื—ืœืฃ ืืช ื”ืขืจืš NULL ื‘- -infinity.
  3. ืื ื• ืžืคื—ื™ืชื™ื ืืช ื›ืœ ืžืงื˜ืข ื”ืขืจื›ื™ื ืฉื‘ื™ืŸ ื”ืขืจืš ื”ืžืชืงื‘ืœ ts ื•ื”ืคืจืžื˜ืจ $1 ืฉื”ื•ืขื‘ืจ ืžื”ืžืžืฉืง (ื”ืขืจืš ื”ืงื•ื“ื ืฉื”ื•ืฆื’ "ื”ืื—ืจื•ืŸ").
  4. ืื ื‘ืœื•ืง ืžื•ื—ื–ืจ ืขื ืคื—ื•ืช ืž-26 ืจืฉื•ืžื•ืช, ื”ื•ื ื”ืื—ืจื•ืŸ.

ืื• ืื•ืชื” ืชืžื•ื ื”:
PostgreSQL Antipatterns: ื ื™ื•ื•ื˜ ื‘ืจื™ืฉื•ื

ื›ื™ ืขื›ืฉื™ื• ื™ืฉ ืœื ื• ืœืžื“ื’ื ืื™ืŸ ืฉื•ื "ื”ืชื—ืœื”" ืกืคืฆื™ืคื™ืช, ืื– ืฉื•ื ื“ื‘ืจ ืœื ืžื•ื ืข ืžืื™ืชื ื• "ืœื”ืจื—ื™ื‘" ืืช ื”ื‘ืงืฉื” ื”ื–ื• ื‘ื›ื™ื•ื•ืŸ ื”ื”ืคื•ืš ื•ืœื™ื™ืฉื ื˜ืขื™ื ื” ื“ื™ื ืžื™ืช ืฉืœ ื‘ืœื•ืงื™ ื ืชื•ื ื™ื ืž"ื ืงื•ื“ืช ื”ื”ืชื™ื™ื—ืกื•ืช" ืœืฉื ื™ ื”ื›ื™ื•ื•ื ื™ื - ื’ื ืœืžื˜ื” ื•ื’ื ืœืžืขืœื”.

ื”ืขืจื”:

  1. ื›ืŸ, ื‘ืžืงืจื” ื–ื” ืื ื• ื ื™ื’ืฉื™ื ืœืื™ื ื“ืงืก ืคืขืžื™ื™ื, ืื‘ืœ ื”ื›ืœ "ืœืคื™ ืื™ื ื“ืงืก ื‘ืœื‘ื“". ืœื›ืŸ, ืฉืื™ืœืชืช ืžืฉื ื” ืชื‘ื™ื ืจืง ืœืกืจื™ืงื” ืื—ืช ื ื•ืกืคืช ืฉืœ ืื™ื ื“ืงืก ื‘ืœื‘ื“.
  2. ื–ื” ื“ื™ ื‘ืจื•ืจ ืฉืืคืฉืจ ืœื”ืฉืชืžืฉ ื‘ื˜ื›ื ื™ืงื” ื”ื–ื• ืจืง ื›ืฉื™ืฉ ืœืš ืขืจื›ื™ื ts ื™ื›ื•ืœ ืœื—ืฆื•ืช ืจืง ื‘ืžืงืจื”, ื• ื›ืžื” ืžื”ื. ืื ื”ืžืงืจื” ื”ื˜ื™ืคื•ืกื™ ืฉืœืš ื”ื•ื "ืžื™ืœื™ื•ืŸ ืจืฉื•ืžื•ืช ื‘ืฉืขื” 00:00:00.000", ืืชื” ืœื ืฆืจื™ืš ืœืขืฉื•ืช ื–ืืช. ื›ืœื•ืžืจ, ืืชื” ืœื ืฆืจื™ืš ืœืืคืฉืจ ืœืžืงืจื” ื›ื–ื” ืœืงืจื•ืช. ืื‘ืœ ืื ื–ื” ืงื•ืจื”, ื”ืฉืชืžืฉ ื‘ืืคืฉืจื•ืช ืขื ืื™ื ื“ืงืก ืžื•ืจื—ื‘.

ืžืงื•ืจ: www.habr.com

ื”ื•ืกืคืช ืชื’ื•ื‘ื”