sql >> Database teknologi >  >> RDS >> PostgreSQL

Sådan drager du fordel af de nye partitioneringsfunktioner i PostgreSQL 11

Hvad er partitionering?

Partitionering opdeler store tabeller i mindre stykker, hvilket hjælper med at øge forespørgselsydeevnen, gøre vedligeholdelsesopgaver lettere, forbedre effektiviteten af ​​dataarkivering og hurtigere databasesikkerhedskopiering. Du kan læse mere om PostgreSQL-partitionering i vores blog "En guide til partitionering af data i PostgreSQL".

Med den seneste udgivelse af PostgreSQL 11 er der en masse nye fantastiske partitioneringsfunktioner. Detaljerne om disse nye partitioneringsfunktioner vil blive dækket i denne blog med et par kodeeksempler.

Opdatering af partitionsnøglerne

Før PostgreSQL 11 var opdateringserklæring, der ændrer værdien af ​​partitionsnøglen, begrænset og ikke tilladt. Dette er nu muligt i den nye version. Opdateringssætning kan ændre værdien af ​​partitionsnøglen; det flytter faktisk rækkerne til den korrekte partitionstabel. Under hætten udfører den stort set DELETE FROM gammel partition og INSERT i ny partition (DELETE + INSERT).

Okay, lad os teste dette af. Opret en tabel og bekræft, hvordan opdateringen fungerer på partitionsnøglen.

CREATE TABLE customers(cust_id bigint NOT NULL,cust_name varchar(32) NOT NULL,cust_address text,
cust_country text)PARTITION BY LIST(cust_country);
CREATE TABLE customer_ind PARTITION OF customers FOR VALUES IN ('ind');
CREATE TABLE customer_jap PARTITION OF customers FOR VALUES IN ('jap');
CREATE TABLE customers_def PARTITION OF customers DEFAULT;
severalnines_v11=# INSERT INTO customers VALUES (2039,'Puja','Hyderabad','ind');
INSERT 0 1
severalnines_v11=#  SELECT * FROM customer_ind;
 cust_id | cust_name | cust_address | cust_country
  2039 | Puja      | Hyderabad    | ind
(1 row)
severalnines_v11=# UPDATE customers SET cust_country ='jap' WHERE cust_id=2039;
UPDATE 1
--  it moved the row to correct  partition table.
severalnines_v11=# SELECT * FROM customer_ind;
 cust_id | cust_name | cust_address | cust_country
---------+-----------+--------------+--------------
(0 rows)
severalnines_v11=# SELECT * FROM customer_jap;
 cust_id | cust_name | cust_address | cust_country
---------+-----------+--------------+--------------
    2039 | Puja      | Hyderabad    | jap
(1 row)

Advarsel:OPDATERING vil fejle, hvis der ikke er nogen standard partitionstabel, og opdaterede værdier ikke stemmer overens med partitionskriterier i nogen underordnet tabel.

severalnines_v11=#  UPDATE customers1 SET cust_country ='ypp' WHERE cust_id=2039;
2018-11-21 00:13:54.901 IST [1479] ERROR:  no partition of relation "customers1" found for row
2018-11-21 00:13:54.901 IST [1479] DETAIL:  Partition key of the failing row contains (cust_country) = (ypp).
2018-11-21 00:13:54.901 IST [1479] STATEMENT:  UPDATE customers1 SET cust_country ='ypp' WHERE cust_id=2039;
ERROR:  no partition of relation "customers1" found for row
DETAIL:  Partition key of the failing row contains (cust_country) = (ypp).
[ -- the value of cust_country was not mapped to any part table so it failed]

Oprettelse af en standardpartition

PostgreSQL 11 DEFAULT-partitionsfunktionen gemmer tupler, der ikke er knyttet til nogen anden partition. Før PostgreSQL 11 ville disse rækker fejle. En række, der ikke er knyttet til nogen partitionstabel, vil blive indsat i standardpartitionen.

Laboratorieeksempel:'USA' landekode blev ikke defineret i partitionstabellen nedenfor, men den bliver stadig indsat i standardtabellen.

CREATE TABLE customers_def PARTITION OF customers DEFAULT;
severalnines_v11=#  INSERT INTO customers VALUES (4499,'Tony','Arizona','USA');
INSERT 0 1
severalnines_v11=#  select * FROM customers_def;
 cust_id | cust_name | cust_address | cust_country
