Happy Party eller et par linjer med minder om introduktionen til partitionering i PostgreSQL10

Forord eller hvordan idéen til partitionering opstod

Begyndelsen på historien her: Kan du huske, hvordan det hele startede? Alt var førstegangs og nyt. Efter at næsten alle ressourcer til optimering af forespørgslen på det tidspunkt var opbrugt, kom spørgsmålet — hvad så nu? Sådan opstod idéen om partitionering.

Happy Party eller et par linjer med minder om introduktionen til partitionering i PostgreSQL10

En lyrisk afstikker:
Netop 'på det tidspunkt', fordi som det viste sig, der var ubenyttede reserver til optimering. Tak asmm og Habr !

Så hvordan kan vi yderligere gøre kunden lidt lykkeligere, samtidig med at vi opgraderer vores egne færdigheder?

Hvis vi forenkler det til det yderste, så er der kun to fundamentale måder at forbedre databaseydelsen markant:
1) Ekstensiv metode — vi øger ressourcerne, ændrer konfigurationen;
2) Intensiv metode — optimering af forespørgsler

Da, som jeg gentager, på det tidspunkt var det allerede uklart, hvad der ellers kunne ændres i forespørgslen for at fremskynde den, blev vejen valgt — ændringer i designet af tabellerne.

Så — det store spørgsmål opstår — hvad og hvordan skal vi ændre?

Startbetingelser

For det første er der denne ERD (vist betinget og forenklet):
Happy Party eller et par linjer med minder om introduktionen til partitionering i PostgreSQL10
Hovedfunktioner:

  1. forholdet 'mange til mange'
  2. tabellen har allerede en potentiel partitioneringsnøgle

Den oprindelige forespørgsel:

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' ;

Resultater af udførelsen på den testdatabase:
Omkostninger : 502 997.55
Udførelsestid: 505 sekunder.

Hvad ser vi? En almindelig forespørgsel, efter tidsinterval.
Lad os lave en simpel logisk antagelse: hvis der er et udvalg af tidsinterval, vil det hjælpe os? Rigtigt — partitionering.

Hvad skal partitioneres?

Ved første øjekast er valget indlysende — deklarativ partitionering af tabellen "shipment" efter nøglen "SHIPMENT_DATE" (men langt fremme — i sidste ende blev det lidt anderledes i produktion).

Hvordan partitionerer man?

Dette spørgsmål er heller ikke alt for svært. Heldigvis, i PostgreSQL 10, er der nu en menneskelig partitioneringsmekanisme.
Så:

  1. Vi gemmer et dump af den oprindelige tabel — pg_dump source_table
  2. Vi sletter den oprindelige tabel — drop table source_table
  3. Vi opretter en overordnet tabel med partitionering efter interval — create table source_table
  4. Vi opretter sektioner — create table source_table, create index
  5. Vi importerer dumpen, der blev lavet i trin 1 — pg_restore

Scripts til partitionering

For enkelhed og bekvemmelighed er trin 2, 3, 4 blevet slået sammen i ét script.

Så:
Vi gemmer et dump af den oprindelige tabel

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

Sletter den oprindelige tabel + Opretter en overordnet tabel med partitionering efter interval + Opretter sektioner

--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 'OPRETTELSE AF TEMPORÆR TABEL FOR SHIPMENT_DATE';
  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 'OPRETTELSE AF PARTITIONER FOR TABEL 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
       --NEW borders only for second and after time 
       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 NAVN =%',index_name;
      EXECUTE format('CREATE INDEX ' || quote_ident(index_name) || ' ON '|| quote_ident(partition_name) ||' USING btree ("SHIPMENT_ID") TABLESPACE pg_default ' ) ; 

      --Drop first time flag
      first_flag := false ;
   
  END LOOP;

end
$$;

Importer dump

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

Vi kontrollerer resultaterne af sektioneringen

Hvad har vi så som resultat? Den fulde tekst for udførelsesplanen er lang og kedelig, så det er helt rimeligt at begrænse sig til de afsluttende tal.

Var

