《Redis开发与运维》第二章 API的理解和使用(下)读书笔记

163 阅读12分钟

集合(set)

集合(set)类型是用来保存多个的字符串元素,但和列表类型不一样的是,集合中不允许有重复元素,并且集合中的元素是无序的,不能通过索引下标获取元素。集合支持多个集合取交集、差集、并集。

如下图所示:

在这里插入图片描述

命令

  • 添加元素
## 命令:sadd key element \[element ... \],返回结果为添加成功的元## 素个数
## 例如:
172.17.236.250:6379> sadd myset a b c
(integer) 3
  • 删除元素
## 命令:srem key element \[element ... \],返回结果为成功删除的元## 素个数
## 例如:
172.17.236.250:6379> srem myset a b
(integer) 2
  • 计算元素个数
## 命令:scard key
## 例如:
172.17.236.250:6379> scard myset
(integer) 1

  scard 的时间复杂度为 O(1) ,它不会遍历集合所有元素,而是直接使用 Redis 内部变量。

  • 判断元素是否在集合中
## 命令:sismember key element,在集合中,返回1,否则返回0
## 例如:
172.17.236.250:6379> sismember myset c
(integer) 1
  • 随机从集合中返回指定个数(不会删除集合中的元素
## 命令:srandmember key [count] ,count 为可选参数,不写默认为1
## 例如:
172.17.236.250:6379> srandmember myset 
"c"
  • 从集合中随机弹出元素(会删除集合中的元素
## 命令:spop key,在 Redis3.2 版本之后,spop 也支持 [count] 参数
## 例如:
172.17.236.250:6379> smembers myset
1) "b"
2) "a"
3) "c"
172.17.236.250:6379> spop myset
"c"
172.17.236.250:6379> smembers myset
1) "b"
2) "a"
  • 获取所有元素
## 命令:smembers key,返回的结果是无序的
## 例如:
172.17.236.250:6379> smembers myset
1) "b"
2) "a"

集合间的操作

  有两个集合,set1set2 ,如下:

172.17.236.250:6379> sadd set1 a b c d e f j l
(integer) 8
172.17.236.250:6379> sadd set2 a b c d e f g h
(integer) 8
  • 求集合间的交集
## 命令:sinter key \[key ... \]
## 例如:求 set1 和 set2 的交集
172.17.236.250:6379> sinter set1 set2
1) "c"
2) "b"
3) "e"
4) "a"
5) "d"
6) "f"
  • 求集合间的并集
## 命令:sunion key [\ key ... \]
## 例如:求 set1 和 set2 的并集
172.17.236.250:6379> sunion set1 set2
 1) "h"
 2) "c"
 3) "e"
 4) "g"
 5) "a"
 6) "l"
 7) "f"
 8) "b"
 9) "j"
10) "d"
  • 求集合的差集
## 命令:sdiff key [key ... \]
## 例如:求 set1 和 set2 的差集
172.17.236.250:6379> sdiff set1 set2
1) "l"
2) "j"
  • 将交集、并集、差集的结果保存
## 交集:sinterstore destination key \[key ... \]
## 并集:suionstore destination key \[key ... \]
## 差集:sdiffstore destination key \[key ... \]
例如:求 set1 和 set2 的交集并保存在集合中
172.17.236.250:6379> sinterstore set1_2 set1 set2
(integer) 6
172.17.236.250:6379> smembers set1_2
1) "a"
2) "b"
3) "c"
4) "e"
5) "d"
6) "f"

  集合常用命令时间复杂度图,如下:

在这里插入图片描述

内部编码

集合类型的内部编码有两种:

  • intset(整数集合): 当集合间的元素都是整数且元素个数小于 set-max-intset-entries 配置(默认512个)时,Redis 会使用 intset 当做集合的内部实现,从而减少内存的使用。
  • hashtable(哈希表): 当集合类型无法满足 intset 的条件时,Redis 会使用 hashtable 当做集合的内部编码来时。

使用场景

  • sadd = Tagging(标签)
  • spop/srandmember = Random item(生成随机数,抽奖等)
  • sadd+sinter = Social Graph(社交需求)

有序集合

有序集合有集合元素不能重复的特性,但是,有序集合中的元素是可以排序的。但是有序集合和列表使用索引下标来排序不同的是,有序集合给每一个元素设置一个分值(score)作为排序的依据。

