VACUM ವಿಫಲವಾದಾಗ, ನಾವು ಟೇಬಲ್ ಅನ್ನು ಹಸ್ತಚಾಲಿತವಾಗಿ ಸ್ವಚ್ಛಗೊಳಿಸುತ್ತೇವೆ

ನಿರ್ವಾತ PostgreSQL ನಲ್ಲಿನ ಟೇಬಲ್‌ನಿಂದ "ಸ್ವಚ್ಛಗೊಳಿಸಬಹುದು" ಎಂಬುದನ್ನು ಮಾತ್ರ ಯಾರೂ ನೋಡಲಾರರು - ಅಂದರೆ, ಈ ದಾಖಲೆಗಳನ್ನು ಬದಲಾಯಿಸುವ ಮೊದಲು ಪ್ರಾರಂಭವಾದ ಒಂದೇ ಒಂದು ಸಕ್ರಿಯ ವಿನಂತಿಯೂ ಇಲ್ಲ.

ಆದರೆ ಅಂತಹ ಅಹಿತಕರ ಪ್ರಕಾರ (OLTP ಡೇಟಾಬೇಸ್‌ನಲ್ಲಿ ದೀರ್ಘಾವಧಿಯ OLAP ಲೋಡ್) ಇನ್ನೂ ಅಸ್ತಿತ್ವದಲ್ಲಿದ್ದರೆ ಏನು? ಹೇಗೆ ಸಕ್ರಿಯವಾಗಿ ಬದಲಾಗುತ್ತಿರುವ ಟೇಬಲ್ ಅನ್ನು ಸ್ವಚ್ಛಗೊಳಿಸಿ ದೀರ್ಘ ಪ್ರಶ್ನೆಗಳಿಂದ ಸುತ್ತುವರಿದಿದೆ ಮತ್ತು ಕುಂಟೆ ಮೇಲೆ ಹೆಜ್ಜೆ ಹಾಕುತ್ತಿಲ್ಲವೇ?

VACUM ವಿಫಲವಾದಾಗ, ನಾವು ಟೇಬಲ್ ಅನ್ನು ಹಸ್ತಚಾಲಿತವಾಗಿ ಸ್ವಚ್ಛಗೊಳಿಸುತ್ತೇವೆ

ಕುಂಟೆ ಬಿಚ್ಚುವುದು

ಮೊದಲಿಗೆ, ನಾವು ಪರಿಹರಿಸಲು ಬಯಸುವ ಸಮಸ್ಯೆ ಏನು ಮತ್ತು ಅದು ಹೇಗೆ ಉದ್ಭವಿಸಬಹುದು ಎಂಬುದನ್ನು ನಿರ್ಧರಿಸೋಣ.

ಸಾಮಾನ್ಯವಾಗಿ ಈ ಪರಿಸ್ಥಿತಿಯು ಸಂಭವಿಸುತ್ತದೆ ತುಲನಾತ್ಮಕವಾಗಿ ಸಣ್ಣ ಮೇಜಿನ ಮೇಲೆ, ಆದರೆ ಇದರಲ್ಲಿ ಅದು ಸಂಭವಿಸುತ್ತದೆ ಬಹಳಷ್ಟು ಬದಲಾವಣೆಗಳು. ಸಾಮಾನ್ಯವಾಗಿ ಇದು ಅಥವಾ ವಿಭಿನ್ನವಾಗಿದೆ ಮೀಟರ್‌ಗಳು/ಒಟ್ಟುಗಳು/ರೇಟಿಂಗ್‌ಗಳು, ನವೀಕರಣವನ್ನು ಹೆಚ್ಚಾಗಿ ಕಾರ್ಯಗತಗೊಳಿಸಲಾಗುತ್ತದೆ, ಅಥವಾ ಬಫರ್-ಕ್ಯೂ ಕೆಲವು ನಿರಂತರವಾಗಿ ನಡೆಯುತ್ತಿರುವ ಘಟನೆಗಳ ಸ್ಟ್ರೀಮ್ ಅನ್ನು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲು, ಅದರ ದಾಖಲೆಗಳನ್ನು ನಿರಂತರವಾಗಿ ಸೇರಿಸಲಾಗುತ್ತದೆ/ಅಳಿಸಲಾಗುತ್ತದೆ.

ರೇಟಿಂಗ್‌ಗಳೊಂದಿಗೆ ಆಯ್ಕೆಯನ್ನು ಪುನರುತ್ಪಾದಿಸಲು ಪ್ರಯತ್ನಿಸೋಣ:

