一、簡介
本節(jié)將按照單個鍵、遍歷鍵、數(shù)據(jù)庫管理三個維度對一些通用命令進行介紹。
二、單個鍵管理
針對單個鍵的命令,前面幾節(jié)已經(jīng)介紹過一部分了,例如:type、del、object、exists、expire等,下面將介紹剩余的幾個重要命令。
1、鍵重命名
rename key newkey
如:
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"
Tip:如果在rename之前,鍵java已經(jīng)存在,那么它的值也將被覆蓋,如:
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不存在時才被覆蓋,如:
127.0.0.1:6379> set a b
OK
127.0.0.1:6379> set c d
OK
127.0.0.1:6379> renamenx a c
(integer) 0
127.0.0.1:6379> get a
"b"
127.0.0.1:6379> get c
"d"
在使用重命名命令時,有兩點需要注意:
- 由于重命名鍵期間會執(zhí)行del命令刪除舊的鍵,如果鍵對應(yīng)的值比較大,會存在阻塞Redis的可能性,這點不要忽視。
- 如果rename和renamenx中的key和newkey如果是相同的,在Redis3.2和之前的版本返回結(jié)果略有不同。Redis3.2中會返回OK,而之前的版本會提示錯誤。
2、隨機返回一個鍵
randomkey
如:
127.0.0.1:6379> dbsize
(integer) 21
127.0.0.1:6379> randomkey
"user:1:follow"
127.0.0.1:6379> randomkey
"d"
3、鍵過期
前面章節(jié)有簡單介紹鍵過期功能,它可以自動將帶有過期時間的鍵刪除,在許多應(yīng)用場景都非常幫助。除了expire、ttl命令外,Redis還提供了expireat、pexpire、pexpireat、pttl、persist等一系列命令,下面分別進行說明:
- expire key seconds:鍵在seconds秒后過期。
- expireat key timestamp:鍵在秒級時間戳timestamp后過期。
如:
127.0.0.1:6379> set hi hhhhhhhhhha
OK
127.0.0.1:6379> expire hi 10
(integer) 1
127.0.0.1:6379> ttl hi
(integer) 8
127.0.0.1:6379> pttl hi
(integer) 6104
127.0.0.1:6379> ttl hi
(integer) 3
127.0.0.1:6379> pttl hi
(integer) 2241
127.0.0.1:6379> ttl hi
(integer) -2
127.0.0.1:6379> pttl hi
(integer) -2
ttl命令和pttl都可以查詢鍵的剩余時間,但是pttl精度更高可以達到毫秒級別,有3種返回結(jié)果:
- 大于等于0的整數(shù):鍵剩余的過期時間(ttl是秒,pttl是毫秒)。
- -1:鍵沒有設(shè)置過期時。
- -2:鍵不存在。
expireat命令可以設(shè)置鍵的秒級過期時間戳,例如需要將鍵hello在2018-09-01 00:00:00(秒級時間戳為1535731200)過期,可以執(zhí)行如下操作:
127.0.0.1:6379> expireat hello 1535731200
(integer) 1
此外,Redis2.6版本后提供了毫秒級的過期方案:
- pexpire key milliseconds:鍵在milliseconds毫秒后過期。
- pexpireat key milliseconds-timestamp:鍵在毫秒級時間戳timestamp后過期。
但無論使用過期時間還是時間戳,秒級還是毫秒級,有Redis內(nèi)部最終使用的都是pexpireat。
在會用Redis相關(guān)過期命令時,需要注意以下幾點:
1)如果expire key的鍵不存在,返回結(jié)果為0
2)如果過期時間為負值,鍵會立即被刪除,猶如使用del命令一樣
3)persist命令可以將鍵的過期時間清除
127.0.0.1:6379> hset hkey a b
(integer) 1
127.0.0.1:6379> expire hkey 50
(integer) 1
127.0.0.1:6379> ttl hkey
(integer) 45
127.0.0.1:6379> persist hkey
(integer) 1
127.0.0.1:6379> ttl hkey
(integer) -1
4)對于字符串型鍵,執(zhí)行set命令會去掉過期時間,這個問題很容易在開發(fā)中被忽視。
127.0.0.1:6379> expire hello 60
(integer) 1
127.0.0.1:6379> ttl hello
(integer) 51
127.0.0.1:6379> set hello seeeeeeeeee
OK
127.0.0.1:6379> ttl hello
(integer) -1
5)Redis不支持二級數(shù)據(jù)結(jié)構(gòu)(例如哈希、列表)內(nèi)部元素的過期功能,例如不能對列表類型的一個元素做過期時間設(shè)置。
6)setex命令作為 set + expire 的組合,但不是原子執(zhí)行,同時減少了一次網(wǎng)絡(luò)通訊的時間。
4、遷移鍵
遷移鍵功能非常重要,因為有時候我們只想把部分數(shù)據(jù)由一個Redis遷移到另一個Redis(例如從生產(chǎn)環(huán)境遷移到測試環(huán)境),Redis發(fā)展歷程中提供了move、dump+restore、migrate三組遷移方法,它們的實現(xiàn)方式以及使用場景不太相同,下面分別介紹。
1)move
如上圖所示,move命令用于在Redis內(nèi)部進行數(shù)據(jù)遷移,Redis內(nèi)部可以有多個數(shù)據(jù)庫,彼此在數(shù)據(jù)上是相互隔離的,move key db就是把指定的鍵從源數(shù)據(jù)庫移動到目標數(shù)據(jù)庫中。但筆者認為多數(shù)據(jù)庫功能不建議在生產(chǎn)環(huán)境使用,所以這個命令讀者知道即可。
2)dump+restore
dump key
restore key ttl value
dump+restore可以實現(xiàn)在不同的Redis實例之間進行數(shù)據(jù)遷移功能,整個遷移的過程分為兩步:
a) 在源Redis上,dump命令會將鍵值序列化,格式采用的是RDB格式。
b) 在目標Redis上,restore命令將上面序列化的值進行還原,其中ttl參數(shù)代表過期時間,如果ttl=0代表沒有過期時間。
注意:
第一,整個遷移過程并非原子性的,而是通過客戶端分步完成的。第二,遷移過程是開啟了兩個客戶端連接,所以dump的結(jié)果不是在源Redis和目標Redis之間進行傳輸。
例子演示:
// 1、在源Redis上執(zhí)行dump
127.0.0.1:6379> set hello 'hi, I am nosee.'
OK
127.0.0.1:6379> dump hello
"\x00\x0fhi, I am nosee.\a\x00\xd8Z\xbbCd=\x8b\xda"
127.0.0.1:6379>
// 2、在目標Redis上執(zhí)行restore
127.0.0.1:6380> get hello
(nil)
127.0.0.1:6380> restore hello 0 "\x00\x0fhi, I am nosee.\a\x00\xd8Z\xbbCd=\x8b\xda"
OK
127.0.0.1:6380> get hello
"hi, I am nosee."
上面兩步對應(yīng)的偽代碼如下:
Redis sourceRedis = new Redis("sourceMachine", 6379);
Redis targetRedis = new Redis("targetMachine", 6380);
targetRedis.restore("hello", 0, sourceRedis.dump(key));
3)migrate
migrate host port key|"" destination-db timeout [copy] [replace] [keys key [key ...]]
migrate命令也是用戶在Redis實例間進行數(shù)據(jù)遷移的,實際上migrate命令就是將dump、restore、del三個命令進行組合,從而簡化了操作流程。
整個過程如下圖所示,實現(xiàn)過程和dump+restore基本類似,但是有3點不太相同:第一,整個過程是原子執(zhí)行的,不需要在多個Redis實例上開啟客戶端的,只需要在源Redis上執(zhí)行migrate命令即可。第二,migrate命令數(shù)據(jù)傳輸直接在源Redis和目標Redis上完成的。第三,目標Redis完成restore后會發(fā)送OK給源Redis,源Redis接收后會根據(jù)migrate對應(yīng)的選項來決定是否在源Redis上刪除對應(yīng)的鍵。
下面對migrate參數(shù)逐個說明:
- host:目標Redis的IP地址。
- port:目標Redis的端口。
- key|"":在Redis 3.0.6版本之前,migrate只支持遷移一個鍵,所以此處是要遷移的鍵,但Redis 3.0.6版本之后支持遷移多個鍵,如果當前需要遷移多個鍵,此處為空字符串""。
- destination-db:目標Redis的數(shù)據(jù)庫索引,例如要遷移到0號數(shù)據(jù)庫,這里就寫0。
- timeout:遷移的超時時間(單位為毫秒)。
- [copy]:如果添加此選項,遷移后不刪除源鍵。
- [replace]:如果添加此選項,migrate不管目標Redis是否存在該鍵都會正常遷移進行數(shù)據(jù)覆蓋。
- [keys key [key ...]]:遷移多個鍵,例如要遷移key1、key2、key3,此處趕寫“keys key1 key2 key3”。
示例情況1:源Redis有鍵hello,目標Redis沒有:
//Redis實例6379上:
127.0.0.1:6379> migrate 127.0.0.1 6380 hello 0 1000
OK
127.0.0.1:6379> get hello
(nil)
//Redis實例6380上:
127.0.0.1:6380> get hello
"hi, I am nosee."
示例情況2:源Redis和目標Redis都有鍵hello:
//Redis實例6379上:
127.0.0.1:6379> set hello 'I am here.'
OK
127.0.0.1:6379> migrate 127.0.0.1 6380 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 6380 hello 0 1000 replace
OK
//Redis實例6380上:
127.0.0.1:6380> get hello
"I am here."
示例情況3:源Redis沒有鍵hello:
127.0.0.1:6379> migrate 127.0.0.1 6380 hello 0 1000
NOKEY
多個鍵遷移示例:
//Redis實例6379上:
127.0.0.1:6379> mset key1 aaa key2 bbb key3 ccc
OK
127.0.0.1:6379> migrate 127.0.0.1 6380 "" 0 1000 keys key1 key2 key3
OK
127.0.0.1:6379> mget key1 key2 key3
1) (nil)
2) (nil)
3) (nil)
//Redis實例6380上:
127.0.0.1:6380> mget key1 key2 key3
1) "aaa"
2) "bbb"
3) "ccc"
三、遍歷鍵
1、全量遍歷鍵
keys pattern
如,獲取所有的鍵:
127.0.0.1:6380> keys *
1) "key2"
2) "key3"
3) "key1"
4) "hello"
上面例子為了遍歷所有的鍵,pattern直接使用星號,這是因為pattern使用的是glob風(fēng)格的通配符:
- *代表匹配任意字符。
- ?代表匹配一個字符。
- []代表匹配部分字符,例如[1,3]代表匹配1,3,[1-10]代表匹配1到10的任意數(shù)字。
- \x用來做轉(zhuǎn)義,例如要匹配星號、問題需要進行轉(zhuǎn)義。
如:
127.0.0.1:6380> keys key?
1) "key2"
2) "key3"
3) "key1"
127.0.0.1:6380> keys key[1,3]
1) "key3"
2) "key1"
當需要遍歷所有鍵時(例如檢測過期或閑置時間、尋找大對象等),keys是一個不很有幫助的命令,例如想刪除所有video字符串開頭的鍵,可以執(zhí)行如下操作:
redis-cli keys video* | xargs redis del
但是如果考慮到Redis的單線程架構(gòu)就不那么美妙了,如果Redis包含了大量的鍵,執(zhí)行keys命令很可能會造成Redis阻塞,所以在一般不建議在生產(chǎn)環(huán)境下使用keys命令。但有時確實有遍歷鍵的需求該怎么辦,可以在以下三種情況使用:
- 在一個不對外提供服務(wù)的Redis從節(jié)點上執(zhí)行,這樣不會阻塞到客戶端的請求,但是會影響到主從復(fù)制。
- 如果確認鍵值總數(shù)確實比較少,可以執(zhí)行該命令。
- 使用下面要介紹的scan命令漸進式的遍歷所有鍵,可以有效防止阻塞。
2、漸進式遍歷
scan cursor [MATCH pattern] [COUNT count]
- cursor是必需參數(shù),實際上cursor是個游標,第一次遍歷從0開始,每次scan遍歷完都會返回當前游標的值,直到游標值為0,表示遍歷結(jié)束。
- match pattern是可靠參數(shù),它的作用是做模式的匹配,這點和keys的模式匹配很像。
- count number是可選參數(shù),它我作用是要表明每次要遍歷的鍵個數(shù),默認值為10,此參數(shù)可以適當增大。
現(xiàn)有一個Redis有26個鍵,現(xiàn)在要遍歷所有鍵,使用scan命令效果的操作如下。第一次執(zhí)行scan 0
,返回結(jié)果分為兩個部分:第一個部分5是下次scan需要的cursor,第二個部分是10個鍵:
127.0.0.1:6380> keys *
1) "key5"
2) "key7"
3) "keyd"
.
.
.
22) "hello" //共22個鍵
127.0.0.1:6380> scan 0
1) "5"
2) 1) "key5"
2) "key9"
3) "key6"
4) "key2"
5) "key7"
6) "keye"
7) "a1"
8) "keyb"
9) "a4"
10) "key3"
使用新的cursor=5,執(zhí)行scan 5
:
127.0.0.1:6380> scan 5
1) "31"
2) 1) "keyd"
2) "a6"
3) "key1"
4) "a7"
5) "keya"
6) "keyc"
7) "key4"
8) "a2"
9) "a3"
10) "a5"
這次得到的cursor=31,繼續(xù)執(zhí)行scan 31
得到結(jié)果corsor變?yōu)?,說明所有的鍵已經(jīng)被遍歷過了:
127.0.0.1:6380> scan 31
1) "0"
2) 1) "key8"
2) "hello"
除了scan以外,Redis提供了面向哈希類型、集合類型、有序集合的掃描遍歷命令,解決諸如hgetall、smembers、zrange可能產(chǎn)生阻塞問題,對應(yīng)的命令分別是hscan、sscan、zscan,它們的用法和scan基本類似。
四、數(shù)據(jù)庫管理
Redis提供了幾個面向Redis數(shù)據(jù)庫的操作,它們分別是dbsize、select、flushdb/flushall命令。
1、切換數(shù)據(jù)庫
select dbIndex
許多關(guān)系型數(shù)據(jù)庫,例如MySQL支持在一個實例下有多個數(shù)據(jù)庫存在的,但是與關(guān)系型數(shù)據(jù)庫用字符來區(qū)分不同數(shù)據(jù)庫名不同,Redis只是用數(shù)字作為多個數(shù)據(jù)庫的實現(xiàn)。Redis默認配置中是有16個數(shù)據(jù)庫的,不同數(shù)據(jù)庫之間的數(shù)據(jù)沒有任何關(guān)聯(lián),甚至可以存在相同的鍵。
127.0.0.1:6380> get hello # 默認進到的是0號數(shù)據(jù)庫
"I am here."
127.0.0.1:6380> select 15 # 切換到15號數(shù)據(jù)庫
OK
127.0.0.1:6380[15]> get hello # 15號數(shù)據(jù)庫沒有設(shè)置hello
(nil)
Redis默認使用的就是0號數(shù)據(jù)庫,當選擇其它數(shù)據(jù)庫時會有[index]的前綴標識,其中index就是數(shù)據(jù)庫的索引下標。
注意:該功能目前版本已經(jīng)被逐漸弱化。
2、flushdb/flushall
flushdb/flushall命令用于清除數(shù)據(jù)庫,兩者的區(qū)別是flushdb只清除當前數(shù)據(jù)庫,flushall會清除所有數(shù)據(jù)庫。
flushdb/flushall命令可以非常方便的清除數(shù)據(jù),但是也帶來兩個問題:
- flushdb/flushall命令會將所有數(shù)據(jù)清除,一旦誤操作后果不堪設(shè)想,后面會介紹rename-command配置規(guī)避這個問題,以及如何在誤操作后快速恢復(fù)數(shù)據(jù)。
- 如果當前數(shù)據(jù)鍵值數(shù)量比較多,flushdb/flushall存在阻塞Redis的可能性。
所以在使用flushdb/flushall 時一定要小心謹慎。
參考:
《Redis開發(fā)與運維》 付磊 & 張益軍