[redis] 10 种数据结构详解

2022/2/11 2:13:03

本文主要是介绍[redis] 10 种数据结构详解,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

[redis] 10 种数据结构详解

简介

5种常见数据结构

  • string: 最常见的 string key value
  • list: 双向链表
  • set: 集合-
  • zset: 有序集合
  • hash: 类似 Java HashMap 和 golang 的 map 有 2 级索引
原语------介绍常见场景
stringstring key/value 最常见的数据类型最常用缓存; 分布式锁; 储存日志; id生成器; 计数器; 限速限流;
hashhashmap(成员少时数组, 多时升级为hashmap)更加细粒度缓存对象
list实现是双向链表或者说一个队列消息队列; 待办事项;
set不重复的列表, 自动去重, 类似数据集合概念(内部实现是一个 value 为 null 的 hashmap)唯一计数器; 打标签; 点赞;投票; 社交关系; 推荐商品;
zset有序的set, 提供额外的 score 参数来排序(内部实现是 hashmap 和 跳表)排行榜; 推荐商品; 自动补全(字典排序)

5种其他数据结构

  • hypperLoglog: 基数统计
  • bitmap: 位图(布隆过滤器)
  • geo: 地理位置坐标
  • pub/sub: 发布订阅
  • stream: 流(主要用于消息队列, 解决 pub/sub 发布订阅没有消息持久化的问题)
原语介绍场景
hpperLogLog唯一计数器(12k就能计数2^64个成员, 计数估计算法误差0.81%)网站访客量ip; 检测垃圾信息;每周/月度/年度计数器
bitmapbit数组, 用户可以通过偏移量(offset)对一个或者多个二进制进行位操作布隆过滤器
geo地理坐标, 通过坐标距离计算范围查找记录坐标; 附近的用户;
pub/sub发布订阅, 消息队列(不能持久化)发布队列
stream消息队列(解决 list, zset 不能范围操作, pub/sub发布订阅不能持久化的问题)消息队列

string key value

string 是 redis 最常用最基本的数据结构, 不仅可以储存 string, 数字, 也可以是图片, 音频等更复杂的二进制数据

1. 储存结构

image-20220209125328895

2.原语

SET key value[EX seconds|PX millisecods|EXAT timestamp|PXAT milliseconds-timestamp|KEEPTTL] [NX|EX] [GET]
原语介绍
SET key value设置key value 值
GET key获取 key value值
DEL key删除key value值(通用的方法能删除其他数据结构)
SET key value NX设置值, key 不存在才能设置成功
SETNX key value设置值, key 不存在才能设置成功(同上)
SET key value XX设置值, key 存在才能设置成功
SETXX key value设置值, key 存在才能设置成功(同上)
SET key value EX seconds设置值和过期时间, 单位秒
SET key value PX milliseconds设置值和过期时间, 单位毫秒
EXPIRE key seconds设置过期时间, 单位秒
INCR key递增 1 , 如果 value 不是数字或者越界会报错
DNCR key递减少 1, 如果 value 不是数字或者越界会报错
INCRBY key integer递增 integer
DNCRBY key integer递减 integer
GETSET key value设置值并返回旧值
GETSET key valueSET并GET旧的值
MGET key [key...]批量 GET 一般搭配 keys 批量获取键来使用
STRLEN key获取 value 长度
GETBIT key offset得到 value 的 offset 偏移
INCRBY key integer给 value 增加 number ; value = value + number
DECRBY key intergervalue 减少 number ; value = value - number
INCRBYFLOAT key increment由给定的数量递增键的浮点值
APPEND key value追加一个value到value的末尾

hash 散列

hash 理解为一个 string 嵌套了一个 string 能执行 string 的大部分操作

1. 解决的问题

我们通过多个 string kv 去储存一个 user 对象

"user:1:name"   ->  "张三"
"user:1:age"    ->   13
"user:2:name"   ->  "李四"
...

