你必须非常努力,才能看起来毫不费力!


前言

前面我们介绍了 Redis 基本数据结构以及相关的衍生结构,本篇文章我们简单介绍下数据库的相关操作。

DBSIZE

可用版本:>= 1.0.0

时间复杂度:O(1)

命令格式

DBSIZE

命令描述

  • 返回当前数据库key的数量

返回值

整数值

示例

127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> mset name jack age 10 gender male
OK
127.0.0.1:6379> dbsize
(integer) 3

KEYS

可用版本:>= 1.0.0

时间复杂度:O(N),N为数据库中key的数量

命令格式

KEYS pattern

命令描述

  • 返回数据库中与pattern匹配的key
  • pattern支持正则表达式,例如:
  • h?llo 匹配 hello, hallo 和 hxllo
  • h*llo 匹配 hllo 和 heeeello
  • h[ae]llo 匹配 hello 和 hallo, 但是不匹配 hillo
  • h[^e]llo 匹配 hallo, hbllo 但是不匹配 hello
  • h[a-b]llo 匹配 hallo 和 hbllo

警告⚠️

keys命令一般不能用于生产环境,适用于测试环境的debug。如果数据库特别大,keys命令会严重影响性能。

如果想要查找与需求匹配的key,可以考虑使用SCAN命令,或者使用set结构来存储数据。

返回值

列表:与pattern匹配的key列表

示例

127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> mset hello 1 hella 2 hetta 3
OK
127.0.0.1:6379> keys hell*
1) "hello"
2) "hella"

FLUSHDB

可用版本:>= 1.0.0

时间复杂度:O(N),N为数据库中key的数量

命令格式

FLUSHDB [ASYNC|SYNC]

命令描述

  • 删除当前数据库中所有数据,该命令不会失败
  • 该命令默认使用同步方式删除所有数据,自Redis 6.2,可以通过配置lazyfree-lazy-user-flush = yes,设置默认删除方式为异步
  • 异步删除时,只会删除命令执行时存在的key,在收到命令与执行命令中间过程中新增的key不会被删除
  • 或者直接在命令中指定同步或异步删除
  • ASYNC: 异步删除
  • SYNC: 同步删除

返回值

字符串

示例

127.0.0.1:6379> set name jack
OK
127.0.0.1:6379> get name
"jack"
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> get name
(nil)

FLUSHALL

可用版本:>= 1.0.0

时间复杂度:O(N),N为数据库中key的数量

命令格式

FLUSHALL [ASYNC|SYNC]

命令描述

  • 删除所有数据库中所有key,该命令不会失败。
  • 该命令默认使用同步方式删除所有数据,自Redis 6.2,可以通过配置lazyfree-lazy-user-flush = yes,设置默认删除方式为异步
  • 异步删除时,只会删除命令执行时存在的key,在收到命令与执行命令中间过程中新增的key不会被删除
  • 或者直接在命令中指定同步或异步删除
  • ASYNC: 异步删除
  • SYNC: 同步删除

返回值

字符串

示例

127.0.0.1:6379> flushall
OK

SELECT

可用版本:>= 1.0.0

时间复杂度:O(N),N为数据库中key的数量

命令格式

SELECT index

命令描述

  • 选择逻辑数据库,默认为0
  • Redis配置文件中有如下配置 databases 16,这里的数据库类似于命名空间,所有的数据库都会持久化到同一个RDB/AOF文件中。但是不同的数据库,可以有相同的key。像FLUSHDBSWAPDBRANDOMKEY只能作用于特定数据库
  • 在实践中,应该用数据库保存同一个应用的不同key,而不是将Redis实例用于不同的应用
  • 当使用Redis Cluster模式时,SELECT命令不可用,因为Redis Cluster只支持0号数据库。

返回值

字符串

示例

127.0.0.1:6379> set age 10
OK
127.0.0.1:6379> get age
"10"

# 切换数据库1
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> get age
(nil)

SWAPDB

可用版本:>= 1.0.0

时间复杂度:O(N),N为监视或者阻塞在两个数据库的客户端数量

命令格式

SWAPDB index1 index2

命令描述

  • 对换指定的两个数据库, 使得两个数据库的数据立即互换。
    例如 SWAPDB 0 1,连接到数据库0的客户端可以立马看到数据库1中的数据,反过来一样。

返回值

字符串:“OK”

示例

SWAPDB 0 1

