如何获取最近的餐厅们

来源:互联网 发布:linux vnc 安装 编辑:程序博客网 时间:2024/05/02 00:32

下班前,老大给了道题目,是最近的一个LBS需求。

要获取你当前位置最近的一些餐厅。

已有的一些:数据库里大概存着一百万个餐厅的详细记录,包括id,name和经纬度等。

移动端给你返回他当前的经纬度,让你找出附近的餐厅。


大概试了下,如果直接查数据库,其实也不慢,在经纬度都加索引的情况下,模糊查询(比如客户端返回的经纬度是38.1024和62.2048),既然是附近你就按38.102-------38.103 和62.204------62.205查,只要经纬度符合这俩区间就算是附近的餐厅。

SQL:

select id,name from tablename where jingdu>38.102 and jingdu<38.103 and weidu>62.204 and weidu<62.205;


执行时间,经纬度不加索引的话 100万条记录大概是0.3秒,加了索引大概0.06秒,已经算挺快了。

但是我们网站日PV 3KW,移动端更多,如果每次都这样就把数据库跑死了,但是如果存缓存,又不太合适,毕竟不能为每个人都存一组他们附近餐厅的缓存,不科学。

所以要用到缓存。以下是我的方法:


客户端传来经度和纬度,我们先模糊化,根据我在百度地图上查到的数据分析,经纬度在小数点第三位的时候,大概距离是80米,符合“附近”,第四位四舍五入(比如我们可以认为经度38.2356的模糊经度为38.236)

所以,当客户端传给我们一个经纬度为  38.1024,69.2048 时,我们先四舍五入后得到 38.102,69.205 这个模糊经纬度,然后根据经纬度哈希值,可以用md5啊类似的之类进行hash,得到的值为key,用redis的hget方法来取

$redis->hget('nearest',$key);

如果没取到,则去mysql里进行查找

select id,name,jingdu,weidu from tablename where ROUND(jingdu,3) = 38.102 and ROUND(weidu,3)=69.205


得到的数据以array存入redis的哈希表,并返回这些数据。

这样下次再有人在这附近查附近的餐厅,就可以直接从redis的哈希表里取出了


以上这种方法不必考虑原始数据的问题,不必跑一遍所有数据把他们都放redis里,而是用到哪里的餐厅找哪里,一次查找终生受用。以后再加入新餐厅的话,直接模糊餐厅经纬度,然后放入redis的hash表里供后面的查阅。


还有一种方法,就只说说思路吧。是基于geohash算法的思路。虽然geohash是专门对经纬度进行哈希的算法,但是他更适用于地图,而不适用于餐厅。他是把地图分为若干个大矩形来存key,每个矩形分为若干个小矩形来存key,然后根据客户端传来的经纬度,先匹配大矩形,再匹配小矩形,来查找餐厅。刚开始分割地图的时候成本太高,并且效率太低,占资源(内存)还无用。因为我们的主要数据是这一百多万餐厅,并非一百多万城市,有些城市可能东南角有一大片餐厅,而西北角一家都没有,如果按照geohash的思路,就要维护东南角和西北角两个key了(假设他俩离得很远)。而西北角的key根本无东西。

所以我们采取了第一种方法,一个区域查的人越多越高效。当然,为了提高准确性和精度,我们可以在外层或内层再加一层数据,加外层,意思就是先查外层,精确到小数点后两位,先查大区域内的餐厅,再查小区域内的餐厅,多了层key,多占用了更多些的内存,不过这样先查大区域是为了避免小区域内真的没餐厅。


大体思路就是这样


客户端传来(38.1234,68.5678) 模糊hash    md5(38.123_68.568),得到key后    redis->hget('nearest',$key)

没有就查数据库

并生成array插入hash表,有就直接返回餐厅name和id


涉世未深,如果本文有哪些不足或者错误还请各位大牛指正


================================补充

现在很多客户端获取最近餐厅的时候,还需要获取这个餐厅在他的哪个方向,还有距离多远,如果用上面我的方法是无法获取的,不过可以在上面的sql里,在查餐厅id和name的时候加上再查经纬度,一并保存在hash表里,这样在娶到最近餐厅们后,就可以根据客户端传来的精确的经纬度,和查到的这些餐厅本身的精确经纬度,来求出餐厅距离本人当前的方向和距离了(大众点评客户端有这个功能)


5 0