问题:

  • 多key: redis 会产生很多键 key, key 过多会拖慢整个 redis
  • 可读性差
  • 浪费内存: key 也要空间, 前缀是重复的
  • 管理困难: 如果要删除一个缓存就需要将所有字段的 key 都删除, 比如有 2 个字段就需要删除 2 个, 比如有 10 个字段就需要删除 10 个

2. 散列的储存结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6OQ9kuE7-1644494146691)(images/image-20220209113746280.png)]

3. 原语

原语介绍
HSET key field value [field value...]为字段设置值(不存在 field 会新建并返回 1 ,存在 field 覆盖并返回0)
HGET key filed获取值(获取不到结果会返回 nil)
HDEL key field删除 key 的 filed
HSETNX key field value只在字段不存在的情况下为它设置值(filed 存在将会失败并返回 0)
HINCRBY key field integer字段递增递减
HINCRBYFLOAT key field float字段递增浮点数
HSTRLEN key field获取字段长度
HEXISTS key field检查字段是否存在
HLEN key获取 key 字段个数
DEL key删除 key
HMSET key field value [field value...]批量设置 HSET 也能这样用
HMGET key filed [field..]批量 get field
HKEYS key获取 key 所有字段
HVALS key获取 key 所有 value
HGETALL key获取 key 所有的字段和value

list 列表

实现是双向链表或者说一个队列

1.储存结构

image-20220209132123723

2.原语

原语介绍
LPUSH key element [element...]将成员推入列表左端
RPUSH key element [element...]将成员推入列表右端
LPUSHX key element [element...]将成员推入列表左端(key不存在返回0)
RPUSHX key element [element...]将成员推入列表右端(key不存在返回0)
LPOP key****弹出列表最左端的成员
RPOP key****弹出列表最右端的成员
RPOPLPUSH key将右端弹出的成员推入左端
LLEN key获取列表的长度
LINSERT key BEFORE|AFTER pivot element插入成员, pivot 是成员中点成员的值
LRANGE key start stop得到范围的成员集合, stop 为 -1 代表所有成员
LTRIM key start stop修建列表, 移除范围之类的所有成员
LREM key count element移除成员, count = 0 移除所有,count>0移除 count 个数
BLPOP key timeout阻塞式左端弹出
BRPOP key timeout阻塞式右端弹出
BRPOPLPUSH key timeout阻塞式将右端弹出的成员推入左端

set 集合

不重复的列表, 自动去重, 类似数据集合概念(内部实现是一个 value 为 null 的 hashmap)

1.储存结构

(内部实现是一个 value 为 null 的 hashmap)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dHrOdotu-1644494146692)(images/image-20220209143243515.png)]

2.原语

原语介绍
SADD key element [element...]将成员添加到集合
SREM key element从集合中移除成员
SMOVE source target element将成员从一个集合移动到另一个集合
SMEMBERS key获取集合中所有成员
SCARD key获取集合成员数量
SISMEMBER key element成员是否属于该集合
SRANDMEMBER key [count]随机获得集合成员, count 默认 为 1
SPOP key [count]随机弹出集合成员, count 默认 为 1
SINTER key [key...]返回多个集合的交集
SINTERSTORE target [key...]多个集合的交集储存到一个新的集合,返回新集合个数
SUNION [key...]返回多个集合的并集
SUNIONSTORE target [key...]多个集合的并集储存到一个新的集合,返回新集合个数
SDIFF [key...]返回多个集合的差集
SDIFFSTORE target [key...]多个集合的差集储存到一个新的集合,返回新集合个数

zset 有序集合

通过新增 score 字段来排序, -inf 和 +inf 代表正无穷和负无穷的分数, 分数一致的成员会使用字典排序

1.储存结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-td9ar9CF-1644494146692)(images/image-20220209164359582.png)]

2.原语

