Happy Party sau câteva rânduri de amintiri despre familiarizarea cu secționarea în PostgreSQL10

Prefață sau cum a apărut ideea secționării

Povestea începe aici: Îți amintești cum a început totul. Totul era pentru prima oară și din nou. După ce aproape toate resursele pentru optimizarea interogării, la acel moment, au fost epuizate, s-a pus întrebarea - și ce urmează? Așa a apărut ideea secționării.

Happy Party sau câteva rânduri de amintiri despre familiarizarea cu secționarea în PostgreSQL10

O digresiune lirică:
Anume 'la acel moment', deoarece așa cum s-a dovedit, existau rezerve nefolosite de optimizare. Mulțumesc asmm și Habr!

Așadar, cum putem face clientul oarecum fericit, în același timp îmbunătățindu-ne propriile abilități?

Dacă simplificăm totul la maxim, atunci, căile de a îmbunătăți radical performanța bazei de date sunt doar două:
1) Calea extensivă - creștem resursele, schimbăm configurația;
2) Calea intensivă - optimizarea interogărilor

Întrucât, repet, la acel moment nu era clar ce să mai schimb în interogare pentru a accelera procesul, s-a ales calea - schimbarea designului tabelelor.

Așadar - apare întrebarea principală - ce și cum vom schimba?

Condițiile inițiale

În primul rând, avem un ERD astfel (prezentat schematic și simplificat):
Happy Party sau câteva rânduri de amintiri despre familiarizarea cu secționarea în PostgreSQL10
Caracteristici principale:

  1. relațiile 'multe la multe'
  2. tabelul are deja o potențială cheie de secționare

Interogarea inițială:

SELECT
            p."PARAMETER_ID" as  parameter_id,
            pc."PC_NAME" AS pc_name,
            pc."CUSTOMER_PARTNUMBER" AS customer_partnumber,
            w."LASERMARK" AS lasermark,
            w."LOTID" AS lotid,
            w."REPORTED_VALUE" AS reported_value,
            w."LOWER_SPEC_LIMIT" AS lower_spec_limit,
            w."UPPER_SPEC_LIMIT" AS upper_spec_limit,
            p."TYPE_CALCUL" AS type_calcul,
            s."SHIPMENT_NAME" AS shipment_name,
            s."SHIPMENT_DATE" AS shipment_date,
            extract(year from s."SHIPMENT_DATE") AS year,
            extract(month from s."SHIPMENT_DATE") as month,
            s."REPORT_NAME" AS report_name,
            p."SPARAM_NAME" AS SPARAM_name,
            p."CUSTOMERPARAM_NAME" AS customerparam_name
        FROM data w INNER JOIN shipment s ON s."SHIPMENT_ID" = w."SHIPMENT_ID"
             INNER JOIN parameters p ON p."PARAMETER_ID" = w."PARAMETER_ID"
             INNER JOIN shipment_pc sp ON s."SHIPMENT_ID" = sp."SHIPMENT_ID"
             INNER JOIN pc pc ON pc."PC_ID" = sp."PC_ID"
             INNER JOIN ( SELECT w2."LASERMARK" , MAX(s2."SHIPMENT_DATE") AS "SHIPMENT_DATE"
                          FROM shipment s2 INNER JOIN data w2 ON s2."SHIPMENT_ID" = w2."SHIPMENT_ID" 
                          GROUP BY w2."LASERMARK"
                         ) md ON md."SHIPMENT_DATE" = s."SHIPMENT_DATE" AND md."LASERMARK" = w."LASERMARK"
        WHERE 
             s."SHIPMENT_DATE" >= '2018-07-01' AND s."SHIPMENT_DATE" <= '2018-09-30';

Rezultatele execuției pe baza de date de test:
Cost : 502 997.55
Timp de execuție: 505 secunde.

Ce vedem? O interogare obișnuită, pe un interval temporal.
Facem o presupunere logică simplă: dacă există un eșantion dintr-o fereastră temporală, ne va ajuta? Corect — secționarea.

Ce să secționăm?

La prima vedere, alegerea este evidentă — secționarea declarativă a tabelului „shipment” după cheia „SHIPMENT_DATE” (avansând puțin, în producție a ieșit puțin diferit).

Cum să secționăm?

