天天看點

Redis-GEO地理位置

 Redis 3.2版本一個重大的更新是新增了GEO地理位置相關的指令。

目前Redis對地理位置支援提供了一下6個指令:

  1. 1. geoadd: 增加地理位置的坐标。

  2. 2. geodist: 擷取兩個地理位置的距離。

  3. 3. geohash: 擷取地理位置的GeoHash值。

  4. 4. geopos: 擷取地理位置的坐标。

  5. 5. georadius: 根據給定經緯度坐标擷取指定範圍内的地理位置集合。

  6. 6. georadiusbymember: 根據給定地理位置擷取指定範圍内的地理位置集合。

GeoHash基本原理

  1. GeoHash是一種位址編碼,通過切分地圖區域為小方塊(切分次數越多,精度越高),它能把二維的經緯度編碼成一維的字元串。

  2. 也就是說,理論上geohash字元串表示的并不是一個點,而是一個矩形區域,隻要矩形區域足夠小,達到所需精度即可。

編碼過程

  1. 以經緯度(116.3906,39.92324)為例:

  2. 1. 對于次元39.92324, 39.92324屬于(0, 90),是以取編碼為1。

  3. 然後再将(0, 90)分成 (0, 45), (45, 90)兩個區間,而39.92324位于(0, 45),是以編碼為0。

  4. 以此類推,直到精度符合要求為止,得到緯度編碼為1011 1000 1100 0111 1001。

  5. 2. 經度也用同樣的算法,對(-180, 180)依次細分,得到116.3906的編碼為1101 0010 1100 0100 0100。

  6. 3. 接下來将經度和緯度的編碼合并,奇數位是緯度,偶數位是經度,得到編碼 11100 11101 00100 01111 00000 01101 01011 00001。

  7. 4. 最後,用0-9、b-z(去掉a, i, l, o)這32個字母進行base32編碼,得到(39.92324, 116.3906)的編碼為wx4g0ec1。

  8. (116.3906,39.92324)對應的地圖位置就是http://geohash.org/wx4g0ec1,

  9. 降低一些精度,就會是

    http://geohash.org/wx4g0ec,

  10. 再降低一些精度,就會是

    http://geohash.org/wx4g0

編碼特性

  1. 不難看出這樣的編碼方式僅用一個字元串儲存經緯度資訊,并且精度由字元串從頭到尾的長度決定,編碼長度越長,精度越高。

  2. GeoHash值的字首相同的位數越多,代表的位置越接近,可以友善索引。(反之不成立,位置接近的GeoHash值不一定相似).

  3. 但這種方案的缺點是:從geohash的編碼算法中可以看出,靠近每個方塊邊界兩側的點雖然十分接近,但所屬的編碼會完全不同。

  4. 實際應用中,需要通過去搜尋環繞目前方塊周圍的8個方塊來解決該問題。

  5. 除此之外,這個方案也無法直接得到距離,需要程式協助進行後續的排序計算。

Redis Geo指令實作

  1. Redis将地理位置的52位GeoHash值作為有序集合的score,将地理位置存放在有序集合中進行儲存。

  2. 後續按位置搜尋時,依據GeoHash的特性搜尋目前方塊與環繞目前方塊的8個方塊來搜尋目标位置集合。

1.GEOADD

  1. 指令:GEOADD key longitude latitude member [longitude latitude member ...]

  2. 指令描述:将指定的地理空間位置(緯度、經度、名稱)添加到指定的key中。

  3. 傳回值:添加到sorted set元素的數目,但不包括已更新score的元素。

  1. Redis中接受的有效的精度範圍為-180到180度,有效次元範圍為-85.05112878到 85.05112878度(靠近南北極的一小塊地方是無法生成索引的)。

  • GEOADD  實作方式
  1. Redis内部使用有序集合來儲存key,每一個member的score大小為一個52位的Geohash值(double類型精度為52位)。

  2. 實際上Redis内部實作的時候就是将GEOADD指令轉換成ZADD指令來實作的。

  3. (這也解釋了為什麼沒有專門的georem指令,地理位置資訊是通過使用ZREM指令來删除成員。)

2.GEODIST 

  1. 指令:GEODIST key member1 member2 [unit]

  2. 指令描述:

  3. 傳回兩個給定位置之間的距離。如果兩個位置之間的其中一個不存在, 那麼指令傳回空值。指定機關的參數 unit 必須是以下機關的其中一個:

    • m 表示機關為米。
    • km 表示機關為千米。
    • mi 表示機關為英裡。
    • ft 表示機關為英尺。
  • GEODIST 實作方式
  1. 使用WGS84坐标系統,計算距離時使用Haversine公式。由于地球并不是嚴格标準的,計算出來的距離有最大約0.5%的誤差。

