微信附近的人,用redis也能實現?(GEO)

2021-01-12 程式設計師養成日記

相信微信附近的人的功能大家都應該用過

我可以很隨意的通過我自己的定位能看到我附近的人,並且能看到那個人距離我的距離,大家有沒有思考過這個是怎麼實現的?

作為一個程序猿任何問題應該都有一個思考的過程,而不是直接看結論,接下來大家一步一步的思考,直到問題解決。

獲取自己的位置

附近的人其實就是一種位置的比對關係,所以第一步是得獲取自己的位置,一般位置都是用經緯度來表示,具體經緯度的獲取得依賴客戶端,作為咱們後端程式設計師直接接收參數就可以了,所以這一步重點是用經緯度來表示各個節點的位置,對經緯度不是很了解的朋友可以複習一下中學的地理知識。

用關係型資料庫(mysql)的方式解決問題

我們先把問題簡化,假如我附近的人都是不動的,也就是說他們的位置是固定的,按照咱們傳統的思路,就是把每個人的經緯度存起來,然後遍歷這些經緯度,我們可以通過某種方法獲取我和各個經緯度之間的距離,然後把相對於我距離在 5km 以內的用戶展示出來就可以了

具體實現如下

把每個人的經緯度存起來,存儲如下

遍歷數據,和自己對比,獲得每個人和自己的距離

把資料庫的所有記錄都遍歷一遍,把每一條記錄的經緯度和自己的經緯度做個對比,就能獲取到各個記錄離自己的距離。

如何根據兩個經緯度,獲取到這兩個點之間的距離我在網上招了個方法,大家可以參考下

/**

* 求兩個已知經緯度之間的距離,單位為米

*

* @param lng1 $ ,lng2 經度

* @param lat1 $ ,lat2 緯度

* @returnfloat 距離,單位米

* @author www.Alixixi.com

*/

function getdistance($lng1, $lat1, $lng2, $lat2) {

// 將角度轉為狐度

$radLat1 = deg2rad($lat1); //deg2rad()函數將角度轉換為弧度

$radLat2 = deg2rad($lat2);

$radLng1 = deg2rad($lng1);

$radLng2 = deg2rad($lng2);

$a = $radLat1 - $radLat2;

$b = $radLng1 - $radLng2;

$s = 2 * asin(sqrt(pow(sin($a / 2), 2) + cos($radLat1) * cos($radLat2) * pow(sin($b / 2), 2))) * 6378.137 * 1000;

return$s;

}

篩選出距離和自己在 5km 以內的數據就是我們想得到的結果

把上次算出來的距離一一對比,在 5km 以內的數據就是我們需要的附近的人的數據。

用關係型資料庫(mysql)存在的問題

其實用 mysql 的方式表面上看著是可以解決問題的,其實不然

首先遍歷數據就是遍歷所有的數據,而且是在一個需要及時返回結果的接口中,這樣做是非常不科學的,用戶量非常多的話根本不現實遍歷完了之後還得繼續計算距離,這個數量級也是非常大的距離那些都弄完了還得再篩選一遍在附近的,又是一遍所有數據的遍歷如果符合附近的人的要求是需要按照距離從近到遠來排序,又得遍歷計算上述方式如果用戶量比較小其實是可以實現的,但是現在移動網際網路公司一般用戶體量都很大,全表遍歷的方式基本都可以 pass 掉,所以接下來我們來看一種新的方案,用 redis geo 的方式來實現

redis geo 介紹

首先我們需要注意的是,redis geo 是 3.2 版本才有的,所以需要用這個功能的朋友記得更新 redis 的版本

其實 redis geo 只有 6 個操作命令,知道這些命令基本思路就出來了

GEOADD:增加某個地理位置的坐標GEOPOS:獲取某個地理位置的坐標GEODIST:獲取兩個地理位置的距離GEORADIUS:根據給定地理位置坐標獲取指定範圍內的地理位置集合GEORADIUSBYMEMBER:根據給定地理位置獲取指定範圍內的地理位置集合GEOHASH:獲取某個地理位置的 geohash 值對於上面的命令,我們直接看例子吧,方便大家更深入的理解