CREATE TABLE tbl(k text PRIMARY KEY, v integer);
CREATE INDEX ON tbl(v DESC); -- по этому индексу будем строить рейтинг

INSERT INTO
  tbl
SELECT
  chr(ascii('a'::text) + i) k
, 0 v
FROM
  generate_series(0, 25) i;

ಮತ್ತು ಸಮಾನಾಂತರವಾಗಿ, ಮತ್ತೊಂದು ಸಂಪರ್ಕದಲ್ಲಿ, ದೀರ್ಘವಾದ, ದೀರ್ಘವಾದ ವಿನಂತಿಯು ಪ್ರಾರಂಭವಾಗುತ್ತದೆ, ಕೆಲವು ಸಂಕೀರ್ಣ ಅಂಕಿಅಂಶಗಳನ್ನು ಸಂಗ್ರಹಿಸುತ್ತದೆ, ಆದರೆ ನಮ್ಮ ಮೇಜಿನ ಮೇಲೆ ಪರಿಣಾಮ ಬೀರುವುದಿಲ್ಲ:

SELECT pg_sleep(10000);

ಈಗ ನಾವು ಕೌಂಟರ್‌ಗಳಲ್ಲಿ ಒಂದರ ಮೌಲ್ಯವನ್ನು ಹಲವು ಬಾರಿ ನವೀಕರಿಸುತ್ತೇವೆ. ಪ್ರಯೋಗದ ಶುದ್ಧತೆಗಾಗಿ, ಇದನ್ನು ಮಾಡೋಣ dblink ಬಳಸಿ ಪ್ರತ್ಯೇಕ ವಹಿವಾಟುಗಳಲ್ಲಿಇದು ವಾಸ್ತವದಲ್ಲಿ ಹೇಗೆ ಸಂಭವಿಸುತ್ತದೆ:

DO $$
DECLARE
  i integer;
  tsb timestamp;
  tse timestamp;
  d double precision;
BEGIN
  PERFORM dblink_connect('dbname=' || current_database() || ' port=' || current_setting('port'));
  FOR i IN 1..10000 LOOP
    tsb = clock_timestamp();
    PERFORM dblink($e$UPDATE tbl SET v = v + 1 WHERE k = 'a';$e$);
    tse = clock_timestamp();
    IF i % 1000 = 0 THEN
      d = (extract('epoch' from tse) - extract('epoch' from tsb)) * 1000;
      RAISE NOTICE 'i = %, exectime = %', lpad(i::text, 5), lpad(d::text, 5);
    END IF;
  END LOOP;
  PERFORM dblink_disconnect();
END;
$$ LANGUAGE plpgsql;

NOTICE:  i =  1000, exectime = 0.524
NOTICE:  i =  2000, exectime = 0.739
NOTICE:  i =  3000, exectime = 1.188
NOTICE:  i =  4000, exectime = 2.508
NOTICE:  i =  5000, exectime = 1.791
NOTICE:  i =  6000, exectime = 2.658
NOTICE:  i =  7000, exectime = 2.318
NOTICE:  i =  8000, exectime = 2.572
NOTICE:  i =  9000, exectime = 2.929
NOTICE:  i = 10000, exectime = 3.808

ಏನಾಯಿತು? ಒಂದೇ ದಾಖಲೆಯ ಸರಳವಾದ ನವೀಕರಣಕ್ಕಾಗಿ ಏಕೆ ಮರಣದಂಡನೆಯ ಸಮಯವನ್ನು 7 ಪಟ್ಟು ಕಡಿಮೆಗೊಳಿಸಲಾಗಿದೆ - 0.524ms ನಿಂದ 3.808ms ವರೆಗೆ? ಮತ್ತು ನಮ್ಮ ರೇಟಿಂಗ್ ಹೆಚ್ಚು ಹೆಚ್ಚು ನಿಧಾನವಾಗಿ ನಿರ್ಮಿಸುತ್ತಿದೆ.

ಇದು ಎಲ್ಲಾ MVCC ಯ ತಪ್ಪು.

ಇದು ಎಲ್ಲಾ ಬಗ್ಗೆ MVCC ಕಾರ್ಯವಿಧಾನ, ಇದು ಪ್ರವೇಶದ ಎಲ್ಲಾ ಹಿಂದಿನ ಆವೃತ್ತಿಗಳನ್ನು ನೋಡಲು ಪ್ರಶ್ನೆಯನ್ನು ಉಂಟುಮಾಡುತ್ತದೆ. ಆದ್ದರಿಂದ "ಡೆಡ್" ಆವೃತ್ತಿಗಳಿಂದ ನಮ್ಮ ಟೇಬಲ್ ಅನ್ನು ಸ್ವಚ್ಛಗೊಳಿಸೋಣ:

