Hội Chứng "Cái Gì Cũng LIKE": Khi Exact Match Bị Lãng Quên & Nỗi Oan Của Database
Câu chuyện xảy ra vào một ngày sale lớn. Bộ phận vận hành (Ops) hốt hoảng báo lỗi: "Em tìm mã đơn hàng ORD-123 để hủy, mà hệ thống nó trả về cả ngàn cái đơn từ ORD-1230 đến ORD-1239. Cuối cùng em lỡ tay hủy nhầm đơn của khách khác!"
Mình lôi source code ra check API tìm kiếm đơn hàng, và đập vào mắt là dòng này:
SELECT * FROM orders WHERE order_code LIKE '%?%' (với tham số truyền vào là ORD-123).
Vâng, cậu em code luồng này mắc một hội chứng rất phổ biến ở các dev mới vào nghề: Hội chứng "Cái gì cũng LIKE". Nghĩ rằng người dùng luôn muốn tìm kiếm tương đối (Fuzzy Search), nên tự động biến mọi ô tìm kiếm thành Full-text Search nửa mùa. Hậu quả là tốc độ thì rùa bò, mà kết quả thì sai bét.
Hôm nay, dưới góc nhìn của một Vibe Coder, chúng ta sẽ trả lại sự công bằng cho một kỹ thuật tối thượng, nhanh gọn và chính xác nhất: Exact Match (Tìm kiếm chính xác).
1. Exact Match là gì? Đừng khinh thường sự đơn giản
Exact Match đơn giản là sự khớp nhau 100% giữa từ khóa tìm kiếm và dữ liệu trong hệ thống. Không thừa một dấu cách, không thiếu một ký tự.
Trong SQL, nó chính là toán tử dấu bằng =:
SELECT * FROM orders WHERE order_code = 'ORD-123'
Nhiều người nghĩ Exact Match quá cứng nhắc, bắt user gõ đúng từng chữ thì UX tệ quá. Nhưng thực tế, không phải trường dữ liệu nào cũng cần tìm kiếm mờ.
- Tên người (Hoàng), Tiêu đề bài viết: Dùng LIKE hoặc Full-text search.
- Số điện thoại, Email, Mã đơn hàng, Mã định danh (CCCD/CMND), Trạng thái (Status = ACTIVE): BẮT BUỘC phải dùng Exact Match.
2. Sức mạnh ánh sáng của Exact Match và B-Tree Index
Lý do một Vibe Coder luôn ưu tiên Exact Match khi có thể, chính là vì hiệu năng (Performance).
Khi bạn dùng LIKE '%keyword%', Database của bạn khóc thét. Nó buộc phải đi quét từng dòng một từ trên xuống dưới (Full Table Scan) để xem có chuỗi nào chứa từ khóa đó không. Dù bạn có đánh Index (Chỉ mục) cho cột đó, cái Index cũng trở nên vô dụng vì có dấu % ở đầu.
Nhưng khi bạn dùng =, Database sẽ kích hoạt sức mạnh của B-Tree Index Nó hoạt động giống như việc bạn lật từ điển: Thay vì đọc từ trang 1 đến trang 1000, nó nhảy thẳng đến vần "O", tìm đúng chữ "ORD-123" và nhặt ra kết quả trong vòng chưa tới 1ms (mili-giây). Nhanh, dứt khoát, ít tốn CPU.
3. Cạm bẫy Exact Match trong Elasticsearch: term vs match
Nếu hệ thống của bạn lớn và chuyển sang dùng Elasticsearch (ES) để tối ưu tìm kiếm, khái niệm Exact Match lại trở thành một cái hố chôn vô số anh em dev.
Trong ES, nếu bạn muốn tìm Exact Match một mã sản phẩm iPhone-15-Pro, bạn không được dùng query match.
matchquery: ES sẽ tự động băm (analyze) chuỗiiPhone-15-Prothành 3 từ:iphone, 15, pro. Sau đó nó đi tìm những bản ghi chứa 1 trong 3 từ đó. Kết quả là bạn searchiPhone 15 Promà nó ra cả ốp lưng iPhone hoặc Macbook Pro.termquery (The Exact Match): Đây mới là chân ái! Khi bạn dùngterm, ES sẽ giữ nguyên cả cụmiPhone-15-Provà đi so sánh chuỗi tuyệt đối. Trúng thì lấy, trượt thì bỏ.
Bài học rút ra: Luôn khai báo kiểu dữ liệu keyword trong ES cho các cột cần Exact Match, và tuyệt đối chỉ dùng query term để gọi chúng.
4. Lời kết: Làm sạch dữ liệu trước khi Exact Match
Bạn có thể thắc mắc: "Lỡ khách hàng copy mã đơn hàng dính dư một dấu cách (space) ở cuối thì sao? Lúc đó Exact Match bằng = sẽ trượt mất!"
Đúng vậy. Đó là lý do Exact Match phải đi đôi với kỹ thuật Sanitize Input (đã nhắc ở bài trước). Trách nhiệm của hệ thống là tự động trim() (cắt khoảng trắng hai đầu), chuyển chữ hoa thành chữ thường toLowerCase() trước khi đưa vào Database để query. Đừng bắt user phải cẩn thận, hãy để hệ thống tự dọn rác, sau đó áp dụng Exact Match để lấy tốc độ tối đa!
Tư duy Vibe Coder: Chọn đúng công cụ cho đúng bài toán. Sự thanh lịch của kiến trúc nằm ở chỗ không làm phức tạp hóa những thứ vốn dĩ rất đơn giản.
Chủ đề tiếp theo: Lời Nguyền Phân Trang (Pagination) - Khi OFFSET Bóp Nghẹt Database 10 Triệu Bản Ghi
Ở bài này chúng ta đã tìm kiếm ra kết quả nhanh chóng. Nhưng giả sử kết quả trả về có 100.000 dòng. Chắc chắn bạn phải làm Phân trang (Pagination) để hiển thị.
Và đây là một thảm họa kinh điển khác: Khách hàng bấm sang "Trang số 5.000" trên website, và bụp... Database quá tải, CPU nhảy 100%. Lệnh LIMIT 20 OFFSET 100000 mà bạn được học trong trường đại học thực chất là một quả bom nổ chậm trên Production.
Làm sao để người dùng bấm đến trang một triệu mà API vẫn trả về trong 10ms? Ở bài viết tới, mình sẽ đập bỏ tư duy phân trang truyền thống và giới thiệu vũ khí tối thượng mang tên Keyset Pagination (Cursor-based Pagination). Anh em nhớ follow để không bỏ lỡ "ma thuật" này nhé!
All Rights Reserved