Redis(2)九大数据类型及落地案例
九大数据类型
redis命令不区分大小写,但key是区分大小写的
Tips help @类型名词 可以获得该类型的所有操作命令
string
string类型是二进制安全的,redis的string可以包含任何数据,如图像、序列化对象。一个键最多能存储512MB。
- 二进制安全是指,在传输数据的时候,能保证二进制数据的信息安全,也就是不会被篡改、破译;如果被攻击,能够及时检测出来
命令 | 含义 |
---|---|
set key value | 命令不区分大小写,但是key区分大小写 |
setnx key value | 当key不存在时设置key的值。(SET if Not eXists),分布式锁的问题 |
setex key_name time key_value | 创建一个key,并且设置他的过期时间 |
psetex | 创建并设置一个key存活的有效期(毫秒) |
get key_name | |
getrange key start end | 获取key中字符串的子字符串,从start开始,end结束 |
setrange key offset value | 设置从offset往后的值(覆盖) |
mget key1 [key2 …] | 获取多个key |
mset | 设置多个key |
msetnx | 仅当要设置的多个key都不存在时候才被设置值 否则均不会被添加 |
getset key_name value: | 返回key的旧值,并设定key的值。当key不存在,返回nil |
strlen key | 返回key所存储的字符串的长度 |
append key_name value | 字符串拼接,追加至末尾,如果不存在,为其赋值 |
incr key_name | INCR命令key中存储的值+1,如果不存在key,则key中的值话先被初始化为0再加1 |
incrby key_name step | 按步长增量 |
incrbyfloat | |
decr key_name | key中的值自减1 |
decrby key_name step | 按步长减 |
应用场景
-
String通常用于保存单个字符串或JSON字符串数据
-
因为String是二进制安全的,所以可以把保密要求高的图片文件内容作为字符串来存储
-
计数器:常规Key-Value缓存应用,如微博数、粉丝数。INCR本身就具有原子性特性,所以不会有线程安全问题
-
接口被调用次数
-
分布式锁
- 比如抖音无限点赞某个视频或者商品,点一下加一次
中小厂可用此方法,高QPS不可用
-
阅读数
只要点击了rest地址,直接可以使用incr key 命令增加一个数字1,完成记录数字。
hash
Map<String,Map<Object,Object>>
Redis hash是一个string类型的field和value的映射表,hash特别适用于存储对象。每个hash可以存储232 - 1(40亿左右)键值对。可以看成KEY和VALUE的MAP容器。相比于JSON,hash占用很少的内存空间。
命令 | 含义 |
---|---|
hset key_name field value | 为指定的key设定field和value |
hmset key field value[field1,value1] | 为指定的key一次性设定多个field和其对应的value |
hsetnx | 当不存在才创建该field |
hget key field | 获取指定key指定字段的值 |
hmget key field[field1] | 返回指定key的指定多个字段 |
hgetall key | 返回hash表中所有字段和值 |
hkeys key | 获取hash表指定key的所有字段 |
hvals key | 获取hash表制定key的所有字段对应的值 |
hlen key | 获取hash表中的字段数量 |
hdel key field [field1] | 删除一个或多个hash表的字段 |
hexists | 在key里面是否存在指定的field |
hincrby key field step | 增加某个field的值 |
应用场景
Hash的应用场景,通常用来存储一个用户信息的对象数据。
-
相比于存储对象的string类型的json串,json串修改单个属性需要将整个值取出来。而hash不需要。
-
相比于多个key-value存储对象,hash节省了很多内存空间
-
如果hash的属性值被删除完,那么hash的key也会被redis删除
-
JD购物车早期 设计目前不再采用,当前小中厂可用
行为 命令 新增商品 hset shopcar:uid1024 334488 1 新增商品 hset shopcar:uid1024 334477 1 增加商品数量 hincrby shopcar:uid1024 334477 1 商品总数 hlen shopcar:uid1024 全部选择 hgetall shopcar:uid1024
list
类似于Java中的LinkedList。一个双端链表的结构, 容量是232个元素,大概40多亿,主要功能有push/pop等,一般用在栈、队列、消息队列等场景。
它是一个字符串链表,left、right都可以插入添加;
如果键不存在,创建新的链表;
如果键已存在,新增内容;
如果值全移除,对应的键也就消失了。
链表的操作无论是头和尾效率都极高,但假如是对中间元素进行操作,效率就很惨淡了。
命令 | 作用 |
---|---|
lpush key value1 [value2] | 从左侧插入,右边的先出,相当于一个栈 lpush list 1 2 3 lrange list 0 -1 输出:3 2 1 |
rpush key value1 [value2]: | 从右侧插入,左边的先出 rpush list 1 2 3 lrange list 0 -1 输出:1 2 3 |
lpop key | 返回并从左侧移除第一个元素 |
rpop key | 返回并从右侧移除第一个元素 |
llen key | 获取列表长度 |
lindex key index | 获取列表指定索引的元素,从零开始 |
lrange key start stop | 获取列表指定范围的元素 |
lrem key num value | 从索引0往后直至删除指定个数的同一元素 |
lrem list 2 3 删掉了集合中的两个3 | |
ltrim key start stop | 对列表进行修改,让列表只保留指定区间的元素,不在指定区间的元素就会被删除 list1中元素1 2 3 4 5 ltrim list1 2 3 list1剩余元素:3 4 |
lset key index value | 修改指定索引的值(索引必须存在) |
linsert key before|after value value | 在列表元素前或则后插入元素 |
lpushx key value | 从左侧插入值,如果list不存在,则不操作 |
rpushx key value | 从右侧插入值,如果list不存在,则不操作 |
blpop key [key1] timeout | 移除并获取列表第一个元素,如果列表没有元素会阻塞列表到等待超时或发现可弹出元素为止 |
brpop key [key1] timeout | 移除并获取列表最后一个元素,如果列表没有元素会阻塞列表到等待超时或发现可弹出元素为止 |
rpoplpush list1 list2 | 移除list1最后一个元素,并将该元素添加到list2并返回此元素 用此命令可以实现订单下单流程、用户系统登录注册短信等。 |
应用场景
-
对数据大的集合数据删减
列表显示、关注列表、粉丝列表、留言评价…分页、热点新闻等 -
任务队列
list通常用来实现一个消息队列,而且可以确保先后顺序,不必像MySQL那样通过order by来排序补充:
rpoplpush list1 list2 此命令可以实现订单下单流程、用户系统登录注册短信等。
-
公众号订阅的消息
- 大V作者李永乐老师和CSDN发布了文章分别是 11 和 22
- MMMM关注了他们两个,只要他们发布了新文章,就会安装进我的List
- lpush likearticle:MMMM 11 22
- 查看MMMM自己的号订阅的全部文章,类似分页,下面0~10就是一次显示10条
- lrange likearticle:MMMM 0 9
-
商品评论列表
需求1:用户针对某一商品发布评论,一个商品会被不同的用户进行评论,保存商品评论时,要按时间顺序排序
需要2:用户在前端页面查询该商品的评论,需要按照时间顺序降序排序
使用list存储商品评论信息,key是该商品的id,value是商品评论信息商品编号为1001的商品评论key【items:comment:1001】
lpush items:comment:1001 {"id":1001,"name":"huawei","date":1600484283054,"content":"lasjfdljsa;fdlkajsd;lfjsa;ljf;lasjf;lasjfdlsad"}
Set
唯一、无序
命令 | 含义 |
---|---|
sadd key value1[value2] | 向集合添加成员 |
scard key | 返回集合成员数量 |
smembers key | 返回集合中所有成员 |
sismember key member | 判断memeber元素是否是集合key成员的成员 |
srandmember key [count] | 返回集合中count个随机数 |
srem key member1 [member2] | 移除集合中一个或多个成员 |
spop key | 移除并返回集合中的一个随机元素 |
smove source destination member: | 将member元素从source集合移动到destination集合 |
sdiff key1 [key2]: | 返回给定的第一个集合和其他集合的差集(即在key1中的值而在其他key中找不到) |
sdiffstore destination key1[key2]: | 返回给定的第一个集合与其他的集合的差集并存储在destination中 set1:1 2 3 set2:3 4 5 6 sdiffstore set3 set1 set2 smembers set3 result:1 2 |
sinter key1 [key2]: | 返回所有集合的交集 |
sunion key1 [key2]: | 返回所有集合的并集 |
应用场景
-
对两个集合间的数据[计算]进行交集、并集、差集运算
-
- 以非常方便的实现如共同关注、共同喜好、二度好友等功能。对上面的所有集合操作,你还可以使用不同的命令选择将结果返回给客户端还是存储到一个新的集合中。
- 利用唯一性,可以统计访问网站的所有独立IP
-
微信抽奖小程序
节点 | 命令 |
---|---|
1 用户ID,立即参与按钮 | sadd key 用户ID |
2 显示已经有多少人参与了,上图23208人参加 | SCARD key |
3 抽奖(从set中任意选取N个中奖人) | SRANDMEMBER key 2 随机抽奖2个人,元素不删除 SPOP key 3 随机抽奖3个人,元素会删除 |
-
微信朋友圈点赞
行为 | 命令 |
---|---|
1 新增点赞 | sadd pub:msgID 点赞用户ID1 点赞用户ID2 |
2 取消点赞 | srem pub:msgID 点赞用户ID |
3 展现所有点赞过的用户 | SMEMBERS pub:msgID |
4 点赞用户数统计,就是常见的点赞红色数字 | scard pub:msgID |
5 判断某个朋友是否对楼主点赞过 | SISMEMBER pub:msgID 用户ID |
-
微博好友关注社交关系
-
共同关注的人 取交集
sadd s1 1 2 3 4 5 6
sadd s2 3 5 9 11
sinter s1 s2--------------------->3 5
-
我关注的人(s1,s2)也关注他)(3)(大家爱好相同)
sismember s1 3
sismember s2 3
-
QQ内推可能认识的人
sadd s1 1 2 3 4 5
sadd s2 3 4 5 6 7
sinter s1 s2 ——————————3 4 5
sdiff s1 s2 -------------------1 2
sdiff s2 s1-------------------6 7
-
zset
有序且不重复。每个元素都会关联一个double类型的分数,Redis通过分数进行从小到大的排序。分数可以重复
命令 | 作用 |
---|---|
zadd key score1 member1 score2 member2 | 向有序集合中加入一个元素和该元素的分数 |
zcard key : | 获取集合中的元素数量 |
zcount key min max | 计算在有序集合中指定区间分数的成员数 |
zrange key start stop 【withscores】 | 指定输出索引范围内的成员,输出结果按照分数大小排序 |
zrangebyscore key min max | 指定输出score区间内的成员 |
zrank key member: | 返回有序集合指定成员的索引 |
zrevrange key start stop : | 返回有序集中指定区间内的成员,通过索引,分数从高到低 |
zincrby key score member | 给某一个元素加分 |
zrem key member [member …] | 移除有序集合中的一个或多个成员 |
zremrangebyrank key start stop | 移除有序集合中给定的索引区间的所有成员(第一名是0)(低到高排序) |
zremrangebyscore key min max | 移除有序集合中给定的分数区间的所有成员 |
zscore | 显示某一元素的分数 |
应用场景
-
常用于排行榜
- 如推特可以以发表时间作为score来存储
- 存储成绩
- 还可以用zset来做带权重的队列,让重要的任务先执行
-
根据商品销售对商品进行排序显示
定义商品销售排行榜(sorted set集合),key为goods:sellsort,分数为商品销售数量。
行为 命令 商品编号1001的销量是9,商品编号1002的销量是15 zadd goods:sellsort 9 1001 15 1002 有一个客户又买了2件商品1001,商品编号1001销量加2 zincrby goods:sellsort 2 1001 求商品销量前10名 ZRANGE goods:sellsort 0 10 withscores -
抖音热搜
行为 命令 1 点击视频 ZINCRBY hotvcr:20200919 1 八佰
ZINCRBY hotvcr:20200919 15 八佰 2 花木兰2 展示当日排行前10条 ZREVRANGE hotvcr:20200919 0 9 withscore
新三大数据类型出现的时代背景
新需求
手机App中的每天的⽤⼾登录信息:1天对应1系列⽤⼾ID或移动设备ID;
电商⽹站上商品的⽤⼾评论列表:1个商品对应了1系列的评论;
⽤⼾在⼿机App上的签到打卡信息:1天对应1系列⽤⼾的签到记录;
应⽤⽹站上的⽹⻚访问信息:1个⽹⻚对应1系列的访问点击。
记录对集合中的数据进行统计
在移动应用中,需要统计每天的新增用户数和第2天的留存用户数;
在电商网站的商品评论中,需要统计评论列表中的最新评论;
在签到打卡中,需要统计一个月内连续打卡的用户数;
在网页访问记录中,需要统计独立访客(UniqueVisitor,UV)量。
痛点:
类似今日头条、抖音、淘宝这样的额用户访问级别都是亿级的,请问如何处理?
换句话说,以上的需求的痛点
亿级数据的收集+统计,要求存的进,取得快,多统计
而真正有价值的是统计
case:滴滴打车 司机id:经纬度 由于车在动,需要五秒钟更新一次车的经纬度位置,高频度更新数据源,MySQL显然无法胜任
亿级系统常见的四种统计
大数据下用户画像的获取进而推进广告+直播卖货
聚合统计
统计多个集合元素的聚合结果,就是前面讲解过的交差并等集合统计
交并差集和聚合函数的应用
排序统计
-
抖音视频最新评论留言的场景,请你设计一个展现列表。
考察你的数据结构和设计思路
-
list
每个商品评价对应一个List集合,这个List包含了对这个商品的所有评论,而且会按照评论时间保存这些评论,每来一个新评论就用LPUSH命令把它插入List的队头。但是,如果在演示第二页前,又产生了一个新评论, 第2页的评论不一样了。原因:
List是通过元素在List中的位置来排序的,当有一个新元素插入时,原先的元素在List中的位置都后移了一位, 原来在第1位的元素现在排在了第2位,当LRANGE读取时,就会读到旧元素。
-
zset
在⾯对需要展示最新列表、排行榜等场景时,如果数据更新频繁或者需要分页显示,建议使⽤ZSet
-
二值统计
集合元素的取值就只有0和1两种。
在钉钉上班签到打卡的场景中,我们只用记录有签到(1)或没签到(0)——————————————bitmap应运而生
基数统计
指统计⼀个集合中不重复的元素个数————————————————————hyperloglog应运而生
bitmap
——本质是string类型(位图)
Bit arrays(or simply bitmaps,我们可以称之为位图),由0和1状态表现的二进制位的bit数组
一个字节(1 Byte)=8位,上图中每一个小格子就是一个个的bit,只能放1或0,用来判断Y/N状态
用于状态统计,Y,N类似AtomicBoolean
命令 | 作用 | 时间复杂度 |
---|---|---|
setbit key offset val | 给指定key的值的第offset赋值val | O(1) |
getbit key offset | 获取指定key的第offset位 | O(1) |
bitcount key start end | 返回指定key中【start,end】中为1的数量 | O(n) |
bitop operation destkey key | 对不同的二进制存储数据进行位运算(AND,OR,NOT,XOR) | O(n) |
bitmap的偏移量从零开始算的
-
按年去存储一个用户的签到情况,365 天只需要 365 / 8 ≈ 46 Byte,1000W 用户量一年也只需要 44 MB 就足够了。
-
假如是亿级的系统, 每天使用1个1亿位的Bitmap约占12MB的内存 (10^8/8/1024/1024),10天的Bitmap的内存开销约为120MB,内存压力不算太高。在实际使用时,最好对Bitmap设置过期时间,让Redis自动删除不再需要的签到记录以节省内存开销。
本质是string类型,实质上是二进制的ASCII编码对应
两个setbit命令对k1进行设置后,对应的二进制串就是0100 0001,二进制串就是0100 0001对应的10进制就是65
扩容机制
不是字符串长度而是占据几个字节,超过8位后自己按照8位一组一byte再扩容
一年365天,全年天天登陆占用多少字节
bitop
连续2天都签到的用户
假如某个网站或者系统,它的用户有1000W,做个用户id和位置的映射
比如0号位对应用户id:uid-092iok-lkj
比如1号位对应用户id:uid-7388c-xxx
应用场景
需求:
-
用户是否登陆过,Y,N比如京东每日签到送京豆
-
电影、广告是否被点赞播放过
-
钉钉打卡上班,签到统计
-
vcr用户是否点击:setbit vcr:01 [userid] 1
案例
日活统计,连续签到打卡,最近一周的活跃用户,统计指定用户一年之中的登录天数,某用户按照一年365天,哪几天登陆过?哪几天没有登陆过?全年中登录的天数共计多少?
京东签到领京东
使用mysql明显难以落地实现
签到用户量较小时这么设计能行,但京东这个体量的用户(估算3000W签到用户,一天一条数据,一个月就是9亿数据) 对于京东这样的体量,如果一条签到记录对应着当日用记录,那会很恐怖...... 如何解决这个痛点?
1 一条签到记录对应一条记录,会占据越来越大的空间。
2 一个月最多31天,刚好我们的int类型是32位,那这样一个int类型就可以搞定一个月,32位大于31天,当天来了位是1没来就是0。
3 一条数据直接存储一个月的签到记录,不再是存储一天的签到记录。
在签到统计时,每个用户一天的签到用1个bit位就能表示,
一个月(假设是31天)的签到情况用31个bit位就可以,一年的签到也只需要用365个bit位,根本不用太复杂的集合类型
HyperLogLog(统计)
名词介绍
-
UV—Unique View 独立访客,一般理解为客户IP 需要去重
-
PV—Page View 页面浏览量,不用去重(Bitmap)
-
DAU—Daily Active User 日活跃用户量,登录或者使用了某个产品的用户数(去重复登录的用户),常用于反应网站、互联网应用或者网络游戏的运营情况
-
MAU—Month Active User 月活跃用户量
产生背景需求
-
统计某个网站的UV,统计某个文章的UV
-
用户搜索网站关键词的数量
-
统计用户每天搜索不同词条个数
去重复统计功能的基数[1]估计算法就是HyperLogLog,Redis在2.8.9版本中添加了HyperLoglog结构。它是用来做基数统计的算法,Hyperloglog的优点是在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的,并且是很小的。
在redis里面,每个Hyperloglog键只需要花费12KB内存[2]就可以计算接近264个不同元素的基数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。
但是,因为HyperLogLog只会根据输入元素来计算基数,而不会存输入元素本身,所以HyperLogLog不能像集合那样,返回输入的各个元素。
对于去重,我们可能首先会想到的是
-
hashset,redis的set,hash
但是在实际使用上对于亿级的统计,HashSet由于Hash冲突以及占用的内存大小,肯定是不能采用的,redis的set同理,占用体积过大。redis——hash = <keyDay,<ip,1>> 按照ipv4的结构来说明,每个ipv4的地址最多是15个字节(ip = "192.168.111.1",最多xxx.xxx.xxx.xxx) ,某一天的1.5亿 * 15个字节= 2G,一个月60G,redis死定了
-
bitmap,将ip与偏移量一一对应,似乎可以,但是
如果数据显较大亿级统计,使用bitmaps同样会有这个问题。
bitmap是通过用位bit数组来表示各元素是否出现,每个元素对应一位,所需的总内存为N个bit。
基数计数则将每一个元素对应到bit数组中的其中一位,比如bit数组010010101(按照从零开始下标,有的就是1、4、6、8)。
新进入的元素只需要将已经有的bit数组和新加入的元素进行按位或计算就行。这个方式能大大减少内存占用且位操作迅速。But,假设一个样本案例就是一亿个基数位值数据,一个样本就是一亿,如果要统计1亿个数据的基数位值,大约需要内存100000000/8/1024/1024约等于12M,内存减少占用的效果显著。
这样得到统计一个对象样本的基数值需要12M。
如果统计10000个对象样本(1w个亿级),就需要117.1875G将近120G,可见使用bitmaps还是不适用大数据量下(亿级)的基数计数场景,
但是bitmaps方法是精确计算的。
-
结论
样本元素越多内存消耗急剧增大,难以管控+各种慢,对于亿级统计不太合适,大数据害死人,于是HyperLogLog登场,通过牺牲准确率来换取空间,对于不要求绝对准确率的场景下可以使用,因为概率算法不直接存储数据本身,通过一定的概率统计方法预估基数值,同时保证误差在一定范围内,由于又不储存数据故此可以大大节约内存。HyperLogLog就是一种概率算法的实现。
原理
只是进行不重复的基数统计,不是集合也不保存数据,只记录数量而不是具体内容。有误差,非精确统计,牺牲准确率来换取空间,误差仅仅只是0.81%左右 Redis new data structure: the HyperLogLog -
为什么redis集群的最大槽数是16384个?
Redis集群并没有使用一致性hash而是引入了哈希槽的概念。 Redis 集群有16384个哈希槽 ,每个key通过CRC16校验后对16384取模来决定放置哪个槽,集群的每个节点负责一部分hash槽。但为什么哈希槽的数量是16384(214)个呢?
CRC16算法产生的hash值有16bit,该算法可以产生216=65536个值。
换句话说值是分布在0~65535之间。那作者在做mod运算的时候,为什么不mod65536,而选择mod16384?
why redis-cluster use 16384 slots? · Issue #2576 · redis/redis (github.com)
正常的心跳数据包带有节点的完整配置,可以用幂等方式用旧的节点替换旧节点,以便更新旧的配置。
这意味着它们包含原始节点的插槽配置,该节点使用2k的空间和16k的插槽,但是会使用8k的空间(使用65k的插槽)。
同时,由于其他设计折衷,Redis集群不太可能扩展到1000个以上的主节点。
因此16k处于正确的范围内,以确保每个主机具有足够的插槽,最多可容纳1000个矩阵,但数量足够少,可以轻松地将插槽配置作为原始位图传播。请注意,在小型群集中,位图将难以压缩,因为当N较小时,位图将设置的slot / N位占设置位的很大百分比。
-
如果槽位为65536,发送心跳信息的消息头达8k,发送的心跳包过于庞大。
在消息头中最占空间的是myslots[CLUSTER_SLOTS/8]。 当槽位为65536时,这块的大小是: 65536÷8÷1024=8kb 因为每秒钟,redis节点需要发送一定数量的ping消息作为心跳包,如果槽位为65536,这个ping消息的消息头太大了,浪费带宽。
-
redis的集群主节点数量基本不可能超过1000个。
集群节点越多,心跳包的消息体内携带的数据越多。如果节点过1000个,也会导致网络拥堵。因此redis作者不建议redis cluster节点数量超过1000个。 那么,对于节点数在1000以内的redis cluster集群,16384个槽位够用了。没有必要拓展到65536个。
-
槽位越小,节点少的情况下,压缩比高,容易传输
Redis主节点的配置信息中它所负责的哈希槽是通过一张bitmap的形式来保存的,在传输过程中会对bitmap进行压缩,但是如果bitmap的填充率slots / N很高的话(N表示节点数),bitmap的压缩率就很低。 如果节点数很少,而哈希槽数量很多的话,bitmap的压缩率就很低。
命令
命令 | 作用 |
---|---|
pfadd key element ... | 将所有元素添加到key中 |
pfcount key | 统计key的估算值(不精确) |
pgmerge new_key k1 k2 | 合并key到新key中 |
The API is constituted of three new commands:
PFADD var elment element ... element
PFCOUNT var
PFMERGE dst src src src src ... src
The commands prefix is "PF" in honor of Philippe Flajolet
应用场景
UV的统计需要去重,一个用户一天内的多次访问只能算作一次
淘宝、天猫首页的UV,平均每天是1~1.5个亿左右
每天存1.5个亿的IP,访问者来了后先去查是否存在,不存在加入
GEO(地理)
本质使用zset
引言
移动互联网时代LBS应用越来越多,交友软件中附近的小姐姐、外卖软件中附近的美食店铺、打车软件附近的车辆等等,那这种附近各种形形色色的XXX地址位置选择是如何实现的?
地球上的地理位置是使用二维的经纬度表示,经度范围 (-180, 180],纬度范围 (-90, 90],只要我们确定一个点的经纬度就可以确定他在地球的位置 。
例如滴滴打车,最直观的操作就是实时记录更新各个车的位置, 然后当我们要找车时,在数据库中查找距离我们(坐标x0,y0)附近r公里范围内部的车辆,使用如下sql即可:select taxi from position where x0-r < x < x0 + r and y0-r < y < y0+r
,但是这样做会有什么问题呢?
-
查询性能问题,如果并发高,数据量大这种查询是要搞垮数据库的
-
这个查询的是一个矩形访问,而不是以我为中心r公里为半径的圆形访问。
-
精准度的问题,我们知道地球不是平面坐标系,而是一个圆球,这种矩形计算在长距离计算时会有很大误差
命令
-
GEOADD——多个经度(longitude)、纬度(latitude)、位置名称(member)添加到指定的 key 中
用于存储指定的地理空间位置,可以将一个或多个经度(longitude),纬度(latitude),位置名称(member)添加到指定的key中,语法如下
GEOADD key longitude latitude member [longitude latitude member ...] ex: > GEOADD city 116.403963 39.915119 "天安门" 116.403414 39.924091 "故宫" 116.024067 40.362639 "长城" > type city zset > zrange city 0 -1
-
GEOPOS 从键里面返回所有给定位置元素的位置(经度和纬度)
用于从给定的key里面返回所有指定名称(member)的位置(经度和纬度),不存在返回nil,语法如下:
GEOPOS key member [member ...] ex: > GEOPOS city 天安门 故宫 116.403963 39.915119 116.403414 39.924091
-
GEODIST 返回两个给定位置之间的距离,语法如下
GEODIST key member1 member2 [m|km|ft|mi] ex: > GEODIST city 天安门 长城 km 59.3390 > GEODIST city 天安门 长城 m 59338.9814
后面参数是距离单位:m 米 km 千米 ft 英尺 mi 英里
-
GEORADIUS 以给定的经纬度为中心, 返回与中心的距离不超过给定最大距离的所有位置元素。
应用场景:以半径为中心,查找附近的XXX
GEORADIUS 以给定的经纬度为中心, 返回键包含的位置元素当中,与中心的距离不超过给定最大距离的所有位置元素。
ex: GEORADIUS city 116.418017 39.914402 10 km withdist withcoord count 10 withhash desc
-
WITHDIST: 在返回位置元素的同时, 将位置元素与中心之间的距离也一并返回。 距离的单位和用户给定的范围单位保持一致。
-
WITHCOORD: 将位置元素的经度和维度也一并返回。
-
WITHHASH: 以 52 位有符号整数的形式, 返回位置元素经过原始 geohash 编码的有序集合分值。 这个选项主要用于底层应用或者调试, 实际中的作用并不大
-
COUNT 限定返回的记录数。
-
-
GEORADIUSBYMEMBER
跟GEORADIUS类似,找出位于指定范围内的元素,中心点是由给定位置的元素决定
-
GEOHASH返回一个或多个位置元素的 Geohash 表示
使用GEOHASH来保存地理位置的坐标,用于获取一个或多个位置元素的geohash值。语法格式如下
GEOHASH key member [member ...] > GEOHASH city 天安门 故宫 长城 wx4gOf6f2vO wx4gOgfqsjO wx4t85y1ktO
核心思想就是将球体转换为平面,区块转换为一点 ,主要分为三步
-
将三维的地球变为二维的坐标
-
在将二维的坐标转换为一维的点块
-
最后将一维的点块转换为二进制再通过base32编码
-
应用场景
美团地图位置的酒店推送
-
微信附近的人或者一公里内的各种营业厅,加油站,理发店,超市
-
找单车
-
附近的酒店
Stream
Redis Stream 是 Redis 5.0 版本新增加的数据结构。 Redis Stream 主要用于消息队列(MQ,Message Queue),
Redis 本身是有一个 Redis 发布订阅 (pub/sub) 来实现消息队列的功能,但它有个缺点就是消息无法持久化,如果出现网络断开、Redis 宕机等,消息就会被丢弃。简单来说发布订阅 (pub/sub) 可以分发消息,但无法记录历史消息。 而 Redis Stream 提供了消息的持久化和主备复制功能,可以让任何客户端访问任何时刻的数据,并且能记住每一个客户端的访问位置,还能保证消息不丢失。它算是redis自己消息功能的补充。
但是, 一般主流MQ都固定了(Kafka/RabbitMQ/RocketMQ/Pulsar) 。