VACUUM VERBOSE tbl;

INFO:  vacuuming "public.tbl"
INFO:  "tbl": found 0 removable, 10026 nonremovable row versions in 45 out of 45 pages
DETAIL:  10000 dead row versions cannot be removed yet, oldest xmin: 597439602

ಓಹ್, ಸ್ವಚ್ಛಗೊಳಿಸಲು ಏನೂ ಇಲ್ಲ! ಸಮಾನಾಂತರ ಚಾಲನೆಯಲ್ಲಿರುವ ವಿನಂತಿಯು ನಮ್ಮೊಂದಿಗೆ ಮಧ್ಯಪ್ರವೇಶಿಸುತ್ತಿದೆ - ಎಲ್ಲಾ ನಂತರ, ಅವನು ಒಂದು ದಿನ ಈ ಆವೃತ್ತಿಗಳಿಗೆ ತಿರುಗಲು ಬಯಸಬಹುದು (ಏನು ವೇಳೆ?), ಮತ್ತು ಅವು ಅವನಿಗೆ ಲಭ್ಯವಿರಬೇಕು. ಮತ್ತು ಆದ್ದರಿಂದ ವ್ಯಾಕ್ಯೂಮ್ ಫುಲ್ ಸಹ ನಮಗೆ ಸಹಾಯ ಮಾಡುವುದಿಲ್ಲ.

ಟೇಬಲ್ ಅನ್ನು "ಕುಗ್ಗಿಸುವಿಕೆ"

ಆದರೆ ಆ ಪ್ರಶ್ನೆಗೆ ನಮ್ಮ ಟೇಬಲ್ ಅಗತ್ಯವಿಲ್ಲ ಎಂದು ನಮಗೆ ಖಚಿತವಾಗಿ ತಿಳಿದಿದೆ. ಆದ್ದರಿಂದ, ಟೇಬಲ್‌ನಿಂದ ಅನಗತ್ಯವಾದ ಎಲ್ಲವನ್ನೂ ತೆಗೆದುಹಾಕುವ ಮೂಲಕ ಸಿಸ್ಟಮ್ ಕಾರ್ಯಕ್ಷಮತೆಯನ್ನು ಸಾಕಷ್ಟು ಮಿತಿಗಳಿಗೆ ಹಿಂತಿರುಗಿಸಲು ನಾವು ಇನ್ನೂ ಪ್ರಯತ್ನಿಸುತ್ತೇವೆ - ಕನಿಷ್ಠ “ಹಸ್ತಚಾಲಿತವಾಗಿ”, ಏಕೆಂದರೆ VACUUM ನೀಡುತ್ತದೆ.

ಅದನ್ನು ಹೆಚ್ಚು ಸ್ಪಷ್ಟಪಡಿಸಲು, ಬಫರ್ ಟೇಬಲ್ನ ಉದಾಹರಣೆಯನ್ನು ನೋಡೋಣ. ಅಂದರೆ, INSERT / DELETE ನ ದೊಡ್ಡ ಹರಿವು ಇದೆ, ಮತ್ತು ಕೆಲವೊಮ್ಮೆ ಟೇಬಲ್ ಸಂಪೂರ್ಣವಾಗಿ ಖಾಲಿಯಾಗಿರುತ್ತದೆ. ಆದರೆ ಅದು ಖಾಲಿಯಾಗಿಲ್ಲದಿದ್ದರೆ, ನಾವು ಮಾಡಬೇಕು ಅದರ ಪ್ರಸ್ತುತ ವಿಷಯಗಳನ್ನು ಉಳಿಸಿ.

#0: ಪರಿಸ್ಥಿತಿಯನ್ನು ನಿರ್ಣಯಿಸುವುದು

ಪ್ರತಿ ಕಾರ್ಯಾಚರಣೆಯ ನಂತರವೂ ನೀವು ಟೇಬಲ್‌ನೊಂದಿಗೆ ಏನನ್ನಾದರೂ ಮಾಡಲು ಪ್ರಯತ್ನಿಸಬಹುದು ಎಂಬುದು ಸ್ಪಷ್ಟವಾಗಿದೆ, ಆದರೆ ಇದು ಹೆಚ್ಚು ಅರ್ಥವಿಲ್ಲ - ನಿರ್ವಹಣಾ ಓವರ್ಹೆಡ್ ಗುರಿ ಪ್ರಶ್ನೆಗಳ ಥ್ರೋಪುಟ್ಗಿಂತ ಸ್ಪಷ್ಟವಾಗಿ ಹೆಚ್ಚಾಗಿರುತ್ತದೆ.