redis> GEOADD nearbyPeople 13.36 38.11 "user_1" 15.08 37.50 "user_2"

(integer) 2

對於上面例子來說 相當於 nearbyPeople 是一個總的 key,user_1 和 user_2 是相當於 nearbyPeople 裡面的兩個元素以及他們對應的經緯度其實上述例子就是說把 user_1 和 user_2 的經緯度存在了 nearbyPeople 這個 key 中

redis> GEOPOS nearbyPeople user_1 user_2

1) 1) "13.36138933897018433"

2) "38.11555639549629859"

2) 1) "15.08726745843887329"

2) "37.50266842333162032"

這個就比較簡單了,就是獲取 nearbyPeople 中的元素 user_1 和 user_2 這兩個元素的經緯度,當然如果之前沒有 geoadd 相對應元素的經緯度的話,會返回 nil

redis> GEODIST nearbyPeople user_1 user_2

"166274.1516"

redis> GEODIST nearbyPeople user_1 user_2 km

"166.2742"

redis> GEODIST nearbyPeople user_1 user_2 mi

"103.3182"

獲取 nearbyPeople 中 user_1 和 user_2 這兩個節點之間的距離,距離單位可以指定,如下所示

m :米,默認單位。km :千米。mi :英裡。ft :英尺。GEORADIUS 這個比較重要,也是比較核心的一個方法,參數也比較多,咱們來具體參照文檔說一說

GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]

參數說明:

m :米,默認單位。km :千米。mi :英裡。ft :英尺。WITHDIST: 在返回位置元素的同時, 將位置元素與中心之間的距離也一併返回。WITHCOORD: 將位置元素的經度和維度也一併返回。WITHHASH: 以 52 位有符號整數的形式, 返回位置元素經過原始 geohash 編碼的有序集合分值。 這個選項主要用於底層應用或者調試, 實際中的作用並不大。COUNT 限定返回的記錄數。ASC: 查找結果根據距離從近到遠排序。DESC: 查找結果根據從遠到近排序。

redis>GEORADIUS nearbyPeople 15 37 200 km WITHDIST

1) 1) "user_1"

2) "190.4424"

2) 1) "user_2"

2) "56.4413"

上述命令也就是說把 nearbyPeople 中的 距離經緯度(15,37)200km 以內的元素都找出來,而且帶上距離

GEORADIUSBYMEMBER 其實和 GEORADIUS 作用都一樣,唯一的區別在於

GEORADIUS 是以某個經緯度為基準點

GEORADIUSBYMEMBER 是以某個元素為基準點

用 redis geo 的方式解決問題

其實上述命令熟悉了的同學這個問題就很好解決了

首先我們可以在後臺把每個人的位置定時刷新到以 nearbyPeople 為 key 的 geo 對象中。

reids> GEOADD nearbyPeople 13.36 38.11 "user_1" 15.08 37.50 "user_2" .......

因為查看附近的人的位置信息也在 nearBy 中,所以顯然用 GEORADIUSBYMEMBER 比較合適

GEORADIUSBYMEMBER nearbyPeople user_n 5 km WITHDIST //user_n為當前查看附近的用戶

這樣就可以完美解決我們的問題了。

相關焦點

  • IM裡「附近的人」功能實現原理是什麼?如何高效率地實現它?
    1、引言基本上以陌生人社交為主的IM產品裡,都會增加「附近的人」、「附近的xxx」這種以LBS(地理位置)為導向的產品特色(微信這個熟人社交產品裡為啥也有「附近的人」?這當然是歷史原因了,微信當初還不是想藉此引流嘛。。。)
  • Redis 的 8 大數據類型,寫得非常好!(建議收藏)
    # 如果不存在值,則返回 nil(nil)127.0.0.1:6379> get db"redis"127.0.0.1:6379> getset db mongodb # 如果存在值,獲取原來的值,並設置新的值"redis"127.0.0.1:6379> get db"mongodb"127.0.0.1:6379>String 的使用場景:value 除了是字符串以外還可以是數字
  • 機率大的 Redis 面試題(含答案)|內存|key|原子性|哈希|redis_網易...
    實現:單獨創建fork()一個子進程,將當前父進程的資料庫數據複製到子進程的內存中,然後由子進程寫入到臨時文件中,持久化的過程結束了,再用這個臨時文件替換上次的快照文件,然後子進程退出,內存釋放。  RDB是Redis默認的持久化方式。按照一定的時間周期策略把內存的數據以快照的形式保存到硬碟的二進位文件。
  • Redis教程:Redis持久化方式
    當子進程完成對新 RDB 文件的寫入時,Redis 用新 RDB 文件替換原來的 RDB 文件,並刪除舊的 RDB 文件。這種工作方式使得 Redis 可以從寫時複製(copy-on-write)機制中獲益。
  • 大數據必學:redis深入了解 Redis 的持久化機制(RDB、AOF)
    因為 redis是一個內存資料庫,所有數據都存儲在內存中,而且內存中的數據非常容易丟失,所以 redis的數據持久化就變得非常重要, redis提供了兩種數據持久化方法,分別用於 RDB和 AOF,而 redis默認用於 RDB的數據持久化方法。
  • redis cluster 集群管理工具
    前言在redis源碼編譯的時候,在src目錄下會有一個redis-trib.rb的腳本,這個腳本是ruby寫的,用於管理redis cluster。安裝系統依賴包yum -y install epel-release yum -y install ruby rubygem-redis redis-trib.rb/opt/redis/bin/redis-trib.rb
  • Python使用redis存儲對象
    Python總的對象存儲到redis中默認為字符串,那麼如何存儲對象呢?下面就看看如何直接將Python中對象存儲到redis中先寫個測試redis是否正常連接上import rediscache = redis.StrictRedis('172.20.0.227',6379)
  • Redis RDB與AOF模式下的持久化原理
    前言:在此之前,如果還不了解Redis的,或者不知道怎麼使用Redis,可以參考官網網站:https://redis.io/documentation自行學習,本文主要針對Redis的核心點之一:RDB和AOF持久化模式進行展開。
  • Redis 3.0.0 RC4 發布,無 Redis Cluster 修復
    此版本包括關於 redis-cli 方面的新特性,一個使用 xterm 256 顏色的延遲光譜可視化工具。Reids 團隊計劃兩周後發布一個 RC 版本或者是 3.0.0 穩定版本。Reids 3.0.0.RC4 常規改進:* [FIX] redis-cli CSV output NIL spurious newline removed.
  • 微信附近的人看不到我怎麼破?附近的人解決方案
    原標題:微信附近的人看不到我怎麼破?附近的人解決方案 隨著的城市的不斷發展, 人與人之間的關係越來越淡漠,於是各種附近的人功能開始盛行,大家就可以附件的人功能搜索附近正在使用微信的小夥伴,進行打招呼聊天,不過有些用戶反映自己無法被微信附近的人搜索到,微信附近的人看不到我怎麼辦,下面小編就給大家帶來微信附近的人看不到我解決教程。
  • redis - aof持久化介紹
    AOF簡介redis持久化存儲的方式有rdb序列化存儲和aof(append only file)。aof就是將操作和數據以格式化指令的方式追加到操作日誌的尾部,在append操作返回後,才進行實際的數據變更。
  • Redis 設計與實現 4:字典
    字典的結構哈希表哈希表的實現代碼在:dict.h/dictht ,Redis 的字典用哈希表的方式實現。struct dictEntry *next;} dictEntry;next 指針是用於當哈希衝突的時候,可以形成鍊表用的。
  • Redis如何存儲和計算一億用戶的活躍度
    01前段時間,在網上看到一道面試題:如何用redis存儲統計1億用戶一年的登陸情況,並快速檢索任意時間窗口內的活躍用戶數量。覺得很有意思,就仔細想了下 。並做了一系列實驗,自己模擬了下 。還是有點收穫的,現整理下來。
  • 吃豆人geo新手怎麼玩 吃豆人geo攻略大全2020
    吃豆人geo這款遊戲是一款非常有趣的休閒遊戲,很多玩家們都喜歡玩這款有趣的吃豆遊戲,大家可以體驗到非常真實的場景,今天40407小編為各位玩家們帶來吃豆人geo攻略大全,快收藏起來吧!
  • Redis中的過期鍵以及如何刪除的?
    ——華羅庚引導語Redis伺服器的資料庫實現進行詳細介紹,說明伺服器保存資料庫的方法,客戶端切換資料庫的方法,資料庫保存鍵值對的方法,以及針對資料庫的添加、刪除、查看、更新操作的實現方法等。除此之外,本章還會說明伺服器保存鍵的過期時間的方法,以及伺服器自動刪除過期鍵的方法。
  • Redis持久化和備份
    比如如下命令redis 127.0.0.1:6379> set key1 HelloOKredis 127.0.0.1:6379> append key1 " World!"(integer) 12redis 127.0.0.1:6379> del key1(integer) 1redis 127.0.0.1:6379> del non_existing_key(integer) 0在AOF中存儲如下$ cat appendonly.aof*2$6SELECT$10*3$3set$4key1$5Hello*3$6append$4key1$7 World
  • redis cluster 之master 選舉過程
    在redis 3.0版本後,官方推出了redis cluster 分布式解決方案,當一個redis節點掛了可以快速地切換到另一個節點。當遇到單機內存、並發等瓶頸時,可以採用分布式方案要解決問題.redis-cluster架構中,被設計成共有16384(2的14次方)個hash slot。每個master分得一部分slot,其算法為:hash_slot = crc16(key) mod 16384 ,這就找到對應slot。群集至少需要3主3從,且每個實例使用不同的配置文件。
  • 男子通過微信搜索「附近的人」 搜出老婆偷情
    張釗 製圖  一男子通過微信搜索「附近的人」,竟搜出老婆偷情。這一幕,在溫州永嘉黃田真實上演。  來自貴州的周某夫婦,租住在永嘉黃田,是一對微信控。他倆有事沒事,總愛捧著手機。  11月27晚,妻子告訴周某要去廠裡加班,獨自在家的周某覺得無聊便用微信搜尋「附近的人」。這一搜,周某不由得大吃一驚,他竟然看到了妻子微信號,距離在他百來米內,可妻子明明告訴他已經去廠裡上班了,工廠的位置距離住處起碼數公裡。  周某不由得疑竇叢生:他聯想到平時妻子經常一個人捧著手機,不時咯咯直笑。他一靠近,妻子就顯得格外慌張。
  • 微信附近的人看不到我怎麼辦?解決方案有哪些?
    我們大家都知道,可以在微信附近的人功能中搜索到正在使用的微信小夥伴們,可以跟他們打招呼,聊天等,但是有些用戶也反映一種情況,就是發現微信附近的人看不到我,那麼這種情況應該怎麼辦呢?下面我們就給大家介紹一下如何進行解決。微信附近的人看不到我怎麼辦?
  • 詳解Redis中兩種持久化機制RDB和AOF(面試常問,工作常用)
    現在主要考慮的是redis如何來實現上面5個保存磁碟的步驟。它提供了兩種策略機制,也就是RDB和AOF。二、RDB機制RDB其實就是把數據以快照的形式保存在磁碟上。什麼是快照呢,你可以理解成把當前時刻的數據拍成一張照片保存下來。RDB持久化是指在指定的時間間隔內將內存中的數據集快照寫入磁碟。