Penerapan indeks yang tepat dapat membuat kueri menjadi sangat cepat.
Indeks menggunakan pointer untuk mengakses halaman data dengan cepat.
Perubahan besar terjadi pada Indeks di PostgreSQL 11, banyak patch yang ditunggu-tunggu telah dirilis.
Mari kita lihat beberapa fitur hebat dari rilis ini.
Pembangunan Indeks B-TREE Paralel
PostgreSQL 11 memperkenalkan patch infrastruktur untuk memungkinkan pembuatan indeks paralel.
Ini hanya dapat digunakan dengan indeks B-Tree seperti untuk saat ini.
Membangun indeks B-Tree paralel dua hingga tiga kali lebih cepat daripada melakukan hal yang sama tanpa kerja paralel (atau pembuatan serial).
Di PostgreSQL 11, pembuatan indeks paralel diaktifkan secara default.
Ada dua parameter penting:
- max_parallel_workers - Menyetel jumlah maksimum pekerja yang dapat didukung sistem untuk kueri paralel.
- max_parallel_maintenance_workers - Mengontrol jumlah maksimum proses pekerja yang dapat digunakan untuk MENCIPTAKAN INDEX.
Mari kita periksa dengan sebuah contoh:
severalnines=# CREATE TABLE test_btree AS SELECT generate_series(1,100000000) AS id;
SELECT 100000000
severalnines=# SET maintenance_work_mem = '1GB';
severalnines=# \timing
severalnines=# CREATE INDEX q ON test_btree (id);
TIME: 25294.185 ms (00:25.294)
Mari kita coba dengan kerja paralel 8 arah:
severalnines=# SET maintenance_work_mem = '2GB';
severalnines=# SET max_parallel_workers = 16;
severalnines=# SET max_parallel_maintenance_workers = 8;
severalnines=# \timing
severalnines=# CREATE INDEX q1 ON test_btree (id);
TIME: 11001.240 ms (00:11.001)
Kita bisa melihat perbedaan kinerja dengan pekerja paralel, lebih dari 60% berkinerja hanya dengan sedikit perubahan. Maintenance_work_mem juga dapat ditingkatkan untuk mendapatkan performa yang lebih baik.
Tabel ALTER juga membantu meningkatkan pekerja paralel. Sintaks di bawah ini dapat digunakan untuk meningkatkan pekerja paralel bersama dengan max_parallel_maintenance_workers. Ini mengabaikan model biaya sepenuhnya.
ALTER TABLE test_btree SET (parallel_workers = 24);
Tip:RESET ke default setelah pembuatan indeks selesai untuk mencegah rencana kueri yang merugikan.
CREATE INDEX dengan opsi CONCURRENTLY mendukung pembuatan paralel tanpa batasan khusus, hanya pemindaian tabel pertama yang benar-benar dilakukan secara paralel.
Tes kinerja yang lebih dalam dapat ditemukan di sini.
Tambahkan Penguncian Predikat untuk Indeks Hash, Intisari, dan Gin
PostgreSQL 11 dikirimkan dengan dukungan kunci predikat untuk indeks hash, indeks gin, dan indeks inti. Ini akan membuat isolasi transaksi SERIALIZABLE jauh lebih efisien dengan indeks tersebut.
Manfaat:penguncian predikat dapat memberikan kinerja yang lebih baik pada tingkat isolasi serial dengan mengurangi jumlah positif palsu yang menyebabkan kegagalan serialisasi yang tidak perlu.
Di PostgreSQL 10, rentang kunci adalah relasinya, tetapi di PostgreSQL 11 kunci ditemukan hanya halaman.
Mari kita uji.
(SEpre>severalnines=# CREATE TABLE sv_predicate_lock1(c1 INT, c2 VARCHAR(10)) ;
CREATE TABLE
severalnines=# CREATE INDEX idx1_sv_predicate_lock1 ON sv_predicate_lock1 USING 'hash(c1) ;
CREATE INDEX
severalnines=# INSERT INTO sv_predicate_lock1 VALUES (generate_series(1, 100000), 'puja') ;
INSERT 0 100000
severalnines=# BEGIN ISOLATION LEVEL SERIALIZABLE ;
BEGIN
severalnines=# SELECT * FROM sv_predicate_lock1 WHERE c1=10000 FOR UPDATE ;
c1 | c2
-------+-------
10000 | puja
(1 row)
Seperti yang dapat kita lihat di bawah, kunci berada di level halaman, bukan relasi. Di PostgreSQL 10 berada di level relasi, jadi ini adalah KEMENANGAN BESAR untuk transaksi bersamaan di PostgreSQL 11.
severalnines=# SELECT locktype, relation::regclass, mode FROM pg_locks ;
locktype | relation | mode
---------------+-------------------------+-----------------
relation | pg_locks | AccessShareLock
relation | idx1_sv_predicate_lock1 | AccessShareLock
relation | sv_predicate_lock1 | RowShareLock
virtualxid | | ExclusiveLock
transactionid | | ExclusiveLock
page | idx1_sv_predicate_lock1 | SIReadLock
tuple | sv_predicate_lock1 | SIReadLock
(7 rows)
Tip:Pemindaian berurutan akan selalu membutuhkan kunci predikat tingkat relasi. Hal ini dapat mengakibatkan peningkatan tingkat kegagalan serialisasi. Mungkin akan membantu untuk mendorong penggunaan pemindaian indeks dengan mengurangi random_page_cost dan/atau meningkatkan cpu_tuple_cost.
Izinkan Pembaruan PANAS untuk Beberapa Indeks Ekspresi
Fitur Heap Only Tuple (HOT), menghilangkan entri indeks yang berlebihan dan memungkinkan penggunaan kembali ruang yang diambil oleh tupel yang DIHAPUS atau UPDATE yang sudah usang tanpa melakukan vakum di seluruh tabel. Ini mengurangi ukuran indeks dengan menghindari pembuatan entri indeks dengan kunci identik.
Jika nilai ekspresi indeks tidak berubah setelah UPDATE, izinkan pembaruan PANAS di mana sebelumnya PostgreSQL tidak mengizinkannya, memberikan peningkatan kinerja yang signifikan dalam kasus tersebut.
Ini sangat berguna untuk indeks seperti bidang JSON->>di mana nilai JSON berubah tetapi nilai yang diindeks tidak.
Fitur ini dibatalkan pada 11.1 karena penurunan kinerja (AT Free BSD hanya menurut Simon), lebih detail / benchmark dapat ditemukan di sini. Ini harus diperbaiki pada rilis mendatang.
Izinkan Seluruh Halaman Indeks Hash untuk Dipindai
Indeks hash:Perencana kueri akan mempertimbangkan untuk menggunakan indeks hash setiap kali kolom yang diindeks terlibat dalam perbandingan menggunakan operator =. Itu juga tidak aman dari kerusakan (tidak masuk WAL) sehingga perlu dibangun kembali setelah DB mogok, dan perubahan pada hash tidak ditulis melalui replikasi streaming.
Di PostgreSQL 10, indeks hash dicatat WAL, artinya, CRASH aman dan dapat direplikasi. Indeks hash menggunakan lebih sedikit ruang dibandingkan dengan B-Tree sehingga dapat lebih cocok di memori.
Di PostgreSQL 11, indeks Btree memiliki optimasi yang disebut "kevakuman satu halaman", yang secara oportunistik menghapus penunjuk indeks mati dari halaman indeks, mencegah sejumlah besar indeks mengasapi, yang jika tidak akan terjadi. Logika yang sama telah di-porting ke indeks Hash. Ini mempercepat daur ulang ruang, mengurangi kembung.
STATISTIKA Indeks Fungsi
Sekarang dimungkinkan untuk menentukan nilai STATISTIK untuk kolom indeks fungsi. Ini sangat berharga untuk efisiensi aplikasi khusus. Kami sekarang dapat mengumpulkan statistik pada kolom ekspresi, yang akan membantu perencana untuk mengambil keputusan yang lebih akurat.
severalnines=# CREATE INDEX idx1_stats ON stat ((s1 + s2)) ;
CREATE INDEX
severalnines=# ALTER INDEX idx1_stats ALTER COLUMN 1 SET STATISTICS 1000 ;
ALTER INDEX
severalnines=# \d+ idx1_stats
Index "public.idx1_stats"
Column | Type | Definition | Storage | Stats target
--------+---------+------------+---------+--------------
expr | numeric | (c1 + c2) | main | 1000
btree, for table "public.stat1"
memeriksa
Modul Contrib baru amcheck telah ditambahkan. Hanya indeks B-Tree yang dapat diperiksa.
Mari kita uji!
severalnines=# CREATE EXTENSION amcheck ;
CREATE EXTENSION
severalnines=# SELECT bt_index_check('idx1_stats') ;
ERROR: invalid page in block 0 of relation base/16385/16580
severalnines=#CREATE INDEX idx1_hash_data1 ON data1 USING hash (c1) ;
CREATE INDEX
severalnines=# SELECT bt_index_check('idx1_hash_data1') ;
ERROR: only B-Tree indexes are supported as targets for verification
DETAIL: Relation "idx1_hash_data1" is not a B-Tree index.
Indeks yang Dipartisi Lokal Dimungkinkan
Sebelum PostgreSQL11, tidak mungkin membuat indeks pada tabel anak atau tabel yang dipartisi.
Di PostgreSQL 11, ketika CREATE INDEX dijalankan pada tabel yang dipartisi/tabel induk, ia membuat entri katalog untuk indeks pada tabel yang dipartisi dan kaskade untuk membuat indeks aktual pada partisi yang ada. Ini juga akan membuatnya di partisi yang akan datang.
Mari kita coba membuat tabel induk dan mempartisinya:
severalnines=# create table test_part ( a int, list varchar(5) ) partition by list (list);
CREATE TABLE
severalnines=# create table part_1 partition of test_part for values in ('India');
CREATE TABLE
severalnines=# create table part_2 partition of test_part for values in ('USA');
CREATE TABLE
severalnines=#
severalnines=# \d+ test_part
Table "public.test_part"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
--------+----------------------+-----------+----------+---------+----------+--------------+-------------
a | integer | | | | plain | |
list | character varying(5) | | | | extended | |
Partition key: LIST (list)
Partitions: part_1 FOR VALUES IN ('India'),
part_2 FOR VALUES IN ('USA')
Mari kita coba membuat indeks pada tabel induk:
severalnines=# create index i_test on test_part (a);
CREATE INDEX
severalnines=# \d part_2
Table "public.part_2"
Column | Type | Collation | Nullable | Default
--------+----------------------+-----------+----------+---------
a | integer | | |
list | character varying(5) | | |
Partition of: test_part FOR VALUES IN ('USA')
Indexes:
"part_2_a_idx" btree (a)
severalnines=# \d part_1
Table "public.part_1"
Column | Type | Collation | Nullable | Default
--------+----------------------+-----------+----------+---------
a | integer | | |
list | character varying(5) | | |
Partition of: test_part FOR VALUES IN ('India')
Indexes:
"part_1_a_idx" btree (a)
Indeks diturunkan ke semua partisi di PostgreSQL 11, yang merupakan fitur yang sangat keren.
Meliputi Indeks (termasuk CLAUSE untuk indeks)
Klausa INCLUDE untuk menambahkan kolom ke indeks dapat ditentukan. Ini efektif saat menambahkan kolom yang tidak terkait dengan batasan unik dari indeks unik. Kolom INCLUDE ada semata-mata untuk memungkinkan lebih banyak kueri mendapatkan manfaat dari pemindaian hanya indeks. Hanya indeks B-tree yang mendukung klausa INCLUDE untuk saat ini.
Mari kita periksa perilakunya tanpa TERMASUK. Itu tidak akan menggunakan pemindaian indeks saja jika kolom tambahan muncul di SELECT. Hal ini dapat dicapai dengan menggunakan klausa INCLUDE.
severalnines=# CREATE TABLE no_include (a int, b int, c int);
CREATE TABLE
severalnines=# INSERT INTO no_include SELECT 3 * val, 3 * val + 1, 3 * val + 2 FROM generate_series(0, 1000000) as val;
INSERT 0 1000001
severalnines=# CREATE UNIQUE INDEX old_unique_idx ON no_include(a, b);
CREATE INDEX
severalnines=# VACUUM ANALYZE;
VACUUM
EXPLAIN ANALYZE SELECT a, b FROM no_include WHERE a < 1000; - It will do index only scan
EXPLAIN ANALYZE SELECT a, b, c FROM no_include WHERE a < 1000; - It will not do index only scan as we have extra column in select.
severalnines=# CREATE INDEX old_idx ON no_include (a, b, c);
CREATE INDEX
severalnines=# VACUUM ANALYZE;
VACUUM
severalnines=# EXPLAIN ANALYZE SELECT a, b, c FROM no_include WHERE a < 1000; - It did index only scan as index on all three columns.
QUERY PLAN
-------------------------------------------------
Index Only Scan using old_idx on no_include
(cost=0.42..14.92 rows=371 width=12)
(actual time=0.086..0.291 rows=334 loops=1)
Index Cond: (a < 1000)
Heap Fetches: 0
Planning Time: 2.108 ms
Execution Time: 0.396 ms
(5 rows)
Mari kita coba dengan menyertakan klausa. Pada contoh di bawah, UNIQUE CONSTRAINT dibuat di kolom a dan b, tetapi indeks menyertakan kolom c.
severalnines=# CREATE TABLE with_include (a int, b int, c int);
CREATE TABLE
severalnines=# INSERT INTO with_include SELECT 3 * val, 3 * val + 1, 3 * val + 2 FROM generate_series(0, 1000000) as val;
INSERT 0 1000001
severalnines=# CREATE UNIQUE INDEX new_unique_idx ON with_include(a, b) INCLUDE (c);
CREATE INDEX
severalnines=# VACUUM ANALYZE;
VACUUM
severalnines=# EXPLAIN ANALYZE SELECT a, b, c FROM with_include WHERE a < 10000;
QUERY PLAN
-----------------------------------------------------
Index Only Scan using new_unique_idx on with_include
(cost=0.42..116.06 rows=3408 width=12)
(actual time=0.085..2.348 rows=3334 loops=1)
Index Cond: (a < 10000)
Heap Fetches: 0
Planning Time: 1.851 ms
Execution Time: 2.840 ms
(5 rows)
Tidak boleh ada tumpang tindih antara kolom dalam daftar kolom utama dan kolom dari daftar penyertaan
severalnines=# CREATE UNIQUE INDEX new_unique_idx ON with_include(a, b) INCLUDE (a);
ERROR: 42P17: included columns must not intersect with key columns
LOCATION: DefineIndex, indexcmds.c:373
Kolom yang digunakan dengan ekspresi di daftar utama berfungsi:
severalnines=# CREATE UNIQUE INDEX new_unique_idx_2 ON with_include(round(a), b) INCLUDE (a);
CREATE INDEX
Ekspresi tidak dapat digunakan dalam daftar sertakan karena tidak dapat digunakan dalam pemindaian indeks saja:
severalnines=# CREATE UNIQUE INDEX new_unique_idx_2 ON with_include(a, b) INCLUDE (round(c));
ERROR: 0A000: expressions are not supported in included columns
LOCATION: ComputeIndexAttrs, indexcmds.c:1446
Kesimpulan
Fitur-fitur baru PostgreSQL pasti akan meningkatkan kehidupan DBA sehingga menjadi pilihan alternatif yang kuat di DB open source. Saya mengerti bahwa beberapa fitur indeks saat ini terbatas pada B-Tree, ini masih merupakan awal yang baik dari era eksekusi Paralel untuk PostgreSQL dan menuju ke alat yang bagus untuk melihat lebih dekat. Terima kasih!