Această întrebare nu este nici prea complicată. Din fericire, în PostgreSQL 10, acum avem un mecanism de secționare uman.
Deci:

  1. Salvăm dump-ul tabelului sursă — pg_dump source_table
  2. Ștergem tabelul sursă — drop table source_table
  3. Creăm tabelul părinte cu secționare pe bază de interval — create table source_table
  4. Creăm secțiuni — create table source_table, create index
  5. Importăm dump-ul creat la pasul 1 — pg_restore

Scripturi pentru secționare

Pentru simplificare și confort, pașii 2, 3, 4 au fost combinați într-un singur script.

Deci:
Salvăm dump-ul tabelului sursă

pg_dump postgres --file=\/dump\/shipment.dmp --format=c --table=shipment --verbose > \/dump\/shipment.log 2>&1

Ștergem tabelul sursă + Creăm tabelul părinte cu secționare pe bază de interval + Creăm secțiuni

--create_partition_shipment.sql
do language plpgsql $$
declare 
rec_shipment_date RECORD ;
partition_name varchar;
index_name varchar;
current_year varchar ;
current_month varchar ;
begin_year varchar ;
begin_month varchar ;
next_year varchar ;
next_month varchar ;
first_flag boolean ;
i integer ;
begin
  RAISE NOTICE 'CREARE TABELA TEMPORARĂ PENTRU DATA LIVRĂRII';
  CREATE TEMP TABLE tmp_shipment_date as select distinct "SHIPMENT_DATE" from shipment order by "SHIPMENT_DATE" ;

  RAISE NOTICE 'DROP TABLE shipment';
  drop table shipment cascade ;
  
  CREATE TABLE public.shipment
  (
    "SHIPMENT_ID" integer NOT NULL DEFAULT nextval('shipment_shipment_id_seq'::regclass),
    "SHIPMENT_NAME" character varying(30) COLLATE pg_catalog."default",
    "SHIPMENT_DATE" timestamp without time zone,
    "REPORT_NAME" character varying(40) COLLATE pg_catalog."default"
  )
  PARTITION BY RANGE ("SHIPMENT_DATE")
  WITH (
      OIDS = FALSE
  )
  TABLESPACE pg_default;

  RAISE NOTICE 'CREARE PARTICI pentru tabela shipment';

  current_year:='0';
  current_month:='0';

  begin_year := '0' ;
  begin_month := '0'  ;
  next_year := '0' ;
  next_month := '0'  ;

  FOR rec_shipment_date IN SELECT * FROM tmp_shipment_date LOOP
      
      RAISE NOTICE 'SHIPMENT_DATE=%',rec_shipment_date."SHIPMENT_DATE";
      
      current_year := date_part('year' ,rec_shipment_date."SHIPMENT_DATE");
      current_month := date_part('month' ,rec_shipment_date."SHIPMENT_DATE") ; 

      IF to_number(current_month,'99') = to_date( begin_year||'.'||begin_month, 'YYYY.MM') AND 
         to_date( current_year||'.'||current_month, 'YYYY.MM') < to_date( next_year||'.'||next_month, 'YYYY.MM') AND 
         NOT first_flag 
      THEN
         CONTINUE ; 
      ELSE
       --NOI limite doar pentru a doua și după aceea
       begin_year := current_year ;
       begin_month := current_month ;   
   
        IF current_month = '12' THEN
          next_year := date_part('year' ,rec_shipment_date."SHIPMENT_DATE" + interval '1 year') ;
        ELSE
          next_year := current_year ;
        END IF;
     
       next_month := date_part('month' ,rec_shipment_date."SHIPMENT_DATE" + interval '1 month') ;

      END IF;      

      partition_name := 'shipment_shipment_date_'||begin_year||'-'||begin_month||'-01-'|| next_year||'-'||next_month||'-01'  ;
 
     EXECUTE format('CREATE TABLE ' || quote_ident(partition_name) || ' PARTITION OF shipment FOR VALUES FROM ( %L ) TO ( %L )  ' , current_year||'-'||current_month||'-01' , next_year||'-'||next_month||'-01'  ) ; 

      index_name := partition_name||'_shipment_id_idx';
      RAISE NOTICE 'INDEX NAME =%',index_name;
      EXECUTE format('CREATE INDEX ' || quote_ident(index_name) || ' ON '|| quote_ident(partition_name) ||' USING btree ("SHIPMENT_ID") TABLESPACE pg_default ' ) ; 

      --Drop prima dată flag
      first_flag := false ;
   
  END LOOP;

end
$$;

Importăm dump-ul

