二、Redis基本操作——String(實戰篇)


 

小喵萬萬沒想到,上一篇博客,居然已經被閱讀600次了!!!讓小喵感覺壓力頗大。萬一有寫錯的地方,豈不是會誤導很多筒子們。所以,懇請大家,如果看到小喵的博客有什么不對的地方,請盡快指正!謝謝!

 

小喵的嘮叨話:上一篇博客,我們詳細的介紹了Redis 中String數據類型的底層實現(http://www.cnblogs.com/idiotgroup/p/5450157.html),相信大家已經在原理上掌握的相當不錯了。這次,我們就介紹Redis的命令行操作。當然,我們實際開發的時候可能不會太經常直接用到Redis的命令,而是通過一些數據庫的封裝來操控(就像我們雖然會使用SQL,但是實際開發的時候,總是有一些特別好用的庫,既安全又方便)。但是,學習最基礎的Redis命令,是針對各種語言的以不變應萬變的法寶,畢竟庫是針對於語言的。同時,學會了Redis的基本命令,將來對各種庫的掌握也能更迅速。

 

小喵的個人博客地址: http://www.miaoerduo.com/ ,歡迎隨時騷擾~

該博客原地址: http://www.miaoerduo.com/redis/二、redis基本操作-string實戰篇.html ,排版應該更精美一點。

 

Redis基本操作——String(實戰篇)

關於Redis的命令,主要參考Redis的官方文檔(http://redis.io/commands#string)。小喵這里主要像是翻譯一下,並且給出一些實用的例子。排版上,小喵按功能重新組織了一下指令,並將比較常用的指令放在了最前面。同時也增加了錨點,方便大家查閱。小喵這里的Redis版本是3.0.7,應該是目前的最新版。

注:redis的命令(SET,GET等)是不區分大小寫的(KEY和VALUE區分的),為了方便所以小喵的操作可能都是小寫的。

指令清單:

一,SET的相關操作

1,SET key value [EX seconds] [PX milliseconds] [NX|XX]
Set the string value of a key

將鍵key設置成指定的字符串value。

我們知道Redis是一種key-value數據庫(當然這么說可能不夠嚴謹,因為Redis支持很多高級的數據結構)。因此所有的數據都是通過key來訪問,key就是鍵。

SET指令可以將字符串的value和key綁定在一起。

如果不加額外的參數。當key中已經保存了一個值的話,這個值會被覆蓋成新的值,並且忽略掉原始類型(一個key對應的不一定是一個字符串,也可以是List等其他的數據結構,這些結構在后面的文章里面會陸續介紹);如果key不存在,那么則會在數據庫中新增一個key,對應的值就是你剛剛設置的。

例子:

 1 redis> get name
 2 (nil)
 3 redis> set name miaoerduo
 4 OK
 5 redis> get name
 6 "miaoerduo"
 7 redis> set name miao
 8 OK
 9 redis> get name
10 "miao"

GET命令用於查看key對應的值,我們下面會講到。

第一個get,由於name這個key不存在,因此我們得到的結果是nil,也就是說空。

之后我們設置一次,然后就get到我們的結果了。之后再設置一次,會發現結果已經被覆蓋。

這就是set命令的基本用法。

我們看到文檔的后面有一些可選的參數。下面依次來介紹:

EX seconds:設置key的過時時間,單位為秒。

PX milliseconds:設置key的過期時間,單位為毫秒。

以ex為例:

1 redis> set name miaoerduo ex 10
2 OK
3 redis> get name
4 "miaoerduo"
5 redis> get name
6 (nil)

我們設置了name的過期時間為10秒。

在設置完name之后,立刻獲取name的值,可以得到正確的輸出。但是過了10s之后,發現只能得到一個nil。說明這個key已經被清空了。

那么這個設置有什么作用呢?

我們說幾個常見的應用場景:

a,驗證碼

我們經常在登錄一些網站或是進行付款等操作的時候,都會收到一些驗證碼,並且說10min后失效。

實際上就可以通過下面一條指令來實現:

set phone_num code ex 600

用手機號作為key,驗證碼作為值,超時6min。這樣當你輸入好驗證碼,提交的時候,后台就可以了先get phone_num,再比較你的輸入和數據庫里面存的值,從而完成身份的驗證。

b,session

早前,網站通過cookie來保存用戶的用戶名和密碼,之后出現了很多的安全隱患,因此就提出了session的機制。

用戶登陸成功之后,本地的cookie會保留一個較長的隨機碼,而網站后的后台則存儲了這個隨機碼和你的用戶id的對應關系。在你第二次登錄的時候,cookie會傳輸到后台,而后台則根據你的隨機碼,獲取你的用戶信息,如果符合,則自動登錄。這樣,即使網站上有不法分子獲取了你的cookie,也得不到你的任何信息,因為你的真實的有用的信息都存儲在網站的后台。

我們在登錄郵箱的時候,通常都會有一個選項,7天內自動登錄。這其實就是給后台存的session設置了一個超時。聰明的你是不是已經會自己實現了呢?

NX:(if Not eXist)只有鍵key不存在的時候才會設置key的值

XX:只有鍵key存在的時候才會設置key的值

NX通常用於實現鎖機制,XX的功能,小喵暫時木有頭緒。。。想到應用場景的筒子們可以留言告訴小喵,小喵看到了一定會更新的。

舉個例子:

 1 redis> del name
 2 (integer) 0
 3 redis> get name
 4 (nil)
 5 redis> set name miaoerduo nx
 6 OK
 7 redis> set name miaoerduo nx
 8 (nil)
 9 redis> get name
10 "miaoerduo"
11 redis> set name miao xx
12 OK
13 redis> get name
14 "miao"
15 redis> del name
16 (integer) 1
17 redis> set name miao xx
18 (nil)

首先我們使用del指令刪除了name(這個指令后面會介紹)。之后使用nx模式設置name,第一次成功了,而第二次就失敗了,因為name已經存在了。之后使用xx模式修改,發現確實可以。del name之后,再使用xx模式,就失敗了,因為此時的name已經不存在了。

以上,就是set的所有的用法。

2,SETRANGE key offset value
Overwrite part of a string at key starting at the specified offset

這個命令可以覆蓋掉key對應的string的一部分。offset表示需要覆蓋的字符串的起始位置,之后會用value的值,覆蓋掉原始string的對應位置的數據。

這里有一些比較有意思的操作:如果原始key不存在,則默認為一個長度為0的字符串。如果offset超過原始string的長度,那么會在之前的string后面補充0以達到offset。如果value的長度超過了原始string后面可以覆蓋的部分,則Redis內部會重新申請內存,完成數據的追加(還記得上一章的內容嗎?),這時候數據庫由於需要分配內存,可能會出現阻塞(需要分配的內存越大,阻塞時間越長)。

例子:

 1 redis> set str "hello world"
 2 OK
 3 redis> setrange str 6 redis
 4 (integer) 11
 5 redis> get str
 6 "hello redis"
 7 redis> setrange str 15 aha
 8 (integer) 18
 9 redis> get str
10 "hello redis\x00\x00\x00\x00aha"
11 redis> del str
12 (integer) 1
13 redis> setrange str 5 "hello miao"
14 (integer) 15
15 redis> get str
16 "\x00\x00\x00\x00\x00hello miao"

在這個實驗中,我們先新建了一個str,內容是"hello world",之后,從位置6開始寫入字符串"redis",則得到了"hello redis"。之后我們在15的位置,寫入"aha",這是offset已經比字符串的長度要大了,則Redis會默認填充0(\x00是0的16進制表達),之后再追加字符串。最后,我們給一個不存在的key使用setrange設置了一個值,結果表現得和空字符串一樣。

3,MSET key value [key value ...]
Set multiple keys to multiple values

一次性設置多個key-value。如果key的值已存在,則會直接覆蓋。

相當與同時調用多次SET命令。不過要注意的是,這個操作是原子的。也就是說,所有的keys都一次性設置的。如果你同時運行兩個MSET來設置相同的keys,那么操作的結果也只會是兩次MSET中的后一次的結果,而不會是混雜的結果。

例子:

1 redis> mset name1 miaoerduo name2 miao name3 love
2 OK
3 redis> get name1
4 "miaoerduo"
5 redis> get name2
6 "miao"
7 redis> get name3
8 "love"

4,MSETNX key value [key value ...]

Set multiple keys to multiple values, only if none of the keys exist

一次性設置多個key-value。如果存在任何一個key已經存在,那么這個操作都不會執行。所以,當使用MSETNX的時候,要么全部key都被修改,要么全部都不被修改。

當然這個操作也是原子的。

 1 redis> get name3
 2 "love"
 3 redis> msetnx name3 miaoerduo name4 miao
 4 (integer) 0
 5 redis> get name4
 6 (nil)
 7 redis> msetnx name4 miaoerduo name5 miao
 8 (integer) 1
 9 redis> get name4
10 "miaoerduo"
11 redis> get name5
12 "miao"

當name3已經存在的時候,我們設置name3和name4,發現,連name4都沒有創建。當設置name4和name5的時候,由於這兩個key之前都不存在,因此設置成功了。

5,SETEX key seconds value
Set the value and expiration of a key
設置key的值和超時。和前面的set key value ex seconds一樣。

6,PSETEX key milliseconds value
Set the value and expiration in milliseconds of a key
設置key的值和超時。和前面的set key value px milliseconds一樣。

7,SETNX key value
Set the value of a key, only if the key does not exist

當key不存在的時候,設置key的值。和set key value nx一樣。

二、GET的相關操作

1,GET key
Get the value of a key

根據給定的key,獲取value值。這個操作,我們之前已經使用了很多次。

如果key不存在,會返回nil。如果key對應的值不是string(List,Set等),則會報錯,因為GET只能處理string類型的value。

演示如下:

 1 redis> get name
 2 (nil)
 3 redis> set name "miaoerduo"
 4 OK
 5 redis> get name
 6 "miaoerduo"
 7 redis> lpush arr 1 2 3 4 5
 8 (integer) 5
 9 redis> get arr
10 (error) WRONGTYPE Operation against a key holding the wrong kind of value

注意,lpush是List部分的內容,arr的值是一個List的結構。這里只需要知道key不是string類型的時候,get的時候就會報錯。

2,MGET key [key ...]
Get the values of all the given keys

還記得我們之前可以同時設置多個key-value嗎?其實我們也可以一次性獲取多個key的值。如果key不存在,則對應的地方返回nil。

那么一次性獲取多個值和單獨一次一次GET有什么區別呢?

小喵認為,主要有兩點,原子操作和查詢效率。

比如博客上會統計評論數和訪問數。如果我們依次讀取這兩部分的值,那么可能得到的兩個值並不是同一時刻的。而如果使用MGET,則得到的一定是同一時刻的。這就是原子操作的威力(注,官方文檔只介紹了SET和GET的操作是原子的,並沒有說MGET是不是,這里小喵果斷的說MGET是原子操作也是不合理的。但考慮到使用MGET的時候,是把查詢指令一次性傳輸到后台來執行,所以應該原子操作。無論如何,在上面的例子中,使用MGET總是比兩次GET要合理的)。

另外,如果數據庫的查詢,都分為三個過程,傳輸查詢指令,執行指令,輸出結果。

如果分多次GET的話,在傳輸指令和輸出結果的這兩個部分就要重復很多次,效率大打折扣。

反派汪:分多次查詢,需要傳輸的指令數目,也不會比單次多很多,為什么一定會影響效率呢?
喵太:其實不然,傳輸數據需要很多的准備工作,不僅僅是數據的具體傳輸,有時候需要考慮連接的創建和關閉、設置鎖等的開銷。
反派汪:說的好像有道理,我再回去研究研究。

1 redis> set key1 a
2 OK
3 redis> set key2 b
4 OK
5 redis> mget key1 key2 key3
6 1) "a"
7 2) "b"
8 3) (nil)

