当前位置: 首页 > news >正文

Redis 常用数据类型 (下)

文章目录

  • 前言
  • 一 Hash 哈希
    • 1. Hash 相关命令
      • hset 和 hget
      • hexists
      • hdel
      • hkeys
      • hvals
      • hgetall
      • hmget
      • hlen
      • hsetnx
      • hincrby
      • incrbyfloat
    • 2. Hash 命令小结
    • 3. Hash 内部编码
    • Hash 在缓存中的应用
      • 场景介绍
      • 缓存方式对比
  • 二、List 列表
    • 1. LIST总体介绍
    • 2. List 普通命令
      • lpush
      • lpushx
      • rpush
      • rpushx
      • lrange
      • lpop
      • rpop
      • linsert
      • lindex
      • llen
      • lrem
      • ltrim
      • lset
    • 3. List 阻塞版本命令
      • blpop
      • brpop
    • 4. List 命令小结
    • 5. List 内部编码
    • List 使用场景
      • 1. 消息队列
      • 2. 微博timeline
  • 三、Set 集合
    • 1.Set 总体介绍
    • 2. Set 普通命令
      • sadd、 smembers、 sismember
      • scard
      • spop
      • srandmember
      • smove
      • srem
    • 3. 集合间操作
      • sinter
      • sinterstore
      • sunion
      • sunionstore
      • sdiff
      • sdiffstore
    • 4. Set 命令小结
    • 5. Set 内部编码
    • 6.应用场景
  • 四、Zset 有序集合
    • 1. Zset 总体认识
    • 2. Zset 普通命令
      • zadd
      • zcard
      • zcount
      • zrange
      • zrevrange
      • zrangebyscore
      • zpopmax 和 zpopmin
      • bzpopmax 和 bzpopmin
      • zrank 和 zrevrank
      • zscore
      • zrem
      • zremrangebyrank
      • zremrangebyscore
      • zincrby
      • Zset 集合间操作命令
      • zinterstore
      • zunionstore
    • 4. 命令小结
    • 5. 内部编码
    • 6. 应用场景---排行榜系统
  • 五、其他类型
  • 六、 补充
    • 渐进式遍历
    • 数据库操作
      • select
      • dbsize
      • flushall 和 flushdb

前言

上篇中介绍到了Redis 的 String 类型,本文是下篇,将继续介绍 Redis 常用数据类型中的 哈希(Hash)** 、列表(List)集合(Set)有序集合(Sorted Set) 和一些其他类型。
Redis常用数据类型(上)

一 Hash 哈希

Redis 本身就是一种 Hash 结构,同时也提供了 Hash 这种数据类型。
下图阐述了二者的一些差别,同时也体现了 Hash 和 String 在使用时的差别

Redis 本身的键值对,官方称之为 key-value,为了区分,对于 Redis 的 Hash 数据类型,其键值对称为 field-value 。在这种嵌套结构中我们需要注意 value 的具体含义

1. Hash 相关命令

hset 和 hget

这是一对最核心的命令,分别用于设置键值对和获取 field 对应的 value

# 支持一次设置多组值
HSET key field value [field value ...]# 注意这里先指定 key 再指定 field
HGET key field

返回值:hset 返回添加的对数,hget 返回指定 filed 对应的值
示例

127.0.0.1:6379> hset key f1 111
(integer) 1
127.0.0.1:6379> hset key f2 222 f3 333 f4 444
(integer) 3
127.0.0.1:6379> hget key f1
"111"
127.0.0.1:6379> hget key f4
"444"

hexists

判断是否存在对应字段

hexists key filed

存在则返回 1,不存在返回 0
语法:

# 接着上一个例子,已经存在 f1, f2, f3, f4
127.0.0.1:6379> HEXISTS key f1 
(integer) 1
127.0.0.1:6379> HEXISTS key f2
(integer) 1
127.0.0.1:6379> HEXISTS key f100
(integer) 0

hdel

删除 hash 中指定的字段,支持删除多个
返回删除成功的个数

# 接着上一个例子,已经存在 f1, f2, f3, f4
127.0.0.1:6379> hdel key f1 f100
(integer) 1
127.0.0.1:6379> hget key f1
(nil)

hkeys

获取指定 key 对应哈希表中 所有 field

hkey key

示例:

127.0.0.1:6379> hkeys key
1) "f2"
2) "f3"
3) "f4"

hkeys 有风险!当 key 对应的hash表数据过多,就会阻塞 redis 服务器

hvals

获取指定 key 对应 hash 表中 所有 value

hvals key

示例:

127.0.0.1:6379> hvals key
1) "222"
2) "333"
3) "444"

hgetall

获取指定 key 对应 hash 表中 所有 field-value 对

hgetall key

示例:

127.0.0.1:6379> hgetall key
1) "f2"
2) "222"
3) "f3"
4) "333"
5) "f4"
6) "444"

hmget

一次获取多个 field 对应的值
返回值:返回对应字段的值或者nill

hmget key field [field ...]

示例:

127.0.0.1:6379> hmget key f1 f2 f3 f4
1) (nil)
2) "222"
3) "333"
4) "444"

在使⽤ HGETALL 时,如果哈希元素个数⽐较多,会存在阻塞 Redis 的可能。如果开发⼈员只需要获取部分 field,可以使⽤ HMGET,如果⼀定要获取全部 field,可以尝试使⽤ HSCAN命令,该命令采⽤渐进式遍历哈希类型,HSCAN 会在后续章节介绍。

hlen

获取 hash 中所有字段的个数

hlen key 

示例:

127.0.0.1:6379> hlen key
(integer) 3

hsetnx

若 field 存在 设置键值对,否则 不设置
语法:

hsetnx key field value

返回 1 表示设置成功,返回 0 表示失败

示例:

127.0.0.1:6379> HSETNX myhash field "Hello"
(integer) 1
127.0.0.1:6379> HSETNX myhash field "Hello"
(integer) 0

hincrby

Hash 中的 value 也可以当成整数

HINCRBY key field increment

对 Hash 中 field 对应的 value 值 加一
返回值:变化后的值,不是整数则报错

示例:

redis> HSET myhash field 5
(integer) 1
redis> HINCRBY myhash field 1
(integer) 6
redis> HINCRBY myhash field -1
(integer) 5
redis> HINCRBY myhash field -10
(integer) -5

incrbyfloat

HINCRBY 的浮点数版本。
语法:

HINCRBYFLOAT key field increment

示例:

redis> HSET mykey field 10.50
(integer) 1
redis> HINCRBYFLOAT mykey field 0.1
"10.6"
redis> HINCRBYFLOAT mykey field -5
"5.6"
redis> HSET mykey field 5.0e3
(integer) 0
redis> HINCRBYFLOAT mykey field 2.0e2
"5200"

2. Hash 命令小结