如下图所示:

在这里插入图片描述
注意:有序集合的元素不能重复,但是 score 可以重复。 有序集合、列表、集合之间的异同如下图:
在这里插入图片描述

命令

  • 添加成员
## 命令:zadd key score member \[score member ... \],返回的结果为成功添加的成员个数
## 例如:为有序集合 user:ranking 添加用户 tom 和他的分数 251
172.17.236.250:6379> zadd user:ranking 261 tom
(integer) 1
  • 注意:

    1. Redis3.2 为 zadd 命令添加了 nxxxchincr 四个选项
    2. nxmember 必须不存在,才可以设置成功,用于新增
    3. xxmember 必须存在,才可设置成功,用于修改
    4. ch:返回此操作后,有序集合的元素和 score 发生变化的个数
    5. incr:对 score 做增加操作
    6. 有序集合提供了排序字段,但是也产生了代价,zadd 的时间复杂度为 O(log(n)),sadd 的时间复杂度为 O(1)
  • 计算成员个数

## 命令:zcard key,时间复杂度为 O(1)
## 例如:
172.17.236.250:6379> zcard user:ranking
(integer) 1
  • 计算某个成员的分数
## 命令:zscore key member,如果成员不存在则返回 nil
## 例如:
172.17.236.250:6379> zscore user:ranking tom
"261"
  • 计算成员的排名
## 从低到高:zrank key member
## 从高到低:zrevrank key member
## 例如:
172.17.236.250:6379> zadd user:ranking 100 tom2
(integer) 1
172.17.236.250:6379> zrank user:ranking tom
(integer) 1
172.17.236.250:6379> zrevrank user:ranking tom
(integer) 0
  • 删除成员
## 命令:zrem key member \[member ... \]
## 例如:将 tom2 从有序集合中删除
172.17.236.250:6379> zrem user:ranking tom2
(integer) 1
  • 增加成员的分数
## 命令:zincrby key increment member
## 例如:给 tom 增加 9 分
172.17.236.250:6379> zincrby user:ranking 9 tom
"270"
  • 返回指定排名范围的成员
## 从低到高:zrange user:ranking start end \[withscores \]
## 从高到低:zrevrange user:ranking start end \[withscores \]
## withscores:可选,加上会显示成员的分数
例如:
172.17.236.250:6379> zrange user:ranking 0 2 withscores
1) "tom"
2) "270"
172.17.236.250:6379> zrevrange user:ranking 0 2 withscores
1) "tom"
2) "270"
  • 返回指定分数范围的成员
## 从低到高:zrangebyscore key min max \[withscores\] \[limit offest count\]
## 从高到低:zrevrangebyscore key max min \[withscores\] \[limit offest count\] 
## limit offest count:限制输出的起始位置和个数
## 例如:
172.17.236.250:6379> zrangebyscore user:ranking 200 300 withscores
1) "tom"
2) "270"
## min 和 max 可以设置开区间(小括号)和闭区间(中括号),-inf 和+inf 代表无限小和无限大
例如:
172.17.236.250:6379> zrangebyscore user:ranking (200 +inf withscores
1) "tom"
2) "270"
  • 返回指定分数范围成员个数
## 命令:zcount key min max
## 例如:
172.17.236.250:6379> zcount user:ranking 200 300
(integer) 1
  • 删除指定排名内的升序元素
## 命令:zremrangebyrank key start end
## 例如:删除 0 到 2 名的成员
172.17.236.250:6379> zremrangebyrank user:ranking 0 1
(integer) 2
  • 删除指定分数范围的成员
## 命令:zremrangebyscore key min max,返回结果为成功删除的个数
## 例如:
172.17.236.250:6379> zremrangebyscore user:ranking 200 290
(integer) 2

集合间的操作

  有两个集合 user:1 和 user:2 ,如下:

172.17.236.250:6379> zadd user:1 1 kris 91 mike 200 frank 220 tim 250 martin 251 tom
(integer) 6
172.17.236.250:6379> zadd user:2 8 james 77 mike 625 martin 888 tom
(integer) 4
  • 交集
