做 GIS 开发第七年,
我最怕客户半夜打电话:
“系统崩了,地图加载转圈转了一分钟!”
别急着重启服务,
先看看你的 SQL 查询语句。
很多兄弟还在用这种写法:
SELECT * FROM locations
WHERE SQRT(POWER(lon - @lon, 2) + POWER(lat - @lat, 2)) < 0.01
这种纯数学计算的写法,
在数据量小的时侯还行,
一旦表里进了百万级数据,
数据库就会全表扫描。
CPU 直接飙到 100%,
服务器风扇呼呼响,
用户体验更是灾难级。
我上个月帮一家物流公司优化,
他们有个车辆轨迹表,
大概 300 万条记录。
每次查附近 5 公里的车辆,
响应时间要 8 秒以上。
客户急得拍桌子,
说再这样要扣款了。
我检查了执行计划,
果然,没有用到索引。
这时候,
sqlserver 数据库 geo 经纬度 的正确打开方式,
就是引入空间数据类型。
把原来的 float 类型字段,
改成 geography 类型。
虽然存储空间稍微大一点,
但查询速度是天壤之别。
第一步,创建空间索引。
这一步最关键,
很多人建了索引但没生效,
是因为参数没配对。
CREATE SPATIAL INDEX IX_Location_Spatial
ON Locations(GeoColumn)
USING GEOGRAPHY_AUTO_GRID;
注意这个 GRID,
默认是中等精度,
如果你的数据分布很不均匀,
比如集中在某个小区域,
建议调整级别参数。
我之前的案例里,
调整 GRID 后,
查询响应从 8 秒降到了 0.2 秒。
这就是 40 倍的提升。
第二步,使用 STDistance 或 STIntersects。
别再自己算勾股定理了,
SQL Server 内置的空间函数,
底层用的是球面几何算法,
比你自己写的代码准得多,
也快得多。
比如查附近的人:
SELECT TOP 10 Name, GeoColumn.STDistance(@point)
FROM Locations
WHERE GeoColumn.STDistance(@point) < 5000
ORDER BY GeoColumn.STDistance(@point);
这里有个坑,
就是坐标系的匹配。
如果你用的是 WGS84,
一定要确保数据库里的 SRID 是 4326。
不然算出来的距离全是错的,
偏差可能高达几公里。
我见过一个项目,
因为 SRID 搞错,
导航导到了隔壁省,
用户投诉电话被打爆。
这种低级错误,
真的不能再犯了。
另外,
关于 sqlserver 数据库 geo 经纬度 的存储,
如果你只需要做简单的范围过滤,
比如查某个矩形区域内的数据,
可以用 B-Tree 索引配合经纬度字段。
但如果要做复杂的距离计算,
或者多边形相交判断,
空间索引是必须的。
不要为了省那点存储空间,
牺牲查询性能。
硬盘现在很便宜,
但开发人员的头发很贵。
优化完索引后,
记得更新统计信息。
DBCC UPDATEUSAGE(0);
GO
STATISTICS 不准,
优化器就会选错执行计划,
再好的索引也白搭。
最后,
给大家一个真心话。
在做地图相关业务时,
前端展示和后端存储要分开。
后端只存高精度的坐标,
前端做适当的简化。
不要把所有数据都推给前端渲染,
那样浏览器会卡死。
sqlserver 数据库 geo 经纬度 的应用,
核心在于“空间索引”和“正确类型”。
只要这两点做对了,
百万级数据秒级响应不是梦。
希望这篇干货,
能帮你在深夜少接几个报警电话。
毕竟,
睡个好觉,
比什么都重要。