命令执行效果时间复杂度
hset key field value设置值O(1)
hget key field获取值O(1)
hdel key field [field …]删除 fieldO(k), k 是 field 个数
hlen key计算 field 个数O(1)
hgetall key获取所有的 field - valueO(k), k 是 field 个数
hmget field [field …]批量获取 field - value
O(k), k 是 field 个数
hmset field value [field value …]批量获取 field - valueO(k), k 是 field 个数
hexists key field判断 field 是否存在O(1)
hkeys key获取所有的 fieldO(k), k 是 field 个数
hvals key获取所有的 valueO(k), k 是 field 个数
hsetnx key field value设置值,但必须在 field 不存在时才能设置成功O(1)
hincrby key field n对应 field - value +nO(1)
hincrbyfloat key field n对应 field - value +nO(1)
hstrlen key field计算 value 的字符串长度O(1)

3. Hash 内部编码

哈希的内部编码有两种:

  • ziplist(压缩列表):当哈希类型元素个数⼩于 hash-max-ziplist-entries 配置(默认 512 个)、同时所有值都⼩于 hash-max-ziplist-value 配置(默认 64 字节)时,Redis 会使⽤ ziplist 作为哈希的内部实现,ziplist 使⽤更加紧凑的结构实现多个元素的连续存储,所以在节省内存⽅⾯⽐hashtable 更加优秀。
  • hashtable(哈希表):当哈希类型⽆法满⾜ ziplist 的条件时,Redis 会使⽤ hashtable 作为哈希的内部实现,因为此时 ziplist 的读写效率会下降,⽽ hashtable 的读写时间复杂度为 O(1)。

ziplist 的代价就是访问速度较慢,所以仅在元素个数少且 value 较短时使用

示例:
(1)元素个数较少时,内部编码采用 ziplist

127.0.0.1:6379> hmset hashkey f1 v1 f2 v2
OK
127.0.0.1:6379> object encoding hashkey
"ziplist"

(2)当有 value 大于 64 字节时,内部编码会转换成 hashtable:

127.0.0.1:6379> hset hashkey f3 "超过64字节 .. 省略"
OK
127.0.0.1:6379> object encoding hashkey
"hashtable

(3)当 field 个数超过 512 时,内部编码也会转换为 hashtable:

127.0.0.1:6379> hmset hashkey f1 v1 h2 v2 f3 v3 ... 省略 ... f513 v513
OK
127.0.0.1:6379> object encoding hashkey
"hashtable"

Hash 在缓存中的应用

场景介绍

前面提到 String 也可用作缓存场景中,但是在存储结构化数据上,使用 Hash 更优优势
使用关系型数据库存储:

使用redis 存储

  • 哈希类型是稀疏的,⽽关系型数据库是完全结构化的,例如哈希类型每个键可以有不同的 field,⽽关系型数据库⼀旦添加新的列,所有⾏都要为其设置值,即便没有,也会默认设置nil,如下图所示
  • 关系数据库可以做复杂的关系查询,⽽ Redis 去模拟关系型复杂查询,例如联表查询、聚合查询等基本不可能,维护成本⾼。

缓存方式对比

缓存方式实现方式示例核心优势主要不足适用场景
原生字符串类型每个属性单独设键(如set user:1:name James实现极简,单个属性操作灵活键数量过多,内存占用大,数据分散无内聚性几乎不推荐,仅临时调试场景可能使用
序列化字符串类型(如JSON)整体序列化存储(如set user:1 '{"name":"James"}'数据内聚性好,整体操作高效,内存利用率高序列化/反序列化有性能开销,局部操作不灵活以整体读写为主的场景(如用户信息完整展示)
哈希类型集中存储键值对(如hmset user:1 name James age 23兼顾简单性与灵活性,局部操作高效需控制内部编码转换,可能引发内存消耗波动频繁进行局部属性读写的场景(推荐首选)

2.3.5 缓存方式对比

目前,缓存用户信息可采用以下三种方式,其实现方法及优缺点分析如下:

  1. 原生字符串类型——以字符串类型存储,每个属性对应一个键
set user:1:name James
set user:1:age 23
set user:1:city Beijing

优点:实现简单,对单个属性的变更操作灵活。
缺点:占用键数量过多,内存消耗较大;用户信息在Redis中存储分散,缺乏内聚性,实际实用性较低。

  1. 序列化字符串类型(如JSON格式)
set user:1 经过序列化 的用户对象字符串

优点:适合整体操作的信息场景适配性好,编程实现简单;若选择合适的序列化方案,内存使用效率较高。
缺点:存在序列化与反序列化的性能开销,对单个属性的操作灵活性差。

  1. 哈希类型
hmset user:1 name James age 23 city Beijing

优点:实现简单、直观,灵活性强,尤其适合信息的局部变更或获取操作。
缺点:需控制哈希在ziplist和hashtable两种内部编码间的转换,可能导致较大的内存消耗。

二、List 列表

1. LIST总体介绍

  • 列表类型用来存储多个字符串,每个字符串可以称为列表的一个元素,一个列表最多存储 232−12^{32}-12321 个元素。
  • 在 Redis 中,可以对列表两端插⼊(push)和弹出(pop),还可以获取指定范围的元素列表、获取指定索引下标的元素等(参考下图)
  • 由于列表的两端插入这种灵活特性,它还可以充当栈和队列的角色,在实际开发中有很多应用
    列表的插入和弹出

    列表的获取和删除

    列表元素特点:
  1. 列表元素的位置确定,支持通过下标直接获取。例如我们可以通过 lindex key 4 获取列表的第 4 个元素
  2. 如上图,删除第二个元素,后面的三个元素均会左移一位
  3. 元素允许重复

Redis 的列表在功能上可类比 c++ 中的双端队列 deque,左右插入都是O(1) 常量级复杂度;随机访问的话需要从头遍历值访问的下标,是线性复杂度

2. List 普通命令

lpush

将一个或多个元素从左侧放入列表中(头插)
时间复杂度:O(k)(k 为插入元素个数,一般直接视为O(1))

LPUSH key element [element ...]

返回值:插入后 list 的长度

127.0.0.1:6379> lpush mylist "world"
(integer) 1
127.0.0.1:6379> lpush mylist "hello"
(integer) 2
127.0.0.1:6379> Lrange mylist 0 -1
1) "hello"
2) "world"

lpushx

当 key 存在时,将一个或多个元素从左侧放入列表中(头插),如果 key 不存在直接返回
返回值:插入 list 的长度
时间复杂度:O(k)(k 为插入元素个数,一般直接为O(1))

127.0.0.1:6379> lpush mylist1 world
(integer) 1
127.0.0.1:6379> lpushx mylist1 Hello
(integer) 2
127.0.0.1:6379> lpushx otherlist hello
(integer) 0
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "world"

rpush

将一个或多个元素从右侧放入列表中(尾插)

RPUSH key element [element ...] 

返回值插入后列表的长度:
时间复杂度:O(k) (k 为插入元素个数,一般直接为O(1))

rpushx

在 key 存在时,将⼀个或者多个元素从右侧放⼊(尾插)到 list 中。
语法:

RPUSHX key element [element ...]

返回值:插入后 list 的长度
时间复杂度:O(N) (N 为插入元素个数,一般插入元素较少,可视为O(1))

示例:

redis> RPUSH mylist "World"
(integer) 1
redis> RPUSHX mylist "Hello"
(integer) 2
redis> RPUSHX otherlist "Hello"
(integer) 0
redis> LRANGE mylist 0 -1
1) "World"
2) "Hello"
redis> LRANGE otherlist 0 -1
(empty array)

lrange

获取从 start 到 end 区间的所有元素,左闭右闭。
语法:

LRANGE key start stop

最坏时间复杂度:O(N)
返回值:指定区间的元素。
示例:

127.0.0.1:6379> lrange mylist 0 0 
1) "hello"
127.0.0.1:6379> lrange mylist -3 2
2) "hello"
3) "world"
127.0.0.1:6379> lrange mylist -100 100
4) "hello"
5) "world"
127.0.0.1:6379> lrange mylist 4 6
(empty array)

lpop

从 list 左侧取出数据(头删)
语法:

lop key

返回值:取出的元素或者 nil
时间复杂度:O(1)

示例:

127.0.0.1:6379> rpush mylist one two three four five
(integer) 5
127.0.0.1:6379> lpop mylist
"one"
127.0.0.1:6379> lpop mylist
"two"
127.0.0.1:6379> lpop mylist
"three"
127.0.0.1:6379> lrange mylist 0 -1
1) "four"
2) "five"

rpop

从右侧取出数据
语法:

rpop key

返回值:取出的元素或者 nil
时间复杂度: O(1)

示例:

127.0.0.1:6379> rpush mylist one two three four five
(integer) 5
127.0.0.1:6379> rpop mylist
"five"
127.0.0.1:6379> rpop mylist
"four"
127.0.0.1:6379> rpop mylist
"three"

linsert

根据提供的值查找位置,然后插入元素

LINSERT key <BEFORE | AFTER> pivot element

返回值:插⼊后的 list ⻓度。
时间复杂度:O(N)

示例:

redis> RPUSH mylist "Hello"
(integer) 1
redis> RPUSH mylist "World"
(integer) 2
redis> LINSERT mylist BEFORE "World" "There"
(integer) 3
redis> LRANGE mylist 0 -1
1) "Hello"
2) "There"
3) "World"

lindex

获取指定下标 index 位置的下标
语法:

lindex key index

时间复杂度:O(N)
返回值:取出的元素或者nil

示例:

127.0.0.1:6379> rpush mylist one two three four five
(integer) 5
127.0.0.1:6379> lindex mylist 0
"one"
127.0.0.1:6379> lindex mylist 5
(nil)
127.0.0.1:6379> lindex mylist -1
"five"
127.0.0.1:6379> lindex mylist 4
"five"
127.0.0.1:6379> lindex mylist -4
"two"
127.0.0.1:6379> lindex mylist -5
"one"

llen

获取 list 长度
语法:

llen key

返回值:list 的长度

127.0.0.1:6379> lpush mylist1 "world"
(integer) 1
127.0.0.1:6379> llen mylist1
(integer) 1

lrem

删除 count 个 element 元素

LREM key count element
  • count > 0: 从左边开始找 count 个 等于 element 的元素
  • count < 0: 从右边开始找 ∣count∣|count|count 个 等于 element 的元素
  • count = 0 : 删除整个 List 中等于 element 的元素
    时间复杂度:O(N)

示例:

  1. 先构造一个列表,内容为 a b a b a b a b
127.0.0.1:6379> rpush mylist a b
(integer) 2
127.0.0.1:6379> rpush mylist a b
(integer) 4
127.0.0.1:6379> rpush mylist a b
(integer) 6
127.0.0.1:6379> rpush mylist a b
(integer) 8
127.0.0.1:6379> lrange mylist 0 -1
1) "a"
2) "b"
3) "a"
4) "b"
5) "a"
6) "b"
7) "a"
8) "b"
  1. 测试lrem 操作:
127.0.0.1:6379> lrem mylist 2 a
(integer) 2
127.0.0.1:6379> lrange mylist 0 -1
1) "b"
2) "b"
3) "a"
4) "b"
5) "a"
6) "b"
127.0.0.1:6379> lrem mylist -2 a
(integer) 2
127.0.0.1:6379> lrange mylist 0 -1
7) "b"
8) "b"
9) "b"
10) "b"
127.0.0.1:6379> lrem mylist 0 b
(integer) 4
127.0.0.1:6379> lrange mylist 0 -1
(empty array)

ltrim

LTRIM key start stop

仅保留 【start, stop】闭区间中的元素,其余直接删除,注意下标从0开始
返回值:start, stop 区间内的元素
时间复杂度:O(N)

示例:

127.0.0.1:6379> rpush mylist1 a b c d e f g h
(integer) 8
127.0.0.1:6379> ltrim mylist1 2 5
OK
127.0.0.1:6379> lrange mylist1 0 -1
1) "c"
2) "d"
3) "e"
4) "f"

lset

lset key index element

修改 index 位置的元素为 element
时间复杂度:O(N)
返回值:成功返回 OK,下标超出范围会报错

1) "c"
2) "d"
3) "e"
4) "f"
127.0.0.1:6379> lset mylist1 2 "hello world"
OK
127.0.0.1:6379> lrange mylist1 0 -1
5) "c"
6) "d"
7) "hello world"
8) "f"
127.0.0.1:6379> lset mylist1 4 "1"
(error) ERR index out of range

3. List 阻塞版本命令

blpop 和 brpop 是 lpop 和 rpop 的阻塞版本:

  • 在列表中有元素的情况下,阻塞和非阻塞表现是一致的。
  • 如果列表中没有元素,执行阻塞版本命令的客户端会根据timeout 阻塞一段时间,期间redis 服务器能正常处理其他客户端的命令
  • 等待过程中,列表中一旦有元素立即返回
  • 若 Redis 中设置了多个键,那么就会从左向右进行遍历,一旦有一个键对应的列表中可以弹出元素,命令立即返回

blpop

lpop 的阻塞版本
语法:

blpop key [key ...] timeout

时间复杂度:O(1)
返回值:取出的数据或者nil

brpop

brpop 的 阻塞版本
语法:

brpop key [key ...] timeout

用法与 blpop 一致,此处不再赘述

brpop 和 blpop 适合用于阻塞队列

4. List 命令小结

