当前位置:网站首页>Redis入门完整教程:键管理
Redis入门完整教程:键管理
2022-07-04 22:29:00 【谷哥学术】
2.7.1 单个键管理
针对单个键的命令,前面几节已经介绍过一部分了,例如type、del、
object、exists、expire等,下面将介绍剩余的几个重要命令。
1.键重命名
rename key newkey
例如现有一个键值对,键为python,值为jedis:
127.0.0.1:6379> get python
"jedis"
下面操作将键python重命名为java:
127.0.0.1:6379> set python jedis
OK
127.0.0.1:6379> rename python java
OK
127.0.0.1:6379> get python
(nil)
127.0.0.1:6379> get java
"jedis"
如果在rename之前,键java已经存在,那么它的值也将被覆盖,如下所
示:
127.0.0.1:6379> set a b
OK
127.0.0.1:6379> set c d
OK
127.0.0.1:6379> rename a c
OK
127.0.0.1:6379> get a
(nil)
127.0.0.1:6379> get c
"b"
为了防止被强行rename,Redis提供了renamenx命令,确保只有newKey
不存在时候才被覆盖,例如下面操作renamenx时,newkey=python已经存在,
返回结果是0代表没有完成重命名,所以键java和python的值没变:
127.0.0.1:6379> set java jedis
OK
127.0.0.1:6379> set python redis-py
OK
127.0.0.1:6379> renamenx java python
(integer) 0
127.0.0.1:6379> get java
"jedis"
127.0.0.1:6379> get python
"redis-py"
在使用重命名命令时,有两点需要注意:
·由于重命名键期间会执行del命令删除旧的键,如果键对应的值比较
大,会存在阻塞Redis的可能性,这点不要忽视。
·如果rename和renamenx中的key和newkey如果是相同的,在Redis3.2和之
前版本返回结果略有不同。
Redis3.2中会返回OK:
127.0.0.1:6379> rename key key
OK
Redis3.2之前的版本会提示错误:
127.0.0.1:6379> rename key key
(error) ERR source and destination objects are the same
2.随机返回一个键
randomkey
下面示例中,当前数据库有1000个键值对,randomkey命令会随机从中
挑选一个键:
127.0.0.1:6379> dbsize
1000
127.0.0.1:6379> randomkey
"hello"
127.0.0.1:6379> randomkey
"jedis"
3.键过期
2.1节简单介绍键过期功能,它可以自动将带有过期时间的键删除,在
许多应用场景都非常有帮助。除了expire、ttl命令以外,Redis还提供了
expireat、pexpire、pexpireat、pttl、persist等一系列命令,下面分别进行说
明:
·expire key seconds:键在seconds秒后过期。
·expireat key timestamp:键在秒级时间戳timestamp后过期。
下面为键hello设置了10秒的过期时间,然后通过ttl观察它的过期剩余时
间(单位:秒),随着时间的推移,ttl逐渐变小,最终变为-2:
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> expire hello 10
(integer) 1
# 还剩 7 秒
127.0.0.1:6379> ttl hello
(integer) 7
...
# 还剩 0 秒
127.0.0.1:6379> ttl hello
(integer) 0
# 返回结果为 -2 ,说明键 hello 已经被删除
127.0.0.1:6379> ttl hello
(integer) -2
ttl命令和pttl都可以查询键的剩余过期时间,但是pttl精度更高可以达到
毫秒级别,有3种返回值:
·大于等于0的整数:键剩余的过期时间(ttl是秒,pttl是毫秒)。
·-1:键没有设置过期时间。
·-2:键不存在。
expireat命令可以设置键的秒级过期时间戳,例如如果需要将键hello在
2016-08-0100:00:00(秒级时间戳为1469980800)过期,可以执行如下操
作:
127.0.0.1:6379> expireat hello 1469980800
(integer) 1
除此之外,Redis2.6版本后提供了毫秒级的过期方案:
·pexpire key milliseconds:键在milliseconds毫秒后过期。
·pexpireat key milliseconds-timestamp键在毫秒级时间戳timestamp后过
期。
但无论是使用过期时间还是时间戳,秒级还是毫秒级,在Redis内部最
终使用的都是pexpireat。
在使用Redis相关过期命令时,需要注意以下几点。
1)如果expire key的键不存在,返回结果为0:
127.0.0.1:6379> expire not_exist_key 30
(integer) 0
2)如果过期时间为负值,键会立即被删除,犹如使用del命令一样:
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> expire hello -2
(integer) 1
127.0.0.1:6379> get hello
(nil)
3)persist命令可以将键的过期时间清除:
127.0.0.1:6379> hset key f1 v1
(integer) 1
127.0.0.1:6379> expire key 50
(integer) 1
127.0.0.1:6379> ttl key
(integer) 46
127.0.0.1:6379> persist key
(integer) 1
127.0.0.1:6379> ttl key
(integer) -1
4)对于字符串类型键,执行set命令会去掉过期时间,这个问题很容易
在开发中被忽视。
如下是Redis源码中,set命令的函数setKey,可以看到最后执行了
removeExpire(db,key)函数去掉了过期时间:
void setKey(redisDb *db, robj *key, robj *val) {
if (lookupKeyWrite(db,key) == NULL) {
dbAdd(db,key,val);
} else {
dbOverwrite(db,key,val);
}
incrRefCount(val);
// 去掉过期时间
removeExpire(db,key);
signalModifiedKey(db,key);
}
下面的例子证实了set会导致过期时间失效,因为ttl变为-1:
127.0.0.1:6379> expire hello 50
(integer) 1
127.0.0.1:6379> ttl hello
(integer) 46
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> ttl hello
(integer) -1
5)Redis不支持二级数据结构(例如哈希、列表)内部元素的过期功
能,例如不能对列表类型的一个元素做过期时间设置。
6)setex命令作为set+expire的组合,不但是原子执行,同时减少了一次
网络通讯的时间。
有关Redis键过期的详细原理,8.2节会深入剖析。
4.迁移键
迁移键功能非常重要,因为有时候我们只想把部分数据由一个Redis迁
移到另一个Redis(例如从生产环境迁移到测试环境),Redis发展历程中提
供了move、dump+restore、migrate三组迁移键的方法,它们的实现方式以及
使用的场景不太相同,下面分别介绍。
(1)move
move key db
如图2-26所示,move命令用于在Redis内部进行数据迁移,Redis内部可
以有多个数据库,由于多个数据库功能后面会进行介绍,这里只需要知道
Redis内部可以有多个数据库,彼此在数据上是相互隔离的,move key db就
是把指定的键从源数据库移动到目标数据库中,但笔者认为多数据库功能不
建议在生产环境使用,所以这个命令读者知道即可。
(2)dump+restore
dump key
restore key ttl value
dump+restore可以实现在不同的Redis实例之间进行数据迁移的功能,整
个迁移的过程分为两步:
1)在源Redis上,dump命令会将键值序列化,格式采用的是RDB格式。
2)在目标Redis上,restore命令将上面序列化的值进行复原,其中ttl参
数代表过期时间,如果ttl=0代表没有过期时间。
整个过程如图2-27所示。
有关dump+restore有两点需要注意:第一,整个迁移过程并非原子性
的,而是通过客户端分步完成的。第二,迁移过程是开启了两个客户端连
接,所以dump的结果不是在源Redis和目标Redis之间进行传输,下面用一个
例子演示完整过程。
1)在源Redis上执行dump:
redis-source> set hello world
OK
redis-source> dump hello
"\x00\x05world\x06\x00\x8f<T\x04%\xfcNQ"
2)在目标Redis上执行restore:
redis-target> get hello
(nil)
redis-target> restore hello 0 "\x00\x05world\x06\x00\x8f<T\x04%\xfcNQ"
OK
redis-target> get hello
"world"
上面2步对应的伪代码如下:
Redis sourceRedis = new Redis("sourceMachine", 6379);
Redis targetRedis = new Redis("targetMachine", 6379);
targetRedis.restore("hello", 0, sourceRedis.dump(key));
(3)migrate
migrate host port key|"" destination-db timeout [copy] [replace] [keys key [key ...]]
migrate命令也是用于在Redis实例间进行数据迁移的,实际上migrate命
令就是将dump、restore、del三个命令进行组合,从而简化了操作流程。
migrate命令具有原子性,而且从Redis3.0.6版本以后已经支持迁移多个键的
功能,有效地提高了迁移效率,migrate在10.4节水平扩容中起到重要作用。
整个过程如图2-28所示,实现过程和dump+restore基本类似,但是有3点
不太相同:第一,整个过程是原子执行的,不需要在多个Redis实例上开启
客户端的,只需要在源Redis上执行migrate命令即可。第二,migrate命令的
数据传输直接在源Redis和目标Redis上完成的。第三,目标Redis完成restore
后会发送OK给源Redis,源Redis接收后会根据migrate对应的选项来决定是否
在源Redis上删除对应的键。
下面对migrate的参数进行逐个说明:
·host:目标Redis的IP地址。
·port:目标Redis的端口。
·key|"":在Redis3.0.6版本之前,migrate只支持迁移一个键,所以此处是
要迁移的键,但Redis3.0.6版本之后支持迁移多个键,如果当前需要迁移多
个键,此处为空字符串""。
·destination-db:目标Redis的数据库索引,例如要迁移到0号数据库,这
157
里就写0。
·timeout:迁移的超时时间(单位为毫秒)。
·[copy]:如果添加此选项,迁移后并不删除源键。
·[replace]:如果添加此选项,migrate不管目标Redis是否存在该键都会
正常迁移进行数据覆盖。
·[keys key[key...]]:迁移多个键,例如要迁移key1、key2、key3,此处填
写“keys key1 key2 key3”。
下面用示例演示migrate命令,为了方便演示源Redis使用6379端口,目
标Redis使用6380端口,现要将源Redis的键hello迁移到目标Redis中,会分为
如下几种情况:
情况1:源Redis有键hello,目标Redis没有:
127.0.0.1:6379> migrate 127.0.0.1 6380 hello 0 1000
OK
情况2:源Redis和目标Redis都有键hello:
127.0.0.1:6379> get hello
"world"
127.0.0.1:6380> get hello
"redis"
如果migrate命令没有加replace选项会收到错误提示,如果加了replace会
返回OK表明迁移成功:
127.0.0.1:6379> migrate 127.0.0.1 6379 hello 0 1000
(error) ERR Target instance replied with error: BUSYKEY Target key name already exists.
127.0.0.1:6379> migrate 127.0.0.1 6379 hello 0 1000 replace
OK
情况3:源Redis没有键hello。如下所示,此种情况会收到nokey的提
示:
127.0.0.1:6379> migrate 127.0.0.1 6380 hello 0 1000
NOKEY
下面演示一下Redis3.0.6版本以后迁移多个键的功能。
·源Redis批量添加多个键:
127.0.0.1:6379> mset key1 value1 key2 value2 key3 value3
OK
·源Redis执行如下命令完成多个键的迁移:
127.0.0.1:6379> migrate 127.0.0.1 6380 "" 0 5000 keys key1 key2 key3
OK
至此有关Redis数据迁移的命令介绍完了,最后使用表2-9总结一下
move、dump+restore、migrate三种迁移方式的异同点,笔者建议使用migrate
命令进行键值迁移。
2.7.2 遍历键
Redis提供了两个命令遍历所有的键,分别是keys和scan,本节将对它们
介绍并简要分析。
1.全量遍历键
keys pattern
本章开头介绍keys命令的简单使用,实际上keys命令是支持pattern匹配
的,例如向一个空的Redis插入4个字符串类型的键值对。
127.0.0.1:6379> dbsize
(integer) 0
127.0.0.1:6379> mset hello world redis best jedis best hill high
OK
如果要获取所有的键,可以使用keys pattern命令:
127.0.0.1:6379> keys *
1) "hill"
2) "jedis"
3) "redis"
4) "hello"
上面为了遍历所有的键,pattern直接使用星号,这是因为pattern使用的
是glob风格的通配符:
·*代表匹配任意字符。
·代表匹配一个字符。
·[]代表匹配部分字符,例如[1,3]代表匹配1,3,[1-10]代表匹配1到10
的任意数字。
·\x用来做转义,例如要匹配星号、问号需要进行转义。
下面操作匹配以j,r开头,紧跟edis字符串的所有键:
127.0.0.1:6379> keys [j,r]edis
1) "jedis"
2) "redis"
例如下面操作会匹配到hello和hill这两个键:
127.0.0.1:6379> keys hll*
1) "hill"
2) "hello"
当需要遍历所有键时(例如检测过期或闲置时间、寻找大对象等),
keys是一个很有帮助的命令,例如想删除所有以video字符串开头的键,可以
执行如下操作:
redis-cli keys video* | xargs redis-cli del
但是如果考虑到Redis的单线程架构就不那么美妙了,如果Redis包含了
大量的键,执行keys命令很可能会造成Redis阻塞,所以一般建议不要在生
产环境下使用keys命令。但有时候确实有遍历键的需求该怎么办,可以在以
下三种情况使用:
·在一个不对外提供服务的Redis从节点上执行,这样不会阻塞到客户端
的请求,但是会影响到主从复制,有关主从复制我们将在第6章进行详细介
绍。
·如果确认键值总数确实比较少,可以执行该命令。
·使用下面要介绍的scan命令渐进式的遍历所有键,可以有效防止阻
塞。
2.渐进式遍历
Redis从2.8版本后,提供了一个新的命令scan,它能有效的解决keys命
令存在的问题。和keys命令执行时会遍历所有键不同,scan采用渐进式遍历
的方式来解决keys命令可能带来的阻塞问题,每次scan命令的时间复杂度是
O(1),但是要真正实现keys的功能,需要执行多次scan。Redis存储键值对
实际使用的是hashtable的数据结构,其简化模型如图2-29所示。
那么每次执行scan,可以想象成只扫描一个字典中的一部分键,直到将
字典中的所有键遍历完毕。scan的使用方法如下:
scan cursor [match pattern] [count number]
·cursor是必需参数,实际上cursor是一个游标,第一次遍历从0开始,每
次scan遍历完都会返回当前游标的值,直到游标值为0,表示遍历结束。
·match pattern是可选参数,它的作用的是做模式的匹配,这点和keys的
模式匹配很像。
·count number是可选参数,它的作用是表明每次要遍历的键个数,默认
值是10,此参数可以适当增大。
现有一个Redis有26个键(英文26个字母),现在要遍历所有的键,使
用scan命令效果的操作如下。第一次执行scan0,返回结果分为两个部分:第
一个部分6就是下次scan需要的cursor,第二个部分是10个键:
127.0.0.1:6379> scan 0
1) "6"
2) 1) "w"
2) "i"
3) "e"
4) "x"
5) "j"
6) "q"
7) "y"
8) "u"
9) "b"
10) "o"
使用新的cursor="6",执行scan6:
127.0.0.1:6379> scan 6
1) "11"
2) 1) "h"
2) "n"
3) "m"
4) "t"
5) "c"
6) "d"
7) "g"
8) "p"
9) "z"
10) "a"
这次得到的cursor="11",继续执行scan11得到结果cursor变为0,说明所
有的键已经被遍历过了:
127.0.0.1:6379> scan 11
1) "0"
2) 1) "s"
2) "f"
3) "r"
4) "v"
5) "k"
6) "l"
除了scan以外,Redis提供了面向哈希类型、集合类型、有序集合的扫
描遍历命令,解决诸如hgetall、smembers、zrange可能产生的阻塞问题,对
应的命令分别是hscan、sscan、zscan,它们的用法和scan基本类似,下面以
sscan为例子进行说明,当前集合有两种类型的元素,例如分别以old:user
和new:user开头,先需要将old:user开头的元素全部删除,可以参考如下
伪代码:
String key = "myset";
// 定义 pattern
String pattern = "old:user*";
// 游标每次从 0 开始
String cursor = "0";
while (true) {
// 获取扫描结果
ScanResult scanResult = redis.sscan(key, cursor, pattern);
List elements = scanResult.getResult();
if (elements != null && elements.size() > 0) {
// 批量删除
redis.srem(key, elements);
}
// 获取新的游标
cursor = scanResult.getStringCursor();
// 如果游标为 0 表示遍历结束
if ("0".equals(cursor)) {
break;
}
}
渐进式遍历可以有效的解决keys命令可能产生的阻塞问题,但是scan并
非完美无瑕,如果在scan的过程中如果有键的变化(增加、删除、修改),
那么遍历效果可能会碰到如下问题:新增的键可能没有遍历到,遍历出了重
复的键等情况,也就是说scan并不能保证完整的遍历出来所有的键,这些是
我们在开发时需要考虑的。
2.7.3 数据库管理
Redis提供了几个面向Redis数据库的操作,它们分别是dbsize、select、
flushdb/flushall命令,本节将通过具体的使用场景介绍这些命令。
1.切换数据库
select dbIndex
许多关系型数据库,例如MySQL支持在一个实例下有多个数据库存在
的,但是与关系型数据库用字符来区分不同数据库名不同,Redis只是用数
字作为多个数据库的实现。Redis默认配置中是有16个数据库:
databases 16
假设databases=16,select0操作将切换到第一个数据库,select15选择最
后一个数据库,但是0号数据库和15号数据库之间的数据没有任何关联,甚
至可以存在相同的键:
127.0.0.1:6379> set hello world # 默认进到 0 号数据库
OK
127.0.0.1:6379> get hello
"world"
127.0.0.1:6379> select 15 # 切换到 15 号数据库
OK
127.0.0.1:6379[15]> get hello # 因为 15 号数据库和 0 号数据库是隔离的,所以 get hello 为空
(nil)
图2-30更加生动地表现出上述操作过程。同时可以看到,当使用redis-
cli-h{ip}-p{port}连接Redis时,默认使用的就是0号数据库,当选择其他数据
库时,会有[index]的前缀标识,其中index就是数据库的索引下标。
那么能不能像使用测试数据库和正式数据库一样,把正式的数据放在0
号数据库,测试的数据库放在1号数据库,那么两者在数据上就不会彼此受
影响了。事实真有那么好吗?
Redis3.0中已经逐渐弱化这个功能,例如Redis的分布式实现Redis
Cluster只允许使用0号数据库,只不过为了向下兼容老版本的数据库功能,
该功能没有完全废弃掉,下面分析一下为什么要废弃掉这个“优秀”的功能
呢?总结起来有三点:
·Redis是单线程的。如果使用多个数据库,那么这些数据库仍然是使用
一个CPU,彼此之间还是会受到影响的。
·多数据库的使用方式,会让调试和运维不同业务的数据库变的困难,
假如有一个慢查询存在,依然会影响其他数据库,这样会使得别的业务方定
位问题非常的困难。
·部分Redis的客户端根本就不支持这种方式。即使支持,在开发的时候
来回切换数字形式的数据库,很容易弄乱。
笔者建议如果要使用多个数据库功能,完全可以在一台机器上部署多个
Redis实例,彼此用端口来做区分,因为现代计算机或者服务器通常是有多
个CPU的。这样既保证了业务之间不会受到影响,又合理地使用了CPU资
源。
2.flushdb/flushall
flushdb/flushall命令用于清除数据库,两者的区别的是flushdb只清除当
前数据库,flushall会清除所有数据库。
例如当前0号数据库有四个键值对、1号数据库有三个键值对:
127.0.0.1:6379> dbsize
(integer) 4
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> dbsize
(integer) 3
如果在0号数据库执行flushdb,1号数据库的数据依然还在:
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> dbsize
(integer) 0
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> dbsize
(integer) 3
在任意数据库执行flushall会将所有数据库清除:
127.0.0.1:6379> flushall
OK
127.0.0.1:6379> dbsize
(integer) 0
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> dbsize
(integer) 0
flushdb/flushall命令可以非常方便的清理数据,但是也带来两个问题:
·flushdb/flushall命令会将所有数据清除,一旦误操作后果不堪设想,第
12章会介绍rename-command配置规避这个问题,以及如何在误操作后快速恢
复数据。
·如果当前数据库键值数量比较多,flushdb/flushall存在阻塞Redis的可能
性。
所以在使用flushdb/flushall一定要小心谨慎。
边栏推荐
- In Linux, I call odspcmd to query the database information. How to output silently is to only output values. Don't do this
- 繁华落尽、物是人非:个人站长该何去何从
- POM in idea XML dependency cannot be imported
- Prosperity is exhausted, things are right and people are wrong: where should personal webmasters go
- Is Huatai Securities a nationally recognized securities firm? Is it safe to open an account?
- Unity vscode emmylua configuration error resolution
- Interview essential leetcode linked list algorithm question summary, whole process dry goods!
- 攻防世界 MISC 进阶区 hong
- 常用技术指标之一文读懂BOLL布林线指标
- NFT insider 64: e-commerce giant eBay submitted an NFT related trademark application, and KPMG will invest $30million in Web3 and metauniverse
猜你喜欢
LOGO特训营 第四节 字体设计的重要性
安装人大金仓数据库
Li Kou 98: verify binary search tree
The new version judges the code of PC and mobile terminal, the mobile terminal jumps to the mobile terminal, and the PC jumps to the latest valid code of PC terminal
Logo special training camp section III initial creative techniques
SPSS installation and activation tutorial (including network disk link)
Advanced area a of attack and defense world misc Masters_ good_ idea
The overview and definition of clusters can be seen at a glance
Locust performance test - environment construction and use
Redis入门完整教程:发布订阅
随机推荐
The overview and definition of clusters can be seen at a glance
SQL中MAX与GREATEST的区别
堆排序代码详解
Short video system source code, click the blank space of the screen, the keyboard does not automatically stow
串口数据帧
Google Earth engine (GEE) -- take modis/006/mcd19a2 as an example to batch download the daily mean, maximum, minimum, standard deviation, statistical analysis of variance and CSV download of daily AOD
关于栈区、堆区、全局区、文字常量区、程序代码区
High school physics: linear motion
How to reset the password of MySQL root account
How diff are the contents of the same configuration item in different environments?
MYSQL架构——逻辑架构
面试必备 LeetCode 链表算法题汇总,全程干货!
剑指 Offer 65. 不用加减乘除做加法
Lost in the lock world of MySQL
华泰证券是国家认可的券商吗?开户安不安全?
UML diagram memory skills
Logo special training camp section 1 Identification logo and logo design ideas
PHP short video source code, thumb animation will float when you like it
Detailed explanation of flask context
Google Earth Engine(GEE)——Tasks升级,实现RUN ALL可以一键下载任务类型中的所有影像