2.1 通用命令
因为通用命令会涉及Redis的数据结构操作,而Redis的数据结构操作也会涉及到通用命令,所以这两部分要结合着看。
不同数据结构的操作内容在下面
1. KEYS¶
通用命令 | KEYS pattern(pattern 为正则表达式) |
---|---|
功能描述 | 查找所有符合给定模式 pattern 的 key |
时间复杂度 | O(N),N 为数据库中 key 的数量。 |
功能描述方面举例:
- KEYS * 匹配数据库中所有 key 。
- KEYS h?llo 匹配 hello,hallo 和 hxllo 等。
- KEYS h*llo 匹配 hllo 和 heeeeello 等。
- KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo 。
特殊符号用 \ 隔开。
示例:
redis> MSET one 1 two 2 three 3 four 4 # 一次设置 4 个 key OK redis> KEYS *o* 1) "four" 2) "two" 3) "one" redis> KEYS t?? 1) "two" redis> KEYS t[w]* 1) "two" redis> KEYS * # 匹配数据库内所有 key 1) "four" 2) "three" 3) "two" 4) "one"
在生产环境中,使用keys命令取出所有key并没有什么意义,而且Redis是单线程应用,如果Redis中存的key很多,使用keys命令会阻塞其他命令执行,所以keys命令一般不在生产环境中使用
2. DBSIZE¶
通用命令 | DBSIZE |
---|---|
功能描述 | 返回当前数据库的 key 的数量 |
时间复杂度 | 时间复杂度: O(1) |
示例
redis> DBSIZE (integer) 5 redis> SET new_key "hello_moto" # 增加一个 key 试试 OK redis> DBSIZE (integer) 6
Redis内置一个计数器,可以实时更新Redis中key的总数,因此dbsize的时间复杂度为O(1),可以在线上使用。
3. EXISTS¶
通用命令 | EXISTS key |
---|---|
功能描述 | 检查给定 key 是否存在。若 key 存在,返回 1 ,否则返回 0 |
时间复杂度 | O(1) |
redis> SET db "redis" OK redis> EXISTS db (integer) 1 redis> DEL db (integer) 1 redis> EXISTS db (integer) 0
4. DEL¶
通用命令 | DEL key [key …] |
---|---|
功能描述 | 删除给定的一个或多个 key 。不存在的 key 会被忽略。返回值是被删除 key 的数量 |
时间复杂度 | O(N), N 为被删除的 key 的数量,其中删除单个字符串类型的 key ,时间复杂度为O(1);删除单个列表、集合、有序集合或哈希表类型的 key ,时间复杂度为O(M), M 为以上数据结构内的元素数量 |
# 删除单个 key redis> SET name huangz OK redis> DEL name (integer) 1 # 删除一个不存在的 key redis> EXISTS phone (integer) 0 redis> DEL phone # 失败,没有 key 被删除 (integer) 0 # 同时删除多个 key redis> SET name "redis" OK redis> SET type "key-value store" OK redis> SET website "redis.com" OK redis> DEL name type website (integer) 3
5. EXPIRE¶
通用命令 | EXPIRE key seconds |
---|---|
功能描述 | 为给定 key 设置生存时间,当 key 过期时(生存时间为 0 ),它会被自动删除。设置成功返回 1 。 当 key 不存在或者不能为 key 设置生存时间时(比如在低于 2.1.3 版本的 Redis 中你尝试更新 key 的生存时间),返回 0 |
时间复杂度 | O(1) |
redis> SET cache_page "www.google.com" OK redis> EXPIRE cache_page 30 # 设置过期时间为 30 秒 (integer) 1 redis> TTL cache_page # 查看剩余生存时间 (integer) 23 redis> EXPIRE cache_page 30000 # 更新过期时间 (integer) 1 redis> TTL cache_page (integer) 29996
6. TTL¶
通用命令 | TTL key |
---|---|
功能描述 | 以秒为单位,返回给定 key 的剩余生存时间(TTL, time to live) |
时间复杂度 | O(1) |
redis> FLUSHDB OK redis> TTL key (integer) -2 # key 存在,但没有设置剩余生存时间 redis> SET key value OK redis> TTL key (integer) -1 # 有剩余生存时间的 key redis> EXPIRE key 10086 (integer) 1 redis> TTL key (integer) 10084
7. PERSIST¶
通用命令 | PERSIST key |
---|---|
功能描述 | 移除给定 key 的生存时间,将这个 key 从“易失的”(带生存时间 key )转换成“持久的”(一个不带生存时间、永不过期的 key )。当生存时间移除成功时,返回 1 . 如果 key 不存在或 key 没有设置生存时间,返回 0 |
时间复杂度 | O(1) |
redis> SET mykey "Hello" OK redis> EXPIRE mykey 10 # 为 key 设置生存时间 (integer) 1 redis> TTL mykey (integer) 10 redis> PERSIST mykey # 移除 key 的生存时间 (integer) 1 redis> TTL mykey (integer) -1
8. TYPE¶
通用命令 | TYPE key |
---|---|
功能描述 | 返回 key 所储存的值的类型 |
时间复杂度 | O(1) |
值的类型有:
- none (key不存在)
- string (字符串)
- list (列表)
- set (集合)
- zset (有序集)
- hash (哈希表)
- stream (流)
# 字符串 redis> SET weather "sunny" OK redis> TYPE weather string # 列表 redis> LPUSH book_list "programming in scala" (integer) 1 redis> TYPE book_list list # 集合 redis> SADD pat "dog" (integer) 1 redis> TYPE pat set
9. Del¶
通用命令 | DEL key [key …] |
---|---|
功能描述 | 删除给定的一个或多个 key 。不存在的 key 会被忽略。返回值是被删除 key 的数量 |
时间复杂度 | O(N), N 为被删除的 key 的数量,其中删除单个字符串类型的 key ,时间复杂度为O(1);删除单个列表、集合、有序集合或哈希表类型的 key ,时间复杂度为O(M), M 为以上数据结构内的元素数量 |
# 删除单个 key redis> SET name huangz OK redis> DEL name (integer) 1 # 删除一个不存在的 key redis> EXISTS phone (integer) 0 redis> DEL phone # 失败,没有 key 被删除 (integer) 0 # 同时删除多个 key redis> SET name "redis" OK redis> SET type "key-value store" OK redis> SET website "redis.com" OK redis> DEL name type website (integer) 3
10. scan¶
通用命令 | SCAN cursor [MATCH pattern] [LIMT count] |
---|---|
功能描述 | 查找(limit个)(符合给定模式 pattern )的 key ,返回值是符合 条件的key |
时间复杂度 | 增量式迭代命令每次执行的复杂度为 O(1) ,对数据集进行一次完整迭代的复杂度为 O(N),其中 N 为数据集中的元素数量 |
刚开始我们已经介绍过了keys,它的缺点非常明显: |
- ⼀次性查出所有满⾜条件的 key,万⼀Redis中有⼏百万个key 满⾜条件,满屏都是输出的结果,眼花缭乱。
- keys由于走的是遍历算法,复杂度是 O(n),如果Redis中有千万级以上的key,这个指令就会导致 Redis 服务卡顿,所有读写Redis 的其它的指令都会被延后甚⾄会超时报错,因为 Redis是单线程程序,顺序执⾏所有指令,其它指令必须等到当前的keys 指令执⾏完了才可以继续。
为了解决这个问题,2.8 版本中的Redis加⼊了scan。
scan 相⽐ keys 具备有以下特点:
- 复杂度虽然也是 O(n),但是它是通过游标(cursor,相当于位置)分步进⾏的,不会阻塞线程;
- 提供 limit 参数(可选),可以控制每次返回结果的最⼤条数(实际上是遍历的key的数量);
- 它也提供模式匹配功能;
- 服务器不需要为游标(cursor)保存状态,游标(cursor)的唯⼀状态就是 - scan 返回给客户端的游标整数;
- 返回的结果可能会有重复,需要客户端去重复(重要);
- 遍历的过程中如果有数据修改,改动后的数据能不能遍历到是不确定的;
- 单次返回的结果是空的并不意味着遍历结束,⽽要看返回的游标值是否为零;
scan 参数提供了三个参数,第⼀个是 cursor 整数值,第⼆个是key 的正则模式,第三个是遍历的limit。第⼀次遍历时,cursor 值为 0,然后将返回结果中第⼀个整数值作为下⼀次遍历的cursor。⼀直遍历到返回的 cursor 值为 0 时结束。
127.0.0.1:6379> scan 0 match key99* count 1000 1) "13976" 2) 1) "key9911" 2) "key9974" 3) "key9994" 4) "key9910" 5) "key9907" 6) "key9989" 7) "key9971" 8) "key99" 127.0.0.1:6379> scan 13976 match key99* count 1000 1) "1996" 2) 1) "key9982" 2) "key9997" 3) "key9963" 4) "key996" 5) "key9912" 6) "key9999" 7) "key9921" 8) "key994" 9) "key9956" 10) "key9919" 127.0.0.1:6379> scan 1996 match key99* count 1000 1) "12594" 2) 1) "key9939" 2) "key9941" 3) "key9967" 4) "key9938" 5) "key9906" 6) "key999" 7) "key9909" ... 127.0.0.1:6379> scan 11687 match key99* count 1000 1) "0" 2) 1) "key9969" 2) "key998" 3) "key9986" 4) "key9968" 5) "key9965" 6) "key9990" 7) "key9915" 8) "key9928" 9) "key9908"
刚才也强调了,limit是遍历的key的个数,从上⾯的过程可以看到虽然提供的 limit 是 1000,但是返回的结果,有的只有10 个。
scan 指令返回的游标就是第⼀维数组的位置索引,我们将这个位置索引称为槽 (slot)。如果不考虑字典的扩容缩容,直接按数组下标挨个遍历就⾏了。limit 参数就表示需要遍历的槽位数,之所以返回的结果可能多可能少,是因为不是所有的槽位上都会挂接链表,有些槽位可能是空的,还有些槽位上挂接的链表上的元素可能会有多个。每⼀次遍历都会将 limit 数量的槽位上挂接的所有链表元素进⾏模式匹配过滤后,⼀次性返回给客户端。
scan除了可以遍历所有的key之外,还可以对指定的容器集合进⾏遍历。⽐如zscan遍历zset集合元素,hscan遍历hash字典的元素、sscan遍历set集合的元素。
常用的通用命令已经介绍完了,下面我们探讨一个Redis总要面临的问题:在 Redis 中有可能会形成很⼤的对象,⽐如⼀个⼀个很⼤的 zset。
这样的对象对Redis的集群数据迁移带来了挑战,在集群环境下,如果某个key太⼤,可能会导致数据迁移卡顿。另外在内存分配上,如果key太⼤,那么当它需要扩容时,会⼀次性申请更⼤的⼀块内存,这也可能会导致卡顿。如果这个⼤ key被删除,内存会⼀次性回收,卡顿现象也有可能再产⽣。
在平时的开发中,尽量避免⼤key的产⽣。如果遇到Redis的内存⼤起⼤落的现象,有可能是因为⼤key导致的,这时候你就需要定位这个大key,进⼀步定位出具体的业务来源,然后再改进相关业务代码设计。
关于大key的寻找,可以通过 scan 指令,对于扫描出来的每⼀个 key,使⽤ type 指令获得 key 的类型,然后使⽤相应数据结构的 size 或者 len ⽅法来得到它的⼤⼩,对于每⼀种类型,保留⼤⼩的前 N 名作为扫描结果展示出来。(需要编写脚本)
除此之外,Redis官⽅在redis-cli
指令中提供了这样的扫描功能
redis-cli -h 127.0.0.1 -p 7001 –-bigkeys
如果你担⼼这个指令会⼤幅抬升 Redis 的 ops ,还可以增加⼀个休眠参数。
redis-cli -h 127.0.0.1 -p 7001 –-bigkeys -i 0.1 # 每隔 100 条 scan 指令就会休眠 0.1s,ops 就不会剧烈抬升,但是扫描的时间会变⻓。
redis中的OPS 即operation per second 每秒操作次数。意味着每秒对Redis的持久化操作