操作类型命令时间复杂度
添加rpush key value [value ...]O(k)O(k)O(k)kkk 是元素个数
添加lpush key value [value ...]O(k)O(k)O(k)kkk 是元素个数
添加linsert key before | after pivot valueO(n)O(n)O(n)nnn 是 pivot 距离头尾的距离
查找lrange key start endO(s+n)O(s+n)O(s+n)sss 是 start 偏移量,nnn 是 start 到 end 的范围
查找lindex key indexO(n)O(n)O(n)nnn 是索引的偏移量
查找llen keyO(1)O(1)O(1)
删除lpop keyO(1)O(1)O(1)
删除rpop keyO(1)O(1)O(1)
删除lrem key count valueO(k)O(k)O(k)kkk 是列表长度
删除ltrim key start endO(k)O(k)O(k)kkk 是列表长度
修改lset key index valueO(k)O(k)O(k)kkk 是索引的偏移量
阻塞操作blpop brpopO(1)O(1)O(1)

5. List 内部编码

对于旧版本 Redis (Redis 3.2 之前):

  • ziplist(压缩列表):元素个数少时使用。这种结构更为紧凑,但访问效率较低,所以仅个数少时使用。
  • linkedlist(链表):当列表类型⽆法满⾜ ziplist 的条件时,Redis 会使⽤ linkedlist 作为列表的内部实现。
    Redis 3.2 之后,采用 quicklist 作为 List 的底部编码
    quicklist 时 ziplist 和 linkedlist 的结合,也就是 linkedlist 的每个节点是 ziplist 。

在 /etc/redis/redis.conf 文件中,存在一个配置项 list-max-ziplist-size 表示单个压缩列表允许的最大字节数。

List 使用场景

普通消息队列

1. 消息队列

如下图所示,生产通过 lpush 放入元素,多个消费者通过 brpop 排队阻塞等待元素。这多个消费者客户端就直接能保证负载均衡和高可用

分频道消息队列
由于 brpop 支持等待多个键的特性,我们可以直接基于此实现一个分频道消息队列,实现订阅不同频道的效果,如下图:

2. 微博timeline

每个用户都有自己的 timeline (微博列表),现需要按照分页来展示文章列表。此时可考虑使用列表,列表顺序固定,且支持按照索引获取元素

  1. 每篇微博使用 hash 结构存储
hmset mblog:1 title xx timestamp 1476536196 content xxxxx
...
hmset mblog:n title xx timestamp 1476536196 content xxxxx
  1. 向用户的 timeline 中添加微博,user:uid:mblogs作为键
lpush user:1:mblogs mblog:1 mblog:3
...
lpush user:k:mblogs mblog:9
  1. 分页获取用户的 timeline,例如获取用户1的前十篇微博
keylist = lrange user:1:mblogs 0 9
for key in keylist {hgetall key
}

这种方案存在两个问题:

  1. 第 3)步中,每篇微博都会执行一次 hgetall,这需要多次网络请求。更推荐的做法是通过 pipeline 流水线式 打包命令,批量提交,一页只请求一次。
  2. 分页获取文章是,lrange 在两端较快,若是在中间获取较慢,可考虑将列表做拆分

三、Set 集合

1.Set 总体介绍

集合类型也是保存多个字符串类型的元素的,需要注意:

  1. Set 中元素无序
  2. Set 中元素不能重复
  3. 一个集合最多存 232−12^{32}-12321 个元素
    Redis 对 Set 提供了增删查改的基本操作,同时还支持取交集、并集、差集。

2. Set 普通命令

sadd、 smembers、 sismember

  1. sadd : 将一个或多个元素添加到set中。注意,重复的元素无法添加到 set 中
SADD key member [member ...]
  • 时间复杂度:O(1)
  • 返回值:本次成功添加的元素个数
    示例:
redis> SADD myset "Hello"
(integer) 1
redis> SADD myset "World"
(integer) 1
redis> SADD myset "World"
(integer) 0
redis> SMEMBERS myset
1) "Hello"
2) "World"
  1. smembers : 获取出集合中的所有元素
  • 时间复杂度: O(1)
  • 返回值: 所有元素
    示例:
redis> SADD myset "Hello"
(integer) 1
redis> SADD myset "World"
(integer) 1
redis> SADD myset "World"
(integer) 0
redis> SMEMBERS myset
1) "Hello"
2) "World"
  1. sismember : 判断一个元素在不在 set 中
SISMEMBER key member
  • 时间复杂度:O(1)
  • 返回值:1表示元素在 set 中,0表示不再

示例:

redis> SADD myset "one"
(integer) 1
redis> SISMEMBER myset "one"
(integer) 1
redis> SISMEMBER myset "two"
(integer) 0

scard

获取 set 中的元素个数

SCARD key
  • 时间复杂度:O(1)
  • 返回值:元素个数
    示例:
127.0.0.1:6379> sadd myset 1 2 3 4 5
(integer) 5
127.0.0.1:6379> scard myset
(integer) 5

spop

从 set 中随机删除并返回一个或多个元素。

spop key [count]
  • 时间复杂度 : O(k) k 是count
  • 返回值:取出的元素
    示例:
127.0.0.1:6379> smembers myset
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
127.0.0.1:6379> spop myset 3
6) "2"
7) "3"
8) "1"
127.0.0.1:6379> smembers myset
9) "4"
10) "5"
127.0.0.1:6379> spop myset 1
11) "4"

srandmember

随机取一个或多个元素

SRANDMEMBER key [count]
  1. 当 count 为正数时:
    返回集合中不重复的随机元素,数量为 count(若集合元素总数小于 count,则返回所有元素)
  2. 当 count 为负数时:
    返回的随机元素可能重复,数量为 count 的绝对值(即使集合元素总数小于该绝对值,也会返回指定数量的元素,允许重复)
  3. 当 count 未指定时:
    默认返回 1 个随机元素(不重复)
  • 时间复杂度:O(N) N 为count

smove

将一个元素从 源 set中取出放入目标 set

smove source destination member
  • 时间复杂度:O(1)
  • 1表示移动成功,0表示失败

如果 destination 集合中有指定元素,则不会认为移动失败,效果相当于从source中删除指定元素,当然如果 source中没有member,则会返回0认为移动失败

示例:

redis> SADD myset "one"
(integer) 1
redis> SADD myset "two"
(integer) 1
redis> SADD myotherset "three"
(integer) 1
redis> SMOVE myset myotherset "two"
(integer) 1
redis> SMEMBERS myset
1) "one"redis> SMEMBERS myotherset
1) "three"
2) "two

srem

将指定元素删除

SREM key member [member ...]
  • 时间复杂度 O(N) N为总元素个数
  • 返回值:本次操作删除的元素个数
    示例:
redis> SADD myset "one"
(integer) 1
redis> SADD myset "two"
(integer) 1
redis> SADD myset "three"
(integer) 1
redis> SREM myset "one"
(integer) 1
redis> SREM myset "four"
(integer) 0
redis> SMEMBERS myset
1) "three"
2) "two"

3. 集合间操作

交集、并集、差集

sinter

获取指定集合的交集

SINTER key [key ...]
  • 时间复杂度:O(N * M), N 是最⼩的集合元素个数. M 是最⼤的集合元素个数.
  • 返回值:交集的元素
