你必须非常努力,才能看起来毫不费力!
前言
前面我们介绍了 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。像FLUSHDB
、SWAPDB
、RANDOMKEY
只能作用于特定数据库 - 在实践中,应该用数据库保存同一个应用的不同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中的元素及其分值。
- 上述命令可以增量迭代整个数据库,每次只会返回少量数据,因此这些命令可以用于生产环境,不用担心像
KEYS
、SMEMBERS
等命令一样阻塞数据库。 - 像
KEYS
、SMEMBERS
等阻塞命令,会一次性返回所有的数据;SCAN
相关命令,一次只会返回有限数据,可以通过多次迭代调用,最终获取全量数据。 -
SCAN
、SSCAN
、HSCAN
、ZSCAN
多个命令的功能类似,这里的解析可以覆盖四个命令。唯一的区别是,SSCAN
、HSCAN
、ZSCAN
命令的第一个参数是要遍历的数据结构的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"