Merancang Kolom tsvector dan Indeks GIN
Hitung terlebih dahulu dan indekskan dokumen pencarian agar kueri teks lengkap tetap berjalan dalam waktu kurang dari satu milidetik pada skala besar.
Merancang Kolom tsvector dan Indeks GIN adalah pelajaran PostgreSQL Performance & Query Optimization gratis di CoddyKit. Ini adalah pelajaran 1 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar PostgreSQL Performance & Query Optimization, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus PostgreSQL Performance & Query Optimization mencakup 4 pelajaran total.
Mengapa tsvector yang Diprata-hitung
Pencarian teks lengkap PostgreSQL membandingkan tsvector (dokumen yang dapat dicari) dengan tsquery (istilah pencarian). Pendekatan naif memanggil to_tsvector() pada kolom teks mentah saat kueri dijalankan.
Pendekatan tersebut berhasil, tetapi pada skala besar memiliki dua biaya:
- CPU per baris: mengurai dan melakukan stemming teks pada setiap pemindaian membutuhkan biaya besar.
- Tidak ada indeks yang dapat digunakan kecuali ekspresi indeks sama persis dengan ekspresi kueri.
Solusinya adalah menghitung terlebih dahulu dokumen sekali dan menyimpannya, lalu membuat indeks untuknya. Pelajaran ini menunjukkan cara merancang kolom tersebut dan indeks GIN agar kueri teks lengkap tetap berjalan dalam waktu di bawah satu milidetik, bahkan pada jutaan baris.
Kueri Naif (dan Jebakannya)
Berikut pola yang biasanya digunakan sebagai awal: simpan teks biasa, lalu buat tsvector secara langsung saat dibutuhkan.
Kueri di bawah ini bekerja dengan benar, tetapi pada tabel besar kueri tersebut memicu pemindaian berurutan dan mengurai ulang body untuk setiap baris. Setiap pemanggilan to_tsvector melakukan stemming dan normalisasi terhadap seluruh teks dokumen.
Tujuan pelajaran ini adalah menghilangkan pekerjaan per baris tersebut sepenuhnya.
SELECT id, title
FROM articles
WHERE to_tsvector('english', body) @@ to_tsquery('english', 'index & scan');Opsi A: Kolom Terbuat yang Disimpan
Desain modern yang paling bersih (PostgreSQL 12+) adalah kolom terbuat yang disimpan. PostgreSQL menghitung tsvector secara otomatis setiap kali baris berubah, sehingga dokumen selalu konsisten dengan teks sumber.
Ingat dua aturan berikut:
- Ekspresi pembentukan harus IMMUTABLE, karena itu regconfig diberikan sebagai literal (
'english'), bukan dengan mengandalkan pengaturan sesi. - Gunakan
coalesce()agar kolom NULL tidak membuat seluruh dokumen menjadi NULL.
ALTER TABLE articles
ADD COLUMN search_doc tsvector
GENERATED ALWAYS AS (
to_tsvector('english', coalesce(title, '') || ' ' || coalesce(body, ''))
) STORED;Memberi Bobot pada Bidang dengan setweight
Tidak semua bidang memiliki tingkat kepentingan yang sama. Kecocokan pada judul biasanya lebih penting daripada kecocokan jauh di dalam isi. setweight() menandai leksem dengan label A, B, C, atau D (A yang tertinggi).
Label ini nantinya memungkinkan ts_rank memberikan skor kecocokan judul lebih tinggi daripada kecocokan isi. Masukkan pembobotan tersebut ke dalam kolom terbuat agar dihitung sekali, bukan saat kueri dijalankan.
ALTER TABLE articles
ADD COLUMN search_doc tsvector
GENERATED ALWAYS AS (
setweight(to_tsvector('english', coalesce(title, '')), 'A') ||
setweight(to_tsvector('english', coalesce(body, '')), 'B')
) STORED;Membuat Indeks GIN
tsvector yang disimpan tetap tidak berguna tanpa indeks. Jenis indeks yang tepat untuk pencarian teks lengkap adalah GIN (Generalized Inverted Index). GIN menyimpan satu entri untuk setiap leksem berbeda yang menunjuk ke baris-baris yang memuatnya, persis seperti yang diperlukan oleh pencocokan @@.
Karena kolom tersebut sudah menyimpan tsvector, indeksnya adalah indeks kolom biasa dan tidak memerlukan ekspresi:
CREATE INDEX idx_articles_search_doc
ON articles
USING GIN (search_doc);Membuat Kueri pada Kolom Terindeks
Sekarang kueri merujuk langsung ke kolom yang disimpan. Perencana dapat menggunakan indeks GIN karena ekspresi dalam klausa WHERE (search_doc) sama persis dengan ekspresi yang diindeks.
Tidak ada to_tsvector() per baris dan tidak ada pemindaian berurutan. Jalankan EXPLAIN ANALYZE dan Anda seharusnya melihat Bitmap Index Scan pada idx_articles_search_doc.
SELECT id, title
FROM articles
WHERE search_doc @@ to_tsquery('english', 'index & scan')
ORDER BY ts_rank(search_doc, to_tsquery('english', 'index & scan')) DESC
LIMIT 20;GIN vs GiST: Memilih yang Tepat
PostgreSQL mendukung dua jenis indeks untuk tsvector. Pilihlah dengan pertimbangan:
- GIN: pencarian lebih cepat, pilihan bawaan untuk pencarian. Ukurannya sedikit lebih besar dan pembuatannya/pembaruan indeksnya lebih lambat. Terbaik ketika operasi baca lebih dominan.
- GiST: lebih kecil dan biaya pembaruannya lebih rendah, tetapi bersifat lossy, sehingga indeks memeriksa ulang baris kandidat dan lebih lambat untuk kueri. Berguna untuk data yang sangat sering ditulis atau terus berubah.
Untuk sebagian besar beban kerja pencarian, ketika Anda melakukan kueri jauh lebih sering daripada menulis, GIN lebih unggul. Gunakan GiST hanya ketika biaya pembaruan indeks menjadi hambatan utama Anda.
Penyetelan GIN: fastupdate dan gin_pending_list_limit
Indeks GIN menggunakan daftar tertunda untuk mengelompokkan penyisipan (fastupdate = on secara bawaan). Hal ini mempercepat penulisan, tetapi daftar tertunda yang besar memperlambat pembacaan karena kueri harus memindai daftar tersebut selain indeks utama.
Untuk tabel pencarian yang lebih banyak dibaca, Anda dapat menyetel atau menonaktifkan perilaku ini. Menonaktifkan fastupdate membuat setiap penyisipan melakukan lebih banyak pekerjaan, tetapi menjaga kueri tetap cepat secara konsisten.
ALTER INDEX idx_articles_search_doc
SET (fastupdate = off);
-- Or cap the pending list size instead of disabling it:
ALTER INDEX idx_articles_search_doc
SET (gin_pending_list_limit = 4096);Pola Sebelum Versi 12: Kolom yang Dipelihara Pemicu
Kolom yang dihasilkan hadir di PostgreSQL 12. Pada versi yang lebih lama, atau ketika Anda memerlukan logika yang bukan IMMUTABLE, Anda memelihara tsvector dengan pemicu.
Pembantu klasiknya adalah tsvector_update_trigger, yang mengisi kolom tujuan dari kolom sumber yang disebutkan. Perhatikan keterbatasannya: pembantu ini menggunakan satu bobot tetap dan satu konfigurasi tetap, sehingga untuk pembobotan per bidang Anda perlu menulis fungsi pemicu BEFORE khusus.
ALTER TABLE articles ADD COLUMN search_doc tsvector;
CREATE TRIGGER trg_articles_search_doc
BEFORE INSERT OR UPDATE ON articles
FOR EACH ROW
EXECUTE FUNCTION
tsvector_update_trigger(search_doc, 'pg_catalog.english', title, body);Mengisi Baris yang Sudah Ada
Pemicu hanya berjalan pada penyisipan dan pembaruan berikutnya. Baris yang sudah ada tetap memiliki search_doc bernilai NULL sampai Anda mengisinya.
Untuk kolom tersimpan yang dihasilkan, PostgreSQL mengisi data secara otomatis saat Anda menambahkan kolom tersebut. Untuk pola pemicu, jalankan UPDATE satu kali. Pada tabel yang sangat besar, lakukan dalam beberapa kelompok berdasarkan rentang kunci utama agar Anda tidak mengunci seluruh tabel atau membuat satu transaksi raksasa membengkak.
UPDATE articles
SET search_doc =
setweight(to_tsvector('english', coalesce(title, '')), 'A') ||
setweight(to_tsvector('english', coalesce(body, '')), 'B')
WHERE id BETWEEN 1 AND 100000;Memastikan Indeks Benar-Benar Digunakan
Selalu pastikan perencana menggunakan indeks GIN Anda, bukan beralih ke pemindaian berurutan. Alasan umum indeks tidak digunakan: ekspresi kueri tidak cocok dengan ekspresi yang diindeks, tabel sangat kecil, atau statistik sudah usang.
Jalankan EXPLAIN (ANALYZE, BUFFERS) dan cari Bitmap Index Scan pada nama indeks Anda. Jika Anda melihat Seq Scan dengan Filter, berarti indeks tidak digunakan; perbaiki kecocokan ekspresinya atau jalankan ANALYZE.
EXPLAIN (ANALYZE, BUFFERS)
SELECT id
FROM articles
WHERE search_doc @@ to_tsquery('english', 'gin & index');Pemeriksaan Singkat
Anda memiliki tabel articles yang besar dan lebih banyak dibaca. Anda menginginkan kueri teks lengkap yang mencocokkan judul dengan bobot lebih tinggi daripada teks isi, tetap di bawah satu milidetik, dan tidak pernah mengurai ulang teks saat kueri dijalankan. Desain mana yang paling memenuhi ketiga tujuan tersebut?
Ringkasan
Anda telah merancang kolom pencarian teks lengkap berperforma tinggi dari awal hingga akhir:
- Prakomputasikan dokumen ke dalam kolom
tsvectorSTORED yang dihasilkan agar teks diurai sekali, bukan pada setiap kueri. - Beri bobot pada bidang dengan
setweight()(A untuk judul, B untuk isi) agarts_rankdapat menilai kecocokan secara bermakna. - Indeks kolom tersebut dengan GIN, yaitu indeks terbalik yang dioptimalkan untuk pembacaan dan pencocokan
@@; pilih GiST hanya untuk data yang sangat sering ditulis dan berubah-ubah. - Sesuaikan penulisan melalui
fastupdatedangin_pending_list_limitketika daftar tertunda memperlambat pembacaan. - Pelihara tabel sebelum versi 12 dengan pemicu dan isi baris yang sudah ada dalam beberapa kelompok.
- Verifikasi dengan
EXPLAIN (ANALYZE, BUFFERS)bahwa Anda mendapatkan Bitmap Index Scan, bukan Seq Scan.
Belajar SQL dengan tutor AI — gratis
Tulis dan jalankan kode asli di browser kamu, dapatkan bantuan instan dari tutor AI 24/7, dan lanjutkan di mana kamu tinggalkan di web atau aplikasi.
- Kursus
- 22
- Pelajaran
- 88
Pertanyaan yang Sering Diajukan
Apakah pelajaran “Merancang Kolom tsvector dan Indeks GIN” gratis?
Ya — teks lengkap “Merancang Kolom tsvector dan Indeks GIN” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus PostgreSQL Performance & Query Optimization, upgrade ke CoddyKit PRO. Kursus PostgreSQL Performance & Query Optimization mencakup 4 pelajaran total.
Apa yang akan aku pelajari di “Merancang Kolom tsvector dan Indeks GIN”?
Hitung terlebih dahulu dan indekskan dokumen pencarian agar kueri teks lengkap tetap berjalan dalam waktu kurang dari satu milidetik pada skala besar. Kamu berlatih PostgreSQL Performance & Query Optimization dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.
Apakah aku perlu pengalaman untuk memulai PostgreSQL Performance & Query Optimization?
Tidak diperlukan pengalaman sebelumnya. PostgreSQL Performance & Query Optimization di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 1 dari 4.
Berapa lama pelajaran “Merancang Kolom tsvector dan Indeks GIN” memakan waktu?
Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.
Bisakah aku menulis dan menjalankan kode dalam pelajaran PostgreSQL Performance & Query Optimization ini?
Ya. Setiap pelajaran PostgreSQL Performance & Query Optimization menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.
Semua pelajaran dalam kursus ini
- Merancang Kolom tsvector dan Indeks GIN
- Penentuan Peringkat dan Penyesuaian Relevansi dengan ts_rank
- Pencocokan Fuzzy dengan Kemiripan pg_trgm
- Menggabungkan Penyaring dengan Predikat Pencarian