我們只創建了key1和key2,使用MGET獲取的時候,由於沒有key3,對應的位置返回了nil。

3,GETRANGE key start end
Get a substring of the string stored at a key

該指令只要用於獲取字符串的字串,在Redis2.0版本之前,叫做SUBSTR。strat和end是字串的起始和結束的位置,可以用負數來表示距離string尾部的未知的下標。-1是最后一個字符,-2是底數第二個字符。這個表達方式和Python的獲取list的子list非常相似。

需要注意的有兩點:

字串包括了start和end這兩個位置的字符。在Python中是不包含end的。

當給出的start和end超出了string的范圍時,指令只會返回在string內的結果。

 1 redis> set str "hello world"
 2 OK
 3 redis> getrange str 6 -1
 4 "world"
 5 redis> getrange str -5 -1
 6 "world"
 7 redis> getrange str 0 4
 8 "hello"
 9 redis> getrange str 0 100
10 "hello world"

上述例子是幾種情況下的輸出。

4,GETSET key value
Set the string value of a key and return its old value

設置key的值,並返回之前的值。如果之前key的值不是string,則會報錯。

這個指令相當於先GET,再SET。

這個指令可以用來配合INCR指令一起使用支持重置的技術功能(INCR我們后面會講到)。先設置count為0,每次INCR使得count加1。等到需要獲取計數的時候,使用GETSET count 0,就能獲取計數的值,並且把計數器重置了。