pg_restore -d postgres --data-only --format=c --table=shipment --verbose  shipment.dmp > \/tmp\/data_dump\/shipment_restore.log 2>&1

Verificăm rezultatele secționării

Ce avem ca rezultat? Textul complet al planului de execuție este lung și plictisitor, așa că s-ar putea să ne limităm la cifrele centrale.

A fost

Cost: 502 997.55
Timp de execuție: 505 secunde.

A devenit

Cost: 77 872.36
Timp de execuție: 79 secunde.

Rezultat destul de bun. Am redus costul și timpul de execuție. Astfel, utilizarea secționării oferă efectul scontat și, în general, fără surprize.

Aproape de satisfacția clientului

Rezultatele testării au fost prezentate clientului spre examinare. Iar după ce s-au familiarizat cu ele, au emis un verdict oarecum neașteptat: „Excelent, secționați tabelul «data»”.

Da, dar am studiat un alt tabel «shipment», tabelul «data» nu are câmpul «SHIPMENT_DATE».

Nicio problemă, adăugați, schimbați. Principalul este ca clientul să fie mulțumit de rezultatul final, detaliile implementării nu sunt chiar atât de importante.

Secționăm tabelul principal «data»

În general, nu au apărut dificultăți deosebite. Deși, algoritmul de secționare, bineînțeles, s-a schimbat puțin.

Adăugăm coloana «SHIPMENT_DATE» în tabelul «data»

psql -h gazdă -U bază -d utilizator
=> ALTER TABLE data ADD COLUMN "SHIPMENT_DATE" timestamp without time zone ;

Populăm valorile coloanei «SHIPMENT_DATE» în tabelul «data», cu valorile coloanei cu același nume din tabelul «shipment»

-----------------------------
--update_data.sql
--actualizare pentru tabelul modificat "data" cu valorile din "shipment_data" din tabelul "shipment"
--versiune 1.0
do language plpgsql $$
declare 
rec_shipment_data RECORD ;
shipment_date timestamp without time zone ; 
row_count integer ;
total_rows integer ;
begin

  select count(*) into total_rows from shipment ; 
  RAISE NOTICE 'Total %',total_rows;
  row_count:= 0 ;

  FOR rec_shipment_data IN SELECT * FROM shipment LOOP

   update data set "SHIPMENT_DATE" = rec_shipment_data."SHIPMENT_DATE" where "SHIPMENT_ID" = rec_shipment_data."SHIPMENT_ID";
   
   row_count:=  row_count +1 ;
   RAISE NOTICE 'row count = % , from %',row_count,total_rows;
  END LOOP;

end
$$;

Salvăm dump-ul tabelului «data»

pg_dump postgres --file=\/dump\/data.dmp --format=c --table=data --verbose > \/dump\/data.log 2>&1<\/source

Recreem tabelul secționat «data»

--create_partition_data.sql
--creați partiții pentru tabela "wafer data" pe baza coloanei "shipment_data" cu o durată de o lună
--versiunea 1.0
do language plpgsql $$
declare 
rec_shipment_date RECORD ;
partition_name varchar;
index_name varchar;
current_year varchar ;
current_month varchar ;
begin_year varchar ;
begin_month varchar ;
next_year varchar ;
next_month varchar ;
first_flag boolean ;
i integer ;

