前言:
上篇博客我们讲到redis五大基本数据类型(也是就下图的第一列)。
【速成Redis】02 Redis 五大基本数据类型常用命令-CSDN博客文章浏览阅读1k次,点赞24次,收藏10次。该篇适用于速成redis。本篇我们将讲解:redis五大基本数据类型的常用语句。https://blog.csdn.net/weixin_71246590/article/details/142366022?spm=1001.2014.3001.5501
这篇博客我们将介绍右边这一列高级数据类型。
食用说明:初学者建议边看边看边敲。复习者可根据目录快速复习某个命令。
目录
1.发布订阅功能
- 订阅某频道:subscribe channel
- 在某频道发布信息:publish channel message
2.消息队列Stream
A:流
- 添加消息:XADD [MAXLEN ] [ ...]
- 求消息个数 :XLEN
- 查看所有消息:XRANGE - +
- 根据消息id删除消息:XDEL message_id
- 删除所有消息:xtrim maxlen 0
- 读消息 :XREAD COUNT BLOCK STREAMS
B:消费者组
- 创建一个新的消费者组 :XGROUP CREATE [MKSTREAM]
- 查询特定流上的消费者组信息:XINFO GROUPS mystream
- 创建消费者:XGROUP CREATECONSUMER key group consumer [MKSTREAM]
- 消费者从消费者组中读取消息:XREADGROUP GROUP COUNT BLOCK STREAMS
- 消费者确认某条信息:XACK [id ...]
- 查看消费者组中待处理的消息(还未被 XACK 确认)XPENDING [ []]
3.地理空间Geospatial
- GEOADD key longitude latitude member [longitude latitude member ...] :将地理空间数据(经纬度坐标)添加到 Redis 集合中
- 获取集合中两个点之间的距离::GEODIST key member1 member2
- GEOREASH 以一个成员的位置或指定的经纬度为中心,按照圆形或者矩形的范围内搜索其他成员。
4.HyperLogLog
- PFADD key member [merber ...] 将指定的元素添加到 HyperLogLog 中,自动去重
-PFCOUNT key:查看基数/返回与指定 HyperLogLog 相关联的唯一元素的近似计数。
- PFMERGE destkey sourcekey [sourcekey ...] :将多个 HyperLogLog 合并为一个
5.位图BitMap
- SETBIT key offset value :设置指定位置上的位 (bit) 为 0 或 1
- GETBIT key offset :获取指定位置上的值
- SET key value
value: 要存储的字符串值(可以是二进制、十进制、十六进制等形式)。
- BITCOUNT key [start end]:计算字符串中的 比特位值为 1 的总数
扩展:为什么BITCOUNT 命令在统计某种状态(如签到、点赞等)时特别好用?
- BITPOS key bit [start] [end] :查找第一个设置为指定值的位(bit)的位置
常见应用场景:
6.位域BitField
BITFIELD key [GET|SET|INCRBY] type offset [value] [overflow]
1.发布订阅功能
- 订阅某频道:subscribe channel
subscribe 频道名
在一个终端订阅daimajiang频道
- 在某频道发布信息:publish channel message
publish 某频道 xx信息
在daimajiang频道发布信息:redis
第一个终端成功收到信息
订阅频道的终端可以有多个 ,发布信息的终端也可以有多个。
发布订阅功能的局限性:消息无法持久化、无法记录历史消息等等。下面学的消息队列Stream就能解决这些问题。
2.消息队列Stream
这是redis5.0版本引入的一个新的数据结构,一个轻量级的消息队列。
命令都以x开头。
A:流
- 添加消息:XADD <stream-name> [MAXLEN <maxlen>] <id> <field1> <value1> [<field2> <value2> ...]
参数说明:
- <stream-name> :要追加消息的流的名称。如果流不存在,Redis 会自动创建它。
- [MAXLEN <maxlen>] :可选参数,用于限制流的长度。它会在追加新消息时自动删除旧消息,保持流的长度在指定的最大值之内。
- <id>:消息的唯一标识符。你可以使用特殊值
*
,由 Redis 自动生成一个基于时间戳的唯一 ID。也可以自己填。- <field1> <value1>:消息的字段和值对。每个消息至少包含一个字段和值对。
示例:
使用*来自动生成消息id。
回响信息就是生成的消息id。
如果使用*生成id的话,redis可以保证id是自增的,手工指定id需要自己保证id自增。
- 求消息个数 :XLEN <stream-name>
表示该流有三个消息。
- 查看所有消息:XRANGE <stream-name> - +
- 根据消息id删除消息:XDEL <stream-name> message_id
- 删除所有消息:xtrim <stream-name> maxlen 0
- 读消息 :XREAD COUNT <count> BLOCK <milliseconds> STREAMS <stream-name> <id>
参数说明:
COUNT
:指定读取的消息数量。BLOCK
:阻塞读取,指定等待新消息的时间,单位是毫秒。设置为0
表示无限等待。STREAMS
:指定从哪些流读取消息。<id>
表示从哪个位置开始读取消息,通常0
表示读取所有消息。
示例:
表示从daimajiang 流里读取两条信息,从下标0开始读,最多等待1s。
$
:表示从流中的最新消息开始读取,并且只读取新插入的消息。此时可以在另一个终端执行xadd操作,这边就能收到最新消息。
B:消费者组
消费者组是一种管理 Redis Streams(流)中的消息处理的机制。
在 Redis 中,一个消费者组不能直接关联到多个流。每个消费者组(consumer group)是与一个特定的消息流(stream)绑定的,消费者组会从这个绑定的流中读取消息并处理。
可以理解为我们通过消费者组,来对流中的信息进行控制。
- 创建一个新的消费者组 :XGROUP CREATE <stream> <group> <id> [MKSTREAM]
<stream>
:Stream 的名称。<group>
:消费者组名称。<id>
:从哪个位置开始读取消息(通常为$
,表示从当前的最新消息开始;或者为0
,表示从Stream的最开始读取消息)。MKSTREAM
:如果 Stream 不存在,则自动创建一个空的消息流。
示例:
创建名为group1的消费者组,并指示它从
daimajiang
流中的第一个消息(ID为0)开始读取。
XGROUP CREATE myStream myGroup $ MKSTREAM
该命令用于创建一个名为
myGroup
的消费者组,关联到myStream
流中,并从最新的消息(ID为$
)开始消费。如果流myStream
不存在,它会通过MKSTREAM
选项自动创建一个空的流。
- 查询特定流上的消费者组信息:XINFO GROUPS mystream
示例:
1) "name" # 消费者组的名称
2) "consumers" # 该组中的消费者数量
3) "pending" # 消费者组中尚未确认的消息数量
4) "last-delivered-id" # 消费者组最后一个交付的消息 ID
- 创建消费者:XGROUP CREATECONSUMER key group consumer [MKSTREAM]
示例:
将新的消费者 consumer1 、consumer2、consumer3加入到名为group1 的消费组中,消费组的标识流是
geekhour
。ps:createconsumer关键字需要6.2以上才能使用,消费者不需要预先创建。
- 消费者从消费者组中读取消息:XREADGROUP GROUP <group> <consumer> COUNT <count> BLOCK <milliseconds> STREAMS <stream> <id>
<group>
:消费者组的名称。<consumer>
:消费者的名称。<count>
:读取的消息条数。<stream>
:消息流的名称。<id>
:消息的 ID,通常使用>
表示读取新消息。
示例:
GROUP group1:组名称
consumer1:消费总金额名称
COUNT 2:表示一次读取两条消息
BLOACK 3000 :如果没有消息阻塞3000ms
STREAMS geekhour:流名称
>:表示从这个消息中读取最新消息
- 消费者确认某条信息:XACK <stream> <group> <id> [id ...]
消费者确认已处理某条消息,Redis 会将该消息从待处理列表(PEL)中移除。
<stream>
:Stream 的名称。<group>
:消费者组的名称。<id>
:消息的 ID。
示例:
XACK myStream myGroup 1526569495631-0
- 查看消费者组中待处理的消息(还未被 XACK
确认)XPENDING <stream> <group> [<start> <end> <count> [<consumer>]]
<stream>
:Stream 的名称。<group>
:消费者组的名称。<start>
和<end>
:消息 ID 范围。<count>
:返回的消息数量。<consumer>
:消费者名称(可选)。
示例:
XACK myStream myGroup 1526569495631-0
3.地理空间Geospatial
Redis3.2版本中的新特性,它提供了存储地理位置信息的数据结构,同时支持对地理位置进行各种计算操作。比如计算两个地理位置的距离,获取某个地理位置的经纬度等。下面使用一个经纬的度的例子展示它的使用方法。
相关命令都以GEO开头
- GEOADD key longitude latitude member [longitude latitude member ...] :将地理空间数据(经纬度坐标)添加到 Redis 集合中
返回1表示成功添加了一个地理位置信息。
将北京的经纬度信息添加到city这个集合里。
也可以一次性添加多个地理位置信息:
添加上海、深圳、广州、杭州的地理位置信息。
返回4表示成功添加4个地理位置信息。
- 查看经纬度:GEOPOS key member
这里可以看到查看的经纬度和存的值不一样,原因是:由于底层是二进制存储,小数存储会出现误差。
Redis 使用
geohash
算法将经纬度转换为一个 52 位的二进制字符串来存储。这种编码方式以空间分割的方法将地球表面划分成格子,但它有一定的精度限制。由于每个格子在不断划分的过程中,最终存储的经纬度数据会有一定的精度误差。
- 获取集合中两个点之间的距离::GEODIST key member1 member2
- GEOREASH 以一个成员的位置或指定的经纬度为中心,按照圆形或者矩形的范围内搜索其他成员。
搜索距离上海300km以内的城市,返回上海。杭州两个城市。
这里是简单了解这个数据结构,就不展示其他命令了。
4.HyperLogLog
HyperLogLog是用于做基数统计的算法,并不是redis特有算法。
什么是基数:集合中唯一且不重复的元素个数。
集合里的唯一且不重复的数字:1 、2、3、4、5。则基数为5
虽然集合里有10个元素,但基数还是5。
HyperLogLog就是用来计算这个基数的。 它的原理使用随机算法来计算,通过牺牲一定的精确度,来换取更小的内存消耗。优点是占用内存小,缺点就是会有一定的误差。所以它适用于一些精确度要求不高,而且数据量非常大的统计操作。例如:统计某个网站的uv、统计某个词的搜索次数。
命令都以PF开头。
- PFADD key member [merber ...] 将指定的元素添加到 HyperLogLog 中,自动去重
-PFCOUNT key:查看基数/返回与指定 HyperLogLog 相关联的唯一元素的近似计数。
- PFMERGE destkey sourcekey [sourcekey ...] :将多个 HyperLogLog 合并为一个
5.位图BitMap
位图是字符串类型的扩展, 可以使用String类型模拟bit数组。
数组下标就是偏移量,数组值只支持0或1。
应用场景:记录用户签到情况、点赞情况、收藏情况、在线状态等。
所有命令都以Bit开头。
- SETBIT key offset value :设置指定位置上的位 (bit) 为 0 或 1
如图设置0下标的值为1,设置1下标的值为0。
- GETBIT key offset :获取指定位置上的值
如图,获取1位置上的值为0。
现在已经学会了如何设置和获取位图的值,但是像这样一位一位设置显然是非常麻烦的。
BITMAP其实就是String类型的拓展,本质上就是一个字符串。我们可以用字符串的SET命令来直接设置它的值。
- SET key value
value: 要存储的字符串值(可以是二进制、十进制、十六进制等形式)。
示例:
key这个值存储的实际是"10"这个字符串
字符
'1'
在 ASCII 表中的编码是49
,其二进制形式是00110001
。字符
'0'
在 ASCII 表中的编码是48
,其二进制形式是00110000
。也就是说其位图表示是:
001100010110000
尝试getbit时:
可以看到确实是这样。
ps:在redis中,字符串的本质是字节组成,一个字节有8个bit。
Redis 默认使用 UTF-8 编码来处理字符串。UTF-8 是一种可变长度的字符编码,它能够表示世界上几乎所有的字符,包括 ASCII 字符。
UTF-8 对于常见的 ASCII 字符(例如英文字母、数字和一些符号)使用一个字节表示,而对于其他字符(如中文、表情符号等)可能使用多个字节表示。
因此通过set设置位图,设置的位数都是8的倍数(因为一个字节有8个bit)。
对于11110000,我们可以通过set快速存入,技巧是存入其16进制。
11110000用十进制表示是240,用十六进制表示是\xF0
(存入的效果图)
- BITCOUNT key [start end]:计算字符串中的 比特位值为 1 的总数
start(可选):指定从字符串的某个字节开始计算。
end(可选):指定到字符串的某个字节结束计算。
扩展:为什么BITCOUNT 命令在统计某种状态(如签到、点赞等)时特别好用?
1. 高效的存储方式:
位图(Bitmap)是一种非常节省空间的数据结构。由于每个用户的状态(如是否签到或点赞)可以用 1 个 bit(位)来表示,Redis 可以在内存中高效地存储大量的用户状态信息。
比如,一个用户的签到状态可以用一个
1
(表示已签到)或0
(表示未签到)来表示。如果有 1 万个用户,只需要 1 万个位(即 1250 字节,约 1.2 KB)即可存储所有用户的状态。相比用其他数据结构(如字符串、哈希等)存储每个用户的状态,位图更节省内存。
2. 快速统计:
BITCOUNT可以快速统计整个位图中比特值为
1
的数量,这使得它在统计用户行为时非常高效。
例如,如果你有 1 万个用户的签到数据存储在位图中,你可以通过BITCOUNT 在瞬间统计出已签到的用户总数,而不需要遍历每个用户的状态。
BITCOUNT
dianzan # 统计所有用户中已点赞(bit 为 1)的数量
这个操作时间复杂度为 O(N),N 是字节数,而不是位的数量,因此即使数据量非常大,BITCOUNT依然能高效执行。
3. 可扩展性强:
位图在用户量扩展时依然保持高效。例如:
假设你要跟踪一百万用户的点赞状态,每个用户的状态只需 1 bit,整个数据仅占用 125 KB(1,000,000 bits ÷ 8 ÷ 1024),而 BITCOUNT依然可以在极短的时间内统计出有多少人点赞。
4. 灵活的范围统计:
BITCOUNT 允许你根据字节范围统计。例如,你可以指定只统计某段用户的数据,而不必一次性统计全部数据。这对于分批处理或特定用户组的统计非常有用。
5. 简化业务逻辑:
通过 BITCOUNT,你可以直接在 Redis 中进行统计操作,而无需将数据取回到应用层再进行计算,减少了网络开销和应用层的复杂度。这使得 Redis 成为一个高效的分布式计数工具。
使用场景:
- 签到系统:每个用户一天的签到状态可以用 1 个 bit 表示,BITCOUNT 能统计出当天签到的用户总数。
- 点赞系统:每个用户的点赞行为也可以用 1 个 bit 表示,通过 BITCOUNT 可以统计总的点赞人数。
- 任务完成情况:可以用位图来跟踪每个用户任务的完成状态,用 BITCOUNT 统计完成任务的用户数量
- BITPOS key bit [start] [end] :查找第一个设置为指定值的位(bit)的位置
主要优点:可以在大量数据中快速定位。
start(可选):从字符串的哪个字节开始查找。
end(可选):在字符串的哪个字节结束查找。
如图,查找第一个出现0和1的位置。(整个范围没有时候返回-1)
常见应用场景:
查找用户签到的第一个日期: 比如,一个用户的签到状态存储在位图中,
1
表示签到。通过 BITPOS,可以快速找到用户第一次签到的日期。查找任务完成情况: 位图可以用来表示任务完成状态,BITPOS能帮助快速查找第一个完成任务(
1
)或第一个未完成任务(0
)的位置。找到可用资源: 在某些分布式系统中,位图可以表示资源的使用状态,BITPOS 可用于查找第一个可用资源的位置(比如第一个
0
表示未占用的资源)。
6.位域BitField
位域能将很多小的整数存储到一个较大的位图中,这样可以更高效的使用内存。
常用操作:
BITFIELD key [GET|SET|INCRBY] type offset [value] [overflow]
- key: 字符串类型的键名。
- GET: 获取位域的值。
- SET: 设置位域的值。
- INCRBY: 对位域的值进行增减操作。
- type: 位域的类型,可以是
u<N>
(无符号整数)或i<N>
(有符号整数),<N>
是位数,比如u8
表示无符号 8 位整数。- offset: 位域的偏移量,即从字符串的哪个位开始。
- value: 用于
SET
和INCRBY
操作时要设置或增加的值。- overflow(选填): 溢出处理模式(仅对
INCRBY
有效),可以是WRAP
(回绕)、SAT
(饱和)或FAIL
(失败)。
实操:
假设在玩一个游戏,你作为1号玩家进入了游戏,你刚刚出生在新手村:
等级是1级,兜里有金币100个,经验为0。
设置player:1的等级。
查看该key:
那如何获取这个等级呢?把set换成get
现在我们来设置金币数:
注意:
SET u32 #1 100表示:
将player:1
键的第二个 32 位无符号整数(偏移#1
,即从第 32 位到第 63 位)设置为100
。tips:'d'的ASCII码值就是100,因此100转换为字符型存储就是d。
来用get获取一下金钱:
假设你现在干掉了一个小boss,走出了新手村,等级变成了二级,金币也多了100、
依然是使用bitfield关键字~只是换成incrby