首页 > 代码库 > 如何获取最近的餐厅们
如何获取最近的餐厅们
下班前,老大给了道题目,是最近的一个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
涉世未深,如果本文有哪些不足或者错误还请各位大牛指正
如何获取最近的餐厅们