127.0.0.1:6379> sadd key 1 2 3 4
(integer) 4
127.0.0.1:6379> sadd key2 3 4 5 6
(integer) 4
127.0.0.1:6379> sinter key key2
1) "3"
2) "4"

sinterstore

求交集,并把交集元素放入一个新创建的 集合中

SINTERSTORE destination key [key ...]
  • 时间复杂度:O(N * M), N 是最⼩的集合元素个数. M 是最⼤的集合元素个数.
  • 返回值:交集的元素个数

示例:

127.0.0.1:6379> sadd key 1 2 3 4
(integer) 4
127.0.0.1:6379> sadd key2 3 4 5 6
(integer) 4
127.0.0.1:6379> SINTERSTORE inter_set key key2
(integer) 2
127.0.0.1:6379> SMEMBERS inter_set
1) "3"
2) "4"

sunion

获取指定集合的并集

SUNION key [key ...]
  • 时间复杂度:O(N ), 总元素个数.
  • 返回值:并集的元素
    示例:
127.0.0.1:6379> sunion key key2
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"

sunionstore

求并集,并把并集元素放入一个新创建的 集合中

SINTERSTORE destination key [key ...]
  • 时间复杂度:O(N),总元素个数
  • 返回值:并集的元素个数
127.0.0.1:6379> sunionstore union_key key key2
(integer) 6
127.0.0.1:6379> SMEMBERS union_key
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"

sdiff

获取给定 set 的元素个数
语法:

sdiff key [key ...]

时间复杂度:O(N),N给定的集合的总的元素个数
返回值:差集的元素

127.0.0.1:6379> sadd key2 3 4 5
(integer) 3
127.0.0.1:6379> sdiff key1 key2
1) "1"
2) "2"
127.0.0.1:6379> sdiff key2 key1
3) "5"

sdiffstore

求差集,并把差集元素放入一个新创建的 集合中

SDIFFSTORE destination key [key ...]
  • O(N), N 给定的所有集合的总的元素个数
  • 返回值:差集的元素个数。

4. Set 命令小结

以下是转换后的 Markdown 格式:

命令时间复杂度
sadd key element [element …]O(k),k 是元素个数
srem key element [element …]O(k),k 是元素个数
scard keyO(1)
sismember key elementO(1)
srandmember key [count]O(n),n 是 count
spop key [count]O(n), n 是 count
smembers keyO(k),k 是元素个数
sinter key [key …] sitnerstoreO(m * k),k 是几个集合中元素最小的个数,m 是键个数
sunion key [key …] sunionstoreO(k),k 是多个集合的元素个数总和
sdiff key [key …] sdiffstoreO(k),k 是多个集合的元素个数总和

5. Set 内部编码

集合的编码有两种:

  • intset (整数集合):当集合中的元素都是整数并且元素的个数⼩于 set-max-intset-entries 配置(默认 512 个)时,Redis 会选⽤ intset 来作为集合的内部实现,从⽽减少内存的占用。
  • hashtable(哈希表):当集合类型⽆法满⾜ intset 的条件时,Redis 会使⽤ hashtable 作为集合的内部实现。

6.应用场景

用户兴趣标签
场景:给用户打兴趣标签(如 “篮球”“音乐”“阅读”),用于个性化推荐。
实现:

  • 用 Set 存储用户的兴趣标签:SADD user:10086:interests 篮球 音乐
    推荐逻辑:
  • 计算用户兴趣交集:SINTER user:10086:interests user:10010:interests(找到与用户 10086 兴趣相似的用户 10010)
  • 还可以通过交集计算用户群体的共同标签
  • 基于标签推送内容:SUNION tag:篮球 tag:音乐(获取用户可能感兴趣的内容 ID)

四、Zset 有序集合

1. Zset 总体认识

有序集合保留了 Set 的元素不能重复特性,并在此基础上给每个元素引入了 分数(score),Zset 会基于分数对元素进行升序排序,这就是 Zset 的有序特性。
Zset 提供了获取指定分数和元素范围查找、计算成员排名等功能,合理利用 Zset 可以帮助我们在实际开发中解决很多问题

Zset 元素不能重复,但不同元素的分数可以一样

关于有序概念在不同上下文中的理解

数据结构是否允许重复元素是否有序有序依据应用场景
列表索引下标时间轴、消息队列等
集合标签、社交等
有序集合分数排行榜系统、社交等

2. Zset 普通命令

zadd

  • 添加或者更新指定的元素以及关联的分数到 zset 中,可添加多对。
  • 分数应该符合 double 类型,+inf/-inf 作为正负极限也是合法的。
    语法:
ZADD key [NX | XX] [GT | LT] [CH] [INCR] score member [score member ...]

选项介绍:

  • XX 表示仅在member 存在的情况下更新元素的分数
  • NX 表示仅在 member 不存在的情况下添加新元素和分数
  • CH:默认情况下,ZADD 返回的是本次添加的元素个数,但指定这个选项之后,就会还会包含本次更新的元素的个数。
  • INCR:此时命令类似 ZINCRBY 的效果,将元素的分数加上指定的分数。此时只能指定⼀个元素和分数。
  • LT : less than 的缩写,表示仅在分数比原来小的时候更新分数
  • GT : greater than 的缩写,表示仅在分数比原来大的时候更新分数

时间复杂度:O(log(N)) N 是 Zset 的总元素个数
返回值:本次添加成功的元素个数
示例:

  1. 不加选项(需要注意的就是每一对元素中,分数在前,名称在后)
127.0.0.1:6379> zadd my_zset 99 Tom 99 Jerry 97 David 95 Tim
(integer) 4
127.0.0.1:6379> zrange 0 -1
1) "Tim"
2) "David"
3) "Jerry"
4) "Tom"127.0.0.1:6379> zrange my_zset 0 -1 withscores
1) "Tim"
2) "95"
3) "David"
4) "97"
5) "Jerry"
6) "99"
7) "Tom"
8) "99"127.0.0.1:6379> zadd my_zset 90 Tom
(integer) 0
127.0.0.1:6379> zrange my_zset 0 -1
1) "Tom"
2) "Tim"
3) "David"
4) "Jerry"
  1. XX 和 NX
127.0.0.1:6379> zadd my_zset NX 0 Jerry 
(integer) 0
127.0.0.1:6379> zrange my_zset 0 -1 withscores
1) "Tom"
2) "90"
3) "Tim"
4) "95"
5) "David"
6) "97"
7) "Jerry"
8) "99"
127.0.0.1:6379> zadd my_zset XX 0 Jerry 
(integer) 0127.0.0.1:6379> zrange my_zset 0 -1 withscores
1) "Jerry"
2) "0"
3) "Tom"
4) "90"
5) "Tim"
6) "95"
7) "David"
8) "97"127.0.0.1:6379> zadd my_zset XX 100 zhangsan 
(integer) 0
127.0.0.1:6379> zrange my_zset 0 -1 withscores
1) "Jerry"
2) "0"
3) "Tom"
4) "90"
5) "Tim"
6) "95"
7) "David"
8) "97"127.0.0.1:6379> zadd my_zset NX 100 zhangsan 
(integer) 1
127.0.0.1:6379> zrange my_zset 0 -1 withscores1) "Jerry"2) "0"3) "Tom"4) "90"5) "Tim"6) "95"7) "David"8) "97"9) "zhangsan"
10) "100"
  1. CH选项