begin

  RAISE NOTICE 'CREAȚI TABEL TEMPORAR PENTRU SHIPMENT_DATE';
  CREATE TEMP TABLE tmp_shipment_date as select distinct "SHIPMENT_DATE" from shipment order by "SHIPMENT_DATE" ;


  RAISE NOTICE 'ȘTERGEȚI TABELA data';
  drop table data cascade ;


  RAISE NOTICE 'CREAȚI TABELA PARTIȚIONATĂ data';
  
  CREATE TABLE public.data
  (
    "RUN_ID" integer,
    "LASERMARK" character varying(20) COLLATE pg_catalog."default" NOT NULL,
    "LOTID" character varying(80) COLLATE pg_catalog."default",
    "SHIPMENT_ID" integer NOT NULL,
    "PARAMETER_ID" integer NOT NULL,
    "INTERNAL_VALUE" character varying(75) COLLATE pg_catalog."default",
    "REPORTED_VALUE" character varying(75) COLLATE pg_catalog."default",
    "LOWER_SPEC_LIMIT" numeric,
    "UPPER_SPEC_LIMIT" numeric , 
    "SHIPMENT_DATE" timestamp without time zone
  )
  PARTITION BY RANGE ("SHIPMENT_DATE")
  WITH (
    OIDS = FALSE
  )
  TABLESPACE pg_default ;


  RAISE NOTICE 'CREAȚI PARTIȚII PENTRU TABELA data';

  current_year:='0';
  current_month:='0';

  begin_year := '0' ;
  begin_month := '0'  ;
  next_year := '0' ;
  next_month := '0'  ;
  i := 1;

  FOR rec_shipment_date IN SELECT * FROM tmp_shipment_date LOOP
      
      RAISE NOTICE 'SHIPMENT_DATE=%',rec_shipment_date."SHIPMENT_DATE";
      
      current_year := date_part('year' ,rec_shipment_date."SHIPMENT_DATE");
      current_month := date_part('month' ,rec_shipment_date."SHIPMENT_DATE") ; 

      --Init borders
      IF   begin_year = '0' THEN
       RAISE NOTICE '***Init borders';
       first_flag := true ; --flag pentru prima dată
       begin_year := current_year ;
       begin_month := current_month ;   
   
        IF current_month = '12' THEN
          next_year := date_part('year' ,rec_shipment_date."SHIPMENT_DATE" + interval '1 year') ;
        ELSE
          next_year := current_year ;
        END IF;
     
       next_month := date_part('month' ,rec_shipment_date."SHIPMENT_DATE" + interval '1 month') ;

      END IF;

--      RAISE NOTICE 'current_year=% , current_month=% ',current_year,current_month;
--      RAISE NOTICE 'begin_year=% , begin_month=% ',begin_year,begin_month;
--      RAISE NOTICE 'next_year=% , next_month=% ',next_year,next_month;

      -- Verificați data curentă în limite, NU pentru prima dată

      RAISE NOTICE 'Data curentă = %',to_char( to_date( current_year||'.'||current_month, 'YYYY.MM'), 'YYYY.MM');
      RAISE NOTICE 'Data de început = %',to_char( to_date( begin_year||'.'||begin_month, 'YYYY.MM'), 'YYYY.MM');
      RAISE NOTICE 'Data următoare = %',to_char( to_date( next_year||'.'||next_month, 'YYYY.MM'), 'YYYY.MM');

      IF to_date( current_year||'.'||current_month, 'YYYY.MM') >= to_date( begin_year||'.'||begin_month, 'YYYY.MM') AND 
         to_date( current_year||'.'||current_month, 'YYYY.MM') < to_date( next_year||'.'||next_month, 'YYYY.MM') AND 
         NOT first_flag 
      THEN
         RAISE NOTICE '***CONTINUAȚI';
         CONTINUE ; 
      ELSE
       --NOI limite doar pentru a doua și după aceea 
       RAISE NOTICE '***NOI LIMITE';
       begin_year := current_year ;
       begin_month := current_month ;   
   
        IF current_month = '12' THEN
          next_year := date_part('year' ,rec_shipment_date."SHIPMENT_DATE" + interval '1 year') ;
        ELSE
          next_year := current_year ;
        END IF;
     
       next_month := date_part('month' ,rec_shipment_date."SHIPMENT_DATE" + interval '1 month') ;


      END IF;      

      IF to_number(current_month,'99') < 10 THEN
        current_month := '0'||current_month ; 
      END IF ;

      IF to_number(begin_month,'99') < 10 THEN
        begin_month := '0'||begin_month ; 
      END IF ;

      IF to_number(next_month,'99') < 10 THEN
        next_month := '0'||next_month ; 
      END IF ;

      RAISE NOTICE 'current_year=% , current_month=% ',current_year,current_month;
      RAISE NOTICE 'begin_year=% , begin_month=% ',begin_year,begin_month;
      RAISE NOTICE 'next_year=% , next_month=% ',next_year,next_month;

      partition_name := 'data_'||begin_year||begin_month||'01_'||next_year||next_month||'01'  ;

      RAISE NOTICE 'NUMĂR PARTIȚIE % , NUME TABEL =%',i , partition_name;
      
      EXECUTE format('CREATE TABLE ' || quote_ident(partition_name) || ' PARTITION OF data FOR VALUES FROM ( %L ) TO ( %L )  ' , begin_year||'-'||begin_month||'-01' , next_year||'-'||next_month||'-01'  ) ; 

      index_name := partition_name||'_shipment_id_parameter_id_idx';
      RAISE NOTICE 'NUME INDEX =%',index_name;
      EXECUTE format('CREATE INDEX ' || quote_ident(index_name) || ' ON '|| quote_ident(partition_name) ||' USING btree ("SHIPMENT_ID", "PARAMETER_ID") TABLESPACE pg_default ' ) ; 

      index_name := partition_name||'_lasermark_idx';
      RAISE NOTICE 'NUME INDEX =%',index_name;
      EXECUTE format('CREATE INDEX ' || quote_ident(index_name) || ' ON '|| quote_ident(partition_name) ||' USING btree ("LASERMARK" COLLATE pg_catalog."default") TABLESPACE pg_default ' ) ; 

      index_name := partition_name||'_shipment_id_idx';
      RAISE NOTICE 'NUME INDEX =%',index_name;
      EXECUTE format('CREATE INDEX ' || quote_ident(index_name) || ' ON '|| quote_ident(partition_name) ||' USING btree ("SHIPMENT_ID") TABLESPACE pg_default ' ) ; 

      index_name := partition_name||'_parameter_id_idx';
      RAISE NOTICE 'NUME INDEX =%',index_name;
      EXECUTE format('CREATE INDEX ' || quote_ident(index_name) || ' ON '|| quote_ident(partition_name) ||' USING btree ("PARAMETER_ID") TABLESPACE pg_default ' ) ; 

      index_name := partition_name||'_shipment_date_idx';
      RAISE NOTICE 'NUME INDEX =%',index_name;
      EXECUTE format('CREATE INDEX ' || quote_ident(index_name) || ' ON '|| quote_ident(partition_name) ||' USING btree ("SHIPMENT_DATE") TABLESPACE pg_default ' ) ; 

      --Ștergeți flag-ul primei vizite
      first_flag := false ;

  END LOOP;
