如果說移動(dòng)互聯(lián)網(wǎng)時(shí)代區(qū)別與主機(jī)互聯(lián)網(wǎng)時(shí)代最大的革命是什么?大部分人都會(huì)認(rèn)為是LBS服務(wù),眾所周知的我們一款社交應(yīng)用,當(dāng)年就是靠著搖一搖跟查看附近的人,一發(fā)不可收,徹底掀起了移動(dòng)互聯(lián)網(wǎng)的革命。今天我們介紹一下如何實(shí)現(xiàn)一個(gè)簡(jiǎn)單的位置服務(wù)功能的架構(gòu)是什么樣子呢?
我們先從業(yè)務(wù)場(chǎng)景入手吧,之前我們團(tuán)隊(duì)是做共享充電寶的,我們經(jīng)常會(huì)遇到這樣的場(chǎng)景,尋找最近的共享充電寶在哪里。一開始,我們這個(gè)東西是這么實(shí)現(xiàn)的,對(duì)于每個(gè)共享充電寶站,都會(huì)存經(jīng)緯度坐標(biāo)在數(shù)據(jù)庫里面。每次用戶過來查詢身邊的充電寶,前端會(huì)把用戶的經(jīng)緯度坐標(biāo)帶上來,然后后臺(tái)從數(shù)據(jù)庫里面撈出所有的坐標(biāo),通過經(jīng)緯度計(jì)算距離,然后找出最小距離的共享充電寶倉庫出來。
慢慢的隨著業(yè)務(wù)的擴(kuò)展,充電寶倉庫越來越多,性能問題就顯現(xiàn)了,畢竟每次拿出全國門店的充電寶來比較,的確性能很差。即便我們把數(shù)據(jù)都緩存了,但依然改變不了很多無所謂的計(jì)算。
一個(gè)簡(jiǎn)單的優(yōu)化是我們按照城市或者區(qū)域去分區(qū),然后每次只檢查同個(gè)區(qū)域的數(shù)據(jù),按照城市分區(qū)是一個(gè)比較簡(jiǎn)單的方案,因?yàn)榫W(wǎng)上有很多API可以根據(jù)經(jīng)緯度而獲取到對(duì)應(yīng)的區(qū)號(hào)。這一個(gè)看似行之有效的優(yōu)化,卻引來了不少投訴。舉個(gè)例子,假如我們以城市分區(qū),那么有些在城市交界的服務(wù)點(diǎn),就會(huì)搜索不到。例如在廣州佛山的交接有個(gè)門店,卻在分區(qū)上歸屬與佛山,這個(gè)時(shí)候在廣州的邊界就會(huì)搜索不到,即便距離很短。
這便引出了我們今天的主角,GeoHash,將經(jīng)緯度編碼成字符串,可以起到一個(gè)降維的作用。如下圖,一般來說,兩個(gè)編碼前面相同的子字符串越多,說明他們?cè)浇咏?。具體的算法大家感興趣可以深入學(xué)習(xí)。
還好開源世界已經(jīng)幫我們提供了一個(gè)很好用的GeoHash庫了,那便是使用Redis。Redis將位置的數(shù)據(jù)存放在zset里面。并對(duì)我們提供了一些API,下面我們簡(jiǎn)單的介紹一下。
1.我們從業(yè)務(wù)場(chǎng)景出發(fā),我們每新增一個(gè)共享充電寶的服務(wù)站,就需要插入這個(gè)服務(wù)站的坐標(biāo)信息,我們使用redis命令geoadd。
2.用戶查詢以自己為中心,附近的充電寶,我們可以使用redis命令georadius
3.后臺(tái)可能要拉去所有的門店列表,可以使用redis命令geodist