---------+-----------+--------------+--------------
    4499 | Tony      | Arizona      | USA

Advarsel:Standardpartition forhindrer enhver tilføjelse af ny partition, hvis denne partitionsværdi findes i standardtabellen. I dette tilfælde eksisterede 'USA' i standardpartitionen, så det vil ikke fungere som nedenfor.

severalnines_v11=# CREATE TABLE customer_usa PARTITION OF customers FOR VALUES IN ('USA');
2018-11-21 00:46:34.890 IST [1526] ERROR:  updated partition constraint for default partition "customers_def" would be violated by some row
2018-11-21 00:46:34.890 IST [1526] STATEMENT:  CREATE TABLE customer_usa PARTITION OF customers FOR VALUES IN ('USA');ERROR:  updated partition constraint for default partition "customers_def" would be violated by some row
severalnines_v11=#
Resolution - You need to move/remove those rows from Default table, then it will then let you create new part table like below.
severalnines_v11=# DELETE FROM customers_def WHERE cust_country in ('USA'); DELETE 1
severalnines_v11=# CREATE TABLE customer_usa PARTITION OF customers FOR VALUES IN ('USA');
CREATE TABLE
severalnines_v11=#
Nudgets :

DEFAULT partition kan ikke angives for HASH partitioneret tabel. Der kan ikke være mere end én DEFAULT-tabel til partitionstabel.

Hash-partitionering

Det er en ny partitionsmekanisme, hvis du ikke kan beslutte dig for en række- eller listepartition (da du ikke er sikker på, hvor stor spanden ville være). Hash-partitionering løser dette datadistributionsproblem.

Tabellen opdeles ved at angive et modul og en rest for hver partition. Hver partition vil indeholde de rækker, for hvilke hashværdien af ​​partitionsnøglen divideret med det specificerede modul vil producere den specificerede rest. HASH-funktionen sikrer, at rækker fordeles stort set jævnt i hele partitionstabellen.

Til at begynde med skal du bestemme, hvor mange numre i partitionstabellen der kræves, og følgelig kan modul og rest defineres; hvis modul ville være 4, kan resten kun være fra [0-3].

[Modul - Antal tabeller | Remainder - Hvilken værdi af resten går til hvilken spand ]

Sådan konfigurerer du en Hash-partition

-- hash partition
CREATE TABLE part_hash_test (x int, y text) PARTITION BY hash (x);
-- create child partitions
CREATE TABLE part_hash_test_0 PARTITION OF part_hash_test FOR VALUES WITH (MODULUS 4, REMAINDER 0);
CREATE TABLE part_hash_test_1 PARTITION OF part_hash_test FOR VALUES WITH (MODULUS 4, REMAINDER 1);
CREATE TABLE part_hash_test_2 PARTITION OF part_hash_test FOR VALUES WITH (MODULUS 4, REMAINDER 2);
CREATE TABLE part_hash_test_3 PARTITION OF part_hash_test FOR VALUES WITH (MODULUS 4, REMAINDER 3);

Indsæt 50.000 poster i den overordnede tabel:

severalnines_v11=# INSERT INTO part_hash_test SELECT generate_series(0,50000);
INSERT 0 50001

og se, hvordan den fordelte poster jævnt i den underordnede tabel ...

severalnines_v11=# SELECT count(1),tableoid::regclass FROM part_hash_test GROUP by 2 order by 2 ;
 count |     tableoid
-------+------------------
 12537 | part_hash_test_0
 12473 | part_hash_test_1
 12509 | part_hash_test_2
 12482 | part_hash_test_3
(4 rows)

Vi kan ikke ændre antallet af partitioner specificeret af `Modulus` tidligere, så du skal planlægge i god tid før kravene til antallet af partitionstabeller.

Det vil fejle, når du forsøger at tilføje en ny partition med en anden rest.

severalnines_v11=# CREATE TABLE part_hash_test_5 PARTITION OF part_hash_test FOR VALUES
WITH (MODULUS 4, REMAINDER 5);severalnines_v11-#
2018-11-21 01:51:28.966 IST [1675] ERROR:  remainder for hash partition must be less than modulus
2018-11-21 01:51:28.966 IST [1675] STATEMENT:  CREATE TABLE part_hash_test_5 PARTITION OF part_hash_test FOR VALUES  WITH (MODULUS 4, REMAINDER 5);