ನಾವು ಮಾನದಂಡಗಳನ್ನು ರೂಪಿಸೋಣ - "ಇದು ಕಾರ್ಯನಿರ್ವಹಿಸಲು ಸಮಯ" ಒಂದು ವೇಳೆ:

  • VACUM ಅನ್ನು ಬಹಳ ಹಿಂದೆಯೇ ಪ್ರಾರಂಭಿಸಲಾಯಿತು
    ನಾವು ಭಾರವಾದ ಹೊರೆಯನ್ನು ನಿರೀಕ್ಷಿಸುತ್ತೇವೆ, ಹಾಗಾಗಿ ಅದು ಇರಲಿ 60 ಸೆಕೆಂಡುಗಳು ಕೊನೆಯ [auto]VACUUM ನಿಂದ.
  • ಭೌತಿಕ ಟೇಬಲ್ ಗಾತ್ರವು ಗುರಿಗಿಂತ ದೊಡ್ಡದಾಗಿದೆ
    ಕನಿಷ್ಠ ಗಾತ್ರಕ್ಕೆ ಹೋಲಿಸಿದರೆ ಪುಟಗಳ ಎರಡು ಪಟ್ಟು (8KB ಬ್ಲಾಕ್‌ಗಳು) ಎಂದು ವ್ಯಾಖ್ಯಾನಿಸೋಣ - ರಾಶಿಗೆ 1 blk + ಪ್ರತಿ ಸೂಚ್ಯಂಕಕ್ಕೆ 1 blk - ಸಂಭಾವ್ಯ ಖಾಲಿ ಟೇಬಲ್‌ಗಾಗಿ. "ಸಾಮಾನ್ಯವಾಗಿ" ಬಫರ್‌ನಲ್ಲಿ ನಿರ್ದಿಷ್ಟ ಪ್ರಮಾಣದ ಡೇಟಾ ಯಾವಾಗಲೂ ಉಳಿಯುತ್ತದೆ ಎಂದು ನಾವು ನಿರೀಕ್ಷಿಸಿದರೆ, ಈ ಸೂತ್ರವನ್ನು ತಿರುಚುವುದು ಸಮಂಜಸವಾಗಿದೆ.

ಪರಿಶೀಲನೆ ವಿನಂತಿ

SELECT
  relpages
, ((
    SELECT
      count(*)
    FROM
      pg_index
    WHERE
      indrelid = cl.oid
  ) + 1) << 13 size_norm -- тут правильнее делать * current_setting('block_size')::bigint, но кто меняет размер блока?..
, pg_total_relation_size(oid) size
, coalesce(extract('epoch' from (now() - greatest(
    pg_stat_get_last_vacuum_time(oid)
  , pg_stat_get_last_autovacuum_time(oid)
  ))), 1 << 30) vaclag
FROM
  pg_class cl
WHERE
  oid = $1::regclass -- tbl
LIMIT 1;

relpages | size_norm | size    | vaclag
-------------------------------------------
       0 |     24576 | 1105920 | 3392.484835

#1: ಇನ್ನೂ ನಿರ್ವಾತ

ಸಮಾನಾಂತರ ಪ್ರಶ್ನೆಯು ನಮ್ಮೊಂದಿಗೆ ಗಮನಾರ್ಹವಾಗಿ ಮಧ್ಯಪ್ರವೇಶಿಸುತ್ತಿದೆಯೇ ಎಂದು ನಮಗೆ ಮುಂಚಿತವಾಗಿ ತಿಳಿಯಲು ಸಾಧ್ಯವಿಲ್ಲ - ಅದು ಪ್ರಾರಂಭವಾದಾಗಿನಿಂದ ಎಷ್ಟು ದಾಖಲೆಗಳು "ಹಳೆಯವು" ಆಗಿವೆ. ಆದ್ದರಿಂದ, ನಾವು ಹೇಗಾದರೂ ಟೇಬಲ್ ಅನ್ನು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲು ನಿರ್ಧರಿಸಿದಾಗ, ಯಾವುದೇ ಸಂದರ್ಭದಲ್ಲಿ, ನಾವು ಮೊದಲು ಅದರ ಮೇಲೆ ಕಾರ್ಯಗತಗೊಳಿಸಬೇಕು ನಿರ್ವಾತ - VACUUM FULL ಗಿಂತ ಭಿನ್ನವಾಗಿ, ಓದಲು-ಬರೆಯುವ ಡೇಟಾದೊಂದಿಗೆ ಕಾರ್ಯನಿರ್ವಹಿಸುವ ಸಮಾನಾಂತರ ಪ್ರಕ್ರಿಯೆಗಳೊಂದಿಗೆ ಇದು ಮಧ್ಯಪ್ರವೇಶಿಸುವುದಿಲ್ಲ.