1 redis> set str "hello"
2 OK
3 redis> getset str "world"
4 "hello"
5 redis> get str
6 "world"

第一行設置str為"hello"

第二行獲取了str原先的值,並把str設置成"world"。

第三行GET的時候,就是修改之后的值了。

三、string的修改操作

1,STRLEN key
Get the length of the value stored in a key

返回key對應的string的長度,如果key對應的不是string,則報錯。如果key不存在,則返回0(還是把key對應的看成空字符串)。

redis> set str "hello world"
OK
redis> strlen str
(integer) 11
redis> get nokey
(nil)
redis> strlen nokey
(integer) 0

2,APPEND key value

Append a value to a key

如果key已經存在,且值為string,則將value追加到值的后面,如果key不存在,則會創建一個空的字符串的key,然后執行追加操作。

 1 redis> set str "hello"
 2 OK
 3 redis> append str " world"
 4 (integer) 11
 5 redis> get str
 6 "hello world"
 7 redis> del str
 8 (integer) 1
 9 redis> append str "hello"
10 (integer) 5
11 redis> get str
12 "hello"

這個例子中,我們先向已有值的str中append了一個字符串。然后向不存在的key,也添加了字符串。

3,INCR key
Increment the integer value of a key by one

對存儲在key的整數值進行原子的加1操作。