127.0.0.1:6379> zadd my_zset 100 Tom
(integer) 0
127.0.0.1:6379> zadd my_zset CH 100 Jerry
(integer) 1
  1. INCR
127.0.0.1:6379> zrange my_zset 0 -1 withscores1) "David"2) "97"3) "Jerry"4) "100"5) "Tim"6) "100"7) "Tom"8) "100"9) "zhangsan"
10) "100"
127.0.0.1:6379> zadd my_zset incr 100 Tim
"200"
127.0.0.1:6379> zrange my_zset 0 -1 withscores11) "David"12) "97"13) "Jerry"14) "100"15) "Tom"16) "100"17) "zhangsan"18) "100"19) "Tim"
20) "200"

zcard

获取 zset 中的元素个数

ZCARD key
  • 时间复杂度:O(1)
  • 返回值:zset 内的元素个数。
127.0.0.1:6379> zrange my_zset 0 -1 withscores1) "David"2) "97"3) "Jerry"4) "100"5) "Tim"6) "100"7) "Tom"8) "100"9) "zhangsan"
10) "100"
127.0.0.1:6379> zcard my_zset
(integer) 5

zcount

返回分数在 min 和 max 之间的元素个数,默认情况下,min 和 max 都是包含的。通过 ( 可以改为开区间

ZCOUNT key min max
  • 时间复杂度:O(log(N))
  • 返回值:满足条件的元素列表个数

示例:

127.0.0.1:6379> zrange my_zset 0 -1 withscores1) "David"2) "97"3) "Jerry"4) "100"5) "Tom"6) "100"7) "zhangsan"8) "100"9) "Tim"
10) "200"
127.0.0.1:6379> zcount my_zset 100 200
(integer) 4
127.0.0.1:6379> zcount my_zset (100 200
(integer) 1
127.0.0.1:6379> zcount my_zset (100 (200
(integer) 0

zrange

返回指定区间⾥的元素,分数按照升序。带上 WITHSCORES 可以把分数也返回。

ZRANGE key start stop [WITHSCORES]

此处的 [start, stop] 为下标构成的闭区间. 从 0 开始, ⽀持负数.

时间复杂度:O(log(N)+M N 是 Zset 总元素个数,M是区间内元素个数
返回值:区间内的元素列表。

zrevrange

返回指定区间⾥的元素,分数按照降序。带上 WITHSCORES 可以把分数也返回。

ZREVRANGE key start stop [WITHSCORES]

备注:这个命令在 6.2.0 之后废弃,并且功能合并到 ZRANGE 中。本博客仅针对Redis 6.0 所以不做过多讨论

时间复杂度:O(log(N)+M)
返回值:区间内的元素列表。

zrangebyscore

返回分数在 min 和 max 之间的元素,默认情况下,min 和 max 都是包含的,可以通过 ( 来改成开区间。

备注:这个命令在 6.2.0 之后废弃,并且功能合并到 ZRANGE 中。

语法:

ZRANGEBYSCORE key min max [WITHSCORES]

时间复杂度:O(log(N)+M)
返回值:区间内的元素列表。

示例:

127.0.0.1:6379> zrangebyscore my_zset 0 100  withscores
1) "David"
2) "97"
3) "Jerry"
4) "100"
5) "Tom"
6) "100"
7) "zhangsan"
8) "100"

zpopmax 和 zpopmin

  1. zpopmax : 删除并返回分数最⾼的 count 个元素。
ZPOPMAX key [count]
  • 时间复杂度:O(log(N) * M)
  • 返回值:分数和元素列表。

优先删除字典序更低的元素

示例:

redis> ZADD myzset 1 "one"
(integer) 1
redis> ZADD myzset 2 "two"
(integer) 1
redis> ZADD myzset 3 "three"
(integer) 1
redis> ZPOPMAX myzset
1) "three"
  1. zpopmin :删除并返回分数最低的 count 个元素。
ZPOPMIN key [count]
  • 时间复杂度:O(log(N) * M)
  • 返回值:分数和元素列表。

分数相同,优先删除字典序低的

redis> ZADD myzset 1 "one"
(integer) 1
redis> ZADD myzset 2 "two"
(integer) 1
redis> ZADD myzset 3 "three"
(integer) 1
redis> ZPOPMIN myzset
1) "one"
2) "1"

bzpopmax 和 bzpopmin

这是 zpopmax 和 zpopmin 的阻塞版本((仅支持弹出一个元素,可阻塞等待多个 key ),可用于实现阻塞优先级队列。

  1. BZPOPMAX
1 BZPOPMAX key [key ...] timeout
  • 时间复杂度:O(log(N))
  • 返回值:元素列表。
    ⽰例:
redis> DEL zset1 zset2
(integer) 0
redis> ZADD zset1 0 a 1 b 2 c
(integer) 3
redis> BZPOPMAX zset1 zset2 0
1) "zset1"
2) "c"
3) "2"
  1. ZPOPMIN
BZPOPMIN key [key ...] timeout
  • 时间复杂度:O(log(N))
  • 返回值:元素列表。
    示例:
redis> ZADD myzset 1 "one"
(integer) 1
redis> ZADD myzset 2 "two"
(integer) 1
redis> ZADD myzset 3 "three"
(integer) 1
redis> ZPOPMIN myzset
1) "one"
2) "1

zrank 和 zrevrank

  1. zrank : 返回指定元素按照升序的排名
ZRANK key member
  • 时间复杂度:O(log(N))
  • 返回值:排名。
redis> ZADD myzset 1 "one"
(integer) 1
redis> ZADD myzset 2 "two"
(integer) 1
redis> ZADD myzset 3 "three"
(integer) 1
redis> ZRANK myzset "three"
(integer) 2
redis> ZRANK myzset "four"
(nil)
  1. zrevrank:返回指定元素的排名,降序。
zrevrank key member
  • 时间复杂度:O(log(N))
  • 返回值:排名。
redis> ZADD myzset 1 "one"
(integer) 1
redis> ZADD myzset 2 "two"
(integer) 1
redis> ZADD myzset 3 "three"
(integer) 1
redis> ZREVRANK myzset "one"
(integer) 2
redis> ZREVRANK myzset "four"
(nil)

zscore

返回指定元素的分数

ZSCORE key member
  • 时间复杂度:O(1),redis对此做了特殊优化
  • 返回值:分数。
    示例:
redis> ZADD myzset 1 "one"
(integer) 1
redis> ZSCORE myzset "one"
"1"

zrem

删除指定的元素

ZREM key member [member ...]
  • 时间复杂度:O(M * log(N)) (M 是删除的元素个数)
  • 返回值:本次操作删除的元素个数。
    示例:
redis> ZADD myzset 1 "one"
(integer) 1
redis> ZADD myzset 2 "two"
(integer) 1
redis> ZADD myzset 3 "three"
(integer) 1
redis> ZREM myzset "two"
(integer) 1
redis> ZRANGE myzset 0 -1 WITHSCORES
1) "one"
2) "1"
3) "three"
4) "3"

zremrangebyrank

按照升序删除指定范围的元素,闭区间

ZREMRANGEBYRANK key start stop
  • 时间复杂度:O(log(N)+M) M 是被删除的元素个数
  • 返回值:本次操作删除的元素个数。
redis> ZADD myzset 1 "one"
(integer) 1
redis> ZADD myzset 2 "two"
(integer) 1
redis> ZADD myzset 3 "three"
(integer) 1
redis> ZREMRANGEBYRANK myzset 0 1
(integer) 2
redis> ZRANGE myzset 0 -1 WITHSCORES
1) "three"
2) "3"

zremrangebyscore

按照分数删除指定范围的元素,左闭右闭。

ZREMRANGEBYSCORE key min max
  • 时间复杂度:O(log(N)+M)
  • 返回值:本次操作删除的元素个数。
redis> ZADD myzset 1 "one"
(integer) 1
redis> ZADD myzset 2 "two"
(integer) 1
redis> ZADD myzset 3 "three"
(integer) 1
redis> ZREMRANGEBYSCORE myzset -inf (2
(integer) 1
redis> ZRANGE myzset 0 -1 WITHSCORES
1) "two"
2) "2"
3) "three"
4) "3"

zincrby

为指定的元素的关联分数加上指定的分数值。

ZINCRBY key increment member
  • 时间复杂度:O(log(N))
  • 返回值:增加后元素的分数

Zset 集合间操作命令

交集

zinterstore

求出给定有序集合中元素的交集并保存进⽬标有序集合中,在合并过程中以元素为单位进⾏合并,元素对应的分数按照不同的聚合⽅式和权重得到新的分数。

ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE <SUM | MIN | MAX>]

选项介绍:

  • numkeys : 必须指定要合并的 key 的数量
  • WEIGHTS:可选,为待合并的key 分配权重
  • AGGREGATE :可选 sum | min | max , 分别是按权重求和,乘以权重后取最小值,乘以权重后取最大值。
    时间复杂度:O(N∗K)+O(M∗log(M))O(N*K)+O(M*log(M))O(NK)+O(Mlog(M)) N 是输⼊的有序集合中, 最小的有序集合的元素个数; K 是输入了几个有序集合; M 是最终结果的有序集合的元素个数
    返回值:最终结果的有序集合的元素个数

示例:

zunionstore

求出给定有序集合中元素的并集并保存进⽬标有序集合中,在合并过程中以元素为单位进⾏合并,元素对应的分数按照不同的聚合⽅式和权重得到新的分数。

ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight[weight ...]] [AGGREGATE <SUM | MIN | MAX>]
  • 时间复杂度:O(N)+O(M∗log(M))O(N)+O(M*log(M))O(N)+O(Mlog(M)) N 是输⼊的有序集合总的元素个数; M 是最终结果的有序集合的元素个数.
  • 返回值:⽬标集合中的元素个数
    选项和 zinterstore 一样
    示例:

4. 命令小结

命令时间复杂度
zadd key score member [score member ...]O(k⋅log⁡(n))O(k \cdot \log(n))O(klog(n))kkk 是添加成员的个数,nnn 是当前有序集合的元素个数
zcard keyO(1)O(1)O(1)
zscore key memberO(1)O(1)O(1)
zrank key member
zrevrank key member
O(log⁡(n))O(\log(n))O(log(n))nnn 是当前有序集合的元素个数
zrem key member [member ...]O(k⋅log⁡(n))O(k \cdot \log(n))O(klog(n))kkk 是删除成员的个数,nnn 是当前有序集合的元素个数
zincrby key increment memberO(log⁡(n))O(\log(n))O(log(n))nnn 是当前有序集合的元素个数
zrange key start end [withscores]
zrevrange key start end [withscores]
O(k+log⁡(n))O(k + \log(n))O(k+log(n))kkk 是获取成员的个数,nnn 是当前有序集合的元素个数
zrangebyscore key min max [withscores]
zrevrangebyscore key max min [withscores]
O(k+log⁡(n))O(k + \log(n))O(k+log(n))kkk 是获取成员的个数,nnn 是当前有序集合的元素个数
zcount key min maxO(log⁡(n))O(\log(n))O(log(n))nnn 是当前有序集合的元素个数
zremrangebyrank key start endO(k+log⁡(n))O(k + \log(n))O(k+log(n))kkk 是获取成员的个数,nnn 是当前有序集合的元素个数
zremrangebyscore key min maxO(k+log⁡(n))O(k + \log(n))O(k+log(n))kkk 是获取成员的个数,nnn 是当前有序集合的元素个数
zinterstore destination numkeys key [key ...]O(n⋅k)+O(m⋅log⁡(m))O(n \cdot k) + O(m \cdot \log(m))O(nk)+O(mlog(m))nnn 是输入的集合最小的元素个数,kkk 是集合个数,mmm 是目标集合元素个数
zunionstore destination numkeys key [key ...]O(n)+O(m⋅log⁡(m))O(n) + O(m \cdot \log(m))O(n)+O(mlog(m))nnn 是输入集合总元素个数,mmm 是目标集合元素个数

5. 内部编码

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

  • ziplist(压缩列表):当有序集合的元素个数⼩于 zset-max-ziplist-entries 配置(默认 128 个),同时每个元素的值都⼩于 zset-max-ziplist-value 配置(默认 64 字节)时,Redis 会⽤ ziplist 来作为有序集合的内部实现,ziplist 可以有效减少内存的使⽤。
  • skiplist(跳表):当 ziplist 条件不满⾜时,有序集合会使⽤ skiplist 作为内部实现,因为此时ziplist 的操作效率会下降。

关于跳表将在后续介绍

6. 应用场景—排行榜系统

在使用 Redis 的有序集合(Zset)维护每天的热榜(以点赞数为维度)这一场景中,Zset 的特性使其成为一个非常合适的选择,以下是具体的介绍:

  1. 初始化热榜:每天开始时,可以创建一个新的 Zset 来存储当天的热榜数据。例如,使用键名 hot_list:2024-10-01 来表示 2024年10月1日的热榜,使用 ZADD 命令向其中添加帖子的 ID 作为成员,初始点赞数(假设为0)作为分数。

    ZADD hot_list:2024-10-01 0 post_1
    ZADD hot_list:2024-10-01 0 post_2
    
  2. 更新点赞数:当有用户对帖子进行点赞操作时,通过 ZINCRBY 命令增加对应帖子在 Zset 中的点赞数(分数)。

    ZINCRBY hot_list:2024-10-01 1 post_1
    
  3. 获取热榜数据:要获取当天点赞数排名前10的帖子,可以使用 ZREVRANGE 命令(因为点赞数越高越热门,所以使用反向排序获取高分元素)。

    ZREVRANGE hot_list:2024-10-01 0 9 WITHSCORES
    

    其中 WITHSCORES 参数用于在返回成员的同时返回其对应的分数(点赞数),方便展示。

  4. 获取特定帖子的排名:如果想知道某个帖子在热榜中的排名,可以使用 ZREVRANK 命令。

    ZREVRANK hot_list:2024-10-01 post_1
    

    该命令会返回帖子 post_1 在热榜中的排名(从0开始计数)。

五、其他类型

Redis 数据类型
Redis 还提供了其他的一些数据类型,虽然这些类型使用率不高,但在特定场景下使用,还是方便且高效的。

  • Streams : 类似于消息队列(阻塞队列),支持高效的消息发布、订阅等功能
  • Geospatial : 用于存储地理坐标的(经纬度)。支持用户在不同坐标减进行距离查询等操作
  • Hyperloglog : 以极低的空间代价,估算大集合元素的个数(单个 Hyperloglog 12 KB)
  • Bitset :位图,用一个bit表示对应位的数字是否存在
  • BitFields :位域类型,位域可以指定每一个元素所占比特位,从而更方便的进行位操作。(类似 c 的位段)

六、 补充

渐进式遍历

keys * 在生产环境中很危险。
通过渐进式遍历,就可以做到,既能够获取所有的key,同时又不会卡死服务器

以渐进式的方式进行键的遍历

SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]
  • 时间复杂度 O(1)
  • 下一次scan的的光标(cursor)以及本次得到的键
    选项:
  • pattern 同 keys 命令
  • count :限制本次遍历能获取到多少元素,默认为 10 。count不是精确值,实际返回的个数可能略有出入
  • type : 仅返回 value 是对应类型的key

count 并非下标,实际返回的数字我们是无法预测的,我们只知道 0 表示开始以及接着上一次scan 命令返回的count 继续遍历

示例:

127.0.0.1:6379> mset k1 1 k2 2 k3 3 k4 4 k5 5 k6 6 k7 7 k8 8 k9 9 k0 0 
OK
127.0.0.1:6379> scan 0
1) "15"
2)  1) "k7"2) "k8"3) "k1"4) "k5"5) "k2"6) "k0"7) "k3"8) "k9"9) "k4"3) "k6"127.0.0.1:6379> scan 15
1) "0"
2) (empty array)127.0.0.1:6379> scan 0 count 5
1) "14"
2) 1) "k7"3) "k8"4) "k1"5) "k5"6) "k2"127.0.0.1:6379> scan 14 count 5
1) "15"
2) 1) "k0"3) "k3"4) "k9"5) "k4"6) "k6"127.0.0.1:6379> scan 15 count 5
1) "0"
2) (empty array)

除了 scan 以外,Redis ⾯向哈希类型、集合类型、有序集合类型分别提供了 hscan、sscan、zscan 命令,它们的⽤法和 scan 基本类似,感兴趣的读者可以⾃⾏做扩展学习。

渐进性遍历 scan 虽然解决了阻塞的问题,但如果在遍历期间键有所变化(增加、修改、删除),可能导致遍历时键的重复遍历或者遗漏,这点务必在实际开发中考虑。

数据库操作

MySQL 中有 database 的概念,一个 MySQL服务器可以有多个 database

Redis 不能创建数据库,而是本身就提供了16个数据库(0 - 15),各个数据库相互独立。默认操作 0 号数据库

Redis提供了数据库相关的命令:dbsize、select、flushdb、flushall 命令。

select

切换数据库

select dbIndex

各个数据库之间相互独立,dbIndex 取 0-15
示例:

127.0.0.1:6379> scan 0
1) "15"
2)  1) "k7"2) "k8"3) "k1"4) "k5"5) "k2"6) "k0"7) "k3"8) "k9"9) "k4"3) "k6"127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> scan 0
1) "0"
2) (empty array)

❗ Redis 中虽然⽀持多数据库,但随着版本的升级,其实不是特别建议使⽤多数据库特性。如果真的需要完全隔离的两套键值对,更好的做法是维护多个 Redis 实例,⽽不是在⼀个Redis 实例中维护多数据库。这是因为本⾝ Redis 并没有为多数据库提供太多的特性,其次⽆论是否有多个数据库,Redis 都是使⽤单线程模型,所以彼此之间还是需要排队等待命令的执⾏。同时多数据库还会让开发、调试和运维⼯作变得复杂。所以实践中,始终使⽤数据库 0 其实是⼀个很好的选择。

dbsize

返回当前数据库键的个数

127.0.0.1:6379> dbsize 
(integer) 10
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> dbsize 
(integer) 0

flushall 和 flushdb

  • flushdb : 清空当前数据库
  • flushall : 清空所有数据库

❗ 永远不要在线上环境执⾏清除数据的操作

http://www.dtcms.com/a/315793.html

相关文章:

  • AR眼镜成本挑战与突破路径:技术创新引领产业变革
  • Opencv: cv::Mat支持的类型汇总
  • 当送餐机器人学会“思考“:Deepoc如何赋予机器人具身智能
  • AI-03a1.Python深度学习-Tensorflow和Keras入门
  • eBay退货管理深度解析:筑牢售后防线,驱动账号长效稳健发展
  • AutoSar AP LT规范中 建模消息和非建模消息都可以使用LogInfo() API吗?
  • visual studio 历史版本安装
  • FLAN-T5:大规模指令微调的统一语言模型框架
  • 为什么要选择时序数据库IoTDB?
  • Redis实现可重入锁
  • “Why“比“How“更重要:层叠样式表CSS
  • 《C++初阶之STL》【模板参数 + 模板特化 + 分离编译】
  • @【JCIDS】【需求论证】联合能力集成与开发系统知识图谱
  • 机器学习通关秘籍|Day 03:决策树、随机森林与线性回归
  • 【工程化】tree-shaking 的作用以及配置
  • Android Framework代码屏蔽未接来电振动及声音通知
  • DHTMLX重磅发布React Scheduler组件,赋能日程管理开发!
  • SELinux加固Linux安全
  • 将普通用户添加到 Docker 用户组
  • 第十七天:原码、反码、补码与位运算
  • RAFT:让语言模型更聪明地用文档答题
  • Java从入门到精通 - 集合框架(一)
  • 最长连续序列(每天刷力扣hot100系列)
  • FastDeploy2.0:报qwen2.embed_tokens.weight
  • 2.4 组件通信
  • 24. 前端-js框架-Vue
  • Occ3D: A Large-Scale 3D Occupancy Prediction Benchmark for Autonomous Driving
  • Python高级编程与实践:Python性能分析与优化
  • Java技术栈/面试题合集(3)-Java并发篇
  • 【功能测试】软件功能上线测试经验总结