Berikut adalah hasil dari kueri tabel pada kolom kedua indeks multikolom .
Efeknya mudah direproduksi untuk siapa saja. Coba saja di rumah.
Saya diuji dengan PostgreSQL 9.0.5 pada Debian menggunakan tabel berukuran sedang dari database kehidupan nyata dengan 23322 baris. Itu mengimplementasikan hubungan n: m antara tabel adr
(alamat) dan att
(atribut), tapi itu tidak relevan di sini. Skema sederhana:
CREATE TABLE adratt (
adratt_id serial PRIMARY KEY
, adr_id integer NOT NULL
, att_id integer NOT NULL
, log_up timestamp(0) NOT NULL DEFAULT (now())::timestamp(0)
, CONSTRAINT adratt_uni UNIQUE (adr_id, att_id)
);
The UNIQUE
kendala efektif menerapkan indeks yang unik. Saya mengulangi tes dengan indeks sederhana untuk memastikan dan mendapatkan hasil yang sama seperti yang diharapkan.
CREATE INDEX adratt_idx ON adratt(adr_id, att_id)
Tabel ini berkerumun di adratt_uni
indeks dan sebelum tes saya berlari:
CLUSTER adratt;
ANALYZE adratt;
Pemindaian berurutan untuk kueri (adr_id, att_id)
secepat mungkin. Indeks multikolom masih akan digunakan untuk kondisi kueri pada kolom indeks kedua saja.
Saya menjalankan kueri beberapa kali untuk mengisi cache dan memilih yang terbaik dari sepuluh run untuk mendapatkan hasil yang sebanding.
1. Permintaan menggunakan kedua kolom
SELECT *
FROM adratt
WHERE att_id = 90
AND adr_id = 10;
adratt_id | adr_id | att_id | log_up
-----------+--------+--------+---------------------
123 | 10 | 90 | 2008-07-29 09:35:54
(1 row)
Output dari EXPLAIN ANALYZE
:
Index Scan using adratt_uni on adratt (cost=0.00..3.48 rows=1 width=20) (actual time=0.022..0.025 rows=1 loops=1)
Index Cond: ((adr_id = 10) AND (att_id = 90))
Total runtime: 0.067 ms
2. Permintaan menggunakan kolom pertama
SELECT * FROM adratt WHERE adr_id = 10
adratt_id | adr_id | att_id | log_up
-----------+--------+--------+---------------------
126 | 10 | 10 | 2008-07-29 09:35:54
125 | 10 | 13 | 2008-07-29 09:35:54
4711 | 10 | 21 | 2008-07-29 09:35:54
29322 | 10 | 22 | 2011-06-06 15:50:38
29321 | 10 | 30 | 2011-06-06 15:47:17
124 | 10 | 62 | 2008-07-29 09:35:54
21913 | 10 | 78 | 2008-07-29 09:35:54
123 | 10 | 90 | 2008-07-29 09:35:54
28352 | 10 | 106 | 2010-11-22 12:37:50
(9 rows)
Output dari EXPLAIN ANALYZE
:
Index Scan using adratt_uni on adratt (cost=0.00..8.23 rows=9 width=20) (actual time=0.007..0.023 rows=9 loops=1)
Index Cond: (adr_id = 10)
Total runtime: 0.058 ms
3. Permintaan menggunakan kolom kedua
SELECT * FROM adratt WHERE att_id = 90
adratt_id | adr_id | att_id | log_up
-----------+--------+--------+---------------------
123 | 10 | 90 | 2008-07-29 09:35:54
180 | 39 | 90 | 2008-08-29 15:46:07
...
(83 rows)
Output dari EXPLAIN ANALYZE
:
Index Scan using adratt_uni on adratt (cost=0.00..818.51 rows=83 width=20) (actual time=0.014..0.694 rows=83 loops=1)
Index Cond: (att_id = 90)
Total runtime: 0.849 ms
4. Nonaktifkan indexscan & bitmapscan
SET enable_indexscan = off;
SELECT * FROM adratt WHERE att_id = 90
Output dari EXPLAIN ANALYZE:
Bitmap Heap Scan on adratt (cost=779.94..854.74 rows=83 width=20) (actual time=0.558..0.743 rows=83 loops=1)
Recheck Cond: (att_id = 90)
-> Bitmap Index Scan on adratt_uni (cost=0.00..779.86 rows=83 width=0) (actual time=0.544..0.544 rows=83 loops=1)
Index Cond: (att_id = 90)
Total runtime: 0.894 ms
SET enable_bitmapscan = off
SELECT * FROM adratt WHERE att_id = 90
Output dari EXPLAIN ANALYZE
:
Seq Scan on adratt (cost=0.00..1323.10 rows=83 width=20) (actual time=0.009..2.429 rows=83 loops=1)
Filter: (att_id = 90)
Total runtime: 2.680 ms
Kesimpulan
Seperti yang diharapkan, indeks multi-kolom digunakan untuk kueri pada kolom kedua saja.
Seperti yang diharapkan, ini kurang efektif, tetapi kueri masih 3x lebih cepat daripada tanpa indeks.
Setelah menonaktifkan pemindaian indeks, perencana kueri memilih pemindaian tumpukan bitmap, yang melakukan hampir sama cepat. Hanya setelah menonaktifkannya, itu juga kembali ke pemindaian berurutan.
vacuum full
dan areindex
. Selain itu akan membantu scan indeks pada pertama atau kedua kolom terkemuka banyak , tapi terluka query pada kolom kedua. Dalam tabel yang baru dikelompokkan, baris dengan nilai yang sama di kolom kedua tersebar, sehingga maksimum blok harus dibaca.ulang 1) Ya dan tidak.
Untuk kueri yang menggunakan kedua kolom, mis
where (user_id1, user_id2) = (1,2)
. Tidak masalah indeks mana yang dibuat.Untuk kueri yang memiliki kondisi hanya pada salah satu kolom mis.
where user_id1 = 1
Itu penting karena biasanya hanya kolom "terkemuka" yang dapat digunakan untuk perbandingan oleh pengoptimal. Jadiwhere user_id1 = 1
akan dapat menggunakan indeks (user_id1, user_id2) tetapi tidak akan dapat indeks (user_id2, user_id1) untuk semua kasus.Setelah bermain-main dengan ini (setelah Erwin dengan ramah menunjukkan kepada kami pengaturan tempat kerjanya), tampaknya hal ini sangat bergantung pada distribusi data kolom kedua meskipun saya belum menemukan situasi mana yang memungkinkan pengoptimal menggunakan kolom tambahan untuk kondisi MANA.
Oracle 11 yang juga dapat (kadang-kadang) menggunakan kolom yang tidak di awal definisi indeks.
re 2) Ya itu akan membuat indeks
Kutipan dari manual
re 2a)
Primary Key (user_id1,user_id2)
akan membuat indeks pada (user_id1, user_id2) (yang dapat Anda temukan sendiri dengan sangat mudah hanya dengan membuat kunci utama seperti itu)Saya sangat menyarankan Anda membaca bab tentang indeks dalam manual , itu pada dasarnya menjawab semua pertanyaan di atas.
Selain itu, Indeks apa yang akan dibuat? oleh depesz melakukan pekerjaan dengan baik menjelaskan urutan pada kolom indeks dan topik terkait indeks lainnya.
sumber
Iklan 1)
Ada batasan dalam PostgreSQL seperti yang dijelaskan oleh @a_horse_with_no_name . Hingga versi 8.0 indeks multicolumn hanya dapat digunakan untuk permintaan pada kolom terkemuka. Ini telah ditingkatkan di versi 8.1. The pengguna saat ini untuk Postgres 10 (diperbaharui) menjelaskan:
Tekankan milikku. Saya dapat mengkonfirmasi itu dari pengalaman.
Lihat juga test case yang ditambahkan jawaban saya nanti di sini .
sumber
Ini sebagai jawaban atas jawaban Jack , komentar tidak akan berlaku.
Tidak ada indeks penutup di PostgreSQL sebelum versi 9.2. Karena model MVCC, setiap tuple dalam set hasil harus dikunjungi untuk memeriksa visibilitas. Anda mungkin berpikir tentang Oracle.
Pengembang PostgreSQL berbicara tentang "hanya scan indeks" . Bahkan, fitur tersebut telah dirilis dengan Postgres 9.2. Baca pesan komit .
Depesz menulis posting Blog yang sangat informatif .
Indeks True covering (pembaruan) diperkenalkan dengan
INCLUDE
klausa dengan Postgres 11. Terkait:Ini agak salah, juga:
Seperti dilaporkan dalam komentar pada jawaban saya yang lain, saya juga menjalankan tes dengan tabel dua bilangan bulat dan tidak ada yang lain. Indeks memiliki kolom yang sama dengan tabel. Ukuran indeks btree adalah sekitar 2/3 dari tabel. Tidak cukup untuk menjelaskan percepatan faktor 3. Saya menjalankan lebih banyak tes, berdasarkan pengaturan Anda, disederhanakan menjadi dua kolom dan dengan 100000 baris. Pada instalasi PostgreSQL 9.0 saya hasilnya konsisten.
Jika tabel memiliki kolom tambahan, percepatan dengan indeks menjadi lebih substansial, tetapi tentu saja bukan satu-satunya faktor di sini .
Singkatnya poin utama:
Indeks multi-kolom dapat digunakan dengan kueri pada kolom non-terkemuka, tetapi percepatan hanya sekitar faktor 3 untuk kriteria selektif (persentase kecil dari baris dalam hasil). Lebih tinggi untuk tupel yang lebih besar, lebih rendah untuk bagian yang lebih besar dari tabel di set hasil.
Buat indeks tambahan pada kolom tersebut jika kinerjanya penting.
Jika semua kolom yang terlibat dimasukkan dalam indeks (mencakup indeks) dan semua baris yang terlibat (per blok) dapat dilihat oleh semua transaksi, Anda bisa mendapatkan "pemindaian hanya indeks" di halaman 9.2 atau yang lebih baru.
sumber
Ini tidak setara dan secara umum indeks (bar, baz) tidak akan efisien untuk permintaan formulir
select * from foo where baz=?
Erwin telah menunjukkan bahwa indeks semacam itu memang dapat mempercepat kueri, tetapi efek ini terbatas dan tidak dengan urutan yang sama seperti yang biasanya Anda harapkan indeks untuk meningkatkan pencarian - itu bergantung pada kenyataan bahwa 'pemindaian penuh' dari indeks sering lebih cepat daripada 'pemindaian penuh' dari tabel yang diindeks karena kolom tambahan dalam tabel yang tidak muncul dalam indeks.
Ringkasan: indeks dapat membantu kueri bahkan pada kolom yang tidak memimpin, tetapi dalam salah satu dari dua cara sekunder dan relatif kecil dan tidak dengan cara dramatis yang biasanya Anda harapkan akan membantu indeks karena struktur btree nya
nb dua cara indeks dapat membantu adalah jika pemindaian penuh indeks secara signifikan lebih murah daripada pemindaian penuh tabel dan baik: 1. pencarian tabel yang murah (karena ada beberapa dari mereka atau mereka mengelompok),
atau 2. indeks mencakup sehingga tidak ada pencarian tabel sama sekalioops, lihat komentar Erwins di sinitestbed:
kueri 1 (tanpa indeks, mencapai 74 buffer ):
kueri 2 (dengan indeks - pengoptimal mengabaikan indeks - memukul 74 buffer lagi):
kueri 2 (dengan indeks - dan kami menipu pengoptimal untuk menggunakannya):
Jadi akses melalui indeks dua kali lebih cepat dalam hal ini mengenai 30 buffer - yang dalam hal pengindeksan 'sedikit lebih cepat' !, dan YMMV tergantung pada ukuran relatif tabel dan indeks, bersama dengan jumlah baris yang disaring dan karakteristik clustering dari data dalam tabel
Sebaliknya, pertanyaan pada kolom terkemuka menggunakan struktur btree dari indeks - dalam hal ini mengenai 2 buffer :
sumber