如果key不存在,則會設置默認值0,再加1。

如果key存在,但是存儲的值不是字符串,或者存儲的字符串不能表示整數,則執行該操作時,會報錯。

這個操作僅限於64位的有符號的整型數據。

比較有意思的是,雖然這個key存儲的值是個字符串,但是該操作的效果和對相應的數字進行操作一樣。而且,Redis中,在存儲這類字符串的時候,底層上其實存儲的就是一個整數,因此不存在存儲上的浪費。

 1 redis> set count 0
 2 OK
 3 redis> incr count
 4 (integer) 1
 5 redis> incr count
 6 (integer) 2
 7 redis> del count
 8 (integer) 1
 9 redis> get count
10 (nil)
11 redis> incr count
12 (integer) 1
13 redis> incr count
14 (integer) 2

值得注意的是,該操作是原子操作,即使有多個請求傳輸到Redis,count執行的結果都不會錯誤,所以我們可以大膽放心的用這個功能實現多線程的計數功能。

4,DECR key
Decrement the integer value of a key by one

對存儲在key的整數值進行原子的減1操作。

注意事項和INCR一樣。

5,INCRBY key increment
Increment the integer value of a key by the given amount

對存儲在key的整數值進行原子的加操作,加increment。

如果key不存在,操作之前,key就會被置為0。如果key的value類型錯誤或者是個不能表示成數字的字符串,就返回錯誤。這個操作最多支持64位有符號的整型數字。基本上和INCR一樣。

