Mari kita membuat beberapa asumsi:
Saya punya tabel yang terlihat seperti ini:
a | b
---+---
a | -1
a | 17
...
a | 21
c | 17
c | -3
...
c | 22
Fakta tentang set saya:
Ukuran seluruh tabel adalah ~ 10 10 baris.
Saya memiliki ~ 100rb baris dengan nilai
a
dalam koloma
, mirip dengan nilai-nilai lain (misalnyac
).Itu berarti ~ 100k nilai berbeda di kolom 'a'.
Sebagian besar pertanyaan saya akan membaca semua atau sebagian besar nilai untuk nilai yang diberikan dalam, misalnya
select sum(b) from t where a = 'c'
.Tabel ditulis sedemikian rupa sehingga nilai berturut-turut secara fisik dekat (baik itu ditulis dalam urutan, atau kami asumsikan
CLUSTER
digunakan pada tabel dan kolom itua
).Tabel jarang jika pernah diperbarui, kami hanya khawatir tentang kecepatan baca.
Tabel ini relatif sempit (katakan ~ 25 byte per tuple, + 23 byte overhead).
Sekarang pertanyaannya adalah, indeks seperti apa yang harus saya gunakan? Pemahaman saya adalah:
BTree Masalah saya di sini adalah bahwa indeks BTree akan sangat besar karena sejauh yang saya tahu itu akan menyimpan nilai duplikat (harus, karena tidak dapat menganggap tabel diurutkan secara fisik). Jika BTree sangat besar, saya harus membaca indeks dan bagian tabel yang ditunjuk indeks. (Kita bisa gunakan
fillfactor = 100
untuk mengurangi ukuran indeks sedikit.)BRIN Pemahaman saya adalah bahwa saya dapat memiliki indeks kecil di sini dengan mengorbankan membaca halaman yang tidak berguna. Menggunakan yang kecil
pages_per_range
berarti bahwa indeksnya lebih besar (yang merupakan masalah dengan BRIN karena saya perlu membaca keseluruhan indeks), memilikipages_per_range
sarana yang besar sehingga saya akan membaca banyak halaman yang tidak berguna. Apakah ada formula ajaib untuk menemukan nilai baguspages_per_range
yang memperhitungkan pengorbanan itu?GIN / GiST Tidak yakin itu relevan di sini karena sebagian besar digunakan untuk pencarian teks lengkap, tetapi saya juga mendengar bahwa mereka pandai menangani kunci duplikat. Apakah indeks
GIN
atauGiST
bantuan di sini?
Pertanyaan lain adalah, akankah Postgres menggunakan fakta bahwa sebuah tabel sudah CLUSTER
diedit (dengan asumsi tidak ada pembaruan) dalam perencana kueri (mis. Dengan pencarian biner untuk halaman awal / akhir yang relevan)? Agak terkait, bisakah saya menyimpan semua kolom saya di BTree dan menjatuhkan tabel sama sekali (atau mencapai sesuatu yang setara, saya percaya itu adalah indeks yang dikelompokkan dalam SQL server)? Apakah ada indeks BTree / BRIN hybrid yang akan membantu di sini?
Saya lebih suka menghindari menggunakan array untuk menyimpan nilai-nilai saya karena permintaan saya akan berakhir kurang dapat dibaca dengan cara itu (saya mengerti ini akan mengurangi biaya 23 byte per tuple overhead dengan mengurangi jumlah tuple).
Jawaban:
Belum tentu - Memiliki indeks btree yang 'meliputi' akan menjadi waktu membaca tercepat, dan jika hanya itu yang Anda inginkan (yaitu jika Anda mampu membeli penyimpanan tambahan), maka itu adalah taruhan terbaik Anda.
Jika Anda tidak mampu membeli overhead penyimpanan dari indeks btree penutup, BRIN sangat ideal untuk Anda, karena Anda sudah memiliki pengelompokan (ini sangat penting bagi BRIN untuk berguna). Indeks BRIN kecil , sehingga semua halaman cenderung berada dalam memori jika Anda memilih nilai yang sesuai
pages_per_range
.Tidak ada rumus ajaib, tetapi mulai dengan
pages_per_range
sedikit kurang dari ukuran rata-rata (dalam halaman) yang ditempati oleh nilai rata-rataa
. Anda mungkin mencoba memperkecil: (jumlah halaman BRIN dipindai) + (jumlah halaman tumpukan yang dipindai) untuk kueri yang khas. CariHeap Blocks: lossy=n
dalam rencana eksekusipages_per_range=1
dan bandingkan dengan nilai-nilai lain untukpages_per_range
- yaitu melihat berapa banyak heap block yang tidak perlu sedang dipindai.GIN mungkin layak dipertimbangkan, tetapi mungkin bukan GST - namun jika pengelompokan alami benar-benar baik, maka BRIN mungkin akan menjadi taruhan yang lebih baik.
Berikut ini adalah perbandingan sampel antara berbagai jenis indeks untuk data tiruan sedikit seperti milik Anda:
tabel dan indeks:
ukuran hubungan:
meliputi btree:
btree polos:
BRIN pages_per_range = 4:
BRIN pages_per_range = 2:
GIN:
Aku di sini
sumber
Bitmap Index Scan
artinya 'membaca seluruh indeks brin` tapi mungkin itu salah baca. OracleCOMPRESS
terlihat seperti sesuatu yang akan berguna di sini karena akan mengurangi ukuran B-tree, tapi saya terjebak dengan pg!Selain btree dan brin yang tampaknya merupakan opsi yang paling masuk akal, beberapa opsi eksotis lainnya yang mungkin layak diselidiki - mungkin bermanfaat atau tidak dalam kasus Anda:
INCLUDE
indeks . Mereka akan - semoga - dalam versi utama berikutnya (10) dari Postgres, di suatu tempat sekitar bulan September 2017. Indeks pada(a) INCLUDE (b)
memiliki struktur yang sama dengan indeks pada(a)
tetapi termasuk dalam halaman daun, semua nilaib
(tetapi tidak tertata). Yang berarti Anda tidak dapat menggunakannya misalnya untukSELECT * FROM t WHERE a = 'a' AND b = 2 ;
. Indeks mungkin digunakan tetapi sementara(a,b)
indeks akan menemukan baris yang cocok dengan pencarian tunggal, indeks sertakan harus melalui nilai (mungkin 100K seperti pada kasus Anda) yang cocoka = 'a'
dan memeriksab
nilai.Di sisi lain, indeks sedikit lebih lebar dari
(a,b)
indeks dan Anda tidak perlu urutanb
untuk kueri Anda untuk menghitungSUM(b)
. Anda juga bisa memiliki misalnya(a) INCLUDE (b,c,d)
yang dapat digunakan untuk kueri yang serupa dengan milik Anda yang mengumpulkan 3 kolom.Indeks yang disaring (sebagian) . Saran yang mungkin terdengar agak gila * pada awalnya:
Satu indeks untuk setiap
a
nilai. Dalam kasus Anda, indeks sekitar 100 ribu. Walaupun ini terdengar banyak, pertimbangkan bahwa setiap indeks akan sangat kecil, baik dalam ukuran (jumlah baris) dan lebar (karena hanya akan menyimpanb
nilai). Dalam semua aspek lain, itu (indeks 100K bersama-sama) akan bertindak sebagai indeks b-tree(a,b)
saat menggunakan ruang(b)
indeks.Kerugiannya adalah Anda harus membuat dan memeliharanya sendiri, setiap kali nilai baru
a
ditambahkan ke dalam tabel. Karena meja Anda agak stabil, tanpa banyak (atau ada) sisipan / pembaruan, itu tidak tampak seperti masalah.Tabel ringkasan. Karena tabelnya agak stabil, Anda selalu dapat membuat dan mengisi tabel ringkasan dengan agregat paling umum yang Anda perlukan (
sum(b), sum(c), sum(d), avg(b), count(distinct b)
, dll). Ini akan menjadi kecil (hanya 100K baris) dan hanya perlu diisi sekali dan diperbarui hanya ketika baris dimasukkan / diperbarui / dihapus pada tabel utama.*: ide disalin dari perusahaan ini yang menjalankan 10 juta indeks dalam sistem produksi mereka: Heap: Menjalankan 10 Juta Indeks Postgresql Dalam Produksi (dan terus bertambah) .
sumber
SUM
sebagai contoh, tetapi dalam praktiknya pertanyaan saya tidak dapat dikomputasi (lebih sepertiselect ... from t where a = '?' and ??
wjere??
akan menjadi beberapa kondisi lain yang ditentukan pengguna.??
itu;)DO
pernyataan dalam jawaban terkait ini .