end
$$;

Încărcăm dump-ul creat la pasul 3.

pg_restore -h host -uuser -d baza --data-only --format=c --table=data --verbose data.dmp > data_restore.log 2>&1

Creăm o secțiune separată pentru datele vechi.

---------------------------------------------------
--create_partition_for_old_dates.sql
--creați partiții pentru păstrarea datelor vechi 
--versiunea 1.0
do language plpgsql $$
declare 
rec_shipment_date RECORD ;
partition_name varchar;
index_name varchar;

begin

      SELECT min("SHIPMENT_DATE") AS min_date INTO rec_shipment_date from data ;

      RAISE NOTICE 'Data veche este %',rec_shipment_date.min_date ;

      partition_name := 'data_old_dates'  ;

      RAISE NOTICE 'NUMELE PARTIȚIEI ESTE %',partition_name;

      EXECUTE format('CREATE TABLE ' || quote_ident(partition_name) || ' PARTITION OF data FOR VALUES FROM ( %L ) TO ( %L )  ' , '1900-01-01' , 
              to_char( rec_shipment_date.min_date,'YYYY')||'-'||to_char(rec_shipment_date.min_date,'MM')||'-01'  ) ; 

      index_name := partition_name||'_shipment_id_parameter_id_idx';
      EXECUTE format('CREATE INDEX ' || quote_ident(index_name) || ' ON '|| quote_ident(partition_name) ||' USING btree ("SHIPMENT_ID", "PARAMETER_ID") TABLESPACE pg_default ' ) ; 

      index_name := partition_name||'_lasermark_idx';
      EXECUTE format('CREATE INDEX ' || quote_ident(index_name) || ' ON '|| quote_ident(partition_name) ||' USING btree ("LASERMARK" COLLATE pg_catalog."default") TABLESPACE pg_default ' ) ; 

      index_name := partition_name||'_shipment_id_idx';
      EXECUTE format('CREATE INDEX ' || quote_ident(index_name) || ' ON '|| quote_ident(partition_name) ||' USING btree ("SHIPMENT_ID") TABLESPACE pg_default ' ) ; 

      index_name := partition_name||'_parameter_id_idx';
      EXECUTE format('CREATE INDEX ' || quote_ident(index_name) || ' ON '|| quote_ident(partition_name) ||' USING btree ("PARAMETER_ID") TABLESPACE pg_default ' ) ; 

      index_name := partition_name||'_shipment_date_idx';
      EXECUTE format('CREATE INDEX ' || quote_ident(index_name) || ' ON '|| quote_ident(partition_name) ||' USING btree ("SHIPMENT_DATE") TABLESPACE pg_default ' ) ; 

end
$$;

Rezultatele finale:

A fost
Cost: 502 997.55
Timp de execuție: 505 secunde.

