0

GIẢI BÀI TOÁN TÌM ĐIỂM ĐÓN GẦN NHẤT: BÀI TOÁN TÌM KIẾM KHÔNG GIAN VỚI BỘ LỌC ĐIỀU KIỆN

bài toán tra cứu khoảng cách địa lý trong cache, chúng ta sẽ bước lên một nấc thang thực chiến cao cấp hơn trong các hệ thống định vị, điều phối xe hoặc logistics: Tìm điểm đón (pickup location) gần nhất so với một điểm gốc cho trước, đi kèm với hai điều kiện khắt khe: loại trừ các điểm đen (blacklisted/excluded) và đảm bảo tính toàn vẹn (tồn tại thực tế trong hệ thống).

Dưới đây là bài viết mổ xẻ chi tiết cách giải quyết bài toán này từ tư duy thuật toán cho đến chiến lược tối ưu hóa hiệu năng.

1. Bản Chất Và Thách Thức Của Bài Toán (The Challenge)

Hãy tưởng tượng hệ thống đặt xe của bạn nhận được một tọa độ của khách hàng (hoặc một điểm đón gốc P0P_0). Hệ thống cần quét qua hàng ngàn điểm đón lân cận để tìm ra điểm đón PnearestP_{nearest} thỏa mãn các tiêu chí:

  1. Khoảng cách ngắn nhất (Min Distance): Gần điểm gốc P0P_0 nhất theo đường chim bay hoặc khoảng cách đường đi thực tế.

  2. Bộ lọc loại trừ (Exclusion Filter): Bỏ qua ngay lập tức các điểm đón đang bảo trì, quá tải, hoặc nằm trong danh sách đen (exclude_ids) mà khách hàng không muốn đến.

  3. Kiểm tra tồn tại (Integrity Check): Điểm tìm được bắt buộc phải ở trạng thái hoạt động (is_active = true) trong cơ sở dữ liệu để tránh việc điều hướng khách hàng đến một trạm ảo hoặc đã đóng cửa.

Nếu giải quyết bằng cách lấy toàn bộ điểm ra, dùng vòng lực duyệt qua từng điểm rồi tính khoảng cách (Brute Force), độ phức tạp thuật toán sẽ là O(N)O(N) — thảm họa khi số lượng điểm đón lên đến hàng trăm nghìn.

2. Tư Duy Thiết Kế Giải Pháp (The Architecture)

Để giải quyết bài toán này với độ trễ thấp (low-latency), các kỹ sư hệ thống thường kết hợp hai vũ khí: Spatial Indexing (Chỉ mục không gian) trên Database và In-memory Filtering.

Cách tiếp cận 1: Sử dụng Spatial Extensions (GIS) trên Database (PostGIS / MySQL Spatial)

Nếu các điểm đón lưu trực tiếp trong cơ sở dữ liệu hỗ trợ không gian địa lý (như PostgreSQL với PostGIS), chúng ta có thể tận dụng kiểu dữ liệu GEOGRAPHY và hàm ST_DWithin hoặc ST_Distance kết hợp với mệnh đề NOT IN.

Ví dụ câu lệnh truy vấn tối ưu hóa bằng Index không gian:

SQL

SELECT id, name, 
       ST_Distance(location, ST_MakePoint(:lng, :lat)::geography) AS distance
FROM pickup_locations
WHERE is_active = true
  AND id NOT IN (:excluded_ids) -- Loại trừ các điểm không mong muốn
ORDER BY location <-> ST_MakePoint(:lng, :lat)::geography -- Sử dụng KNN Operator siêu tốc của PostGIS
LIMIT 1;

Ưu điểm: Tận dụng được R-Tree Index của database, không bị quét toàn bộ bảng (Full Table Scan).

Cách tiếp cận 2: Sử dụng Redis GEO (Tốc độ ánh sáng cho hệ thống Real-time)

Đối với các hệ thống yêu cầu tốc độ phản hồi tính bằng mili-giây (như Grab, Gojek), việc tra cứu tọa độ trên Database đôi khi vẫn còn chậm. Lúc này, ta dùng Redis GEO:

  1. Đưa toàn bộ danh sách điểm đón vào một Redis GEO Key (ví dụ: pickup:geo:city_hcm).

  2. Khi có yêu cầu tìm điểm gần nhất, sử dụng lệnh GEORADIUS hoặc GEOSEARCH với bán kính cố định (ví dụ 5km) từ điểm gốc P0P_0.

  3. Redis sẽ trả về danh sách các điểm trong bán kính đó sắp xếp theo thứ tự từ gần đến xa cực kỳ nhanh.

  4. Ứng dụng (Backend) nhận danh sách ID, tiến hành loại trừ các ID nằm trong danh sách excluded_ids, và kiểm tra trạng thái tồn tại cuối cùng trong bộ nhớ Cache/DB rồi bốc ra kết quả đầu tiên thỏa mãn.

3. Hiện Thực Hóa Luồng Xử Lý (Logic Flow)

Dưới đây là sơ đồ tư duy logic mà Backend Service sẽ thực thi khi nhận request:

Plaintext

[Nhận Request: Tọa độ gốc P0, Danh sách exclude_ids]
       │
       ▼
[Truy vấn Redis GEO/Spatial DB tìm top N điểm gần nhất]
       │
       ▼
[Lọc qua danh sách exclude_ids (Loại bỏ các điểm không muốn)]
       │
       ▼
[Kiểm tra tính tồn tại & trạng thái Active trong hệ thống]
       │
       ├──► Nếu hợp lệ ──► [Trả về điểm đón P_nearest cho Client]
       │
       └──► Nếu không hợp lệ (Đã bị vô hiệu hóa) ──► [Bỏ qua, xét điểm tiếp theo trong danh sách top N]

4. Lưu Ý Kỹ Thuật Khi Vận Hành (Engineering Best Practices)

  • Xử lý trường hợp không tìm thấy điểm nào: Nếu trong bán kính tìm kiếm, toàn bộ các điểm gần nhất đều bị nằm trong danh sách exclude_ids, hệ thống phải có cơ chế mở rộng bán kính tìm kiếm tự động (Fallback Radius Expansion) thay vì trả về lỗi trống.

  • Cache trạng thái Active: Việc kiểm tra xem một điểm đón có còn tồn tại hay không nên được cache sẵn trên RAM (Redis Hash) thay vì cứ mỗi lần tìm lại phải SELECT vào bảng chính của Database.

💡 Lời Kết

Giải bài toán tìm điểm đón gần nhất có kèm bộ lọc không đơn thuần là một thuật toán hình học thuần túy, mà là sự phối hợp nhịp nhàng giữa Cấu trúc dữ liệu không gian (Spatial Index)Tư duy lọc dữ liệu thông minh. Việc thiết kế đúng tầng lưu trữ và thuật toán sẽ giúp hệ thống chịu tải mượt mà ngay cả trong giờ cao điểm.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí