Redis 鍵

單個鍵管理

在 《Redis 概述》 中咱們已經介紹過 DELEXISTSEXPIRESCAN 的用法了,下面咱們介紹其餘比較重要的命令。java

查看存儲類型

TYPE

自1.0.0可用。python

時間複雜度:O(1)。redis

語法:TYPE key
說明:

返回 key 所儲存的值的類型。shell

返回值:

none (key不存在)數據庫

string (字符串)windows

list (列表)緩存

set (集合)網絡

zset (有序集)數據結構

hash (哈希表)架構

示例:
coderknock> OBJECT ENCODING zinterstoreTest
"ziplist"
coderknock> TYPE embstrKey
string
coderknock> TYPE setTest
zset
# 元素不存在
coderknock> TYPE nonKey
none

查看對象內部

OBJECT

自2.2.3可用。

時間複雜度:O(1)。

語法:OBJECT subcommand [arguments [arguments ...]]
說明:

OBJECT 命令容許從內部察看給定 key 的 Redis 對象。

它一般用在除錯(debugging)或者瞭解爲了節省空間而對 key 使用特殊編碼的狀況。

當將Redis用做緩存程序時,你也能夠經過 OBJECT 命令中的信息,決定 key 的驅逐策略(eviction policies)。

OBJECT 命令有多個子命令:

  • OBJECT REFCOUNT <key> 返回給定 key 引用所儲存的值的次數。此命令主要用於除錯。

  • OBJECT ENCODING <key> 返回給定 key 所儲存的值所使用的內部表示(representation)。

  • OBJECT IDLETIME <key> 返回給定 key 自儲存以來的空閒時間(idle, 沒有被讀取也沒有被寫入),以秒爲單位。

對象能夠以多種方式編碼:

  • 字符串能夠被編碼爲 raw (通常字符串)或 int (爲了節約內存,Redis 會將字符串表示的 64 位有符號整數編碼爲整數來進行儲存)。

  • 列表能夠被編碼爲 ziplistlinkedlistquicklistziplist 是爲節約大小較小的列表空間而做的特殊表示。

  • 集合能夠被編碼爲 intset 或者 hashtableintset 是隻儲存數字的小集合的特殊表示。

  • 哈希表能夠編碼爲 ziplist 或者 hashtableziplist 是小哈希表的特殊表示。

  • 有序集合能夠被編碼爲 ziplist 或者 skiplist 格式。 ziplist 用於表示小的有序集合,而 skiplist 則用於表示任何大小的有序集合。

假如你作了什麼讓 Redis 沒辦法再使用節省空間的編碼時(好比將一個只有 1 個元素的集合擴展爲一個有 100 萬個元素的集合),特殊編碼類型(specially encoded types)會自動轉換成通用類型(general type)。

返回值:

REFCOUNTIDLETIME 返回數字。

ENCODING 返回相應的編碼類型。

若是您嘗試檢查的對象不存在,則返回 nil

示例:
coderknock> OBJECT IDLETIME nonKey
(nil)
coderknock> OBJECT IDLETIME embstrKey
(integer) 2886
coderknock> OBJECT REFCOUNT embstrKey
(integer) 1
coderknock> OBJECT REFCOUNT setTest
(integer) 1
coderknock>

查看存儲類型

RENAME

自1.0.0可用。
時間複雜度:O(1)。

語法:RENAME key newkey
說明:

key 更名爲 newkey

keynewkey 相同,或者 key 不存在時,返回一個錯誤。

newkey 已經存在時, RENAME 命令將覆蓋舊值。

返回值:

更名成功時提示 OK ,失敗時候返回一個錯誤。

示例:
# 重命名不存在的 key 
coderknock> RENAME nonKey newNonKey
(error) ERR no such key
coderknock> RENAME setTest newSetTest
OK
coderknock>  RENAME newSetTest newSetTest
OK

爲了防止被強行 RENAME ,Redis 提供了 RENAMENX 命令,確保只有 newKey 不存在時候才被覆蓋。

RENAMENX

自1.0.0可用。

時間複雜度:O(1)。

語法:RENAMENX key newkey
說明:

當且僅當 newkey 不存在時,將 key 更名爲 newkey

key 不存在時,返回一個錯誤。

返回值:

修改爲功時,返回 1

若是 newkey 已經存在,返回 0

示例:
# 已經存在的就重命名不了了
coderknock>  RENAMENX newSetTest newSetTest
(integer) 0

在使用重命名命令時,有兩點須要注意:

  • 因爲重命名鍵期間會執行 DEL 命令刪除舊的鍵,若是鍵對應的值比較大,會存在阻塞Redis的可能性,這點不要忽視。

  • 若是 RENAMERENAMENX 中的 keynewkey 若是是相同的,在 Redis3.2 和以前版本返回結果略有不一樣。

Redis3.2中會返回OK:

coderknock> rename key key
OK

Redis3.2 以前的版本會提示錯誤:

coderknock> rename key key
(error) ERR source and destination objects are the same

###隨機返回一個鍵

#### RANDOMKEY

自1.0.0可用。

時間複雜度:O(1)。

語法:RANDOMKEY
說明:

從當前數據庫中隨機返回(不刪除)一個 key 。值的類型。

返回值:

當數據庫不爲空時,返回一個 key

當數據庫爲空時,返回 nil

示例:
coderknock> RANDOMKEY
"ztest"
coderknock> RANDOMKEY
"testIntset"
# 數據庫爲空
coderknock> FLUSHDB  # 刪除當前數據庫全部 key
OK
coderknock> RANDOMKEY
(nil)

鍵過時

Redis 鍵過時處理以前介紹的 EXPIRE 命令外還有 EXPIREAT PEXPIRE PEXPIREAT PTTL PERSIST

若是過時時間爲負值,鍵會當即被刪除,猶如使用 DEL 命令同樣。

對於字符串類型鍵,執行 SET 命令會去掉過時時間,這個問題很容易在開發中被忽視。

Redis 不支持二級數據結構(例如哈希、列表)內部元素的過時功能

SETEX 命令做爲 SET + EXPIRE 的組合,不可是原子執行,同時減小了一次網絡通信的時間#### EXPIREAT

自1.2.0可用。

時間複雜度:O(1)。

語法:EXPIREAT key timestamp
說明:

EXPIREAT 的做用和 *EXPIRE* 相似,都用於爲 key 設置生存時間。

不一樣在於 EXPIREAT 命令接受的時間參數是 UNIX 時間戳(unix timestamp)。

返回值:

若是生存時間設置成功,返回 1

key 不存在或沒辦法設置生存時間,返回 0

示例:
coderknock> EXPIREAT test 1497338910 # 2017/6/13 15:28:30 過時
(integer) 1
coderknock> EXPIREAT nonkey 1497338910
(integer) 0

#### PEXPIRE

自2.6.0可用。

時間複雜度:O(1)。

語法:PEXPIRE key milliseconds
說明:

這個命令和 EXPIRE 命令的做用相似,可是它以毫秒爲單位設置 key 的生存時間,而不像 EXPIRE 命令那樣,以秒爲單位。

返回值:

設置成功,返回 1

key 不存在或設置失敗,返回 0

示例:
coderknock> SET test "coderknock"
OK
# 這裏設置的比較小須要一塊兒輸入這三個命令才能看出效果,或者時間設置的長些
coderknock> PEXPIRE test 1500
(integer) 1
coderknock> TTL test    # TTL 的返回值以秒爲單位
(integer) 2
coderknock> PTTL test   # PTTL 能夠給出準確的毫秒數
(integer) 1489

#### PEXPIREAT

自2.6.0可用。

時間複雜度:O(1)。

語法:PEXPIREAT key milliseconds-timestamp
說明:

這個命令和 EXPIREAT 命令相似,但它以毫秒爲單位設置 key 的過時 unix 時間戳,而不是像 `EXPIREAT 那樣,以秒爲單位。

返回值:

若是生存時間設置成功,返回 1

key 不存在或沒辦法設置生存時間時,返回 0 。(查看 EXPIRE 命令獲取更多信息)失敗,返回 0

示例:
coderknock> PEXPIREAT test 1497338910000 # 2017/6/13 15:28:30 過時
(integer) 1
coderknock> PEXPIREAT nonkey 1497338910000
(integer) 0

#### PTTL

自2.6.0可用。

時間複雜度:O(1)。

語法:PTTL key
說明:

這個命令相似於 TTL 命令,但它以毫秒爲單位返回 key 的剩餘生存時間,而不是像 TTL 命令那樣,以秒爲單位。

返回值:

key 不存在時,返回 -2

key 存在但沒有設置剩餘生存時間時,返回 -1

不然,以毫秒爲單位,返回 key 的剩餘生存時間。

在 Redis 2.8 之前,當 key 不存在,或者 key 沒有設置剩餘生存時間時,命令都返回 -1

示例:
# 不存在的 key
coderknock> FLUSHDB
OK
coderknock> PTTL key
            (integer) -2

# key 存在,但沒有設置剩餘生存時間
coderknock> SET key value
OK
coderknock> PTTL key
            (integer) -1


# 有剩餘生存時間的 key
coderknock> PEXPIRE key 10086
(integer) 1
coderknock> PTTL key
(integer) 6179

#### PERSIST

自2.2.0可用。

時間複雜度:O(1)。

語法:PERSIST key
說明:

移除給定 key 的生存時間,將這個 key 從『易失的』(帶生存時間 key )轉換成『持久的』(一個不帶生存時間、永不過時的 key )。

返回值:

當生存時間移除成功時,返回 1 .

若是 key 不存在或 key 沒有設置生存時間,返回 0

示例:
coderknock> SET test "coderknock"
OK
coderknock> EXPIRE test 1500
(integer) 1
coderknock> TTL test
(integer) 1494
coderknock> PERSIST test # 移除 key 的生存時間
(integer) 1
coderknock> TTL test
                    (integer) -1

遷移鍵功能很是重要,由於有時候咱們只想把部分數據由一個 Redis 遷移到另外一個 Redis(例如從生產環境遷移到測試環境),Redis 發展歷程中提供了 MOVEDUMP + RESTOREMIGRATE 三組遷移鍵的方法,它們的實現方式以及使用的場景不太相同,下面分別介紹。

遷移鍵 MOVE 方式

MOVE

自1.0.0可用。

時間複雜度:O(1)。

語法:MOVE key db
說明:

將當前數據庫的 key 移動到給定的數據庫 db 當中。(Redis內部能夠有多個數據庫,彼此數據是相互隔離的)。

若是當前數據庫(源數據庫)和給定數據庫(目標數據庫)有相同名字的給定 key ,或者 key 不存在於當前數據庫,那麼 MOVE 沒有任何效果。

所以,也能夠利用這一特性,將 MOVE 看成鎖(locking)原語(primitive)。

返回值:

移動成功返回 1 ,失敗則返回 0

示例:
coderknock> SELECT 0 # 默認數據庫就是 0 ,這裏爲了讓你們更清晰
OK
coderknock> GET test
"a"
coderknock> MOVE test 1
(integer) 1
coderknock> GET test # MOVE 後本庫的 test 鍵就被刪除了
(nil)
coderknock> SELECT 1
OK
coderknock[1]> GET test
(nil)
coderknock[1]> GET test
"a"
coderknock[1]>  SET newTest db1
OK
coderknock[1]> SELECT 0
coderknock> SET newTest db0
OK
coderknock> MOVE newTest 1 # db 1 中有該鍵因此沒有遷移成功
(integer) 0
coderknock> GET newTest    # db 0 中該鍵沒有刪除
"db0"
coderknock> SELECT 1
OK
coderknock[1]> GET newTest # db1 中 newTest也沒有變化
"db1"

### 遷移鍵 DUMP + RESTORE

#### DUMP

自2.6.0可用。

時間複雜度:查找給定鍵的複雜度爲 O(1) ,對鍵進行序列化的複雜度爲 O(N*M) ,其中 N 是構成 key 的 Redis 對象的數量,而 M 則是這些對象的平均大小。

若是序列化的對象是比較小的字符串,那麼複雜度爲 O(1) 。

語法:DUMP key
說明:

序列化給定 key ,並返回被序列化的值,使用 *RESTORE* 命令能夠將這個值反序列化爲 Redis 鍵。

序列化生成的值有如下幾個特色:

  • 它帶有 64 位的校驗和,用於檢測錯誤, *RESTORE* 在進行反序列化以前會先檢查校驗和。

  • 值的編碼格式和 RDB 文件保持一致。

  • RDB 版本會被編碼在序列化值當中,若是由於 Redis 的版本不一樣形成 RDB 格式不兼容,那麼 Redis 會拒絕對這個值進行反序列化操做。

序列化的值不包括任何生存時間信息。

返回值:

若是 key 不存在,那麼返回 nil

不然,返回序列化以後的值。

示例:
coderknock> DUMP newSetTest
"\x0c\"\"\x00\x00\x00\x1f\x00\x00\x00\x06\x00\x00\x03one\x05\xf2\x02\x03two\x05\xf3\x02\x05three\a\xf4\xff\a\x00\xde\xde\xc5\xd8|\x84\xd6\xd0"

#### RESTORE

自2.6.0可用。

時間複雜度:查找給定鍵的複雜度爲 O(1) ,對鍵進行反序列化的複雜度爲 O(N*M) ,其中 N 是構成 key 的 Redis 對象的數量,而 M 則是這些對象的平均大小。

有序集合(sorted set)的反序列化複雜度爲 O(NMlog(N)) ,由於有序集合每次插入的複雜度爲 O(log(N)) 。

若是反序列化的對象是比較小的字符串,那麼複雜度爲 O(1) 。

語法:RESTORE key ttl serialized-value [REPLACE]
說明:

反序列化給定的序列化值,並將它和給定的 key 關聯。

參數 ttl 以毫秒爲單位爲 key 設置生存時間;若是 ttl0 ,那麼不設置生存時間。

RESTORE 在執行反序列化以前會先對序列化值的 RDB 版本和數據校驗和進行檢查,若是 RDB 版本不相同或者數據不完整的話,那麼 RESTORE 會拒絕進行反序列化,並返回一個錯誤。

若是鍵 key 已經存在, 而且給定了 REPLACE 選項, 那麼使用反序列化得出的值來代替鍵 key 原有的值; 相反地, 若是鍵 key 已經存在, 可是沒有給定 REPLACE 選項, 那麼命令返回一個錯誤。

更多信息能夠參考 *DUMP* 命令。

返回值:

若是反序列化成功那麼返回 OK ,不然返回一個錯誤。

示例
coderknock[1]> RESTORE restoreSet 0 "\x0c\"\"\x00\x00\x00\x1f\x00\x00\x00\x06\x00\x00\x03one\x05\xf2\x02\x03two\x05\xf3\x02\x05three\a\xf4\xff\a\x00\xde\xde\xc5\xd8|\x84\xd6\xd0"
OK
coderknock[1]> ZRANGE restoreSet 0 -1
1) "one"
2) "two"
3) "three"
coderknock[1]> SELECT 0
coderknock> RESTORE restoreSet 0 "\x0c\"\"\x00\x00\x00\x1f\x00\x00\x00\x06\x00\x00\x03one\x05\xf2\x02\x03two\x05\xf3\x02\x05three\a\xf4\xff\a\x00\xde\xde\xc5\xd8|\x84\xd6\xd0"
# 若是 key 已經存在但沒有設置 REPLACE 會 報錯
coderknock> RESTORE restoreSet 0 "\x0c\"\"\x00\x00\x00\x1f\x00\x00\x00\x06\x00\x00\x03one\x05\xf2\x02\x03two\x05\xf3\x02\x05three\a\xf4\xff\a\x00\xde\xde\xc5\xd8|\x84\xd6\xd0"
(error) BUSYKEY Target key name already exists.
coderknock>  SADD restoreSet 1 java # 這裏修改下 restoreSet 中的值
coderknock>  RESTORE restoreSet 0 "\x0c\"\"\x00\x00\x00\x1f\x00\x00\x00\x06\x00\x00\x03one\x05\xf2\x02\x03two\x05\xf3\x02\x05three\a\xf4\xff\a\x00\xde\xde\xc5\xd8|\x84\xd6\xd0" REPLACE
OK
coderknock> ZRANGE restoreSet 0 -1
1) "one"
2) "two"
3) "three"
# 隨便輸一條序列化語句
coderknock> RESTORE test 0  "sui bian shu de"
(error) ERR DUMP payload version or checksum are wrong

整個遷移過程並不是原子性的,而是經過客戶端分步完成的。

遷移過程是開啓了兩個客戶端鏈接,因此 DUMP 的結果不是在源 Redis 和目標 Redis 之間進行傳輸。

遷移鍵 MIGRATE

MIGRATE

自2.6.0可用。

時間複雜度:這個命令在源實例上實際執行 *DUMP* 命令和 *DEL* 命令,在目標實例執行 *RESTORE* 命令,查看以上命令的文檔能夠看到詳細的複雜度說明。

key 數據在兩個實例之間傳輸的複雜度爲 O(N) 。

語法:MIGRATE host port key|"" destination-db timeout [COPY][REPLACE][KEYS key [key ...]]
說明:

host:目標Redis的IP地址 port:目標Redis的端口 timeout:遷移的超時時間(單位爲毫秒)。

key 原子性地從當前實例傳送到目標實例的指定數據庫上,一旦傳送成功, key 保證會出如今目標實例上,而當前實例上的 key 會被刪除。

這個命令是一個原子操做,它在執行的時候會阻塞進行遷移的兩個實例,直到如下任意結果發生:遷移成功,遷移失敗,等待超時。

命令的內部實現是這樣的:它在當前實例對給定 key 執行 DUMP 命令 ,將它序列化,而後傳送到目標實例,目標實例再使用 RESTORE 對數據進行反序列化,並將反序列化所得的數據添加到數據庫中;當前實例就像目標實例的客戶端那樣,只要看到 RESTORE 命令返回 OK ,它就會調用 DEL 刪除本身數據庫上的 key

timeout 參數以毫秒爲格式,指定當前實例和目標實例進行溝通的最大間隔時間。這說明操做並不必定要在 timeout 毫秒內完成,只是說數據傳送的時間不能超過這個 timeout 數。

MIGRATE 命令須要在給定的時間規定內完成 IO 操做。若是在傳送數據時發生 IO 錯誤,或者達到了超時時間,那麼命令會中止執行,並返回一個特殊的錯誤: IOERR

IOERR 出現時,有如下兩種可能:

  • key 可能存在於兩個實例

  • key 可能只存在於當前實例

惟一不可能發生的狀況就是丟失 key ,所以,若是一個客戶端執行 MIGRATE 命令,而且不幸趕上 IOERR 錯誤,那麼這個客戶端惟一要作的就是檢查本身數據庫上的 key 是否已經被正確地刪除。

若是有其餘錯誤發生,那麼 MIGRATE 保證 key 只會出如今當前實例中。(固然,目標實例的給定數據庫上可能有和 key 同名的鍵,不過這和 MIGRATE 命令沒有關係)。

可選項:

  • COPY :不移除源實例上的 key

  • REPLACE :替換目標實例上已存在的 key

  • KEYS - 若是key參數是一個空字符串,命令將會轉移 KEYS 選項後面的全部鍵(有關更多信息,請參閱上述部分)。

COPYREPLACE 僅在3.0及更高版本中可用。 KEYS 從 Redis 3.0.6 開始可用。

返回值:

遷移成功時返回 OK ,不然返回相應的錯誤。

示例:

咱們須要再啓動一個 Redis 或者使用遠程 Redis(注意可訪問性)

redis-server --port 6370

咱們啓動兩個客戶端,一個鏈接默認 6379 的 Redis 一個鏈接 剛啓動的 6370 的Redis

redis-cli -p 6370 # 這裏只是列出了鏈接 6370 的方法

下面示例中注意端口的變化:

# 在 6379 中添加數據
coderknock: 6379> SADD 1 java 2 go 3 python
(integer) 5
coderknock: 6379> SRANDMEMBER 1
"python"
coderknock: 6379> keys *
                1) "1"
coderknock: 6379> flushdb
OK
coderknock: 6379> SADD set java python go
(integer) 3
coderknock: 6379> SMEMBERS set
1) "python"
2) "java"
3) "go"
coderknock: 6379> ZADD zSet 100coderknock 20 www.coderknock
(integer) 2
coderknock: 6379> ZRANGE zSet 0 -1
1) "www.coderknock"
2) "coderknock"
coderknock: 6379> SET hellocoderknock
OK
coderknock: 6379> KEYS *
1) "zSet"
2) "set"
3) "hello"
# 在 6370 中添加數據
coderknock: 6370> SET db 6370
OK
coderknock: 6370> SET set a b c
(error) ERR syntax error
coderknock: 6370> SADD set a b c
(integer) 3
coderknock: 6370> KEYS *
                1) "set"
2) "db"
# 遷移一個 key
coderknock: 6379> MIGRATE 127.0.0.1 6370 hello 0 1000
OK
coderknock: 6379> GET hello # 當前的 Redis 中 hello 被刪除了
(nil)
coderknock: 6370> GET hello # 6370 能夠查到 hello 這個 key 了
"CoderKnock"
# 當使用 keys 參數時要求 key 必須是 空字符串(不能不設也不能設爲其餘)
coderknock: 6379>  MIGRATE 127.0.0.1 6370  0 1000 COPY KEYS ""
(error) ERR When using MIGRATE KEYS option, the key argument must be set to the empty string
coderknock: 6379> MIGRATE 127.0.0.1 6370 "zSet" 0 1000 COPY KEYS ""
(error) ERR When using MIGRATE KEYS option, the key argument must be set to the empty string
# 當輸入的 keys 都不存在時返回 NOKEY
coderknock: 6379> MIGRATE 127.0.0.1 6370 ""  0 1000 COPY KEYS a
NOKEY
# 這裏不能使用通配符,通配符會當作普通字符處理
coderknock: 6379>  MIGRATE 127.0.0.1 6370 ""  0 1000 COPY KEYS *
                                                        NOKEY
# 當目標庫有相同 key 會報錯
coderknock: 6379> MIGRATE 127.0.0.1 6370 ""  0 1000 COPY KEYS zSet set
(error) ERR Target instance replied with error: BUSYKEY Target key name already exists.
# 加入 REPLACE 參數正常 這裏 KEYS 須要在參數列表最後 否則會將 COPY 等當作是一個 key
coderknock: 6379> MIGRATE 127.0.0.1 6370 ""  0 1000 COPY REPLACE KEYS zSet set
OK
# 使用了 COPY 因此當期庫中數據沒刪除
coderknock: 6379> KEYS *
                1) "zSet"
2) "set"
# 6370 中數據被遷移
coderknock: 6370> KEYS *
                1) "set"
2) "hello"
3) "zSet"
4) "db"
# set 的數據被替換
coderknock: 6370> SMEMBERS set
1) "java"
2) "go"
3) "python"

MIGRATE 命令也是用於在 Redis 實例間進行數據遷移的,實際上 MIGRATE 命令就是將DUMPRESTOREDEL 三個命令進行組合,從而簡化了操做流程。MIGRATE 命令具備原子性,並且從 Redis3.0.6 版本之後已經支持遷移多個鍵的功能,有效地提升了遷移效率。

遍歷鍵

《Redis 概覽》中的 KEYS 以及 SCAN

當須要遍歷全部鍵時(例如檢測過時或閒置時間、尋找大對象等), KEYS 是一個頗有幫助的命令,例如想刪除全部以 s 字符串開頭的鍵,能夠執行以下操做:

[coderknock ~]# redis-cli
coderknock> set s1 a
OK
coderknock> set s2 b
OK
coderknock> set s3 c
OK
coderknock> set a a
OK
    # 測試過程當中發現 windows 版本沒法這樣操做
[coderknock ~]# redis-cli keys s* | xargs redis-cli del
(integer) 3

可是若是考慮到 Redis 的單線程架構就不那麼美妙了,若是 Redis 包含了大量的鍵,執行 KEYS 命令極可能會形成 Redis 阻塞,因此通常建議不要在生產環境下使用 KEYS 命令。但有時候確實有遍歷鍵的需求該怎麼辦,能夠在如下三種狀況使用:

  • 在一個不對外提供服務的Redis從節點上執行,這樣不會阻塞到客戶端的請求,可是會影響到主從複製。

  • 若是確認鍵值總數確實比較少,能夠執行該命令。

  • 使用 SCAN 命令漸進式的遍歷全部鍵,能夠有效防止阻塞。

SCAN 的過程當中若是有鍵的變化(增長、刪除、修改),那麼遍歷效果可能會碰到以下問題:新增的鍵可能沒有遍歷到,遍歷出了重複的鍵等狀況,也就是說 SCAN 並不能保證完整的遍歷出來全部的鍵,這些是咱們在開發時須要考慮的。

相關文章
相關標籤/搜索