SCAN

可用版本:>= 1.0.0

时间复杂度:单次调用时间复杂度为O(1),完整迭代数据库时间复杂度为O(N),其中 N 为数据集中的元素数量。

命令格式

SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]

命令描述

  • SCAN 命令及其相关的 SSCAN 命令、 HSCAN 命令和 ZSCAN 命令都用于增量地迭代集合元素
  • SCAN 命令用于迭代当前数据库中的数据库键。
  • SSCAN 命令用于迭代Set结构中的元素。
  • HSCAN 命令用于迭代Hash结构中的键值对。
  • ZSCAN 命令用于迭代Sorted Set中的元素及其分值。
  • 上述命令可以增量迭代整个数据库,每次只会返回少量数据,因此这些命令可以用于生产环境,不用担心像KEYSSMEMBERS等命令一样阻塞数据库。
  • KEYSSMEMBERS等阻塞命令,会一次性返回所有的数据;SCAN相关命令,一次只会返回有限数据,可以通过多次迭代调用,最终获取全量数据。
  • SCANSSCANHSCANZSCAN 多个命令的功能类似,这里的解析可以覆盖四个命令。唯一的区别是,SSCANHSCANZSCAN命令的第一个参数是要遍历的数据结构的key,而SCAN命令不需要key,因为SCAN命令是遍历的当前数据库。

SCAN基础用法

SCAN是基于迭代器的游标,每次命令调用,服务器都会返回一个更新的游标用于下一次调用。当迭代开始时需要将游标设置为0,当Redis返回0时表示迭代结束。下面是SCAN迭代的一个例子:

首先将游标设置为0,表示迭代开始;第二次迭代时,使用第一次迭代返回的17作为游标,第二次迭代的第一个返回值为0,表示整个迭代过程结束。

从例子可以看到SCAN的返回值有两个,第一个是下次迭代的游标值,第二个是元素数组。

redis 127.0.0.1:6379> scan 0
1) "17"
2)  1) "key:12"
    2) "key:8"
    3) "key:4"
    4) "key:14"
    5) "key:16"
    6) "key:17"
    7) "key:15"
    8) "key:10"
    9) "key:3"
   10) "key:7"
   11) "key:1"
   
redis 127.0.0.1:6379> scan 17
1) "0"
2) 1) "key:5"
   2) "key:18"
   3) "key:0"
   4) "key:2"
   5) "key:19"
   6) "key:13"
   7) "key:6"
   8) "key:9"
   9) "key:11"

SCAN命令的保证

SCAN命令,以及SCAN家族的其他命令,在完成整个迭代过程后,会给用户如下保证:

  • 一个完整的迭代,会将从开始迭代到结束迭代,位于集合中的所有元素返回。也就是说,如果一个元素在迭代开始时位于集合中,并且在迭代结束时依然在集合中,那么这个元素一定会在某个SCAN迭代的返回值里。
  • 一个完整的迭代,不会返回从开始到结束,不存在集合中的元素。也就是说,如果从迭代开始前,一个元素被移除,并且在整个迭代过程中没有被再次添加进来,那么SCAN可以保证不会返回该元素。

因为SCAN使用游标来表示遍历状态,会有如下缺点:

  • 一个元素可能被返回多次
  • 如果一个元素在迭代过程中,不是始终存在于集合中的,那么能否返回该元素时不确定的。

返回元素个数

SCAN命令家族的命令,并不保证每次返回的元素数量在给定范围内。命令也有可能返回0个元素,但只要返回的游标值不是0,就不能认为迭代结束了。

但是,迭代返回的元素数量还是符合一定规则的:

  • 如果迭代的是一个较大数据集,每次迭代会返回10左右的元素个数;
  • 如果迭代的是一个较小数据集(较小的Set、Hash、Sorted Set),迭代可能会返回所有元素;

但是,用户可以使用 COUNT 选项调整每次调用返回的元素的数量级。

COUNT选项

尽管SCAN命令并不保证返回的元素数量,但是我们可以通过COUNT参数,对命令行为进行一定情况的调整。

通过COUNT参数,用户指定每次迭代应该返回的元素数量。

  • 虽然 COUNT 参数只是对代命令的一种提示,但是在大多数情况下,这种提示都是有效的。
  • 在迭代一个较大的集合时,每次返回的元素数量就是COUNT值,或者会多一点;
  • 在迭代一个较小的集合时,命令会无视COUNT参数,返回所有元素;
  • 每次迭代使用的COUNT值可以不同