ಅದೇ ಸಮಯದಲ್ಲಿ, ನಾವು ತೆಗೆದುಹಾಕಲು ಬಯಸುವ ಹೆಚ್ಚಿನದನ್ನು ತಕ್ಷಣವೇ ಸ್ವಚ್ಛಗೊಳಿಸಬಹುದು. ಹೌದು, ಮತ್ತು ಈ ಕೋಷ್ಟಕದಲ್ಲಿನ ನಂತರದ ಪ್ರಶ್ನೆಗಳು ನಮಗೆ ಹೋಗುತ್ತವೆ "ಹಾಟ್ ಕ್ಯಾಶ್" ಮೂಲಕ, ಇದು ಅವರ ಅವಧಿಯನ್ನು ಕಡಿಮೆ ಮಾಡುತ್ತದೆ - ಮತ್ತು, ಆದ್ದರಿಂದ, ನಮ್ಮ ಸೇವಾ ವಹಿವಾಟಿನಿಂದ ಇತರರನ್ನು ನಿರ್ಬಂಧಿಸುವ ಒಟ್ಟು ಸಮಯ.

#2: ಯಾರಾದರೂ ಮನೆಯಲ್ಲಿದ್ದಾರೆಯೇ?

ಕೋಷ್ಟಕದಲ್ಲಿ ಏನಾದರೂ ಇದೆಯೇ ಎಂದು ಪರಿಶೀಲಿಸೋಣ:

TABLE tbl LIMIT 1;

ಒಂದೇ ಒಂದು ದಾಖಲೆಯು ಉಳಿದಿಲ್ಲದಿದ್ದರೆ, ಸರಳವಾಗಿ ಮಾಡುವ ಮೂಲಕ ನಾವು ಪ್ರಕ್ರಿಯೆಯಲ್ಲಿ ಬಹಳಷ್ಟು ಉಳಿಸಬಹುದು ಮೊಟಕುಗೊಳಿಸಿ:

ಇದು ಪ್ರತಿ ಟೇಬಲ್‌ಗೆ ಬೇಷರತ್ತಾದ DELETE ಆಜ್ಞೆಯಂತೆ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತದೆ, ಆದರೆ ಇದು ವಾಸ್ತವವಾಗಿ ಕೋಷ್ಟಕಗಳನ್ನು ಸ್ಕ್ಯಾನ್ ಮಾಡದ ಕಾರಣ ಹೆಚ್ಚು ವೇಗವಾಗಿರುತ್ತದೆ. ಇದಲ್ಲದೆ, ಇದು ತಕ್ಷಣವೇ ಡಿಸ್ಕ್ ಜಾಗವನ್ನು ಮುಕ್ತಗೊಳಿಸುತ್ತದೆ, ಆದ್ದರಿಂದ ನಂತರ VACUUM ಕಾರ್ಯಾಚರಣೆಯನ್ನು ನಿರ್ವಹಿಸುವ ಅಗತ್ಯವಿಲ್ಲ.

ನೀವು ಟೇಬಲ್ ಸೀಕ್ವೆನ್ಸ್ ಕೌಂಟರ್ (ರೀಸ್ಟಾರ್ಟ್ ಐಡೆಂಟಿಟಿ) ಅನ್ನು ಮರುಹೊಂದಿಸಬೇಕೆ ಎಂದು ನಿರ್ಧರಿಸಲು ನಿಮಗೆ ಬಿಟ್ಟದ್ದು.

#3: ಎಲ್ಲರೂ - ತಿರುವುಗಳನ್ನು ತೆಗೆದುಕೊಳ್ಳಿ!

ನಾವು ಹೆಚ್ಚು ಸ್ಪರ್ಧಾತ್ಮಕ ವಾತಾವರಣದಲ್ಲಿ ಕೆಲಸ ಮಾಡುತ್ತಿರುವುದರಿಂದ, ಟೇಬಲ್‌ನಲ್ಲಿ ಯಾವುದೇ ನಮೂದುಗಳಿಲ್ಲ ಎಂದು ನಾವು ಪರಿಶೀಲಿಸುತ್ತಿರುವಾಗ, ಯಾರಾದರೂ ಈಗಾಗಲೇ ಅಲ್ಲಿ ಏನನ್ನಾದರೂ ಬರೆದಿರಬಹುದು. ನಾವು ಈ ಮಾಹಿತಿಯನ್ನು ಕಳೆದುಕೊಳ್ಳಬಾರದು, ಹಾಗಾದರೆ ಏನು? ಅದು ಸರಿ, ಯಾರೂ ಅದನ್ನು ಖಚಿತವಾಗಿ ಬರೆಯಲು ಸಾಧ್ಯವಿಲ್ಲ ಎಂದು ನಾವು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಬೇಕು.