Omkostninger: 502 997.55
Udførelsestid: 505 sekunder.

Er blevet

Omkostninger: 77 872.36
Udførelsestid: 79 sekunder.

Et ganske godt resultat. Vi har reduceret omkostningerne og udførelsestiden. Sådan set giver brugen af partitionering den ønskede effekt, og generelt – uden overraskelser.

At glæde kunden

Testresultaterne blev præsenteret for kunden til overvejelse. Efter at have set på dem gav de en noget uventet dom: "Fremragende, partitioner tabellen 'data'."

Ja, men vi har jo undersøgt en helt anden tabel 'shipment', tabellen 'data' har ikke feltet 'SHIPMENT_DATE'.

Det er ikke et problem, tilføj, ændr. Det vigtigste er, at kunden er tilfreds med, hvad der kommer ud af det, detaljerne omkring implementeringen er ikke så vigtige.

Vi partitionerer hovedtabellen 'data'

Overordnet set opstod der ikke særlige vanskeligheder. Selvom algoritmen for partitionering selvfølgelig ændrede sig en smule.

Vi tilføjer kolonnen 'SHIPMENT_DATE' til tabellen 'data'

psql -h vært -U database -d bruger
=> ALTER TABLE data ADD COLUMN "SHIPMENT_DATE" timestamp without time zone ;

Vi fylder værdierne i kolonnen 'SHIPMENT_DATE' i tabellen 'data' med værdierne fra den tilsvarende kolonne i tabellen 'shipment'

-----------------------------
--update_data.sql
--opdatering for den ændrede tabel "data" til værdierne af "SHIPMENT_DATE" fra tabellen "shipment"
--version 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
$$;

Vi gemmer dumpen af tabellen 'data'

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

Vi genskaber den partitionerede tabel 'data'

--create_partition_data.sql
--opret partitioner for tabellen "wafer data" efter interval-kolonnen "shipment_data" med en varighed på en måned
--version 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 'OPRET TEMPORÆR TABEL FOR SHIPMENT_DATE';
  CREATE TEMP TABLE tmp_shipment_date as select distinct "SHIPMENT_DATE" from shipment order by "SHIPMENT_DATE" ;


  RAISE NOTICE 'DROP TABLE data';
  drop table data cascade ;


  RAISE NOTICE 'OPRET PARTITIONERET TABEL 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 'OPRET PARTITIONER FOR TABELLEN 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 grænser
      IF   begin_year = '0' THEN
       RAISE NOTICE '***Init grænser';
       first_flag := true ; --første gang flag
       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;

      -- Tjek nuværende dato mod grænserne IKKE for første gang

      RAISE NOTICE 'Nuværende data = %',to_char( to_date( current_year||'.'||current_month, 'YYYY.MM'), 'YYYY.MM');
      RAISE NOTICE 'Begin data = %',to_char( to_date( begin_year||'.'||begin_month, 'YYYY.MM'), 'YYYY.MM');
      RAISE NOTICE 'Næste data = %',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 '***FORTSÆT';
         CONTINUE ; 
      ELSE
       --NYE grænser kun for anden gang og derefter 
       RAISE NOTICE '***NYE GRÆNSER';
       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 'PARTITION NUMMER % , TABEL NAVN =%',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 'INDEX NAVN =%',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 'INDEX NAVN =%',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 'INDEX NAVN =%',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 'INDEX NAVN =%',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 'INDEX NAVN =%',index_name;
      EXECUTE format('CREATE INDEX ' || quote_ident(index_name) || ' ON '|| quote_ident(partition_name) ||' USING btree ("SHIPMENT_DATE") TABLESPACE pg_default ' ) ; 

      --Fjern første gang flag
      first_flag := false ;

  END LOOP;
end
$$;

Vi indlæser dump-filen oprettet i trin 3.

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

Opretter en separat sektion for gamle data

---------------------------------------------------
--create_partition_for_old_dates.sql
--opret partitioner til opbevaring af gamle datoer 
--version 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 'Gammel dato er %', rec_shipment_date.min_date ;

      partition_name := 'data_old_dates'  ;

      RAISE NOTICE 'PARTITIONNAVN ER %', 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