6,DECRBY key decrement
Decrement the integer value of a key by the given number

對存儲在key的整數值進行原子的減操作,減increment。

其他和INCR一樣。

7,INCRBYFLOAT key increment
Increment the float value of a key by the given amount

對存儲造key中的浮點數進行原子的加操作,加increment。

如果key不存在,操作之前,key就會被置為0。如果key的value類型錯誤或者是個不能表示成浮點數的字符串,就返回錯誤。

我們並沒有DECRBYFLOAT這個操作,因此想要實現減操作,只需要把increment設成負的就可以。

 1 redis> set a 1.5
 2 OK
 3 redis> incrbyfloat a 10.1
 4 "11.6"
 5 redis> incrbyfloat a 10.1
 6 "21.7"
 7 redis> incrbyfloat a -10.1
 8 "11.6"
 9 redis> incrbyfloat a -1.5e2
10 "-138.39999999999999999"

浮點數可以用一般的小數和科學計數法表示。

四、二進制操作

1,SETBIT key offset value
Sets or clears the bit at offset in the string value stored at key

設置或者清空key的value(字符串)在offset處的bit值。這里將string看成由bit組成的數組。

指定位置的bit可以被設置,或者被清空,這個由value(只能是0或者1)來決定。當key不存在的時候,就創建一個新的字符串value。要確保這個字符串足夠長到在offset處有bit值。參數offset需要大於等於0,並且小於2^32(限制bitmap大小為512)。當key對應的字符串增大的時候,新增的部分bit值都是設置為0。

該操作返回value原來的offset位置的bit值。

 1 redis> setbit a 0 1
 2 (integer) 0
 3 redis> setbit a 1 1
 4 (integer) 0
 5 redis> setbit a 2 1
 6 (integer) 0
 7 redis> setbit a 3 1
 8 (integer) 0
 9 redis> get a
10 "\xf0"

a最開始不存在,使用setbit操作,將a的前4位都設置成1。最終就得到了\xf0,這是16進制表示的結果,前4位都是1,其他都是0。

2,GETBIT key offset
Returns the bit value at offset in the string value stored at key

獲取key對應的string在offset處的bit值。當offset超出了字符串長度的時候,這個字符串就被假定為由0比特填充的連續空間。當key不存在的時候,它就認為是一個空字符串,所以offset總是超出范圍,然后value也被認為是由0比特填充的連續空間。

1 redis> setbit a 7 1
2 (integer) 0
3 redis> getbit a 0
4 (integer) 0
5 redis> getbit a 7
6 (integer) 1
7 redis> getbit a 100
8 (integer) 0

3,BITCOUNT key [start end]

 Count set bits in a string

統計key的string的二進制中1的個數。

start和end分別表示string的起始和結束位置,含義和GETRANGE中一樣。

1 redis> setbit mykey 0 1
2 (integer) 0
3 redis> setbit mykey 10 1
4 (integer) 0
5 redis> setbit mykey 5 1
6 (integer) 0
7 redis> bitcount mykey
8 (integer) 3

4,BITOP operation destkey key [key ...]

Perform bitwise operations between strings

對string進行bit級別的操作。具體操作有4種。AND,OR,XOR,NOT。

用法如下:

BITOP AND destkey srckey1 srckey2 srckey3 ... srckeyN
BITOP OR destkey srckey1 srckey2 srckey3 ... srckeyN
BITOP XOR destkey srckey1 srckey2 srckey3 ... srckeyN
BITOP NOT destkey srckey

NOT操作后面只有一個目標key和srckey,是因為NOT操作是一元的。

對於AND,OR和XOR操作,Redis會將srckey1,srckey2,...,srckeyN這些字符串對位進行相關操作,之后將結果存入destkey中。

如果srckey的length不相等的話,Redis內部會將短的字符串補齊,並填充上0。

 1 redis> set key1 "\xf0\xf0"
 2 OK
 3 redis> set key2 "\x0f\x0f"
 4 OK
 5 redis> set key3 "\xff\xff"
 6 OK
 7 redis> bitop and destkey key1 key2 key3
 8 (integer) 2
 9 redis> get destkey
10 "\x00\x00"
11 redis> bitop or destkey key1 key2 key3
12 (integer) 2
13 redis> get destkey
14 "\xff\xff"
15 redis> bitop xor destkey key1 key2 key3
16 (integer) 2
17 redis> get destkey
18 "\x00\x00"
19 redis> bitop not destkey key1
20 (integer) 2
21 redis> get destkey
22 "\x0f\x0f"

5,BITPOS key bit [start] [end]

Find first bit set or clear in a string

返回string的二進制中第一個0或1的位置。

這里將string看成一個有許多bit組成的數組。其中start和end表示string的一個間隔,如果指定了start和end,則BITPOS只會查詢這個區間。注意,start和end表示的字符的位置,不是bit的位置。

即使指定了start和end,BITPOS這個操作也只會這個目標bit的絕對地址。

有幾點需要注意:

在沒有指定查詢區間或只指定start的時候,查詢bit位為0的位置時,如果string中沒有該位,則會返回string的bit位的總數。比如在\xff\xff\xff直接查詢bit為0的位置,Redis默認該字符串后面都是0,因此,返回的結果就是12(下標從0開始數的)。

如果指定了查詢區間,無論查詢0或是1,在沒查詢到的時候只會返回-1。

在沒有指定查詢區間時,查詢bit位為1的位置時,如果string中沒有該位,則會返回-1,表示未查詢到。

redis> set bits "\x00\xff\x00\xff"
OK
redis> strlen bits
(integer) 4
redis> bitpos bits 0
(integer) 0
redis> bitpos bits 1
(integer) 8
redis> bitpos bits 0 1 
(integer) 16
redis> bitpos bits 0 1 -1
(integer) 16
redis> bitpos bits 1 1 -1
(integer) 8
redis> bitpos bits 0 3
(integer) 32
redis> bitpos bits 0 3 3
(integer) -1

看上面的例子,bits的初始設置的二進制表示為:

00000000 11111111 00000000 11111111

直接獲取0(bitpos bits 0)的位置為0,獲取1(bitpos bits 1)的位置為8。

指定開始位置為1(start = 1)的時候,0第一次出現(bitpos bits 1)的位置為16。

指定起始位置為3(start = 3)的時候,0第一次出現(bitpos bits 0 3)的位置為32。這是因為Redis在查詢字符串的時候查詢到了字符串的末尾,之后默認末尾的后面都是0,因此得到了32這的個位置。

最后,當指定必須在第3個字節(從0開始數)查詢0的時候,由於查不到0,因此只返回了-1。

 

這篇博客的內容,小喵自己也感覺有點多,每個指令小喵都是參考了官方的文檔,然后都自己動手做了實驗,再加入了自己的一點理解。可能有些地方,小喵沒有理解的很好,這時就需要大家多多指教了。

 

轉載請注明出處~~喵嗚~~

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM