Indeks dan kinerja multikolom

31

Saya memiliki tabel dengan indeks multikolom, dan saya ragu tentang penyortiran indeks yang tepat untuk mendapatkan kinerja maksimum pada kueri.

Skenario:

  • PostgreSQL 8.4, tabel dengan sekitar satu juta baris

  • Nilai dalam kolom c1 dapat memiliki sekitar 100 nilai yang berbeda . Kita dapat mengasumsikan nilainya terdistribusi secara merata, jadi kami memiliki sekitar 10.000 baris untuk setiap nilai yang mungkin.

  • Kolom c2 dapat memiliki 1000 nilai yang berbeda . Kami memiliki 1000 baris untuk setiap nilai yang memungkinkan.

Saat mencari data, kondisi selalu menyertakan nilai untuk dua kolom ini, sehingga tabel memiliki indeks multikolom yang menggabungkan c1 dan c2. Saya telah membaca tentang pentingnya memesan kolom dengan benar dalam indeks multikolom jika Anda memiliki pertanyaan menggunakan hanya satu kolom untuk memfilter. Ini tidak terjadi dalam skenario kami.

Pertanyaan saya adalah yang ini:

Mengingat fakta bahwa salah satu filter memilih set data yang jauh lebih kecil, dapatkah saya meningkatkan kinerja jika indeks pertama adalah yang paling selektif (yang memungkinkan set lebih kecil)? Saya tidak pernah mempertimbangkan pertanyaan ini sampai saya melihat gambar dari artikel yang direferensikan:

masukkan deskripsi gambar di sini

Gambar diambil dari artikel yang direferensikan tentang indeks multikolom .

Kueri menggunakan nilai dari dua kolom untuk memfilter. Saya tidak memiliki pertanyaan menggunakan hanya satu kolom untuk memfilter. Semua dari mereka adalah: WHERE c1=@ParameterA AND c2=@ParameterB. Ada juga kondisi seperti ini:WHERE c1 = "abc" AND c2 LIKE "ab%"

jap1968
sumber

Jawaban:

36

Menjawab

Karena Anda merujuk ke situs web use-the-index-luke.com, pertimbangkan bab ini:

Gunakan Index, Luke ›The Where Clause› Mencari Ranges › Greater, Less and ANTARA

Ini memiliki contoh yang cocok dengan situasi Anda dengan sempurna (indeks dua kolom, satu diuji untuk kesetaraan , yang lain untuk rentang ), menjelaskan (dengan lebih banyak grafik indeks yang bagus) mengapa saran @ ypercube akurat dan merangkumnya:

Rule of thumb: index for equality first  then for ranges.

Juga bagus hanya untuk satu kolom?

Apa yang harus dilakukan untuk kueri hanya pada satu kolom tampaknya menjadi jelas. Lebih detail dan tolok ukur mengenai hal itu di bawah pertanyaan terkait ini:

Kolom kurang selektif dulu?

Terlepas dari itu, bagaimana jika Anda hanya memiliki kondisi kesetaraan untuk kedua kolom ?

Itu tidak masalah . Letakkan kolom pertama yang lebih mungkin untuk menerima kondisi sendiri, yang sebenarnya penting.

Pertimbangkan demo ini, atau buat ulang sendiri. Saya membuat tabel sederhana dari dua kolom dengan 100k baris. Satu dengan sangat sedikit , yang lain dengan banyak nilai berbeda:

CREATE TEMP TABLE t AS
SELECT (random() * 10000)::int AS lots
     , (random() * 4)::int     AS few
FROM generate_series (1, 100000);

DELETE FROM t WHERE random() > 0.9;  -- create some dead tuples, more "real-life"

ANALYZE t;

SELECT count(distinct lots)   -- 9999
     , count(distinct few)    --    5
FROM   t;

Pertanyaan:

SELECT *
FROM   t
WHERE  lots = 2345
AND    few = 2;

EXPLAIN ANALYZE output (Terbaik dari 10 untuk mengecualikan efek caching):

Pemindaian Seq pada t (biaya = 0,00.,5840,84 baris = 2 lebar = 8)
               (waktu aktual = 5.646..15.535 baris = 2 putaran = 1)
  Saring: ((banyak = 2345) DAN (beberapa = 2))
  Buffer: klik lokal = 443
Total runtime: 15,557 ms

Tambahkan indeks, tes ulang:

CREATE INDEX t_lf_idx ON t(lots, few);
Pemindaian indeks menggunakan t_lf_idx pada t (biaya = 0,00..3.76 baris = 2 lebar = 8)
                                (waktu aktual = 0,008..0.011 baris = 2 loop = 1)
  Indeks Cond: ((banyak = 2345) DAN (beberapa = 2))
  Buffer: klik lokal = 4
Total runtime: 0,027 ms

Tambahkan indeks lain, tes ulang:

DROP INDEX t_lf_idx;
CREATE INDEX t_fl_idx  ON t(few, lots);
Pemindaian indeks menggunakan t_fl_idx pada t (biaya = 0,00..3.74 baris = 2 lebar = 8)
                                (waktu aktual = 0,007..0.011 baris = 2 loop = 1)
  Indeks Cond: ((beberapa = 2) DAN (banyak = 2345))
  Buffer: klik lokal = 4
Total runtime: 0,027 ms
Erwin Brandstetter
sumber
Apakah ini juga kasus untuk 3 (atau lebih) kolom dalam indeks?
hayd
@ Hayd: Tidak yakin apa yang dimaksud dengan "ini". Anda mungkin mengajukan pertanyaan baru . Anda selalu dapat referensi ini untuk konteks. (Dan berikan komentar di sini untuk menautkan kembali.)
Erwin Brandstetter
Dengan "ini" maksud saya "tidak memesan dari masalah definisi indeks jika ada lebih dari 2 kolom dalam definisi indeks"
hayd
@hayd: Poin terpenting: indeks btree baik untuk kueri dengan kondisi kesetaraan pada ekspresi indeks utama . Ketertiban di antara mereka sebagian besar tidak relevan. Banyak detail lain yang tidak sesuai dengan komentar ...
Erwin Brandstetter
Terima kasih, saya akan mencoba dan menulis pertanyaan yang koheren dan tautan ke sana.
hayd
11

Jika, seperti yang Anda katakan, kueri yang melibatkan 2 kolom ini, semuanya adalah pemeriksaan kesetaraan dari kedua kolom, misalnya:

WHERE c1=@ParameterA AND c2=@ParameterB

jangan repot-repot dengan ini. Saya ragu akan ada perbedaan dan jika ada, itu akan diabaikan. Tentu saja Anda selalu dapat menguji, dengan data dan pengaturan server Anda. Versi DBMS yang berbeda dapat berperilaku sedikit berbeda mengenai pengoptimalan.

Urutan di dalam indeks akan berpengaruh pada jenis kueri lain, memiliki pemeriksaan satu kolom saja, atau kondisi ketidaksetaraan, atau kondisi di satu kolom dan pengelompokan di kolom lain, dll.

Jika saya memilih salah satu dari dua pesanan, saya akan memilih untuk menempatkan kolom yang kurang selektif terlebih dahulu. Pertimbangkan tabel dengan kolom yeardan month. Itu lebih mungkin bahwa Anda memerlukan suatu WHERE year = 2000kondisi atau a WHERE year BETWEEN 2000 AND 2013atau a WHERE (year, month) BETWEEN (1999, 6) AND (2000, 5).

Permintaan jenis ini WHERE month = 7 GROUP BY yearmungkin diinginkan (Cari orang yang lahir pada bulan Juli), tetapi akan lebih jarang. Itu tentu saja tergantung pada data aktual yang disimpan di tabel Anda. Pilih satu pesanan untuk saat ini, ucapkan (c1, c2)dan Anda selalu dapat menambahkan indeks lain di lain waktu (c2, c1).


Perbarui, setelah komentar OP:

Ada juga kondisi seperti ini: WHERE c1 = 'abc' AND c2 LIKE 'ab%'

Jenis kueri ini jika tepatnya rentang kondisi pada c2kolom dan akan membutuhkan (c1, c2)indeks. Jika Anda juga memiliki pertanyaan dengan tipe terbalik:

WHERE c2 = 'abc' AND c1 LIKE 'ab%'

maka akan lebih baik jika Anda memiliki (c2, c1)indeks juga.

ypercubeᵀᴹ
sumber