09_redis_GeoHash

Redis3.2增加了GEO模块, 可以实现附件的人

地图的元素位置数据是使用二维的经纬度表示,当两个元素距离不是很远的时候可以使用勾股定理计算元素的距离。但是经纬度坐标的密度不一样,经度360,维度180。勾股定理计算方差的时候再求和时,需要按照一定的系数加权求和。

给一个坐标,计算附近的其他元素。按照距离排序
不能计算所有的元素和目标元素的距离然后再排序,计算量太大了。一般是通过矩阵来限定区域,对区域内的元素进行计算这样可以减少计算量。
可以指定一个半径,一条sql就可以查出来了

select id from positions where x0-r < x < x0+r and y0-r < y < y0+r

为了满足高性能矩阵算法,需要在经纬坐标 加上复合双向索引。优化查询效率。
但是在高并发的情况下,不是一个好的方案。

GEO

常用的算法是GeoHash算法。
GeoHash是将坐标映射到一维的整数,所有的元素都挂载到一条线上面,距离近的坐标映射到一维后点之间的距离也很接近,当要计算附件的元素的时候,直接取一维点附近的元素就可以了。
映射算法的具体实现就是,将地图元素坐标都放置到一个唯一格子中,格子越小位置越精准。然后对格子进行编码,越是靠近的格子编码越接近。
那是如何编码的呢?
简单的方案就是切蛋糕法,将整个蛋糕,二刀下去分为四块,每小块可以标记成00,01,10,11四个二进制整数,然后对每个小块继续二刀法分割,这样每个小小块就可以使用4bit 的二进制整数表示,然后继续分割,块越来越小,二进制整数越来越长,位置精准程度也越来越高。
编码过后,每个地图元素的坐标都是一个整数,通过整数可以还原坐标,整数越长,还原的坐标精准度损失越小。对于找附件的人,这点损失的精准度无大碍。
GeoHash 会继续对这个整数进行base32(09,az,去掉a,i,l,o)位的编码变成一个字符串。
在Redis中,经纬度使用52位整数进行编码,放到zset中,zset的value 是元素的key,score是GeoHash饿52位整数值,zset的score虽然是浮点型,但是存储52位的整数型,可以无损存储。

Geo基本命令

Geo只是普通的zset结构

添加geoadd

携带集合名称以及多个经纬度名称三元组,注意这里可以加入多个三元组

127.0.0.1:6379> geoadd company 116.48105 39.996794 juejin
(integer) 1
127.0.0.1:6379> geoadd company 116.514203 39.905409 ireader
(integer) 1
127.0.0.1:6379> geoadd company 116.489033 40.007669 meituan
(integer) 1
127.0.0.1:6379> geoadd company 116.562108 39.787602 jd 116.334255 40.027400 xiaomi
(integer) 2

计算geodist

计算两个元素之间的距离,携带集合名称、2 个名称和距离单位。
距离单位可以是 m、km、ml、ft,分别代表米、千米、英里和尺

127.0.0.1:6379> geodist company juejin ireader km
"10.5501"
127.0.0.1:6379> geodist company juejin meituan km
"1.3878"
127.0.0.1:6379> geodist company juejin jd km
"24.2739"
127.0.0.1:6379> geodist company juejin xiaomi km
"12.9606"
127.0.0.1:6379> geodist company juejin juejin km
"0.0000"

获取元素的位置geopos

可以获取集合中任意元素的经纬度坐标,可以一次获取多个

127.0.0.1:6379> geopos company juejin
1) 1) "116.48104995489120483"
 2) "39.99679348858259686"
127.0.0.1:6379> geopos company ireader
1) 1) "116.5142020583152771"
 2) "39.90540918662494363"
127.0.0.1:6379> geopos company juejin ireader
1) 1) "116.48104995489120483"
 2) "39.99679348858259686"
2) 1) "116.5142020583152771"
 2) "39.90540918662494363"

获取的经纬度坐标和 geoadd 进去的坐标有轻微的误差,原因是 geohash 对二维坐标进行的一维映射是有损的,通过映射再还原回来的值会出现较小的差别。对于[附近的人」这种功能来说,这点误差根本不是事。

获取元素的hash值 geohash

获取元素的经纬度编码字符串,是base32位。
以使用这个编码值去 http://geohash.org/${hash}中进行直接定位,它是 geohash 的标准编码值。

127.0.0.1:6379> geohash company ireader
1) "wx4g52e1ce0"
127.0.0.1:6379> geohash company juejin
1) "wx4gd94yjn0"

附件的公司 georadiusbymember

用来查询指定元素附近的其它元素,它的参数非常复杂


# 范围 20 公里以内最多 3 个元素按距离正排,它不会排除自身
127.0.0.1:6379> georadiusbymember company ireader 20 km count 3 asc
1) "ireader"
2) "juejin"
3) "meituan"
# 范围 20 公里以内最多 3 个元素按距离倒排
127.0.0.1:6379> georadiusbymember company ireader 20 km count 3 desc
1) "jd"
2) "meituan"
3) "juejin"
# 三个可选参数 withcoord withdist withhash 用来携带附加参数
# withdist 很有用,它可以用来显示距离
127.0.0.1:6379> georadiusbymember company ireader 20 km withcoord withdist withhash count 3 asc
1) 1) "ireader"
 2) "0.0000"
 3) (integer) 4069886008361398
 4) 1) "116.5142020583152771"
 2) "39.90540918662494363"
2) 1) "juejin"
 2) "10.5501"
 3) (integer) 4069887154388167
 4) 1) "116.48104995489120483"
 2) "39.99679348858259686"
3) 1) "meituan"
 2) "11.5748"
 3) (integer) 4069887179083478
 4) 1) "116.48903220891952515"
 2) "40.00766997707732031"

还可以通过坐标值来查找附件的人,参数和georadiusbymember 基本一致,除了将目标元素改成经纬度坐标值

  127.0.0.1:6379> georadius company 116.514202 39.905409 20 km withdist count 3 asc
1) 1) "ireader"
 2) "0.0000"
2) 1) "juejin"
 2) "10.5501"
3) 1) "meituan"
 2) "11.5748"

注意

在地图应用中,地图元素会有n条,如果使用redis的geo数据结构,他们都会放大一个zset集合中,在集群环境中,数据可能从一个节点迁移到另一个节点中,当单个key的数据过大的话,对迁移造成较大的影响。集群中,单个key最好不要超过1m,否则迁移会引起卡顿,影响线上服务。
建议geo的数据使用单独的redis进行部署。数据量过大,需要对geo数据进行拆分,按照国家,省,市拆分。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,999评论 4 368
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,102评论 1 302
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,709评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,439评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,846评论 3 294
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,881评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,062评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,783评论 0 205
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,517评论 1 248
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,762评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,241评论 1 264
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,568评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,236评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,145评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,941评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,965评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,802评论 2 275