MATCh选项

和上述KEYS命令类似,SCAN命令可以只迭代满足指定模式的元素。如:

redis 127.0.0.1:6379> sadd myset 1 2 3 foo foobar feelsgood
(integer) 6
redis 127.0.0.1:6379> sscan myset 0 match f*
1) "0"
2) 1) "foo"
   2) "feelsgood"
   3) "foobar"

需要注意的是,MATCH参数是在从集合中取得数据后,才进行的过滤,然后再返回给客户端。也就是说如果在集合中可以匹配到的元素很少,那么每次迭代返回的数据也会很少甚至为空:

redis 127.0.0.1:6379> scan 0 MATCH *11*
1) "288"
2) 1) "key:911"
redis 127.0.0.1:6379> scan 288 MATCH *11*
1) "224"
2) (empty list or set)
redis 127.0.0.1:6379> scan 224 MATCH *11*
1) "80"
2) (empty list or set)
redis 127.0.0.1:6379> scan 80 MATCH *11*
1) "176"
2) (empty list or set)
redis 127.0.0.1:6379> scan 176 MATCH *11* COUNT 1000
1) "0"
2)  1) "key:611"
    2) "key:711"
    3) "key:118"
    4) "key:117"
    5) "key:311"
    6) "key:112"
    7) "key:111"
    8) "key:110"
    9) "key:113"
   10) "key:211"
   11) "key:411"
   12) "key:115"
   13) "key:116"
   14) "key:114"
   15) "key:119"
   16) "key:811"
   17) "key:511"
   18) "key:11"

从上面例子可以看出,前几次的调用返回的数据很少甚至为空,最后一次通过指定COUNT=1000,强制迭代更多的元素,才返回了较多的匹配。

TYPE参数

自Redis 6.0,可以通过指定TYPE参数,来返回特定类型key。TYPE参数只适用SCAN命令,不使用SSCAN、HSCAN、ZSCAN等命令。

TYPE是针对的底层基础数据类型,对于Geo、HyperLogLog、Bitmap,TYPE字段不能区分,因为这几个的底层实现都是基于其他基础类型的,比如Geo就是基于ZSET实现的。

redis 127.0.0.1:6379> GEOADD geokey 0 0 value
(integer) 1
redis 127.0.0.1:6379> ZADD zkey 1000 value
(integer) 1
redis 127.0.0.1:6379> TYPE geokey
zset
redis 127.0.0.1:6379> TYPE zkey
zset
redis 127.0.0.1:6379> SCAN 0 TYPE zset
1) "0"
2) 1) "geokey"
   2) "zkey"

和MATCH选项一样,TYPE参数也是在取得数据之后进行过滤,然后再返回给客户端,所以使用该参数,也不会减少需要遍历的元素个数。

并发执行多个迭代

因为迭代的状态是使用游标标识的,而游标状态客户端进行保存,因此多个客户端可以同时对一个集合执行迭代。

中途停止迭代

因为迭代的游标是客户端保存的,服务端不保存状态,客户端可以随意停止迭代,不会影响到服务器。

使用错误的游标进行迭代

使用间断的、负数值、超出范围内或者其他非法游标进行迭代,不会造成服务器崩溃, 但可能会让命令产生未知行为。有效的游标值如下:

  • 开始迭代时给定的0值
  • 上一次迭代返回的游标值

迭代结束的保证

只有迭代的数据集大小在给定的范围内,迭代才会停止,否则迭代一个不断增长的数据集,可能会永远不会停止。

其实可以很好理解,一个集合不断增长,而如果迭代结束,就需要保证迭代的速度大于增长速度,否则永远不会停止迭代。

返回值

SCAN、SSCAN、HSCAN、ZSCAN都会返回两个值,第一个值是更新后的游标值,第二个值是元素数组。

  • SCAN返回的数组是key列表
  • SSCAN返回的数组是Set元素列表
  • HSCAN返回的元素数组中,包含两种元素:filed域和value值
  • ZSCAN返回的元素数组中,包含两种元素:元素和分值

示例

redis 127.0.0.1:6379> hmset hash name Jack age 33
OK
redis 127.0.0.1:6379> hscan hash 0
1) "0"
2) 1) "name"
   2) "Jack"
   3) "age"
   4) "33"