A devenit
Cost: 68 533.70
Timp de execuție: 69 secunde

Onorabil, destul de onorabil. Și având în vedere că pe parcurs am reușit să învăț mai mult sau mai puțin mecanismul de partiționare în PostgreSQL 10 — Un rezultat excelent.

O mică digresiune

Se poate face chiar mai bine — DA, SE POATE!Pentru asta trebuie să folosim MATERIALIZED VIEW.
CREATE MATERIALIZED VIEW LASERMARK_VIEW

CREATE MATERIALIZED VIEW LASERMARK_VIEW 
AS
SELECT w."LASERMARK" , MAX(s."SHIPMENT_DATE") AS "SHIPMENT_DATE"
FROM shipment s INNER JOIN data w ON s."SHIPMENT_ID" = w."SHIPMENT_ID" 
GROUP BY w."LASERMARK" ;

CREATE INDEX lasermark_vw_shipment_date_ind on lasermark_view USING btree ("SHIPMENT_DATE") TABLESPACE pg_default;
analyze lasermark_view ;

Încă o dată rescriem interogarea:
Interogare folosind materialized view

SELECT
            p."PARAMETER_ID" as  parameter_id,
            pc."PC_NAME" AS pc_name,
            pc."CUSTOMER_PARTNUMBER" AS customer_partnumber,
            w."LASERMARK" AS lasermark,
            w."LOTID" AS lotid,
            w."REPORTED_VALUE" AS reported_value,
            w."LOWER_SPEC_LIMIT" AS lower_spec_limit,
            w."UPPER_SPEC_LIMIT" AS upper_spec_limit,
            p."TYPE_CALCUL" AS type_calcul,
            s."SHIPMENT_NAME" AS shipment_name,
            s."SHIPMENT_DATE" AS shipment_date,
            extract(year from s."SHIPMENT_DATE") AS year,
            extract(month from s."SHIPMENT_DATE") as month,
            s."REPORT_NAME" AS report_name,
            p."STC_NAME" AS STC_name,
            p."CUSTOMERPARAM_NAME" AS customerparam_name
        FROM data w INNER JOIN shipment s ON s."SHIPMENT_ID" = w."SHIPMENT_ID"
             INNER JOIN parameters p ON p."PARAMETER_ID" = w."PARAMETER_ID"
             INNER JOIN shipment_pc sp ON s."SHIPMENT_ID" = sp."SHIPMENT_ID"
             INNER JOIN pc pc ON pc."PC_ID" = sp."PC_ID"
             INNER JOIN LASERMARK_VIEW md ON md."SHIPMENT_DATE" = s."SHIPMENT_DATE" AND md."LASERMARK" = w."LASERMARK"
        WHERE 
              s."SHIPMENT_DATE" >= '2018-07-01' AND s."SHIPMENT_DATE" <= '2018-09-30';

Și obținem încă un rezultat:
A fost
Cost: 502 997.55
Timp de execuție: 505 secunde

A devenit
Cost: 42 481.16
Timp de execuție: 43 secunde.

Deși, desigur, un rezultat atât de promițător este înșelător; trebuie să facem refresh pe reprezentări. Așa că timpul final pentru obținerea datelor nu va ajuta prea mult. Dar ca experiment, este destul de interesant.

De fapt, după cum s-a dovedit, încă o dată mulțumim asmm și Habr !- interogarea poate fi îmbunătățită și mai mult.

Cuvânt înainte

Deci, clientul este mulțumit. Și nevoie profită de situație.

Sarcină nouă: Ce putem inventa pentru a aprofunda și extinde?

Și aici îmi amintesc — băieți, dar noi nu avem monitorizare pentru bazele noastre de date PostgreSQL.

Punând mâna pe inimă, există un fel de monitorizare sub formă de Cloud Watch pe AWS. Dar ce folos are această monitorizare pentru DBA? Practic, deloc.

Dacă a apărut ocazia de a face ceva util și interesant și pentru mine, nu pot să nu profit de o astfel de ocazie...
CĂCI

Happy Party sau câteva rânduri de amintiri despre familiarizarea cu secționarea în PostgreSQL10

Așa am ajuns la cel mai interesant subiect:

3 Decembrie 2018.
Decizia de a începe lucrările pentru a explora oportunitățile de monitorizare a performanței interogărilor PostgreSQL.

Dar aceasta este deja o poveste complet diferită.

Continuarea urmează...

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster