PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

Cara Memanfaatkan Fitur Partisi Baru di PostgreSQL 11

Apa itu Partisi?

Partisi membagi tabel besar menjadi bagian-bagian yang lebih kecil, yang membantu meningkatkan kinerja kueri, membuat tugas pemeliharaan lebih mudah, meningkatkan efisiensi pengarsipan data, dan pencadangan basis data yang lebih cepat. Anda dapat membaca lebih lanjut tentang partisi PostgreSQL di blog kami “Panduan untuk Mempartisi Data Di PostgreSQL”.

Dengan rilis PostgreSQL 11 baru-baru ini, ada banyak fitur partisi baru yang menakjubkan. Detail fitur partisi baru ini akan dibahas di blog ini dengan beberapa contoh kode.

Memperbarui Tombol Partisi

Sebelum PostgreSQL 11, Perbarui pernyataan yang mengubah nilai kunci partisi dibatasi dan tidak diizinkan. Ini sekarang dimungkinkan dalam versi baru. Pernyataan pembaruan dapat mengubah nilai kunci partisi; itu benar-benar memindahkan baris ke tabel partisi yang benar. Di bawah tenda pada dasarnya mengeksekusi DELETE FROM partisi lama dan INSERT ke partisi baru ( DELETE + INSERT).

Baiklah, mari kita uji ini. Buat tabel dan verifikasi cara kerja pembaruan pada kunci partisi.

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)

Perhatian:UPDATE akan error, jika tidak ada tabel partisi default dan nilai yang diperbarui tidak cocok dengan kriteria partisi di tabel turunan mana pun.

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]

Membuat Partisi Default

Fitur partisi PostgreSQL 11 DEFAULT menyimpan tupel yang tidak dipetakan ke partisi lain. Sebelum PostgreSQL 11, baris ini akan error. Baris yang tidak dipetakan ke tabel partisi mana pun akan dimasukkan ke dalam partisi default.

Contoh Lab:Kode negara `USA` tidak ditentukan dalam tabel partisi di bawah ini, namun tetap berhasil dimasukkan ke dalam tabel default.

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

Peringatan:Partisi default akan mencegah penambahan partisi baru jika nilai partisi tersebut ada di tabel default. Dalam hal ini `USA` ada di partisi Default sehingga tidak akan berfungsi seperti di bawah ini.

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 :

Partisi DEFAULT tidak dapat ditentukan untuk tabel yang dipartisi HASH. Tidak boleh ada lebih dari satu tabel DEFAULT untuk tabel partisi.

Partisi Hash

Ini adalah mekanisme partisi baru, jika Anda tidak dapat memutuskan rentang atau daftar partisi (karena Anda tidak yakin seberapa besar embernya). Partisi hash memecahkan masalah distribusi data ini.

Tabel dipartisi dengan menentukan modulus dan sisa untuk setiap partisi. Setiap partisi akan menampung baris yang nilai hash dari kunci partisi dibagi dengan modulus yang ditentukan akan menghasilkan sisa yang ditentukan. Fungsi HASH memastikan bahwa baris akan didistribusikan secara merata di semua tabel partisi.

Untuk memulainya, Anda perlu memutuskan berapa banyak nomor tabel partisi yang diperlukan dan, karenanya, modulus dan sisanya dapat ditentukan; jika modulus adalah 4, sisanya hanya dapat dari [0-3].

[Modulus - Jumlah tabel | Sisa - Nilai sisa mana yang masuk ke ember mana ]

Cara Menyiapkan Partisi Hash

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

Masukkan 50k record di tabel induk:

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

dan lihat bagaimana ia mendistribusikan catatan secara merata di tabel anak ...

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)

Kami tidak dapat mengubah jumlah partisi yang ditentukan oleh `Modulus` sebelumnya, jadi Anda perlu merencanakan dengan baik sebelum persyaratan jumlah tabel partisi.

Ini akan error saat Anda mencoba menambahkan partisi baru dengan sisa yang berbeda.

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

Partisi hash dapat bekerja pada semua tipe data dan juga dapat bekerja untuk tipe UUID. Jumlah tabel selalu disarankan untuk pangkat 2, dan juga tidak wajib untuk menggunakan modulus yang sama saat membuat tabel; ini akan membantu untuk membuat tabel partisi nanti sesuai kebutuhan.

Implementasi ini juga akan membuat vakum lebih cepat dan dapat mengaktifkan partisi yang bijaksana bergabung.

Dukungan untuk Kunci Asing

Sebelum PostgreSQL 11, kunci asing di tabel partisi tidak didukung. Kunci asing dimungkinkan di tabel partisi sekarang dan di bawah ini adalah caranya...

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

Pembuatan Indeks Otomatis pada Tabel Anak

Di PostgreSQL versi sebelumnya, ini adalah upaya manual untuk membuat indeks di setiap tabel partisi. Di PostgreSQL versi 11, cukup nyaman bagi pengguna. Setelah indeks dibuat pada tabel master, maka secara otomatis akan membuat indeks dengan konfigurasi yang sama pada semua partisi anak yang ada dan juga menangani tabel partisi yang akan datang.

