Nếu bạn thêm các trường của trình trợ giúp vào bảng tọa độ, bạn có thể cải thiện thời gian phản hồi của truy vấn.
Như thế này:
CREATE TABLE `Coordinates` (
`id` INT(10) UNSIGNED NOT NULL COMMENT 'id for the object',
`type` TINYINT(4) UNSIGNED NOT NULL DEFAULT '0' COMMENT 'type',
`sin_lat` FLOAT NOT NULL COMMENT 'sin(lat) in radians',
`cos_cos` FLOAT NOT NULL COMMENT 'cos(lat)*cos(lon) in radians',
`cos_sin` FLOAT NOT NULL COMMENT 'cos(lat)*sin(lon) in radians',
`lat` FLOAT NOT NULL COMMENT 'latitude in degrees',
`lon` FLOAT NOT NULL COMMENT 'longitude in degrees',
INDEX `lat_lon_idx` (`lat`, `lon`)
)
Nếu bạn đang sử dụng TokuDB, bạn sẽ có hiệu suất cao hơn nữa nếu bạn thêm các chỉ mục phân cụm trên một trong các vị từ, ví dụ như thế này:
alter table Coordinates add clustering index c_lat(lat);
alter table Coordinates add clustering index c_lon(lon);
Bạn sẽ cần lat và lon cơ bản về độ cũng như sin (lat) tính bằng radian, cos (lat) * cos (lon) tính bằng radian và cos (lat) * sin (lon) tính bằng radian cho mỗi điểm. Sau đó, bạn tạo một hàm mysql, smth như thế này:
CREATE FUNCTION `geodistance`(`sin_lat1` FLOAT,
`cos_cos1` FLOAT, `cos_sin1` FLOAT,
`sin_lat2` FLOAT,
`cos_cos2` FLOAT, `cos_sin2` FLOAT)
RETURNS float
LANGUAGE SQL
DETERMINISTIC
CONTAINS SQL
SQL SECURITY INVOKER
BEGIN
RETURN acos(sin_lat1*sin_lat2 + cos_cos1*cos_cos2 + cos_sin1*cos_sin2);
END
Điều này cho bạn khoảng cách.
Đừng quên thêm chỉ mục vào lat / lon để quyền anh có thể giúp tìm kiếm thay vì làm chậm nó (chỉ mục đã được thêm vào trong truy vấn CREATE TABLE ở trên).
INDEX `lat_lon_idx` (`lat`, `lon`)
Đưa ra một bảng cũ chỉ có tọa độ lat / lon, bạn có thể thiết lập một tập lệnh để cập nhật nó như thế này: (php sử dụng meekrodb)
$users = DB::query('SELECT id,lat,lon FROM Old_Coordinates');
foreach ($users as $user)
{
$lat_rad = deg2rad($user['lat']);
$lon_rad = deg2rad($user['lon']);
DB::replace('Coordinates', array(
'object_id' => $user['id'],
'object_type' => 0,
'sin_lat' => sin($lat_rad),
'cos_cos' => cos($lat_rad)*cos($lon_rad),
'cos_sin' => cos($lat_rad)*sin($lon_rad),
'lat' => $user['lat'],
'lon' => $user['lon']
));
}
Sau đó, bạn tối ưu hóa truy vấn thực tế để chỉ thực hiện tính toán khoảng cách khi thực sự cần thiết, ví dụ bằng cách giới hạn vòng tròn (tốt, hình bầu dục) từ bên trong và bên ngoài. Vì thế, bạn sẽ cần tính toán trước một số số liệu cho chính truy vấn:
// assuming the search center coordinates are $lat and $lon in degrees
// and radius in km is given in $distance
$lat_rad = deg2rad($lat);
$lon_rad = deg2rad($lon);
$R = 6371; // earth's radius, km
$distance_rad = $distance/$R;
$distance_rad_plus = $distance_rad * 1.06; // ovality error for outer bounding box
$dist_deg_lat = rad2deg($distance_rad_plus); //outer bounding box
$dist_deg_lon = rad2deg($distance_rad_plus/cos(deg2rad($lat)));
$dist_deg_lat_small = rad2deg($distance_rad/sqrt(2)); //inner bounding box
$dist_deg_lon_small = rad2deg($distance_rad/cos(deg2rad($lat))/sqrt(2));
Với những sự chuẩn bị đó, truy vấn sẽ giống như thế này (php):
$neighbors = DB::query("SELECT id, type, lat, lon,
geodistance(sin_lat,cos_cos,cos_sin,%d,%d,%d) as distance
FROM Coordinates WHERE
lat BETWEEN %d AND %d AND lon BETWEEN %d AND %d
HAVING (lat BETWEEN %d AND %d AND lon BETWEEN %d AND %d) OR distance <= %d",
// center radian values: sin_lat, cos_cos, cos_sin
sin($lat_rad),cos($lat_rad)*cos($lon_rad),cos($lat_rad)*sin($lon_rad),
// min_lat, max_lat, min_lon, max_lon for the outside box
$lat-$dist_deg_lat,$lat+$dist_deg_lat,
$lon-$dist_deg_lon,$lon+$dist_deg_lon,
// min_lat, max_lat, min_lon, max_lon for the inside box
$lat-$dist_deg_lat_small,$lat+$dist_deg_lat_small,
$lon-$dist_deg_lon_small,$lon+$dist_deg_lon_small,
// distance in radians
$distance_rad);
GIẢI THÍCH cho truy vấn trên có thể nói rằng nó không sử dụng chỉ mục trừ khi có đủ kết quả để kích hoạt như vậy. Chỉ mục sẽ được sử dụng khi có đủ dữ liệu trong bảng tọa độ. Bạn có thể thêm FORCE INDEX (lat_lon_idx) vào CHỌN để làm cho nó sử dụng chỉ mục không liên quan đến kích thước bảng, do đó bạn có thể xác minh với EXPLAIN rằng nó đang hoạt động chính xác.
Với các mẫu mã ở trên, bạn sẽ có một triển khai tìm kiếm đối tượng hoạt động và có thể mở rộng theo khoảng cách với sai số tối thiểu.