3.GEOPOS 

  1. 指令:GEOPOS key member [member ...]

  2. 指令描述:從key裡傳回所有給定位置元素的位置(經度和緯度)。

  3. 傳回值:GEOPOS 指令傳回一個數組, 數組中的每個項都由兩個元素組成: 

  4. 第一個元素為給定位置元素的經度, 而第二個元素則為給定位置元素的緯度。當給定的位置元素不存在時, 對應的數組項為空值。

4.GEOHASH 

  1. 指令:GEOHASH key member [member ...]

  2. 指令描述:傳回一個或多個位置元素的 Geohash 表示。通常使用表示位置的元素使用不同的技術,使用Geohash位置52點整數編碼。

  3. 由于編碼和解碼過程中所使用的初始最小和最大坐标不同,編碼的編碼也不同于标準。此指令傳回一個标準的Geohash

  4. 傳回值:一個數組, 數組的每個項都是一個 geohash 。 指令傳回的 geohash 的位置與使用者給定的位置元素的位置一一對應。

  • GEOHASH 實作方式
  1. Redis在内部生成有序集合成員score時的geohash值與标準的算法略有差異(Redis内部使用-85,85作為次元範圍,标準使用-90,90)。

  2. 這個指令傳回的是标準值,與https://en.wikipedia.org/wiki/Geohash中标準算法和geohash.org網站的結果一緻。

5.GEORADIUS

  1. 指令:GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count]

  2. 指令描述:

  3. 以給定的經緯度為中心, 傳回鍵包含的位置元素當中, 與中心的距離不超過給定最大距離的所有位置元素。

  4. 範圍可以使用以下其中一個機關:

    1. m 表示機關為米。

    2. km 表示機關為千米。

    3. mi 表示機關為英裡。

    4. ft 表示機關為英尺。

  1. 在給定以下可選項時, 指令會傳回額外的資訊:

    1. WITHDIST: 在傳回位置元素的同時, 将位置元素與中心之間的距離也一并傳回。 距離的機關和使用者給定的範圍機關保持一緻。

    2. WITHCOORD: 将位置元素的經度和次元也一并傳回。

    3. WITHHASH: 以 52 位有符号整數的形式, 傳回位置元素經過原始 geohash 編碼的有序集合分值。 這個選項主要用于底層應用或者調試, 實際中的作用并不大。

  1. 指令預設傳回未排序的位置元素。 通過以下兩個參數, 使用者可以指定被傳回位置元素的排序方式:

    1. ASC: 根據中心的位置, 按照從近到遠的方式傳回位置元素。

    2. DESC: 根據中心的位置, 按照從遠到近的方式傳回位置元素。

  1. 在預設情況下, GEORADIUS 指令會傳回所有比對的位置元素。 雖然使用者可以使用 COUNT <count> 選項去擷取前 N 個比對元素, 但是因為指令在内部可能會需要對所有被比對的元素進行處理, 是以在對一個非常大的區域進行搜尋時, 即使隻使用 COUNT 選項去擷取少量元素, 指令的執行速度也可能會非常慢。 但是從另一方面來說, 使用 COUNT 選項去減少需要傳回的元素數量, 對于減少帶寬來說仍然是非常有用的。

  2. 傳回值:

    1. 在沒有給定任何 WITH 選項的情況下, 指令隻會傳回一個像 [“New York”,”Milan”,”Paris”] 這樣的線性(linear)清單。

    2. 在指定了 WITHCOORD 、 WITHDIST 、 WITHHASH 等選項的情況下, 指令傳回一個二層嵌套數組, 内層的每個子數組就表示一個元素。

      1. 在傳回嵌套數組時, 子數組的第一個元素總是位置元素的名字。 至于額外的資訊, 則會作為子數組的後續元素, 按照以下順序被傳回:

        1. 以浮點數格式傳回的中心與位置元素之間的距離, 機關與使用者指定範圍時的機關一緻。

        2. geohash 整數。

        3. 由兩個元素組成的坐标,分别為經度和緯度。

  • GEORADIUS實作方式
  1. GeoHash值的字首相同的位數越多,代表的位置越接近,可以友善索引。但反之不成立,位置接近的GeoHash值不一定相似。

  2. 靠近每個方塊邊界兩側的點雖然十分接近,但所屬的編碼會完全不同。實際應用中,需要通過去搜尋環繞目前方塊周圍的8個方塊來解決該問題。

  3. 搜尋的時候會檢查擋牆方塊+8個覆寫整個搜尋半徑的區域,不斷的去除geohash的低位,直到這9個方塊能覆寫搜尋半徑位置。再一次搜尋計算每個位置的距離。

6.GEORADIUSBYMEMBER 

  1. 指令:GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count]

  2. 指令描述:這個指令和 GEORADIUS 指令一樣, 都可以找出位于指定範圍内的元素, 但是 GEORADIUSBYMEMBER 的中心點是由給定的位置元素決定的。

繼續閱讀