Hash-partitionering kan fungere på enhver datatype, og det kan også fungere for UUID-type. Det anbefales altid, at antallet af tabeller skal være en potens af 2, og det er heller ikke obligatorisk at bruge det samme modul, mens du opretter tabellen; dette vil hjælpe med at oprette partitionstabellen senere efter behov.

Denne implementering ville også gøre vakuum hurtigere og kan muliggøre partitionsmæssig joinforbindelse.

Support til fremmednøgler

Før PostgreSQL 11 var den fremmede nøgle i partitionstabellen ikke understøttet. De fremmede nøgler er mulige i partitionstabellen nu og nedenfor er hvordan...

severalnines_v11=# CREATE TABLE customers2 ( cust_id integer PRIMARY KEY );
CREATE TABLE
severalnines_v11=# CREATE TABLE account (
    ac_date   date    NOT NULL,
    cust_id  integer REFERENCES customers2(cust_id),
     amount INTEGER NOT NULL) PARTITION BY RANGE (ac_date);
CREATE TABLE

Automatisk indeksoprettelse på underordnede tabeller

I tidligere versioner af PostgreSQL var det en manuel indsats at oprette et indeks på hver partitionstabel. I PostgreSQL version 11 er det ret praktisk for brugerne. Når indekset er oprettet på mastertabellen, vil det automatisk oprette indekset med den samme konfiguration på alle eksisterende underordnede partitioner og også tage sig af eventuelle fremtidige partitionstabeller.

Indeks oprettet på mastertabel

severalnines_v11=# CREATE index idx_name ON customers(cust_name);
CREATE INDEX

Det oprettede automatisk indekset på alle underordnede tabeller som nedenfor. (Bekræft med katalogtabel)

severalnines_v11=# SELECT tablename,indexname,indexdef FROM pg_indexes WHERE tablename ilike '%customer_%';
   tablename   |          indexname          |       indexdef
---------------+-----------------------------+------------------------------------------------------------------------------------------
 customer_ind  | customer_ind_cust_name_idx  | CREATE INDEX customer_ind_cust_name_idx ON public.customer_ind USING btree (cust_name)
 customer_jap  | customer_jap_cust_name_idx  | CREATE INDEX customer_jap_cust_name_idx ON public.customer_jap USING btree (cust_name)
 customer_usa  | customer_usa_cust_name_idx  | CREATE INDEX customer_usa_cust_name_idx ON public.customer_usa USING btree (cust_name)
 customers_def | customers_def_cust_name_idx | CREATE INDEX customers_def_cust_name_idx ON public.customers_def USING btree (cust_name)
(4 rows)

Indeks kan kun oprettes på en mastertabel, det kan ikke være på en undertabel. Automatisk genererede indekser kan ikke slettes individuelt.

Automatisk oprettelse af trigger på underordnede tabeller

Når triggeren er oprettet på mastertabellen, vil den automatisk oprette triggeren på alle underordnede tabeller (denne adfærd ligner den, der ses for indeks).

Kan oprette et unikt indeks

I version 11 kan unikke indekser tilføjes til mastertabellen, som vil skabe den unikke begrænsning på alle eksisterende underordnede tabeller og fremtidige partitionstabeller.

Lad os skabe en mastertabel med unikke begrænsninger.

CREATE TABLE uniq_customers(  cust_id bigint NOT NULL, cust_name varchar(32) NOT NULL, cust_address text, cust_country text,cust_email text, unique(cust_email,cust_id,cust_country)  )PARTITION BY LIST(cust_country);

Den unikke begrænsning er blevet oprettet på den underordnede tabel automatisk som nedenfor.

severalnines_v11=# SELECT table_name,constraint_name,constraint_type FROM information_schema.table_constraints WHERE table_name ilike '%uniq%' AND constraint_type = 'UNIQUE';
    table_name     |                    constraint_name                    | constraint_type
-------------------+-------------------------------------------------------+-----------------
 uniq_customers    | uniq_customers_cust_email_cust_id_cust_country_key    | UNIQUE
 uniq_customer_ind | uniq_customer_ind_cust_email_cust_id_cust_country_key | UNIQUE