$$;

Endelige resultater:

Var
Omkostninger: 502 997.55
Udførelsestid: 505 sekunder.

Er blevet
Omkostninger: 68 533.70
Udførelsestid: 69 sekunder

Imponerende, ganske imponerende. Og i betragtning af at vi på vejen mere eller mindre har mestret partitioneringsmekanismen i PostgreSQL 10 — Et fremragende resultat.

Lyrisk afstikker

Kan vi gøre det endnu bedre — JA, DET KAN VI!Til dette skal vi bruge 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 ;

Vi skriver forespørgslen om igen:
Forespørgsel med brug af materialized view

VÆLG
            p."PARAMETER_ID" som parameter_id,
            pc."PC_NAME" som pc_name,
            pc."CUSTOMER_PARTNUMBER" som customer_partnumber,
            w."LASERMARK" som lasermark,
            w."LOTID" som lotid,
            w."REPORTED_VALUE" som reported_value,
            w."LOWER_SPEC_LIMIT" som lower_spec_limit,
            w."UPPER_SPEC_LIMIT" som upper_spec_limit,
            p."TYPE_CALCUL" som type_calcul,
            s."SHIPMENT_NAME" som shipment_name,
            s."SHIPMENT_DATE" som shipment_date,
            ekstrakt(år fra s."SHIPMENT_DATE") som år,
            ekstrakt(måned fra s."SHIPMENT_DATE") som måned,
            s."REPORT_NAME" som report_name,
            p."STC_NAME" som STC_name,
            p."CUSTOMERPARAM_NAME" som customerparam_name
        FRA data w INDRE JOIN shipment s PÅ s."SHIPMENT_ID" = w."SHIPMENT_ID"
             INDRE JOIN parameters p PÅ p."PARAMETER_ID" = w."PARAMETER_ID"
             INDRE JOIN shipment_pc sp PÅ s."SHIPMENT_ID" = sp."SHIPMENT_ID"
             INDRE JOIN pc pc PÅ pc."PC_ID" = sp."PC_ID"
             INDRE JOIN LASERMARK_VIEW md PÅ md."SHIPMENT_DATE" = s."SHIPMENT_DATE" OG md."LASERMARK" = w."LASERMARK"
        HVOR 
              s."SHIPMENT_DATE" >= '2018-07-01' OG s."SHIPMENT_DATE" <= '2018-09-30';

Og vi får endnu et resultat:
Var
Omkostninger: 502 997.55
Udførelsestid: 505 sekunder

Er blevet
Omkostninger: 42 481.16
Udførelsestid: 43 sekunder.

Selvom dette lovende resultat er vildledende, da præsentationerne skal opdateres. Så den samlede tid til dataindsamling vil ikke rigtig hjælpe. Men som et eksperiment er det ret interessant.

Faktisk, som det viser sig, tak igen asmm og Habr !- forespørgslen kan forbedres yderligere.

Efterord

Så kunden er tilfreds. Og man skal udnytte situationen.

Ny opgave: Hvad kan vi finde på for at uddybe og udvide?

Og her kommer jeg til at tænke på — folk, vi har faktisk ikke overvågning af vores PostgreSQL databaser.

Ærligt talt, der er en form for overvågning i form af Cloud Watch på AWS, men hvilken nytte har denne overvågning for DBA? Næsten ingen.

Hvis der er en mulighed for at lave noget nyttigt og interessant for os selv, bør man ikke lade den chance gå fra sig ...
FOR

Happy Party eller et par linjer med minder om introduktionen til partitionering i PostgreSQL10

Sådan er vi kommet til det mest interessante:

3. december 2018.
Beslutningen om at påbegynde arbejdet med at undersøge de tilgængelige muligheder for overvågning af PostgreSQL forespørgselsydelse.

Men det er en helt anden historie.

Fortsættelse følger…

Kilde: habr.com

Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere 🔥 Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere | ProHoster