小喵萬萬沒想到,上一篇博客,居然已經被閱讀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區分的),為了方便所以小喵的操作可能都是小寫的。
指令清單:
- APPEND
- BITCOUNT
- BITOP
- BITPOS
- DECR
- DECRBY
- GET
- GETBIT
- GETRANGE
- GETSET
- INCR
- INCRBY
- INCRBYFLOAT
- MGET
- MSET
- MSETNX
- PSETEX
- SET
- SETBIT
- SETEX
- SETNX
- SETRANGE
- STRLEN
一,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
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
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。
這篇博客的內容,小喵自己也感覺有點多,每個指令小喵都是參考了官方的文檔,然后都自己動手做了實驗,再加入了自己的一點理解。可能有些地方,小喵沒有理解的很好,這時就需要大家多多指教了。
轉載請注明出處~~喵嗚~~