(2 rows)

Forsigtig:En unik begrænsning på den overordnede tabel garanterer faktisk ikke unikhed på tværs af hele partitioneringshierarkiet. Det er ikke global begrænsning, det er kun lokalt.

Download Whitepaper Today PostgreSQL Management &Automation med ClusterControlFå flere oplysninger om, hvad du skal vide for at implementere, overvåge, administrere og skalere PostgreSQLDownload Whitepaper

Hurtigere forespørgselsydeevne

Dynamisk partitionsbeskæring

I PostgreSQL 11 muliggør den binære søgning hurtigere identifikation af nødvendige underordnede tabeller, uanset om det er LIST- eller RANGE-partitioneret. Hashing-funktionen finder den matchende partition til HASH-partitionen. Det eliminerer faktisk dynamisk de partitionstabeller, som ikke er nødvendige, og øger forespørgselsydeevnen.

Den dynamiske partitionsbeskæring kan styres af parameteren `enable_partition_pruning`.

severalnines_v11=# show enable_partition_pruning;
 enable_partition_pruning
--------------------------
 off
(1 row)
severalnines_v11=# EXPLAIN SELECT * from customers where cust_country = 'ind';
                             QUERY PLAN
---------------------------------------------------------------------
 Append  (cost=0.00..18.54 rows=5 width=154)
   ->  Seq Scan on customer_ind  (cost=0.00..1.01 rows=1 width=154)
         Filter: (cust_country = 'ind'::text)
   ->  Seq Scan on customer_jap  (cost=0.00..1.00 rows=1 width=154)
         Filter: (cust_country = 'ind'::text)
   ->  Seq Scan on customer_usa  (cost=0.00..15.50 rows=2 width=154)
         Filter: (cust_country = 'ind'::text)
   ->  Seq Scan on customers_def  (cost=0.00..1.00 rows=1 width=154)
         Filter: (cust_country = 'ind'::text)
(9 rows)
Enabled the parameter to ON.
severalnines_v11=# set enable_partition_pruning TO on;
SET
severalnines_v11=# EXPLAIN SELECT * from customers where cust_country = 'ind';
                             QUERY PLAN
--------------------------------------------------------------------
 Append  (cost=0.00..1.02 rows=1 width=154)
   ->  Seq Scan on customer_ind  (cost=0.00..1.01 rows=1 width=154)
         Filter: (cust_country = 'ind'::text)
(3 rows)

Den anden fantastiske implementering er sådan her.

Execution-Time Partition Beskæring

I PostgreSQL-versioner før 11 kan partitionsbeskæring kun ske på plantidspunktet; planner kræver en værdi af partitionsnøglen for at identificere den korrekte partition. Denne adfærd er rettet i PostgreSQL 11, da udførelsestidsplanlæggeren ville vide, hvilken værdi der bliver leveret, og baseret på at partitionsvalg/-eliminering er mulig og ville køre meget hurtigere. Use casen kan være en forespørgsel, der bruger parameter (forberedt sætning) ELLER underforespørgsel, der angiver værdien som en parameter.

Example : cus_country is partition key and getting value from subquery
severalnines_v11=# explain analyze  select * from customers WHERE cust_country = (select cust_count_x FROM test_execution_prun1);
                                                        QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------
 Append  (cost=23.60..42.14 rows=5 width=154) (actual time=0.019..0.020 rows=0 loops=1)
   InitPlan 1 (returns $0)
     ->  Seq Scan on test_execution_prun1  (cost=0.00..23.60 rows=1360 width=32) (actual time=0.006..0.007 rows=1 loops=1)
   ->  Seq Scan on customer_ind  (cost=0.00..1.01 rows=1 width=154) (never executed)
         Filter: (cust_country = $0)
   ->  Seq Scan on customer_jap  (cost=0.00..1.00 rows=1 width=154) (never executed)
         Filter: (cust_country = $0)
   ->  Seq Scan on customer_usa  (cost=0.00..15.50 rows=2 width=154) (never executed)
         Filter: (cust_country = $0)
   ->  Seq Scan on customers_def  (cost=0.00..1.00 rows=1 width=154) (actual time=0.003..0.003 rows=0 loops=1)
         Filter: (cust_country = $0)
 Planning Time: 0.237 ms
 Execution Time: 0.057 ms
(13 rows)

I forklaringsplanen ovenfor kan vi se, at planlæggeren på udførelsestidspunktet identificerede den korrekte partitionstabel baseret på parameterværdien og kørte meget hurtigere og brugte ikke tid på scanning/loop på anden partitionstabel (se aldrig udført afsnit i forklare planen ovenfor). Dette er meget kraftfuldt og startede en ny æra med forbedring af ydeevnen inden for partitionering.

Partition Wise Aggregate

Parameter:enable_partitionwise_aggregate

Hvis partitionsnøglen matcher grupperingsnøglen, vil hver partition producere et diskret sæt grupper i stedet for at scanne hele partitionen på én gang. Den vil foretage den parallelle aggregation for hver partition, og under det endelige resultat sammenkæder den alle resultater.

severalnines_v11=# explain SELECT count(1),cust_country FROM customers GROUP BY 2;
                                 QUERY PLAN
----------------------------------------------------------------------------
 HashAggregate  (cost=21.84..23.84 rows=200 width=40)
   Group Key: customer_ind.cust_country
   ->  Append  (cost=0.00..19.62 rows=443 width=32)
         ->  Seq Scan on customer_ind  (cost=0.00..1.01 rows=1 width=32)
         ->  Seq Scan on customer_jap  (cost=0.00..1.00 rows=1 width=32)
         ->  Seq Scan on customer_usa  (cost=0.00..14.40 rows=440 width=32)
         ->  Seq Scan on customers_def  (cost=0.00..1.00 rows=1 width=32)
(7 rows)
severalnines_v11=# SET  enable_partitionwise_aggregate TO on;
SET
severalnines_v11=#  explain SELECT count(1),cust_country FROM customers GROUP BY 2;
                                 QUERY PLAN
----------------------------------------------------------------------------
 Append  (cost=1.01..22.67 rows=203 width=40)
   ->  HashAggregate  (cost=1.01..1.02 rows=1 width=40)
         Group Key: customer_ind.cust_country
         ->  Seq Scan on customer_ind  (cost=0.00..1.01 rows=1 width=32)
   ->  HashAggregate  (cost=1.00..1.01 rows=1 width=40)
         Group Key: customer_jap.cust_country
         ->  Seq Scan on customer_jap  (cost=0.00..1.00 rows=1 width=32)
   ->  HashAggregate  (cost=16.60..18.60 rows=200 width=40)
         Group Key: customer_usa.cust_country
         ->  Seq Scan on customer_usa  (cost=0.00..14.40 rows=440 width=32)
   ->  HashAggregate  (cost=1.00..1.01 rows=1 width=40)
         Group Key: customers_def.cust_country
         ->  Seq Scan on customers_def  (cost=0.00..1.00 rows=1 width=32)
(13 rows)

Dette er helt sikkert hurtigere, da det inkluderer parallel aggregeringsbehandling og scanning pr. partition.

Katalogforespørgsel kan bruges til at kende alle overordnede partitionstabeller.

SELECT relname FROM pg_class WHERE oid in (select partrelid FROM  pg_partitioned_table);

Kort partitionsfunktionsmatrix

Partitioneringsfunktioner v11 v10
Standardpartition JA NEJ
Udenlandsk tabelarv JA NEJ
Partitionering med Hash Key JA NEJ
Support til PK &FK JA NEJ
OPDATERING på en partitionsnøgle JA NEJ
Automatiske indekseringer på CT JA NEJ
Automatiske triggere på CT JA NEJ
Udførelsestidspartitionsbeskæring JA NEJ
Deltag med hensyn til partition JA NEJ
Dynamisk partitionsbeskæring JA NEJ

Hvad så?

Partitioneringsydelse

Dette er et af de mest aktive arbejdsområder nu i PostgreSQL-fællesskabet. PostgreSQL Version 12 vil blive pakket med endnu flere ydeevneforbedringer i partitioneringsrummet. Version 12 forventes at blive frigivet i november 2019.


  1. How Now() virker i PostgreSQL

  2. Hvordan current_timestamp() virker i PostgreSQL

  3. Sådan fungerer ORD() i MariaDB

  4. Android med rum - Sådan indstilles en fremmednøgle til null