Kami menambahkan dua indeks pg_trgm ke tabel, untuk mengaktifkan pencarian fuzzy baik dengan alamat email atau nama, karena kami perlu menemukan pengguna berdasarkan nama, atau alamat email yang salah eja saat mendaftar (mis. "@ Gmail.con"). ANALYZE
dijalankan setelah pembuatan indeks.
Namun, melakukan pencarian peringkat pada salah satu indeks ini sangat lambat dalam sebagian besar kasus. yaitu dengan peningkatan batas waktu, permintaan mungkin kembali dalam 60 detik, pada kesempatan yang sangat jarang secepat 15 detik, tetapi biasanya permintaan akan habis waktu.
pg_trgm.similarity_threshold
adalah nilai default dari 0.3
, tetapi menabrak ini 0.8
tampaknya tidak membuat perbedaan.
Tabel khusus ini memiliki lebih dari 25 juta baris, dan terus-menerus ditanyai, diperbarui, dan dimasukkan ke dalam (waktu rata-rata untuk masing-masing adalah di bawah 2ms). Penyiapannya adalah PostgreSQL 9.6.6 yang berjalan pada instance RDS db.m4.large dengan penyimpanan SSD tujuan umum, dan parameter default lebih banyak atau kurang. Ekstensi pg_trgm adalah versi 1.3.
Pertanyaan:
SELECT * FROM users WHERE email % 'chris@example.com' ORDER BY email <-> 'chris@example.com' LIMIT 10;
SELECT * FROM users WHERE (first_name || ' ' || last_name) % 'chris orr' ORDER BY (first_name || ' ' || last_name) <-> 'chris orr' LIMIT 10;
Kueri ini tidak perlu dijalankan sangat sering (puluhan kali sehari), tetapi harus didasarkan pada kondisi tabel saat ini, dan idealnya kembali dalam waktu sekitar 10 detik.
Skema:
=> \d+ users
Table "public.users"
Column | Type | Collation | Nullable | Default | Storage
-------------------+-----------------------------+-----------+----------+---------+----------
id | uuid | | not null | | plain
email | citext | | not null | | extended
email_is_verified | boolean | | not null | | plain
first_name | text | | not null | | extended
last_name | text | | not null | | extended
created_at | timestamp without time zone | | | now() | plain
updated_at | timestamp without time zone | | | now() | plain
… | boolean | | not null | false | plain
… | character varying(60) | | | | extended
… | character varying(6) | | | | extended
… | character varying(6) | | | | extended
… | boolean | | | | plain
Indexes:
"users_pkey" PRIMARY KEY, btree (id)
"users_email_key" UNIQUE, btree (email)
"users_search_email_idx" gist (email gist_trgm_ops)
"users_search_name_idx" gist (((first_name || ' '::text) || last_name) gist_trgm_ops)
"users_updated_at_idx" btree (updated_at)
Triggers:
update_users BEFORE UPDATE ON users FOR EACH ROW EXECUTE PROCEDURE update_modified_column()
Options: autovacuum_analyze_scale_factor=0.01, autovacuum_vacuum_scale_factor=0.05
(Saya sadar bahwa kita harus mungkin juga menambahkan unaccent()
untuk users_search_name_idx
dan query nama ...)
Menjelaskan:
EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM users WHERE (first_name || ' ' || last_name) % 'chris orr' ORDER BY (first_name || ' ' || last_name) <-> 'chris orr' LIMIT 10;
:
Limit (cost=0.42..40.28 rows=10 width=152) (actual time=58671.973..58676.193 rows=10 loops=1)
Buffers: shared hit=66227 read=231821
-> Index Scan using users_search_name_idx on users (cost=0.42..100264.13 rows=25153 width=152) (actual time=58671.970..58676.180 rows=10 loops=1)
Index Cond: (((first_name || ' '::text) || last_name) % 'chris orr'::text)
Order By: (((first_name || ' '::text) || last_name) <-> 'chris orr'::text"
Buffers: shared hit=66227 read=231821
Planning time: 0.125 ms
Execution time: 58676.265 ms
Pencarian email lebih cenderung ke waktu daripada pencarian nama, tetapi itu mungkin karena alamat email sangat mirip (misalnya banyak alamat @ gmail.com).
EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM users WHERE email % 'chris@example.com' ORDER BY email <-> 'chris@example.com' LIMIT 10;
:
Limit (cost=0.42..40.43 rows=10 width=152) (actual time=58851.719..62181.128 rows=10 loops=1)
Buffers: shared hit=83 read=428918
-> Index Scan using users_search_email_idx on users (cost=0.42..100646.36 rows=25153 width=152) (actual time=58851.716..62181.113 rows=10 loops=1)
Index Cond: ((email)::text % 'chris@example.com'::text)
Order By: ((email)::text <-> 'chris@example.com'::text)
Buffers: shared hit=83 read=428918
Planning time: 0.100 ms
Execution time: 62181.186 ms
Apa yang bisa menjadi alasan untuk waktu permintaan yang lambat? Ada hubungannya dengan jumlah buffer yang dibaca? Saya tidak dapat menemukan banyak informasi tentang cara mengoptimalkan jenis permintaan ini, dan pertanyaannya sangat mirip dengan yang ada dalam dokumentasi pg_trgm.
Apakah ini sesuatu yang bisa kami optimalkan, atau terapkan lebih baik di Postgres, atau ingin sesuatu seperti Elasticsearch lebih cocok untuk kasus penggunaan khusus ini?
<->
operator yang menggunakan indeks?
pg_trgm
setidaknya 1,3? Anda dapat memeriksa dengan "\ dx" dipsql
.