原语介绍
ZADD key score element[score element...]添加或者更新成员
ZREM key element移除成员
ZSCORE key element获取成员的分数
ZINCRBY key score element增加成员分数
ZCARD key获取集合成员个数
ZRANK key element获取成员升序排名
ZREVRANK key element获取成员降序排名
ZRANGE key start end获取排名区间成员(升序)
ZREVRANGE key start end获取排名区间成员(降序)
ZRANGEBYSCORE key min max获取分数区间的成员(升序)
ZREVRANGEBYSCORE key max min获取分数区间的成员(降序)
ZCOUNT key min max获取分数区间的成员个数(升序)
ZREMRANGEBYRANK key start end移除排名区间的成员(升序), 负数可以实现倒数
ZREMRANGEBYSCORE key min max移除分数区间的成员(升序)
ZUNIONSTORE target key [key...]多个集合的并集储存到一个新的集合,相同的 element 会累加 score
ZRANGEBYLEX key min max [limit offset count]获取字典排序指定范围成员
ZREVRANGEBYLEX key min max [limit offset count]获取字典排序指定范围成员(降序)
ZLEXCOUNT key min max获取字典排序指定范围成员数量
ZREMRANGEBYLEX key min max移除字典排序指定范围成员
ZPOPMAX key [count]弹出分值高的成员
ZPOPMIN key [count]弹出分值低的成员
BZPOPMAX key [key...] timeout阻塞弹出分值高的成员, 从多个 zset 中 select 弹出
BZPOPMIN key [key...] timeout阻塞弹出分值低的成员, 从多个 zset 中 select 弹出

HyperLogLog 集合唯一计数器

唯一计数器(12k就能计数2^64个成员, 计数估计算法误差0.81%)

  • 统计集合成员的个数
  • 并集多个 HyperLogLog

2.原语

原语介绍
PFADD key element [element...]添加成员(成功返回1,成员存在返回0)
PFCOUNT key计数成员
PFMERGE target key [key...]并集 key

bitmap 位图

bitmap 是一个二进制的bit数组, 能对指定的 offset 进行位操作

1. 数据结构

下面展示一个 1 byte = 8 bit(位) 的 bitmpa

位图存储的值为10010100

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ez8XcVCy-1644494146692)(images/image-20220209231147095.png)]

2.原语

原语介绍
SETBIT key offset 0|1设置offset的值位(0或者1)
GETBIT key offset获取offset的值位(0或者1)
BITCOUNT key获取1的数量
BITPOS key 0|1 start end获取位的offset(注意: start end 的单位式 byte)
BITOP target key [key...]一个或者多个bitmap执行位运算并将结果储存到 target 中(AND|OR|XOR|NOT)
BITFIELD key SET type offset value设置整数值(type: i8 有符号8位, u8无符号8位,类推)
BITFIELD key SET type #index value设置整数值(offset 变为 #index, #1 表示第一个 type 类型的位置)
BITFIELD key GET type offset获取整数值
BITFIELD key GET type #index获取整数值(根据 index )
GET key可以使用 string 命令对 bitmiap 操作, 因为 bitmap 是基于 string 实现的

geo 地理坐标

2.原语

原语介绍
GEOADD key [NX|XX] x y name[key x y name...]设置坐标, x 经度, y 纬度, name 是别名, 返回已有的数量
GEOPOS key name[name...]获取坐标
GEODIST key name name计算 2 个坐标的距离
GEORADIUS key x y radius unit查找坐标半径内的其他成员, radius 是半径, unit 单位(m km mi ft)
GEORADIUSBYMEMBER key name radius unit查找成员半径内的其他成员

stream 流(重要)

1. XADD 添加新元素到末尾

(1) id

XADD key id key value [key value...]

流元素的 id 由 毫秒时间戳 + 序列号组成 需要他们整体呈递增, 不然将会报错

id 使用 * 将自动生成 id

127.0.0.1:6379> XADD x1 * k1 v1
"1644465874779-0"

(2) 执行 XADD 同时用 MAXLEN 修剪流

注意观察, 这个是 XADD 执行时候修剪, 注意使用时机

127.0.0.1:6379> XADD x1 MAXLEN = 2 * k1 v1
"1644466561573-0"
127.0.0.1:6379> XADD x1 * k2 v2
"1644466573705-0"
127.0.0.1:6379> XRANGE x1 - +
1) 1) "1644466561573-0"
   2) 1) "k1"
      2) "v1"
2) 1) "1644466573705-0"
   2) 1) "k2"
      2) "v2"
# 这里没有添加 MAXLEN 限制将不会生效 MAXLEN      
127.0.0.1:6379> XADD x1 * k3 v3
"1644466587887-0"
127.0.0.1:6379> XRANGE x1 - +
1) 1) "1644466561573-0"
   2) 1) "k1"
      2) "v1"
2) 1) "1644466573705-0"
   2) 1) "k2"
      2) "v2"
3) 1) "1644466587887-0"
   2) 1) "k3"
      2) "v3"
# 这里添加 MAXLEN 限制将会生效 MAXLEN    
127.0.0.1:6379> XADD x1 MAXLEN = 2  * k4 v4
"1644466613469-0"
127.0.0.1:6379> XRANGE x1 - +
1) 1) "1644466587887-0"
   2) 1) "k3"
      2) "v3"
2) 1) "1644466613469-0"
   2) 1) "k4"
      2) "v4"
127.0.0.1:6379> 

2. XTRIM 修剪流

我们除了执行 XADD 的时候同时使用 MAXLEN 修剪流, 我们也能用 XTRIM 单独修剪流

XTRIM key MAXLENTH|MINID =|~ threshold [limit count]

示例

127.0.0.1:6379> XRANGE x1 - +
1) 1) "1644466587887-0"
   2) 1) "k3"
      2) "v3"
2) 1) "1644466613469-0"
   2) 1) "k4"
      2) "v4"
127.0.0.1:6379> XTRIM x1 MAXLEN 1
(integer) 1
127.0.0.1:6379> XRANGE x1 - +
1) 1) "1644466613469-0"
   2) 1) "k4"
      2) "v4"
127.0.0.1:6379> 

3. XDEL 移除流中的 ID

使用

XDEL key [id...]

示例

127.0.0.1:6379> XRANGE x1 - +
1) 1) "1-0"
   2) 1) "k1"
      2) "v1"
2) 1) "2-0"
   2) 1) "k2"
      2) "v2"
127.0.0.1:6379> XDEL x1 1-0 
(integer) 1
127.0.0.1:6379> XRANGE x1 - +
1) 1) "2-0"
   2) 1) "k2"
      2) "v2"
127.0.0.1:6379> 

4. XLEN 获取元素数量

XLEN key

示例

127.0.0.1:6379> XRANGE x1 - +
1) 1) "2-0"
   2) 1) "k2"
      2) "v2"
127.0.0.1:6379> XLEN x1
(integer) 1
127.0.0.1:6379> 

5. XRANGE、XREVRANGE:访问流中元素

XRANGE 可以获取流的单个元素, 多个元素, 以及迭代整个流

XRANGE key start-id end-id [COUNT n]

迭代整个流可以通过每次访问用 COUNT 限制 1 个然后下一次访问的时候用 start-id 赋值位 上一次的成员的 id + 1

就能范围缩小从而迭代下一个元素

这里 start-id 将会获取大于等于 start-id 的成员

6. XREAD 从一个和多个 stream 用阻塞或者非阻塞的方式获取流

XREAD [COUNT n] [BLOCK milliseconds] STREAMS key [key...] id [id...]

这里 id 将会获取大于 id 的成员

如果 id 为 $ 将会仅仅获取命令执行之后的最新的流

BLOCK 有流就会返回, 比如要求有 COUNT 为 3, 只能读取到 2 个就直接返回 2 个.

7. XGROUP 消费组

(1) 创建消费组

XGROUP [CREATE key groupname id-or-$] [SETID key groupname id-or-$] [DESTROY key groupname] [DELCONSUMER key groupname consumername]
  • key 为流的名称,不存在就创建
  • groupname 是消费者的名称
  • id-or-$: id 代表从 id 位置开始, $ 代表从尾部开始, 就是之前的信息会忽略