ಇದನ್ನು ಮಾಡಲು ನಾವು ಸಕ್ರಿಯಗೊಳಿಸಬೇಕಾಗಿದೆ ಧಾರಾವಾಹಿ-ನಮ್ಮ ವಹಿವಾಟಿಗೆ ಪ್ರತ್ಯೇಕತೆ (ಹೌದು, ಇಲ್ಲಿ ನಾವು ವಹಿವಾಟನ್ನು ಪ್ರಾರಂಭಿಸುತ್ತೇವೆ) ಮತ್ತು ಟೇಬಲ್ ಅನ್ನು "ಬಿಗಿಯಾಗಿ" ಲಾಕ್ ಮಾಡುತ್ತೇವೆ:

BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
LOCK TABLE tbl IN ACCESS EXCLUSIVE MODE;

ನಿರ್ಬಂಧಿಸುವಿಕೆಯ ಈ ಮಟ್ಟವನ್ನು ನಾವು ಅದರ ಮೇಲೆ ನಿರ್ವಹಿಸಲು ಬಯಸುವ ಕಾರ್ಯಾಚರಣೆಗಳಿಂದ ನಿರ್ಧರಿಸಲಾಗುತ್ತದೆ.

#4: ಹಿತಾಸಕ್ತಿ ಸಂಘರ್ಷ

ನಾವು ಇಲ್ಲಿಗೆ ಬರುತ್ತೇವೆ ಮತ್ತು ಚಿಹ್ನೆಯನ್ನು "ಲಾಕ್" ಮಾಡಲು ಬಯಸುತ್ತೇವೆ - ಆ ಕ್ಷಣದಲ್ಲಿ ಯಾರಾದರೂ ಅದರಲ್ಲಿ ಸಕ್ರಿಯರಾಗಿದ್ದರೆ, ಉದಾಹರಣೆಗೆ, ಅದರಿಂದ ಓದುವುದು? ಈ ಬ್ಲಾಕ್ ಬಿಡುಗಡೆಯಾಗುವವರೆಗೆ ನಾವು "ಹ್ಯಾಂಗ್" ಮಾಡುತ್ತೇವೆ ಮತ್ತು ಓದಲು ಬಯಸುವ ಇತರರು ನಮ್ಮೊಳಗೆ ಓಡುತ್ತಾರೆ...

ಇದು ಸಂಭವಿಸುವುದನ್ನು ತಡೆಯಲು, ನಾವು "ನಮ್ಮನ್ನು ತ್ಯಾಗ ಮಾಡುತ್ತೇವೆ" - ಒಂದು ನಿರ್ದಿಷ್ಟ (ಸ್ವೀಕಾರಾರ್ಹವಾಗಿ ಕಡಿಮೆ) ಸಮಯದೊಳಗೆ ನಾವು ಲಾಕ್ ಅನ್ನು ಪಡೆಯಲು ಸಾಧ್ಯವಾಗದಿದ್ದರೆ, ನಾವು ಬೇಸ್ನಿಂದ ವಿನಾಯಿತಿಯನ್ನು ಪಡೆಯುತ್ತೇವೆ, ಆದರೆ ಕನಿಷ್ಠ ನಾವು ಹೆಚ್ಚು ಹಸ್ತಕ್ಷೇಪ ಮಾಡುವುದಿಲ್ಲ. ಇತರರು.

ಇದನ್ನು ಮಾಡಲು, ಸೆಷನ್ ವೇರಿಯಬಲ್ ಅನ್ನು ಹೊಂದಿಸಿ ಲಾಕ್_ಟೈಮ್‌ಔಟ್ (9.3+ ಆವೃತ್ತಿಗಳಿಗೆ) ಅಥವಾ/ಮತ್ತು ಹೇಳಿಕೆ_ಕಾಲಾವಧಿ. ನೆನಪಿಡಬೇಕಾದ ಮುಖ್ಯ ವಿಷಯವೆಂದರೆ ಸ್ಟೇಟ್‌ಮೆಂಟ್_ಟೈಮ್‌ಔಟ್ ಮೌಲ್ಯವು ಮುಂದಿನ ಹೇಳಿಕೆಯಿಂದ ಮಾತ್ರ ಅನ್ವಯಿಸುತ್ತದೆ. ಅಂದರೆ, ಅಂಟಿಕೊಳ್ಳುವಲ್ಲಿ ಈ ರೀತಿ - ಕೆಲಸ ಮಾಡುವುದಿಲ್ಲ:

SET statement_timeout = ...;LOCK TABLE ...;

ನಂತರ ವೇರಿಯೇಬಲ್ನ "ಹಳೆಯ" ಮೌಲ್ಯವನ್ನು ಮರುಸ್ಥಾಪಿಸುವುದನ್ನು ಎದುರಿಸದಿರಲು, ನಾವು ಫಾರ್ಮ್ ಅನ್ನು ಬಳಸುತ್ತೇವೆ ಸ್ಥಳೀಯ ಹೊಂದಿಸಿ, ಇದು ಪ್ರಸ್ತುತ ವಹಿವಾಟಿಗೆ ಸೆಟ್ಟಿಂಗ್ ವ್ಯಾಪ್ತಿಯನ್ನು ಮಿತಿಗೊಳಿಸುತ್ತದೆ.

ಸ್ಟೇಟ್‌ಮೆಂಟ್_ಟೈಮ್‌ಔಟ್ ಎಲ್ಲಾ ನಂತರದ ವಿನಂತಿಗಳಿಗೆ ಅನ್ವಯಿಸುತ್ತದೆ ಎಂದು ನಾವು ನೆನಪಿಸಿಕೊಳ್ಳುತ್ತೇವೆ ಆದ್ದರಿಂದ ಟೇಬಲ್‌ನಲ್ಲಿ ಸಾಕಷ್ಟು ಡೇಟಾ ಇದ್ದರೆ ವಹಿವಾಟು ಸ್ವೀಕಾರಾರ್ಹವಲ್ಲದ ಮೌಲ್ಯಗಳಿಗೆ ವಿಸ್ತರಿಸುವುದಿಲ್ಲ.

#5: ಡೇಟಾವನ್ನು ನಕಲಿಸಿ

ಟೇಬಲ್ ಸಂಪೂರ್ಣವಾಗಿ ಖಾಲಿಯಾಗಿಲ್ಲದಿದ್ದರೆ, ಸಹಾಯಕ ತಾತ್ಕಾಲಿಕ ಕೋಷ್ಟಕವನ್ನು ಬಳಸಿಕೊಂಡು ಡೇಟಾವನ್ನು ಮರು-ಉಳಿಸಬೇಕಾಗುತ್ತದೆ:

CREATE TEMPORARY TABLE _tmp_swap ON COMMIT DROP AS TABLE tbl;

ಸಹಿ ಕಮಿಟ್ ಡ್ರಾಪ್‌ನಲ್ಲಿ ವಹಿವಾಟು ಕೊನೆಗೊಳ್ಳುವ ಕ್ಷಣದಲ್ಲಿ, ತಾತ್ಕಾಲಿಕ ಕೋಷ್ಟಕವು ಅಸ್ತಿತ್ವದಲ್ಲಿಲ್ಲ, ಮತ್ತು ಸಂಪರ್ಕದ ಸಂದರ್ಭದಲ್ಲಿ ಅದನ್ನು ಹಸ್ತಚಾಲಿತವಾಗಿ ಅಳಿಸುವ ಅಗತ್ಯವಿಲ್ಲ.

ಸಾಕಷ್ಟು "ಲೈವ್" ಡೇಟಾ ಇಲ್ಲ ಎಂದು ನಾವು ಭಾವಿಸುವುದರಿಂದ, ಈ ಕಾರ್ಯಾಚರಣೆಯು ಸಾಕಷ್ಟು ಬೇಗನೆ ನಡೆಯಬೇಕು.

ಸರಿ, ಅಷ್ಟೆ! ವಹಿವಾಟು ಮುಗಿದ ನಂತರ ಮರೆಯಬೇಡಿ ವಿಶ್ಲೇಷಣೆ ರನ್ ಮಾಡಿ ಅಗತ್ಯವಿದ್ದರೆ ಟೇಬಲ್ ಅಂಕಿಅಂಶಗಳನ್ನು ಸಾಮಾನ್ಯಗೊಳಿಸಲು.

ಅಂತಿಮ ಸ್ಕ್ರಿಪ್ಟ್ ಅನ್ನು ಒಟ್ಟುಗೂಡಿಸಲಾಗುತ್ತಿದೆ