Indeks Dibuat di Tabel Master

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

Secara otomatis membuat indeks pada semua tabel anak seperti di bawah ini. ( Verifikasi dengan tabel katalog)

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 hanya bisa dibuat di tabel master, tidak bisa di tabel anak. Indeks yang dibuat secara otomatis tidak dapat dihapus satu per satu.

Pembuatan Pemicu Otomatis pada Tabel Anak

Setelah pemicu dibuat di tabel master, pemicu akan otomatis dibuat di semua tabel anak (perilaku ini mirip dengan yang terlihat untuk indeks).

Mampu Membuat Indeks Unik

Dalam versi 11, indeks unik dapat ditambahkan ke tabel master yang akan membuat batasan unik pada semua tabel anak yang ada dan tabel partisi yang akan datang.

Mari buat tabel master dengan batasan unik.

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

Batasan unik telah dibuat di tabel anak secara otomatis seperti di bawah ini.

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)

Perhatian :Batasan unik pada tabel induk sebenarnya tidak menjamin keunikan di seluruh hierarki partisi. Ini bukan batasan global, ini hanya lokal.

Unduh Whitepaper Hari Ini Pengelolaan &Otomatisasi PostgreSQL dengan ClusterControlPelajari tentang apa yang perlu Anda ketahui untuk menerapkan, memantau, mengelola, dan menskalakan PostgreSQLUnduh Whitepaper

Kinerja Kueri Lebih Cepat

Pemangkasan Partisi Dinamis

Di PostgreSQL 11, pencarian biner memungkinkan identifikasi lebih cepat dari tabel anak yang diperlukan apakah itu LIST atau RANGE dipartisi. Fungsi hashing menemukan partisi yang cocok untuk partisi HASH. Ini sebenarnya secara dinamis menghilangkan tabel partisi yang tidak diperlukan dan meningkatkan kinerja Kueri.

Pemangkasan partisi dinamis dapat dikontrol oleh parameter `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)

Implementasi luar biasa lainnya adalah seperti ini.

Pemangkasan Partisi Waktu Eksekusi

Dalam versi PostgreSQL sebelum 11, pemangkasan partisi hanya dapat terjadi pada waktu rencana; perencana memerlukan nilai kunci partisi untuk mengidentifikasi partisi yang benar. Perilaku ini diperbaiki di PostgreSQL 11, karena perencana waktu eksekusi akan mengetahui nilai apa yang diberikan dan berdasarkan pemilihan/penghapusan partisi itu dimungkinkan dan akan berjalan jauh lebih cepat. Use case dapat berupa query yang menggunakan parameter (pernyataan yang disiapkan) ATAU subquery yang memberikan nilai sebagai 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)

Dalam menjelaskan rencana di atas, kita dapat melihat, pada saat eksekusi, perencana dengan cepat mengidentifikasi tabel partisi yang benar berdasarkan nilai parameter, dan berjalan lebih cepat dan tidak menghabiskan waktu untuk memindai/mengulangi tabel partisi lain (lihat tidak pernah dilaksanakan bagian dalam menjelaskan rencana di atas). Ini sangat kuat dan memulai era baru peningkatan kinerja dalam partisi.

Agregat Bijaksana Partisi

Parameter:enable_partitionwise_aggregate

Jika kunci partisi cocok dengan kunci pengelompokan, setiap partisi akan menghasilkan kumpulan grup yang terpisah alih-alih memindai semua partisi sekaligus. Ini akan melakukan agregat paralel untuk setiap partisi dan selama hasil akhir menggabungkan semua hasil.

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)

Ini tentunya lebih cepat karena mencakup pemrosesan agregasi paralel dan pemindaian per partisi.

Kueri katalog dapat digunakan untuk mengetahui semua tabel partisi induk.

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

Matriks Fitur Partisi Singkat

Fitur Partisi v11 v10
Partisi Default YA TIDAK
Warisan tabel asing YA TIDAK
Mempartisi dengan Kunci Hash YA TIDAK
Dukungan untuk PK &FK YA TIDAK
PERBARUI pada kunci partisi YA TIDAK
Inexes Otomatis pada CT YA TIDAK
Pemicu Otomatis pada CT YA TIDAK
Pemangkasan partisi waktu eksekusi YA TIDAK
Berdasarkan partisi, Gabung YA TIDAK
Pemangkasan partisi dinamis YA TIDAK

Apa Selanjutnya?

Kinerja Partisi

Ini adalah salah satu area kerja paling aktif sekarang di komunitas PostgreSQL. PostgreSQL Versi 12 akan dikemas dengan lebih banyak peningkatan kinerja di ruang partisi. Versi 12 diharapkan akan dirilis pada November 2019.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. PostgreSQL, PILIH dari max id

  2. Menambahkan kunci asing ke model Rails

  3. Bagaimana cara membuat enum Java &Postgres bekerja sama untuk pembaruan?

  4. Mengimpor .csv dengan kolom stempel waktu (dd.mm.yyyy hh.mm.ss) menggunakan psql \copy

  5. Perbaikan Partisi di PostgreSQL 11