从头开始消费

XGROUP CREATE mystream groupname 0-0

只是消费最新的信息

XGROUP CREATE mystream groupname $

(2) 在消费组里消费信息

  • 同一个 stream 可以创建多个消费组
  • 同一个消费组的信息只能被一个消费者读取
XREADGROUP GROUP group consumer [COUNT count] [BLOCK milliseconds] [NOACK] STREAMS key [key ...] ID [ID ...]

同理 XREAD 命令, 新增的字段如下

  • group 是创建的消费组的名称
  • consumer 是消费者的名称, 用户自定义
  • id: 消息的 id (> 就是读取一条未递送的消息), 读取之后的消息同一个消息组的其他消费者将不能再次读取
127.0.0.1:6379> XADD x1 1 k1 v1
"1-0"
# 创建 g1 消费组, 读取最新的内容
127.0.0.1:6379> XGROUP CREATE x1 g1 $
OK
# 使用 c1 读取 g1 消费组中的内容, 没有最新内容返回 nil
127.0.0.1:6379> XREADGROUP GROUP g1 c1 STREAMS x1 > 
(nil)
# 给 stream x1 添加内容, g1 会共享到 x1 的天降
127.0.0.1:6379> XADD x1 2 k2 v2
"2-0"
# 使用 c1 读取 g1 消费组中的内容
127.0.0.1:6379> XREADGROUP GROUP g1 c1 STREAMS x1 > 
1) 1) "x1"
   2) 1) 1) "2-0"
         2) 1) "k2"
            2) "v2"
# 使用 c1 读取 g1 消费组中的内容, 没有最新内容返回 nil            
127.0.0.1:6379> XREADGROUP GROUP g1 c1 STREAMS x1 > 
(nil)
# 使用 新的消费者 c2 读取 g1 消费组中的内容, 没有最新内容返回 nil, 一个消息在一个消费组里面只能被消费者消费一次
127.0.0.1:6379> XREADGROUP GROUP g1 c2 STREAMS x1 > 
(nil)
# 消费者处理完消息之后发送 ACK 确认, 消息就从待处理变成已确认
127.0.0.1:6379> XACK x1 g1 2-0
(integer) 1
# 只能确认一次
127.0.0.1:6379> XACK x1 g1 2-0
(integer) 0
127.0.0.1:6379> 

(3) XACK 确认

XACK key groupname id

通过 id 确认消息

消息添加后在消息组中 4 个状态

  1. 不存在
  2. 未递送
  3. 待处理
  4. 已确认

转换关系如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g0O6zVS7-1644494146693)(images/image-20220210170444852.png)]

示例

127.0.0.1:6379> XREADGROUP GROUP g1 c1 STREAMS x1 >
1) 1) "x1"
   2) 1) 1) "3-0"
         2) 1) "k3"
            2) "v3"
# 查看没有确认的消息            
127.0.0.1:6379> XPENDING x1 g1 
1) (integer) 1
2) "3-0"
3) "3-0"
4) 1) 1) "c1"
      2) "1"
# 确认消息      
127.0.0.1:6379> XACK x1 g1 3-0
(integer) 1
127.0.0.1:6379> XPENDING x1 g1 
1) (integer) 0
2) (nil)
3) (nil)
4) (nil)
127.0.0.1:6379> 

(4) 管理消费组

原语介绍
XGROUP SETID key groupname id修改消费组的id(消费组会读取大于id的消息,$代表最新的消息), 该命令会重置 group 的未读消息
XGROUP DELCOMSUMER key groupname consuemr删除消费者
XGROUP DISTORY key groupname删除消费组

8. 查看取待处理的消息

XPENDING key groupname [start stop count] [consumer]

(1) 查看所有消费者的待处理消息