ನಾವು ಈ "ಹುಸಿ-ಪೈಥಾನ್" ಅನ್ನು ಬಳಸುತ್ತೇವೆ:

# собираем статистику с таблицы
stat <-
  SELECT
    relpages
  , ((
      SELECT
        count(*)
      FROM
        pg_index
      WHERE
        indrelid = cl.oid
    ) + 1) << 13 size_norm
  , pg_total_relation_size(oid) size
  , coalesce(extract('epoch' from (now() - greatest(
      pg_stat_get_last_vacuum_time(oid)
    , pg_stat_get_last_autovacuum_time(oid)
    ))), 1 << 30) vaclag
  FROM
    pg_class cl
  WHERE
    oid = $1::regclass -- table_name
  LIMIT 1;

# таблица больше целевого размера и VACUUM был давно
if stat.size > 2 * stat.size_norm and stat.vaclag is None or stat.vaclag > 60:
  -> VACUUM %table;
  try:
    -> BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
    # пытаемся захватить монопольную блокировку с предельным временем ожидания 1s
    -> SET LOCAL statement_timeout = '1s'; SET LOCAL lock_timeout = '1s';
    -> LOCK TABLE %table IN ACCESS EXCLUSIVE MODE;
    # надо убедиться в пустоте таблицы внутри транзакции с блокировкой
    row <- TABLE %table LIMIT 1;
    # если в таблице нет ни одной "живой" записи - очищаем ее полностью, в противном случае - "перевставляем" все записи через временную таблицу
    if row is None:
      -> TRUNCATE TABLE %table RESTART IDENTITY;
    else:
      # создаем временную таблицу с данными таблицы-оригинала
      -> CREATE TEMPORARY TABLE _tmp_swap ON COMMIT DROP AS TABLE %table;
      # очищаем оригинал без сброса последовательности
      -> TRUNCATE TABLE %table;
      # вставляем все сохраненные во временной таблице данные обратно
      -> INSERT INTO %table TABLE _tmp_swap;
    -> COMMIT;
  except Exception as e:
    # если мы получили ошибку, но соединение все еще "живо" - словили таймаут
    if not isinstance(e, InterfaceError):
      -> ROLLBACK;

ಡೇಟಾವನ್ನು ಎರಡನೇ ಬಾರಿ ನಕಲಿಸದಿರಲು ಸಾಧ್ಯವೇ?ತಾತ್ವಿಕವಾಗಿ, ಟೇಬಲ್‌ನ ಐಡಿಯು BL ಕಡೆಯಿಂದ ಅಥವಾ DB ಬದಿಯಿಂದ FK ಯಿಂದ ಯಾವುದೇ ಇತರ ಚಟುವಟಿಕೆಗಳಿಗೆ ಸಂಬಂಧಿಸದಿದ್ದರೆ ಅದು ಸಾಧ್ಯ:

CREATE TABLE _swap_%table(LIKE %table INCLUDING ALL);
INSERT INTO _swap_%table TABLE %table;
DROP TABLE %table;
ALTER TABLE _swap_%table RENAME TO %table;

ಮೂಲ ಕೋಷ್ಟಕದಲ್ಲಿ ಸ್ಕ್ರಿಪ್ಟ್ ಅನ್ನು ರನ್ ಮಾಡೋಣ ಮತ್ತು ಮೆಟ್ರಿಕ್‌ಗಳನ್ನು ಪರಿಶೀಲಿಸೋಣ:

VACUUM tbl;
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
  SET LOCAL statement_timeout = '1s'; SET LOCAL lock_timeout = '1s';
  LOCK TABLE tbl IN ACCESS EXCLUSIVE MODE;
  CREATE TEMPORARY TABLE _tmp_swap ON COMMIT DROP AS TABLE tbl;
  TRUNCATE TABLE tbl;
  INSERT INTO tbl TABLE _tmp_swap;
COMMIT;

relpages | size_norm | size   | vaclag
-------------------------------------------
       0 |     24576 |  49152 | 32.705771

ಎಲ್ಲವೂ ಕೆಲಸ ಮಾಡಿದೆ! ಟೇಬಲ್ 50 ಬಾರಿ ಕುಗ್ಗಿದೆ ಮತ್ತು ಎಲ್ಲಾ ನವೀಕರಣಗಳು ಮತ್ತೆ ವೇಗವಾಗಿ ರನ್ ಆಗುತ್ತಿವೆ.

ಮೂಲ: www.habr.com

ಕಾಮೆಂಟ್ ಅನ್ನು ಸೇರಿಸಿ