Saya menggunakan PostgreSQL 9.1 di Ubuntu 12.04.
Saya perlu memilih catatan dalam rentang waktu: meja saya time_limits
memiliki dua timestamp
bidang dan satu integer
properti. Ada kolom tambahan di tabel aktual saya yang tidak terlibat dengan kueri ini.
create table (
start_date_time timestamp,
end_date_time timestamp,
id_phi integer,
primary key(start_date_time, end_date_time,id_phi);
Tabel ini berisi sekitar 2 juta catatan.
Pertanyaan seperti berikut ini membutuhkan banyak waktu:
select * from time_limits as t
where t.id_phi=0
and t.start_date_time <= timestamp'2010-08-08 00:00:00'
and t.end_date_time >= timestamp'2010-08-08 00:05:00';
Jadi saya mencoba menambahkan indeks lain - kebalikan dari PK:
create index idx_inversed on time_limits(id_phi, start_date_time, end_date_time);
Saya mendapat kesan bahwa kinerja meningkat: Waktu untuk mengakses catatan di tengah meja tampaknya lebih masuk akal: di suatu tempat antara 40 dan 90 detik.
Tetapi masih beberapa puluh detik untuk nilai di tengah rentang waktu. Dan dua kali lagi ketika menargetkan ujung meja (berbicara secara kronologis).
Saya mencoba explain analyze
untuk pertama kalinya untuk mendapatkan paket permintaan ini:
Bitmap Heap Scan on time_limits (cost=4730.38..22465.32 rows=62682 width=36) (actual time=44.446..44.446 rows=0 loops=1)
Recheck Cond: ((id_phi = 0) AND (start_date_time <= '2011-08-08 00:00:00'::timestamp without time zone) AND (end_date_time >= '2011-08-08 00:05:00'::timestamp without time zone))
-> Bitmap Index Scan on idx_time_limits_phi_start_end (cost=0.00..4714.71 rows=62682 width=0) (actual time=44.437..44.437 rows=0 loops=1)
Index Cond: ((id_phi = 0) AND (start_date_time <= '2011-08-08 00:00:00'::timestamp without time zone) AND (end_date_time >= '2011-08-08 00:05:00'::timestamp without time zone))
Total runtime: 44.507 ms
Apa yang bisa saya lakukan untuk mengoptimalkan pencarian? Anda dapat melihat semua waktu yang dihabiskan memindai dua kolom cap waktu setelah id_phi
diatur ke 0
. Dan saya tidak mengerti pemindaian besar (baris 60K!) Pada cap waktu. Bukankah mereka diindeks oleh kunci primer dan idx_inversed
saya menambahkan?
Haruskah saya mengubah dari jenis stempel waktu ke yang lain?
Saya telah membaca sedikit tentang indeks GIST dan GIN. Saya rasa mereka bisa lebih efisien pada kondisi tertentu untuk jenis kustom. Apakah ini opsi yang layak untuk kasus penggunaan saya?
sumber
explain analyze
output adalah waktu permintaan yang diperlukan di server . Jika kueri Anda membutuhkan waktu 45 detik, maka waktu tambahan dihabiskan untuk mentransfer data dari database ke program yang menjalankan kueri. Lagipula 62682 baris dan jika setiap baris besar (misalnya panjangvarchar
atautext
kolom), ini dapat memengaruhi waktu transfer secara drastis.rows=62682 rows
adalah perkiraan perencana . Kueri mengembalikan 0 baris.(actual time=44.446..44.446 rows=0 loops=1)
Jawaban:
Untuk Postgres 9.1 atau lebih baru:
Dalam kebanyakan kasus, urutan semacam indeks hampir tidak relevan. Postgres dapat memindai mundur secara praktis secepat. Tetapi untuk kueri rentang pada beberapa kolom, ini bisa membuat perbedaan besar . Erat terkait:
Pertimbangkan permintaan Anda:
Urutan urutan kolom pertama
id_phi
dalam indeks tidak relevan. Karena ini diperiksa untuk persamaan (=
), itu harus didahulukan. Anda punya hak itu. Lebih banyak dalam jawaban terkait ini:Postgres dapat melompat ke
id_phi = 0
dalam waktu dekat dan mempertimbangkan dua kolom indeks pencocokan berikut. Ini dipertanyakan dengan berbagai kondisi urutan urutan terbalik (<=
,>=
). Dalam indeks saya, baris yang memenuhi kualifikasi didahulukan. Harus menjadi cara tercepat yang mungkin dengan indeks B-Tree 1 :start_date_time <= something
: indeks memiliki stempel waktu paling awal terlebih dahulu.Perulangan hingga baris pertama gagal memenuhi syarat (super cepat).
end_date_time >= something
: indeks memiliki stempel waktu terakhir lebih dulu.Lanjutkan dengan nilai berikutnya untuk kolom 2 ..
Postgres dapat memindai maju atau mundur. Cara Anda memiliki indeks, harus membaca semua baris yang cocok pada dua kolom pertama dan kemudian filter ketiga. Pastikan untuk membaca Indeks
ORDER BY
bab dan dalam manual. Ini cocok dengan pertanyaan Anda dengan cukup baik.Berapa banyak baris yang cocok pada dua kolom pertama?
Hanya sedikit yang
start_date_time
mendekati awal rentang waktu tabel. Tapi hampir semua baris denganid_phi = 0
ujung kronologis dari tabel! Jadi kinerja memburuk dengan waktu mulai nanti.Perkiraan perencana
Perencana memperkirakan
rows=62682
untuk contoh kueri Anda. Dari mereka, tidak ada yang memenuhi syarat (rows=0
). Anda mungkin mendapatkan perkiraan yang lebih baik jika Anda meningkatkan target statistik untuk tabel. Untuk 2.000.000 baris ...... mungkin membayar. Atau bahkan lebih tinggi. Lebih banyak dalam jawaban terkait ini:
Saya kira Anda tidak memerlukan itu untuk
id_phi
(hanya beberapa nilai berbeda, didistribusikan secara merata), tetapi untuk cap waktu (banyak nilai berbeda, didistribusikan tidak merata).Saya juga tidak menganggapnya penting dengan peningkatan indeks.
CLUSTER
/ pg_repackJika Anda menginginkannya lebih cepat, Anda dapat merampingkan urutan fisik baris di tabel Anda. Jika Anda mampu mengunci meja Anda secara eksklusif untuk jangka waktu pendek (misalnya, di luar jam) untuk menulis ulang tabel Anda dan memesan baris sesuai dengan indeks:
Dengan akses bersamaan, pertimbangkan pg_repack , yang dapat melakukan hal yang sama tanpa kunci eksklusif.
Either way, efeknya adalah bahwa blok lebih sedikit perlu dibaca dari tabel dan semuanya sudah diurutkan. Ini efek satu kali memburuk dari waktu ke waktu dengan menulis di atas meja yang memecah urutan fisik.
Indeks GiST di Postgres 9.2+
1 Dengan hal 9.2+ ada opsi lain yang mungkin lebih cepat: indeks GiST untuk kolom rentang.
Ada tipe rentang bawaan untuk
timestamp
dantimestamp with time zone
:tsrange
,tstzrange
. Indeks btree biasanya lebih cepat untukinteger
kolom tambahan sepertiid_phi
. Lebih kecil dan lebih murah untuk dirawat. Tetapi secara keseluruhan kueri mungkin akan lebih cepat dengan indeks gabungan.Ubah definisi tabel Anda atau gunakan indeks ekspresi .
Untuk indeks GiST multikolom yang tersedia, Anda juga memerlukan modul tambahan yang
btree_gist
terpasang (satu kali per basis data) yang menyediakan kelas operator untuk menyertakaninteger
.Trifecta! Sebuah indeks GIST multicolumn fungsional :
Gunakan operator "berisi rentang"
@>
dalam kueri Anda sekarang:Indeks SP-GiST di Postgres 9.3+
Sebuah SP-GIST indeks mungkin lebih cepat untuk jenis query - kecuali bahwa, mengutip manual :
Masih benar di Postgres 12.
Anda harus menggabungkan
spgist
indeks hanya(tsrange(...))
denganbtree
indeks kedua aktif(id_phi)
. Dengan tambahan overhead, saya tidak yakin ini bisa bersaing.Jawaban terkait dengan tolok ukur hanya untuk satu
tsrange
kolom:sumber
Jawaban Erwin sudah komprehensif, namun:
Jenis rentang untuk cap waktu tersedia dalam PostgreSQL 9.1 dengan ekstensi temporal dari Jeff Davis: https://github.com/jeff-davis/PostgreSQL-Temporal
Catatan: memiliki fitur terbatas (menggunakan Timestamptz, dan Anda hanya dapat memiliki gaya '[)' tumpang tindih afaik). Juga, ada banyak alasan bagus lainnya untuk meningkatkan ke PostgreSQL 9.2.
sumber
Anda dapat mencoba membuat indeks multikolom dalam urutan yang berbeda:
Saya memposting sekali pertanyaan serupa juga terkait dengan pemesanan indeks pada indeks multicolumn. Kuncinya adalah mencoba menggunakan terlebih dahulu kondisi yang paling ketat untuk mengurangi ruang pencarian.
Sunting : Kesalahan saya. Sekarang saya melihat bahwa Anda sudah menentukan indeks ini.
sumber
Bitmap Index Scan on idx_time_limits_phi_start_end
Saya berhasil meningkat dengan cepat (dari 1 detik ke 70 ms)
Saya memiliki tabel dengan agregasi banyak pengukuran dan banyak level (
l
kolom) (30s, 1m, 1h, dll) ada dua kolom rentang terikat:$s
untuk awal dan$e
akhir.Saya membuat dua indeks multikolom: satu untuk memulai dan satu untuk akhir.
Saya menyesuaikan kueri pemilihan: pilih rentang di mana batas awal mereka berada dalam kisaran yang diberikan. Selain itu pilih rentang di mana ujungnya terikat dalam kisaran yang diberikan.
Jelaskan menunjukkan dua aliran baris menggunakan indeks kami secara efisien.
Indeks:
Pilih kueri:
Menjelaskan:
Kuncinya adalah bahwa node rencana Anda hanya berisi baris yang diinginkan. Sebelumnya kami mendapat ribuan baris dalam node rencana karena dipilih
all points from some point in time to the very end
, kemudian node berikutnya menghapus baris yang tidak perlu.sumber