## 命令:zinterstore destination numkeys key \[key ... \] \[weights 
## wright \[weight]] \[aggregate sum|min|max]
## destination:交集计算结果保存到该键
## numkeys:需要做交集计算键的个数
## key[key ... \]:需要做交集计算的键
## weights weight[weight...]:每个键的权重,在做交集计算时,每个
## 键中的每一个 member 会将自己分数乘以这个权重,每个键的权重
## 默认值是1
## aggregate sum|min|max:计算成员交集后,分值可以按照 
## sum(和)、min(最小值)、max(最大值)做汇总,默认值是 su
## m
## 例如:对 user:1 和 user:2 求交集,weights 和 aggregate 使用默认## 配置,可以看到目标键对分值做了 sum 操作
172.17.236.250:6379> zinterstore user:1_2 2 user:1 user:2
(integer) 3
172.17.236.250:6379> zrange user:1_2 0 -1 withscores
1) "mike"
2) "168"
3) "martin"
4) "875"
5) "tom"
6) "1139"
  • 并集
## 命令:zunionstore destination numkeys key \[key ... \] \[weights 
## weight ... \[weight ... \] \[aggregate sum|min|max]]
## 该命令所有参数都和 zinterstore 一样。只不过是做并集计算。
## 例如:对 user:1 和 user:2 求并集,weights 和 aggregate 使用默认## 配置,可以看到目标键对分值做了 sum 操作
172.17.236.250:6379> zunionstore user2:1_2 2 user:1 user:2
(integer) 7
172.17.236.250:6379> zrange user2:1_2 0 -1 withscores
 1) "kris"
 2) "1"
 3) "james"
 4) "8"
 5) "mike"
 6) "168"
 7) "frank"
 8) "200"
 9) "tim"
10) "220"
11) "martin"
12) "875"
13) "tom"
14) "1139"

  下图为有序集合命令时间复杂度表,可根据此表选择合适的命令进行开发。

在这里插入图片描述

内部编码

有序集合的内部编码有两种:

  • ziplist(压缩列表):当有序集合的元素个数小于 zset-max-ziplist-entries 配置(默认128个),每个元素的值都小于 zset-max-ziplist-value 配置(默认64字节)时,Redis 内部会使用 ziplist 来作为有序集合的内部实现。
  • skiplist(跳跃表):当 ziplist 的条件不满足时,有序集合会使用 skiplist 作为内部实现,因为此时 ziplist 的读写性能会下降。

使用场景

有序集合比较典型的使用场景就是排行榜系统。假设有一个视频网站按照赞数去排名的,则该排行榜主要需要实现以下几个功能。

  • 添加用户的赞数
## 用户 mike 上传视频并获得 3 个赞
172.17.236.250:6379> zadd user:ranking 3 mike
(integer) 1
## 如果再获得一个赞可以使用 zincrby
172.17.236.250:6379> zincrby user:ranking 1 mike
"4"
  • 取消用户的赞
## 取消赞可以直接删除成员 tom(也可以直接将赞数修改为 0)
172.17.236.250:6379> zrem user:ranking mike
(integer) 1
  • 展示获取赞数前 10 的用户
172.17.236.250:6379> zrevrange user:ranking 0 9
1) "mike"
  • 展示用户信息以及用户分数
## 将用户名作为后缀,将用户信息保存在 hash 中,用户的分数和排名
## 可以使用 score 和 zrank 。
172.17.236.250:6379> hgetall user:info:mike
(empty list or set)
172.17.236.250:6379> zscore user:ranking mike
"3"
172.17.236.250:6379> zrank user:ranking mike
(integer) 0

键管理

键重命名

rename key newkey

注意:如果在 rename 之前,键 newkey 已经存在,那么它的值也会被覆盖。

172.17.236.250:6379> set a b
OK
172.17.236.250:6379> set c d
OK
172.17.236.250:6379> rename a c
OK
172.17.236.250:6379> get c
"b"
  • 只有在 newkey 不存在的情况下才允许重命名
renamenx key newkey

注意:重命名期间会执行 del 命令删除旧键,如果键对应的值比较大,可能会引起 Redis 阻塞。

  • 随机返回一个键
randomkey

键过期

## 键在 seconds 秒后过期
expire key seconds
## 键在秒级时间戳 timestamp 后过期
expireat key timestamp
## 过期剩余时间,返回值:
## 大于 0 的整数(键过期剩余时间,ttl 为秒,pttl 为毫秒)
## -1 (没有设置过期时间)
## -2 (键不存在)
ttl key 
pttl key 
## 键在 millisecondes 毫秒过期
pexpire key millisecondes
## 键在毫秒级时间戳 millisecondes-timestamp 过期
pexpireat key millisecondes-timestamp
## 清楚过期时间
persist key

注意: 1. 如果 expire key 的键不存在,返回结果为 0 2. 如果过期时间为负值,键会被立即删除 3. 对于字符串类型键,执行 set 命令会去掉过期时间 4. Redis 不支持二级数据结构内部元素的过期功能 5. setexset +expire 的组合) 不但是原子执行,同时减少了一次网 络通讯时间

迁移键

  • move

在 Redis 内部可能有多个数据库,这些数据库彼此在数据上是相互隔离的。

如下图:

在这里插入图片描述

## 将指定的键从源数据库迁移到目标数据库
move key db
  • dump + restore
dump key
restore key ttl value

   dump + restore可以实现在不同的 Redis 实例之间进行数据迁移功能,整个迁移的过长分为两步:   1、在源 Redis 上,dump 命令会将键值序列化(RDB)格式。   2、在目标 Redis 上,restore 命令将上面序列化的值进行复原,如果 ttl = 0,则没有过期时间。 整个过程如下图所示:

在这里插入图片描述
注意:   1、整个迁移过程并非原子性,而是通过客户端分步完成   2、迁移过程中开启了两个客户端连接,所以 dump 的结果不是在源 Redis 和目标 Redis 之间进行传输。

示例:

  1、在源 Redis 上执行 dump

set hello world
OK
dump hello
"\\x00\\x05world\\x06\\x00\\x8f<T\\x04%\\xfcNQ"

  2、在目标 Redis 上执行 restore

restore hello 0 "\\x00\\x05world\\x06\\x00\\x8f<T\\x04%\\xfcNQ"
  • migrate
## host:目标 Redis 的 ip 地址
## port:目标 Redis 的端口
## key|"":迁移的键,如需迁移多个键则此处为空字符串
## destination-db:目标 Redis 的数据库索引
## timeout:迁移的超时时间(单位为毫秒)
## copy:如果添加该参数,则迁移后不删除源键
## replace:不管目标 Redis 是否存在该键都会正常迁移进行数据覆盖
## keys key \ ... \:要迁移的多个键
migrate host port key | "" destination-db timeout \[copy\] \[replace\] \[keys key \[key ... \]]

  migrate的整个过程是原子性的,数据的传输直接在源 Redis 和目标 Redis 之间完成,目标 Redis 完成 restore 后会发送 OK 给源 Redis ,源 Redis 接收后会根据 migrate 对应的选项来决定是否在源 Redis 上删除对应的键。

下图为 migrate 在 Redis 之间原子性迁移数据:

在这里插入图片描述

下图为 movedump+restoremigrate 三个命令比较:

在这里插入图片描述

遍历键

  • 全量遍历键
## pattern可以为 *(任意字符)、.*(一个字符)、.[](匹配部分字符)、.\x(* 或者 ? 需要转义)
keys pattern    
  • 渐进式遍历
## cursor:是一个游标,必须参数,从0开始遍历,遍历完会返回当前游## 标值,当为0时,则遍历结束。
## match pattern:可选参数,用做模式匹配
## count number:每次遍历的键的个数,默认是10
scan cursor \[match pattern\] \[count number\]

  哈希类型、集合类型、有序集合类型对应的命令分别是 hscansscanzscan注意:在使用 scan 命令遍历时并不能保证完整的遍历出所有的键,这在开发的时候需要考虑。

数据库管理

Redis 默认配置是可以有16个数据库,这16个数据库之间没有任何关联。redis-cli-h{ip}-p{port}就是默认的使用的第一个数据库

  • 切换数据库
select dbIndex

缺点:   1、Redis是单线程的。如果使用多个数据库,这些数据库共用一个CPU,彼此之间是会受到影响的。   2、会让调试和运维不同的数据库变得困难,使业务方定位问题非常的困难。   3、部分 Redis 客户端不支持这种方式。

  • flushdb/flushall flushdb清楚当前数据库数据,flushall 清楚所有数据库的数据。慎用这两个命令