Chúng tôi đã thêm hai chỉ số pg_trgm vào một bảng để cho phép tìm kiếm mờ theo địa chỉ email hoặc tên, vì chúng tôi cần tìm người dùng theo tên hoặc địa chỉ email đã bị sai chính tả trong quá trình đăng ký (ví dụ: "@ gmail.con"). ANALYZE
được chạy sau khi tạo chỉ mục.
Tuy nhiên, thực hiện tìm kiếm được xếp hạng trên một trong hai chỉ số này là rất chậm trong phần lớn các trường hợp. tức là với thời gian chờ tăng lên, một truy vấn có thể trở lại sau 60 giây, trong những trường hợp rất hiếm có nhanh như 15 giây, nhưng thông thường các truy vấn sẽ hết thời gian.
pg_trgm.similarity_threshold
là giá trị mặc định của 0.3
, nhưng việc tăng giá trị này 0.8
dường như không tạo ra sự khác biệt.
Bảng đặc biệt này có hơn 25 triệu hàng và liên tục được truy vấn, cập nhật và chèn vào (thời gian trung bình cho mỗi hàng là dưới 2ms). Thiết lập là PostgreSQL 9.6.6 chạy trên phiên bản RDS db.m4.large với bộ lưu trữ SSD cho mục đích chung và các tham số mặc định nhiều hơn hoặc ít hơn. Phần mở rộng pg_trgm là phiên bản 1.3.
Truy vấn:
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;
Các truy vấn này không cần phải chạy thường xuyên (hàng chục lần một ngày), nhưng chúng nên được dựa trên trạng thái bảng hiện tại và lý tưởng trở lại trong vòng khoảng 10 giây.
Lược đồ:
=> \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
(Tôi biết rằng có lẽ chúng ta cũng nên thêm unaccent()
vào users_search_name_idx
và truy vấn tên tên)
Giải thích:
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
Tìm kiếm email có nhiều thời gian hơn tìm kiếm tên, nhưng điều đó có lẽ là do các địa chỉ email rất giống nhau (ví dụ: rất nhiều địa chỉ @ 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
Điều gì có thể là một lý do cho thời gian truy vấn chậm? Một cái gì đó để làm với số lượng bộ đệm được đọc? Tôi không thể tìm thấy nhiều thông tin về việc tối ưu hóa loại truy vấn cụ thể này và các truy vấn rất giống với các truy vấn trong tài liệu pg_trgm.
Đây có phải là thứ gì đó mà chúng ta có thể tối ưu hóa, hoặc triển khai tốt hơn trong Postgres, hoặc sẽ tìm kiếm thứ gì đó như Elaticsearch phù hợp hơn cho trường hợp sử dụng cụ thể này?
<->
toán tử sử dụng một chỉ mục?
pg_trgm
ít nhất 1.3? Bạn có thể kiểm tra với "\ dx" trongpsql
.