127.0.0.1:6379> XPENDING x1 g1 
1) (integer) 2 # 待处理消息数量
2) "3-0"       # 首条未读消息的id
3) "4-0"       # 最后一台未读消息的id
4) 1) 1) "c1"  # 各个消费者目前正在处理消息的数量
      2) "1"
   2) 1) "c2"
      2) "1"
127.0.0.1:6379> 

(2) 查看具体一个消费者的待处理消息

127.0.0.1:6379> XPENDING x1 g1 - + 1 c1
1) 1) "4-0"                 # 消息 id
   2) "c1"                  # 消费者
   3) (integer) 3321593     # 消息处理的时间
   4) (integer) 1           # 消息消费的次数
127.0.0.1:6379> 

9. XCLAIM:转移消息的归属权

XCLAIM key groupname new_consuemr max_pending_time id [id...]

旧的消费者需要在 max_pending_time 的期限中 XACK 信息, 否则消息的归属权将会转移

10. XINFO:查看流和消费者组的相关信息

(1) HELP 帮助

127.0.0.1:6379> XINFO HELP
1) XINFO <subcommand> [<arg> [value] [opt] ...]. Subcommands are:
2) CONSUMERS <key> <groupname>           # 1.展示 consuemr 信息
3)     Show consumers of <groupname>.     
4) GROUPS <key>                          # 2. 展示 group 信息
5)     Show the stream consumer groups.  
6) STREAM <key> [FULL [COUNT <count>]    # 3. 展示 stream 消息
7)     Show information about the stream. 
8) HELP
9)     Prints this help.
127.0.0.1:6379> 

(2) CONSUMERS 查看消费组里的消费者

127.0.0.1:6379> XINFO CONSUMERS x1 g1
1) 1) "name"        # 1. 消费者名称
   2) "c1"
   3) "pending"     # 2. pending 
   4) (integer) 1
   5) "idle"        # 3. 处理的时间,还没有规范
   6) (integer) 4116697
2) 1) "name"
   2) "c2"
   3) "pending"
   4) (integer) 1   
   5) "idle"
   6) (integer) 3980477
127.0.0.1:6379> 

(3) STREAM 查看流的信息

127.0.0.1:6379> XINFO STREAM x1
 1) "length"  #成员数量
 2) (integer) 4
 3) "radix-tree-keys" #基树键的数量
 4) (integer) 1
 5) "radix-tree-nodes" # 基树节点的数量
 6) (integer) 2
 7) "last-generated-id" #最后一个节点的 id
 8) "4-0"
 9) "groups"  #消费者数量
10) (integer) 1
11) "first-entry"  #最开始的成员
12) 1) "1-0"
    2) 1) "k1"
       2) "v1"
13) "last-entry" #最后的成员
14) 1) "4-0"
    2) 1) "k4"
       2) "v4"
127.0.0.1:6379> 

pub/sub 发布订阅

发布订阅就像广播, 是无状态的, 订阅者没有收到就丢失了, 不会储存旧信息也不会持久化

1. 发布订阅频道

发布者发布的消息能被多个订阅者接收

image-20220210193949177

2. 发布订阅模式

模式匹配可以 一个 pattern 订阅多个模式

image-20220210195122170

3.原语

原语介绍
PUBLISH key message发布者发布消息
SUBSCRIBE key [key...]订阅者订阅消息
UNSUBSCRIBE [key...]退订频道, 默认退订所有频道
PSUBSCRIBE pattern [pattern...]订阅模式, 一个 pattern 可以匹配多个频道
PUNSUBSCRIBE pattern [pattern...]退订模式
PUBSUB CHANNELS [pattern]列出目前被订阅的频道
PUBSUB NUMSUB [key ...]查询频道订阅的数量
PUBSUB NUMPAT查询频道订阅的总数

数据库

原语介绍
KEYS pattern返回匹配正则表达式的所有的 key
EXISTS key时候存在 key
DBSIZEkey 数量
TTL keykey 的过期时间(秒)
PTTL keykey 的过期时间(毫秒)


这篇关于[redis] 10 种数据结构详解的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程