1. 程式人生 > >Redis詳解(原理,安裝,配置,使用,命令)

Redis詳解(原理,安裝,配置,使用,命令)

一、Redis介紹

Redis是一個開源的使用ANSI C語言編寫、支援網路、可基於記憶體亦可持久化的日誌型、Key-Value資料庫,並提供多種語言的API。從2010年3月15日起,Redis的開發工作由VMware主持。

Redis能執行在大多數POSIX(Linux, *BSD, OS X 和Solaris等)系統上,官方沒有支援Windows的版本。目前最新的版本是2.2.11,這個版本主要是修復了一個2.2.7版本中遍歷方式優化帶來的一個bug。 和普通的Key-Value結構不同,Redis的Key支援靈活的資料結構,除了strings,還有hashes、lists、 sets 和sorted sets等結構。正是這些靈活的資料結構,豐富了Redis的應用場景,能滿足更多業務上的靈活儲存需求。 Redis的資料都儲存在記憶體中,而且底層實現上是自己寫了epoll event loop部分,而沒有采用開源的libevent等通用框架,所以讀寫效率很高。為了實現資料的持久化,Redis支援定期重新整理(可通過配置實現)或寫日誌的方式來儲存資料到磁碟。

1、資料型別

作為Key-value型資料庫,Redis也提供了鍵(Key)和鍵值(Value)的對映關係。但是,除了常規的數值或字串,Redis的鍵值還可以是以下形式之一: ●Lists (列表) ●Sets (集合) ●Sorted sets (有序集合) ●Hashes (雜湊表) 鍵值的資料型別決定了該鍵值支援的操作。Redis支援諸如列表、集合或有序集合的交集、並集、查集等高階原子操作;同時,如果鍵值的型別是普通數字,Redis則提供自增等原子操作。

2、持久化

通常,Redis將資料儲存於記憶體中,或被配置為使用虛擬記憶體。通過兩種方式可以實現資料持久化:使用截圖的方式,將記憶體中的資料不斷寫入磁碟;或使用類似MySQL的日誌方式,記錄每次更新的日誌。前者效能較高,但是可能會引起一定程度的資料丟失;後者相反。

3、主從同步

Redis支援將資料同步到多臺從庫上,這種特性對提高讀取效能非常有益。

4、效能

相比需要依賴磁碟記錄每個更新的資料庫,基於記憶體的特性無疑給Redis帶來了非常優秀的效能。讀寫操作之間有顯著的效能差異。

5、提供API的語言

●C ●C++ ●C# ●Clojure ●Common Lisp ●Erlang ●Haskell ●Java ●Javascript ●Lua ●Objective-C ●Perl ●PHP ●Python ●Ruby ●Scala ●Go ●Tcl

6、適用場合

毫無疑問,Redis開創了一種新的資料儲存思路,使用Redis,我們不用在面對功能單調的資料庫時,把精力放在如何把大象放進冰箱這樣的問題上,而是利用Redis靈活多變的資料結構和資料操作,為不同的大象構建不同的冰箱。希望你喜歡這個比喻。 下面是Redis適用的一些場景:

(1)、取最新N個數據的操作 比如典型的取你網站的最新文章,通過下面方式,我們可以將最新的5000條評論的ID放在Redis的List集合中,並將超出集合部分從資料庫獲取。 使用LPUSH latest.comments命令,向list集合中插入資料 插入完成後再用LTRIM latest.comments 0 5000命令使其永遠只儲存最近5000個ID 然後我們在客戶端獲取某一頁評論時可以用下面的邏輯 FUNCTION get_latest_comments(start,num_items): id_list = redis.lrange(“latest.comments”,start,start+num_items-1) IF id_list.length < num_items id_list = SQL_DB(“SELECT … ORDER BY time LIMIT …”) END RETURN id_list END 如果你還有不同的篩選維度,比如某個分類的最新N條,那麼你可以再建一個按此分類的List,只存ID的話,Redis是非常高效的。

(2)、排行榜應用,取TOP N操作 這個需求與上面需求的不同之處在於,前面操作以時間為權重,這個是以某個條件為權重,比如按頂的次數排序,這時候就需要我們的sorted set出馬了,將你要排序的值設定成sorted set的score,將具體的資料設定成相應的value,每次只需要執行一條ZADD命令即可。

(3)、需要精準設定過期時間的應用 比如你可以把上面說到的sorted set的score值設定成過期時間的時間戳,那麼就可以簡單地通過過期時間排序,定時清除過期資料了,不僅是清除Redis中的過期資料,你完全可以把Redis裡這個過期時間當成是對資料庫中資料的索引,用Redis來找出哪些資料需要過期刪除,然後再精準地從資料庫中刪除相應的記錄。

(4)、計數器應用 Redis的命令都是原子性的,你可以輕鬆地利用INCR,DECR命令來構建計數器系統。

(5)、Uniq操作,獲取某段時間所有資料排重值 這個使用Redis的set資料結構最合適了,只需要不斷地將資料往set中扔就行了,set意為集合,所以會自動排重。

(6)、實時系統,反垃圾系統 通過上面說到的set功能,你可以知道一個終端使用者是否進行了某個操作,可以找到其操作的集合並進行分析統計對比等。沒有做不到,只有想不到。

(7)、Pub/Sub構建實時訊息系統 Redis的Pub/Sub系統可以構建實時的訊息系統,比如很多用Pub/Sub構建的實時聊天系統的例子。(8)、構建佇列系統 使用list可以構建佇列系統,使用sorted set甚至可以構建有優先順序的佇列系統。(9)、快取 這個不必說了,效能優於Memcached,資料結構更多樣化。

二、安裝及使用

步驟一: 下載Redis 下載安裝包:wget http://download.redis.io/releases/redis-3.0.6.tar.gz [[email protected] 4setup]# wget http://download.redis.io/releases/redis-3.0.6.tar.gz

步驟二: 編譯源程式 [[email protected] 4setup]# ll [[email protected] 4setup]# tar xzf redis-3.0.6.tar.gz [[email protected] 4setup]# cd redis-3.0.6 [[email protected] redis-2.2.12]# make cd src && make all make[1]: Entering directory `/root/4setup/redis-3.0.6/src’

步驟三: 啟動Redis服務 src/redis-server [[email protected] redis-2.2.12]# src/redis-server Redis 服務端的預設連線埠是 6379。 使用指定配置檔案啟動 src/redis-server redis.conf步驟四: 將Redis作為 Linux 服務隨機啟動 vi /etc/rc.local, 使用vi編輯器開啟隨機啟動配置檔案,並在其中加入下面一行程式碼。 /root/4setup/redis-3.0.6/src/redis-server步驟五: 客戶端連線驗證 新開啟一個Session輸入:src/redis-cli,如果出現下面提示,那麼您就可以開始Redis之旅了。 [[email protected] redis-3.0.6]# src/redis-cli redis 127.0.0.1:6379>步驟六: 檢視Redis日誌 檢視伺服器端session,即可對Redis的執行狀況進行檢視或分析了。 [6246] 05 Aug 19:24:33 – 0 clients connected (0 slaves), 539544 bytes in use [6246] 05 Aug 19:24:37 – Accepted 127.0.0.1:51381 [6246] 05 Aug 19:24:38 – 1 clients connected (0 slaves), 547372 bytes in use 以上的幾個步驟就OK了!!這樣一個簡單的Redis資料庫就可以暢通無阻地執行起來了。步驟七: 停止Redis例項 最簡單的方法是在啟動例項的session中,直接使用Control-C來將例項停止。 我們還可以用客戶端來停止服務,如可以用shutdown來停止Redis例項, 具體如下: [[email protected] redis-3.0.6]# src/redis-cli shutdown

三、配置Redis

使用配置檔案啟動:src/redis-server redis.conf 主要配置項: Redis支援很多的引數,但都有預設值。 ●daemonize: 預設情況下,redis不是在後臺執行的,如果需要在後臺執行,把該項的值更改為yes。 ●pidfile 當Redis在後臺執行的時候,Redis預設會把pid檔案放在/var/run/redis.pid,你可以配置到其他地址。當執行多個redis服務時,需要指定不同的pid檔案和埠。 ●bind 指定Redis只接收來自於該IP地址的請求,如果不進行設定,那麼將處理所有請求,在生產環境中最好設定該項。 ●port 監聽埠,預設為6379。 ●timeout 設定客戶端連線時的超時時間,單位為秒。當客戶端在這段時間內沒有發出任何指令,那麼關閉該連線。 ●loglevel log等級分為4級,debug, verbose, notice, 和warning。生產環境下一般開啟notice。 ●logfile 配置log檔案地址,預設使用標準輸出,即列印在命令列終端的視窗上。 ●databases 設定資料庫的個數,可以使用SELECT 命令來切換資料庫。預設使用的資料庫是0。 ●save 設定Redis進行資料庫映象的頻率。 if(在60秒之內有10000個keys發生變化時){ 進行映象備份 }else if(在300秒之內有10個keys發生了變化){ 進行映象備份 }else if(在900秒之內有1個keys發生了變化){ 進行映象備份 } ●rdbcompression 在進行映象備份時,是否進行壓縮。 ●dbfilename 映象備份檔案的檔名。 ●dir 資料庫映象備份的檔案放置的路徑。這裡的路徑跟檔名要分開配置是因為Redis在進行備份時,先會將當前資料庫的狀態寫入到一個臨時檔案中,等備份完成時,再把該該臨時檔案替換為上面所指定的檔案,而這裡的臨時檔案和上面所配置的備份檔案都會放在這個指定的路徑當中。 ●slaveof 設定該資料庫為其他資料庫的從資料庫。 ●masterauth 當主資料庫連線需要密碼驗證時,在這裡指定。 ●requirepass 設定客戶端連線後進行任何其他指定前需要使用的密碼。警告:因為redis速度相當快,所以在一臺比較好的伺服器下,一個外部的使用者可以在一秒鐘進行150K次的密碼嘗試,這意味著你需要指定非常非常強大的密碼來防止暴力破解。 ●maxclients 限制同時連線的客戶數量。當連線數超過這個值時,redis將不再接收其他連線請求,客戶端嘗試連線時將收到error資訊。 ●maxmemory 設定redis能夠使用的最大記憶體。當記憶體滿了的時候,如果還接收到set命令,redis將先嚐試剔除設定過expire資訊的key,而不管該key的過期時間還沒有到達。在刪除時,將按照過期時間進行刪除,最早將要被過期的key將最先被刪除。如果帶有expire資訊的key都刪光了,那麼將返回錯誤。這樣,redis將不再接收寫請求,只接收get請求。maxmemory的設定比較適合於把redis當作於類似memcached的快取來使用。 ●appendonly 預設情況下,redis會在後臺非同步的把資料庫映象備份到磁碟,但是該備份是非常耗時的,而且備份也不能很頻繁,如果發生諸如拉閘限電、拔插頭等狀況,那麼將造成比較大範圍的資料丟失。所以redis提供了另外一種更加高效的資料庫備份及災難恢復方式。開啟append only模式之後,redis會把所接收到的每一次寫操作請求都追加到appendonly.aof檔案中,當redis重新啟動時,會從該檔案恢復出之前的狀態。但是這樣會造成appendonly.aof檔案過大,所以redis還支援了BGREWRITEAOF指令,對appendonly.aof進行重新整理。所以我認為推薦生產環境下的做法為關閉映象,開啟appendonly.aof,同時可以選擇在訪問較少的時間每天對appendonly.aof進行重寫一次。 ●appendfsync 設定對appendonly.aof檔案進行同步的頻率。always表示每次有寫操作都進行同步,everysec表示對寫操作進行累積,每秒同步一次。這個需要根據實際業務場景進行配置。 ●vm-enabled 是否開啟虛擬記憶體支援。因為redis是一個記憶體資料庫,而且當記憶體滿的時候,無法接收新的寫請求,所以在redis 2.0中,提供了虛擬記憶體的支援。但是需要注意的是,redis中,所有的key都會放在記憶體中,在記憶體不夠時,只會把value值放入交換區。這樣保證了雖然使用虛擬記憶體,但效能基本不受影響,同時,你需要注意的是你要把vm-max-memory設定到足夠來放下你的所有的key。 ●vm-swap-file 設定虛擬記憶體的交換檔案路徑。 ●vm-max-memory 這裡設定開啟虛擬記憶體之後,redis將使用的最大實體記憶體的大小。預設為0,redis將把他所有的能放到交換檔案的都放到交換檔案中,以儘量少的使用實體記憶體。在生產環境下,需要根據實際情況設定該值,最好不要使用預設的0。 ●vm-page-size 設定虛擬記憶體的頁大小,如果你的value值比較大,比如說你要在value中放置部落格、新聞之類的所有文章內容,就設大一點,如果要放置的都是很小的內容,那就設小一點。 ●vm-pages 設定交換檔案的總的page數量,需要注意的是,page table資訊會放在實體記憶體中,每8個page就會佔據RAM中的1個byte。總的虛擬記憶體大小 = vm-page-size * vm-pages。 ●vm-max-threads 設定VM IO同時使用的執行緒數量。因為在進行記憶體交換時,對資料有編碼和解碼的過程,所以儘管IO裝置在硬體上本上不能支援很多的併發讀寫,但是還是如果你所儲存的vlaue值比較大,將該值設大一些,還是能夠提升效能的。 ●glueoutputbuf 把小的輸出快取放在一起,以便能夠在一個TCP packet中為客戶端傳送多個響應,具體原理和真實效果我不是很清楚。所以根據註釋,你不是很確定的時候就設定成yes。 ●hash-max-zipmap-entries 在redis 2.0中引入了hash資料結構。當hash中包含超過指定元素個數並且最大的元素沒有超過臨界時,hash將以一種特殊的編碼方式(大大減少記憶體使用)來儲存,這裡可以設定這兩個臨界值。 ●activerehashing 開啟之後,redis將在每100毫秒時使用1毫秒的CPU時間來對redis的hash表進行重新hash,可以降低記憶體的使用。當你的使用場景中,有非常嚴格的實時性需要,不能夠接受Redis時不時的對請求有2毫秒的延遲的話,把這項配置為no。如果沒有這麼嚴格的實時性要求,可以設定為yes,以便能夠儘可能快的釋放記憶體。

四、操作Redis

1、插入資料 redis 127.0.0.1:6379> set name wwl OK 設定一個key-value對。2、查詢資料 redis 127.0.0.1:6379> get name “wwl” 取出key所對應的value。3、刪除鍵值 redis 127.0.0.1:6379> del name 刪除這個key及對應的value。4、驗證鍵是否存在 redis 127.0.0.1:6379> exists name (integer) 0 其中0,代表此key不存在;1代表存在。

五、各型別的基本操作

1)strings型別及操作

string是最簡單的型別,你可以理解成與Memcached是一模一樣的型別,一個key對應一個value,其上支援的操作與Memcached的操作類似。但它的功能更豐富。 string型別是二進位制安全的。意思是redis的string可以包含任何資料,比如jpg圖片或者序列化的物件。從內部實現來看其實string可以看作byte陣列,最大上限是1G位元組,下面是string型別的定義: struct sdshdr { long len; long free; char buf[]; }; len是buf陣列的長度。 free是陣列中剩餘可用位元組數,由此可以理解為什麼string型別是二進位制安全的了,因為它本質上就是個byte陣列,當然可以包含任何資料了 buf是個char陣列用於存貯實際的字串內容,其實char和c#中的byte是等價的,都是一個位元組。 另外string型別可以被部分命令按int處理.比如incr等命令,如果只用string型別,redis就可以被看作加上持久化特性的memcached。當然redis對string型別的操作比memcached還是多很多的,具體操作方法如下:1、set 設定key對應的值為string型別的value。 例如我們新增一個name= HongWan的鍵值對,可以這樣做: redis 127.0.0.1:6379> set name HongWan OK redis 127.0.0.1:6379>2、setnx 設定key對應的值為string型別的value。如果key已經存在,返回0,nx是not exist的意思。 例如我們新增一個name= HongWan_new的鍵值對,可以這樣做: redis 127.0.0.1:6379> get name “HongWan” redis 127.0.0.1:6379> setnx name HongWan_new (integer) 0 redis 127.0.0.1:6379> get name “HongWan” redis 127.0.0.1:6379> 由於原來name有一個對應的值,所以本次的修改不生效,且返回碼是0。3、setex 設定key對應的值為string型別的value,並指定此鍵值對應的有效期。 例如我們新增一個haircolor= red的鍵值對,並指定它的有效期是10秒,可以這樣做: redis 127.0.0.1:6379> setex haircolor 10 red OK redis 127.0.0.1:6379> get haircolor “red” redis 127.0.0.1:6379> get haircolor (nil) redis 127.0.0.1:6379> 可見由於最後一次的呼叫是10秒以後了,所以取不到haicolor這個鍵對應的值。4、setrange 設定指定key的value值的子字串。 例如我們希望將HongWan的126郵箱替換為gmail郵箱,那麼我們可以這樣做: redis 127.0.0.1:6379> get name “[email protected]” redis 127.0.0.1:6379> setrange name 8 gmail.com (integer) 17 redis 127.0.0.1:6379> get name “[email protected]” redis 127.0.0.1:6379> 其中的8是指從下標為8(包含8)的字元開始替換5、mset 一次設定多個key的值,成功返回ok表示所有的值都設定了,失敗返回0表示沒有任何值被設定。 redis 127.0.0.1:6379> mset key1 HongWan1 key2 HongWan2 OK redis 127.0.0.1:6379> get key1 “HongWan1″ redis 127.0.0.1:6379> get key2 “HongWan2″ redis 127.0.0.1:6379>6、msetnx 一次設定多個key的值,成功返回ok表示所有的值都設定了,失敗返回0表示沒有任何值被設定,但是不會覆蓋已經存在的key。 redis 127.0.0.1:6379> get key1 “HongWan1″ redis 127.0.0.1:6379> get key2 “HongWan2″ redis 127.0.0.1:6379> msetnx key2 HongWan2_new key3 HongWan3 (integer) 0 redis 127.0.0.1:6379> get key2 “HongWan2″ redis 127.0.0.1:6379> get key3 (nil) 可以看出如果這條命令返回0,那麼裡面操作都會回滾,都不會被執行。7、get 獲取key對應的string值,如果key不存在返回nil。 例如我們獲取一個庫中存在的鍵name,可以很快得到它對應的value redis 127.0.0.1:6379> get name “HongWan” redis 127.0.0.1:6379> 我們獲取一個庫中不存在的鍵name1,那麼它會返回一個nil以表時無此鍵值對 redis 127.0.0.1:6379> get name1 (nil) redis 127.0.0.1:6379>8、getset 設定key的值,並返回key的舊值。 redis 127.0.0.1:6379> get name “HongWan” redis 127.0.0.1:6379> getset name HongWan_new “HongWan” redis 127.0.0.1:6379> get name “HongWan_new” redis 127.0.0.1:6379> 接下來我們看一下如果key不存的時候會什麼樣兒? redis 127.0.0.1:6379> getset name1 aaa (nil) redis 127.0.0.1:6379> 可見,如果key不存在,那麼將返回nil9、getrange 獲取指定key的value值的子字串。 具體樣例如下: redis 127.0.0.1:6379> get name “[email protected]” redis 127.0.0.1:6379> getrange name 0 6 “HongWan” redis 127.0.0.1:6379> 字串左面下標是從0開始的 redis 127.0.0.1:6379> getrange name -7 -1 “126.com” redis 127.0.0.1:6379> 字串右面下標是從-1開始的 redis 127.0.0.1:6379> getrange name 7 100 “@126.com” redis 127.0.0.1:6379> 當下標超出字串長度時,將預設為是同方向的最大下標10、mget 一次獲取多個key的值,如果對應key不存在,則對應返回nil。 具體樣例如下: redis 127.0.0.1:6379> mget key1 key2 key3 1) “HongWan1″ 2) “HongWan2″ 3) (nil) redis 127.0.0.1:6379> key3由於沒有這個鍵定義,所以返回nil。11、incr 對key的值做加加操作,並返回新的值。注意incr一個不是int的value會返回錯誤,incr一個不存在的key,則設定key為1 redis 127.0.0.1:6379> set age 20 OK redis 127.0.0.1:6379> incr age (integer) 21 redis 127.0.0.1:6379> get age “21” redis 127.0.0.1:6379>12、incrby 同incr類似,加指定值 ,key不存在時候會設定key,並認為原來的value是 0 redis 127.0.0.1:6379> get age “21” redis 127.0.0.1:6379> incrby age 5 (integer) 26 redis 127.0.0.1:6379> get name “[email protected]” redis 127.0.0.1:6379> get age “26” redis 127.0.0.1:6379>13、decr 對key的值做的是減減操作,decr一個不存在key,則設定key為-1 redis 127.0.0.1:6379> get age “26” redis 127.0.0.1:6379> decr age (integer) 25 redis 127.0.0.1:6379> get age “25” redis 127.0.0.1:6379>14、decrby 同decr,減指定值。 redis 127.0.0.1:6379> get age “25” redis 127.0.0.1:6379> decrby age 5 (integer) 20 redis 127.0.0.1:6379> get age “20” redis 127.0.0.1:6379> decrby完全是為了可讀性,我們完全可以通過incrby一個負值來實現同樣效果,反之一樣。 redis 127.0.0.1:6379> get age “20” redis 127.0.0.1:6379> incrby age -5 (integer) 15 redis 127.0.0.1:6379> get age “15” redis 127.0.0.1:6379>15、append 給指定key的字串值追加value,返回新字串值的長度。例如我們向name的值追加一個@126.com字串,那麼可以這樣做: redis 127.0.0.1:6379> append name @126.com (integer) 15 redis 127.0.0.1:6379> get name “[email protected]” redis 127.0.0.1:6379>16、strlen 取指定key的value值的長度。 redis 127.0.0.1:6379> get name “HongWan_new” redis 127.0.0.1:6379> strlen name (integer) 11 redis 127.0.0.1:6379> get age “15” redis 127.0.0.1:6379> strlen age (integer) 2 redis 127.0.0.1:6379>

2)hash

Redis hash是一個string型別的field和value的對映表.它的新增、刪除操作都是O(1)(平均)。hash特別適合用於儲存物件。相較於將物件的每個欄位存成單個string型別。將一個物件儲存在hash型別中會佔用更少的記憶體,並且可以更方便的存取整個物件。省記憶體的原因是新建一個hash物件時開始是用zipmap(又稱為small hash)來儲存的。這個zipmap其實並不是hash table,但是zipmap相比正常的hash實現可以節省不少hash本身需要的一些元資料儲存開銷。儘管zipmap的新增,刪除,查詢都是O(n),但是由於一般物件的field數量都不太多。所以使用zipmap也是很快的,也就是說新增刪除平均還是O(1)。如果field或者value的大小超出一定限制後,Redis會在內部自動將zipmap替換成正常的hash實現. 這個限制可以在配置檔案中指定 hash-max-zipmap-entries 64 #配置欄位最多64個。 hash-max-zipmap-value 512 #配置value最大為512位元組。1、hset 設定hash field為指定值,如果key不存在,則先建立。 redis 127.0.0.1:6379> hset myhash field1 Hello (integer) 1 redis 127.0.0.1:6379>2、hsetnx 設定hash field為指定值,如果key不存在,則先建立。如果field已經存在,返回0,nx是not exist的意思。 redis 127.0.0.1:6379> hsetnx myhash field “Hello” (integer) 1 redis 127.0.0.1:6379> hsetnx myhash field “Hello” (integer) 0 redis 127.0.0.1:6379> 第一次執行是成功的,但第二次執行相同的命令失敗,原因是field已經存在了。3、hmset 同時設定hash的多個field。 redis 127.0.0.1:6379> hmset myhash field1 Hello field2 World OK redis 127.0.0.1:6379>4、hget 獲取指定的hash field。 redis 127.0.0.1:6379> hget myhash field1 “Hello” redis 127.0.0.1:6379> hget myhash field2 “World” redis 127.0.0.1:6379> hget myhash field3 (nil) redis 127.0.0.1:6379> 由於資料庫沒有field3,所以取到的是一個空值nil。5、hmget 獲取全部指定的hash filed。 redis 127.0.0.1:6379> hmget myhash field1 field2 field3 1) “Hello” 2) “World” 3) (nil) redis 127.0.0.1:6379> 由於資料庫沒有field3,所以取到的是一個空值nil。6、hincrby 指定的hash filed 加上給定值。 redis 127.0.0.1:6379> hset myhash field3 20 (integer) 1 redis 127.0.0.1:6379> hget myhash field3 “20” redis 127.0.0.1:6379> hincrby myhash field3 -8 (integer) 12 redis 127.0.0.1:6379> hget myhash field3 “12” redis 127.0.0.1:6379> 在本例中我們將field3的值從20降到了12,即做了一個減8的操作。7、hexists 測試指定field是否存在。 redis 127.0.0.1:6379> hexists myhash field1 (integer) 1 redis 127.0.0.1:6379> hexists myhash field9 (integer) 0 redis 127.0.0.1:6379> 通過上例可以說明field1存在,但field9是不存在的。8、hlen 返回指定hash的field數量。 redis 127.0.0.1:6379> hlen myhash (integer) 4 redis 127.0.0.1:6379> 通過上例可以看到myhash中有4個field。9、hdel 返回指定hash的field數量。 redis 127.0.0.1:6379> hlen myhash (integer) 4 redis 127.0.0.1:6379> hdel myhash field1 (integer) 1 redis 127.0.0.1:6379> hlen myhash (integer) 3 redis 127.0.0.1:6379>10、hkeys 返回hash的所有field。 redis 127.0.0.1:6379> hkeys myhash 1) “field2″ 2) “field” 3) “field3″ redis 127.0.0.1:6379> 說明這個hash中有3個field。11、hvals 返回hash的所有value。 redis 127.0.0.1:6379> hvals myhash 1) “World” 2) “Hello” 3) “12” redis 127.0.0.1:6379> 說明這個hash中有3個field。12、hgetall 獲取某個hash中全部的filed及value。 redis 127.0.0.1:6379> hgetall myhash 1) “field2″ 2) “World” 3) “field” 4) “Hello” 5) “field3″ 6) “12” redis 127.0.0.1:6379> 可見,一下子將myhash中所有的field及對應的value都取出來了。

3)list

Redis的list型別其實就是一個每個子元素都是string型別的雙向連結串列。連結串列的最大長度是(2的32次方)。我們可以通過push,pop操作從連結串列的頭部或者尾部新增刪除元素。這使得list既可以用作棧,也可以用作佇列。 有意思的是list的pop操作還有阻塞版本的,當我們[lr]pop一個list物件時,如果list是空,或者不存在,會立即返回nil。但是阻塞版本的b[lr]pop可以則可以阻塞,當然可以加超時時間,超時後也會返回nil。為什麼要阻塞版本的pop呢,主要是為了避免輪詢。舉個簡單的例子如果我們用list來實現一個工作佇列。執行任務的thread可以呼叫阻塞版本的pop去獲取任務這樣就可以避免輪詢去檢查是否有任務存在。當任務來時候工作執行緒可以立即返回,也可以避免輪詢帶來的延遲。說了這麼多,接下來看一下實際操作的方法吧:1、lpush 在key對應list的頭部新增字串元素: redis 127.0.0.1:6379> lpush mylist “world” (integer) 1 redis 127.0.0.1:6379> lpush mylist “hello” (integer) 2 redis 127.0.0.1:6379> lrange mylist 0 -1 1) “hello” 2) “world” redis 127.0.0.1:6379> 在此處我們先插入了一個world,然後在world的頭部插入了一個hello。其中lrange是用於取mylist的內容。2、rpush 在key對應list的尾部新增字串元素: redis 127.0.0.1:6379> rpush mylist2 “hello” (integer) 1 redis 127.0.0.1:6379> rpush mylist2 “world” (integer) 2 redis 127.0.0.1:6379> lrange mylist2 0 -1 1) “hello” 2) “world” redis 127.0.0.1:6379> 在此處我們先插入了一個hello,然後在hello的尾部插入了一個world。3、linsert 在key對應list的特定位置之前或之後新增字串元素: redis 127.0.0.1:6379> rpush mylist3 “hello” (integer) 1 redis 127.0.0.1:6379> rpush mylist3 “world” (integer) 2 redis 127.0.0.1:6379> linsert mylist3 before “world” “there” (integer) 3 redis 127.0.0.1:6379> lrange mylist3 0 -1 1) “hello” 2) “there” 3) “world” redis 127.0.0.1:6379> 在此處我們先插入了一個hello,然後在hello的尾部插入了一個world,然後又在world的前面插入了there。4、lset 設定list中指定下標的元素值(下標從0開始): redis 127.0.0.1:6379> rpush mylist4 “one” (integer) 1 redis 127.0.0.1:6379> rpush mylist4 “two” (integer) 2 redis 127.0.0.1:6379> rpush mylist4 “three” (integer) 3 redis 127.0.0.1:6379> lset mylist4 0 “four” OK redis 127.0.0.1:6379> lset mylist4 -2 “five” OK redis 127.0.0.1:6379> lrange mylist4 0 -1 1) “four” 2) “five” 3) “three” redis 127.0.0.1:6379> 在此處我們依次插入了one,two,three,然後將標是0的值設定為four,再將下標是-2的值設定為five。5、lrem 從key對應list中刪除count個和value相同的元素。 count>0時,按從頭到尾的順序刪除,具體如下: redis 127.0.0.1:6379> rpush mylist5 “hello” (integer) 1 redis 127.0.0.1:6379> rpush mylist5 “hello” (integer) 2 redis 127.0.0.1:6379> rpush mylist5 “foo” (integer) 3 redis 127.0.0.1:6379> rpush mylist5 “hello” (integer) 4 redis 127.0.0.1:6379> lrem mylist5 2 “hello” (integer) 2 redis 127.0.0.1:6379> lrange mylist5 0 -1 1) “foo” 2) “hello” redis 127.0.0.1:6379> count<0時,按從尾到頭的順序刪除,具體如下: redis 127.0.0.1:6379> rpush mylist6 “hello” (integer) 1 redis 127.0.0.1:6379> rpush mylist6 “hello” (integer) 2 redis 127.0.0.1:6379> rpush mylist6 “foo” (integer) 3 redis 127.0.0.1:6379> rpush mylist6 “hello” (integer) 4 redis 127.0.0.1:6379> lrem mylist6 -2 “hello” (integer) 2 redis 127.0.0.1:6379> lrange mylist6 0 -1 1) “hello” 2) “foo” redis 127.0.0.1:6379> count=0時,刪除全部,具體如下: redis 127.0.0.1:6379> rpush mylist7 “hello” (integer) 1 redis 127.0.0.1:6379> rpush mylist7 “hello” (integer) 2 redis 127.0.0.1:6379> rpush mylist7 “foo” (integer) 3 redis 127.0.0.1:6379> rpush mylist7 “hello” (integer) 4 redis 127.0.0.1:6379> lrem mylist7 0 “hello” (integer) 3 redis 127.0.0.1:6379> lrange mylist7 0 -1 1) “foo” redis 127.0.0.1:6379>6、ltrim 保留指定key 的值範圍內的資料: redis 127.0.0.1:6379> rpush mylist8 “one” (integer) 1 redis 127.0.0.1:6379> rpush mylist8 “two” (integer) 2 redis 127.0.0.1:6379> rpush mylist8 “three” (integer) 3 redis 127.0.0.1:6379> rpush mylist8 “four” (integer) 4 redis 127.0.0.1:6379> ltrim mylist8 1 -1 OK redis 127.0.0.1:6379> lrange mylist8 0 -1 1) “two” 2) “three” 3) “four” redis 127.0.0.1:6379>7、lpop 從list的頭部刪除元素,並返回刪除元素: redis 127.0.0.1:6379> lrange mylist 0 -1 1) “hello” 2) “world” redis 127.0.0.1:6379> lpop mylist “hello” redis 127.0.0.1:6379> lrange mylist 0 -1 1) “world” redis 127.0.0.1:6379>8、rpop 從list的尾部刪除元素,並返回刪除元素: redis 127.0.0.1:6379> lrange mylist2 0 -1 1) “hello” 2) “world” redis 127.0.0.1:6379> rpop mylist2 “world” redis 127.0.0.1:6379> lrange mylist2 0 -1 1) “hello” redis 127.0.0.1:6379>9、rpoplpush 從第一個list的尾部移除元素並新增到第二個list的頭部,最後返回被移除的元素值,整個操作是原子的.如果第一個list是空或者不存在返回nil: redis 127.0.0.1:6379> lrange mylist5 0 -1 1) “three” 2) “foo” 3) “hello” redis 127.0.0.1:6379> lrange mylist6 0 -1 1) “hello” 2) “foo” redis 127.0.0.1:6379> rpoplpush mylist5 mylist6 “hello” redis 127.0.0.1:6379> lrange mylist5 0 -1 1) “three” 2) “foo” redis 127.0.0.1:6379> lrange mylist6 0 -1 1) “hello” 2) “hello” 3) “foo” redis 127.0.0.1:6379>10、lindex 返回名稱為key的list中index位置的元素: redis 127.0.0.1:6379> lrange mylist5 0 -1 1) “three” 2) “foo” redis 127.0.0.1:6379> lindex mylist5 0 “three” redis 127.0.0.1:6379> lindex mylist5 1 “foo” redis 127.0.0.1:6379>11、llen 返回key對應list的長度: redis 127.0.0.1:6379> llen mylist5 (integer) 2 redis 127.0.0.1:6379>

4)sets

Redis的set是string型別的無序集合。set元素最大可以包含(2的32次方)個元素。 set的是通過hash table實現的,所以新增、刪除和查詢的複雜度都是O(1)。hash table會隨著新增或者刪除自動的調整大小。需要注意的是調整hash table大小時候需要同步(獲取寫鎖)會阻塞其他讀寫操作,可能不久後就會改用跳錶(skip list)來實現,跳錶已經在sorted set中使用了。關於set集合型別除了基本的新增刪除操作,其他有用的操作還包含集合的取並集(union),交集(intersection),差集(difference)。通過這些操作可以很容易的實現sns中的好友推薦和blog的tag功能。下面詳細介紹set相關命令:1、sadd 向名稱為key的set中新增元素: redis 127.0.0.1:6379> sadd myset “hello” (integer) 1 redis 127.0.0.1:6379> sadd myset “world” (integer) 1 redis 127.0.0.1:6379> sadd myset “world” (integer) 0 redis 127.0.0.1:6379> smembers myset 1) “world” 2) “hello” redis 127.0.0.1:6379> 本例中,我們向myset中添加了三個元素,但由於第三個元素跟第二個元素是相同的,所以第三個元素沒有新增成功,最後我們用smembers來檢視myset中的所有元素。2、srem 刪除名稱為key的set中的元素member: redis 127.0.0.1:6379> sadd myset2 “one” (integer) 1 redis 127.0.0.1:6379> sadd myset2 “two” (integer) 1 redis 127.0.0.1:6379> sadd myset2 “three” (integer) 1 redis 127.0.0.1:6379> srem myset2 “one” (integer) 1 redis 127.0.0.1:6379> srem myset2 “four” (integer) 0 redis 127.0.0.1:6379> smembers myset2 1) “three” 2) “two” redis 127.0.0.1:6379> 本例中,我們向myset2中添加了三個元素後,再呼叫srem來刪除one和four,但由於元素中沒有four所以,此條srem命令執行失敗。3、spop 隨機返回並刪除名稱為key的set中一個元素: redis 127.0.0.1:6379> sadd myset2 “one” (integer) 1 redis 127.0.0.1:6379> sadd myset2 “two” (integer) 1 redis 127.0.0.1:6379> sadd myset2 “three” (integer) 1 redis 127.0.0.1:6379> srem myset2 “one” (integer) 1 redis 127.0.0.1:6379> srem myset2 “four” (integer) 0 redis 127.0.0.1:6379> smembers myset2 1) “three” 2) “two” redis 127.0.0.1:6379> 本例中,我們向myset3中添加了三個元素後,再呼叫spop來隨機刪除一個元素,可以看到three元素被刪除了。4、sdiff 返回所有給定key與第一個key的差集: redis 127.0.0.1:6379> sadd myset2 “one” (integer) 1 redis 127.0.0.1:6379> sadd myset2 “two” (integer) 1 redis 127.0.0.1:6379> sadd myset2 “three” (integer) 1 redis 127.0.0.1:6379> srem myset2 “one” (integer) 1 redis 127.0.0.1:6379> srem myset2 “four” (integer) 0 redis 127.0.0.1:6379> smembers myset2 1) “three” 2) “two” redis 127.0.0.1:6379> 本例中,我們可以看到myset2中的元素與myset3中不同的只是three,所以只有three被查出來了,而不是three和one,因為one是myset3的元素。 我們也可以將myset2和myset3換個順序來看一下結果: redis 127.0.0.1:6379> sdiff myset3 myset2 1) “one” redis 127.0.0.1:6379> 這個結果中只顯示了,myset3中的元素與myset2中不同的元素。5、sdiffstore 返回所有給定key與第一個key的差集,並將結果存為另一個key: redis 127.0.0.1:6379> smembers myset2 1) “three” 2) “two” redis 127.0.0.1:6379> smembers myset3 1) “two” 2) “one” redis 127.0.0.1:6379> sdiffstore myset4 myset2 myset3 (integer) 1 redis 127.0.0.1:6379> smembers myset4 1) “three” redis 127.0.0.1:6379>6、sinter 返回所有給定key的交集: redis 127.0.0.1:6379> smembers myset2 1) “three” 2) “two” redis 127.0.0.1:6379> smembers myset3 1) “two” 2) “one” redis 127.0.0.1:6379> sinter myset2 myset3 1) “two” redis 127.0.0.1:6379> 通過本例的結果可以看出, myset2和myset3的交集two被查出來了。7、sinterstore 返回所有給定key的交集,並將結果存為另一個key redis 127.0.0.1:6379> smembers myset2 1) “three” 2) “two” redis 127.0.0.1:6379> smembers myset3 1) “two” 2) “one” redis 127.0.0.1:6379> sinterstore myset5 myset2 myset3 (integer) 1 redis 127.0.0.1:6379> smembers myset5 1) “two” redis 127.0.0.1:6379> 通過本例的結果可以看出, myset2和myset3的交集被儲存到myset5中了8、sunion 返回所有給定key的並集 redis 127.0.0.1:6379> smembers myset2 1) “three” 2) “two” redis 127.0.0.1:6379> smembers myset3 1) “two” 2) “one” redis 127.0.0.1:6379> sunion myset2 myset3 1) “three” 2) “one” 3) “two” redis 127.0.0.1:6379> 通過本例的結果可以看出, myset2和myset3的並集被查出來了9、sunionstore 返回所有給定key的並集,並將結果存為另一個key redis 127.0.0.1:6379> smembers myset2 1) “three” 2) “two” redis 127.0.0.1:6379> smembers myset3 1) “two” 2) “one” redis 127.0.0.1:6379> sunionstore myset6 myset2 myset3 (integer) 3 redis 127.0.0.1:6379> smembers myset6 1) “three” 2) “one” 3) “two” redis 127.0.0.1:6379> 通過本例的結果可以看出, myset2和myset3的並集被儲存到myset6中了10、smove 從第一個key對應的set中移除member並新增到第二個對應set中 redis 127.0.0.1:6379> smembers myset2 1) “three” 2) “two” redis 127.0.0.1:6379> smembers myset3 1) “two” 2) “one” redis 127.0.0.1:6379> smove myset2 myset7 three (integer) 1 redis 127.0.0.1:6379> smembers myset7 1) “three” redis 127.0.0.1:6379> 通過本例可以看到,myset2的three被移到myset7中了11、scard 返回名稱為key的set的元素個數 redis 127.0.0.1:6379> scard myset2 (integer) 1 redis 127.0.0.1:6379> 通過本例可以看到,myset2的成員數量為112、sismember 測試member是否是名稱為key的set的元素 redis 127.0.0.1:6379> smembers myset2 1) “two” redis 127.0.0.1:6379> sismember myset2 two (integer) 1 redis 127.0.0.1:6379> sismember myset2 one (integer) 0 redis 127.0.0.1:6379> 通過本例可以看到,two是myset2的成員,而one不是。13、srandmember 隨機返回名稱為key的set的一個元素,但是不刪除元素 redis 127.0.0.1:6379> smembers myset3 1) “two” 2) “one” redis 127.0.0.1:6379> srandmember myset3 “two” redis 127.0.0.1:6379> srandmember myset3 “one” redis 127.0.0.1:6379>

5)sorted sets

和set一樣sorted set也是string型別元素的集合,不同的是每個元素都會關聯一個double型別的score。sorted set的實現是skip list和hash table的混合體。 當元素被新增到集合中時,一個元素到score的對映被新增到hash table中,所以給定一個元素獲取score的開銷是O(1),另一個score到元素的對映被新增到skip list,並按照score排序,所以就可以有序的獲取集合中的元素。新增,刪除操作開銷都是O(log(N))和skip list的開銷一致,redis的skip list實現用的是雙向連結串列,這樣就可以逆序從尾部取元素。sorted set最經常的使用方式應該是作為索引來使用.我們可以把要排序的欄位作為score儲存,物件的id當元素儲存。下面是sorted set相關命令1、zadd 向名稱為key的zset中新增元素member,score用於排序。如果該元素已經存在,則根據score更新該元素的順序 redis 127.0.0.1:6379> zadd myzset 1 “one” (integer) 1 redis 127.0.0.1:6379> zadd myzset 2 “two” (integer) 1 redis 127.0.0.1:6379> zadd myzset 3 “two” (integer) 0 redis 127.0.0.1:6379> zrange myzset 0 -1 withscores 1) “one” 2) “1” 3) “two” 4) “3” redis 127.0.0.1:6379> 本例中我們向myzset中添加了one和two,並且two被設定了2次,那麼將以最後一次的設定為準,最後我們將所有元素都顯示出來並顯示出了元素的score。2、zrem 刪除名稱為key的zset中的元素member redis 127.0.0.1:6379> zrange myzset 0 -1 withscores 1) “one” 2) “1” 3) “two” 4) “3” redis 127.0.0.1:6379> zrem myzset two (integer) 1 redis 127.0.0.1:6379> zrange myzset 0 -1 withscores 1) “one” 2) “1” redis 127.0.0.1:6379> 可以看到two被刪除了3、zincrby 如果在名稱為key的zset中已經存在元素member,則該元素的score增加increment;否則向集合中新增該元素,其score的值為increment redis 127.0.0.1:6379> zadd myzset2 1 “one” (integer) 1 redis 127.0.0.1:6379> zadd myzset2 2 “two” (integer) 1 redis 127.0.0.1:6379> zincrby myzset2 2 “one” “3” redis 127.0.0.1:6379> zrange myzset2 0 -1 withscores 1) “two” 2) “2” 3) “one” 4) “3” redis 127.0.0.1:6379> 本例中將one的score從1增加了2,增加到了34、zrank 返回名稱為key的zset中member元素的排名(按score從小到大排序)即下標 redis 127.0.0.1:6379> zrange myzset3 0 -1 withscores 1) “one” 2) “1” 3) “two” 4) “2” 5) “three” 6) “3” 7) “five” 8) “5” redis 127.0.0.1:6379> zrank myzset3 two (integer) 1 redis 127.0.0.1:6379> 本例中將two的下標是1,我這裡取的是下標,而不是score5、zrevrank 返回名稱為key的zset中member元素的排名(按score從大到小排序)即下標 redis 127.0.0.1:6379> zrange myzset3 0 -1 withscores 1) “one” 2) “1” 3) “two” 4) “2” 5) “three” 6) “3” 7) “five” 8) “5” redis 127.0.0.1:6379> zrank myzset3 two (integer) 1 redis 127.0.0.1:6379> 按從大到小排序的話two是第三個元素,下標是26、zrevrange 返回名稱為key的zset(按score從大到小排序)中的index從start到end的所有元素 redis 127.0.0.1:6379> zrevrange myzset3 0 -1 withscores 1) “five” 2) “5” 3) “three” 4) “3” 5) “two” 6) “2” 7) “one” 8) “1” redis 127.0.0.1:6379> 首先按score從大到小排序,再取出全部元素7、zrangebyscore 返回集合中score在給定區間的元素 redis 127.0.0.1:6379> zrange myzset3 0 -1 withscores 1) “one” 2) “1” 3) “two” 4) “2” 5) “three” 6) “3” 7) “five” 8) “5” redis 127.0.0.1:6379> zrangebyscore myzset3 2 3 withscores 1) “two” 2) “2” 3) “three” 4) “3” redis 127.0.0.1:6379> 本例中,返回了score在2~3區間的元素8、zcount 返回集合中score在給定區間的數量 redis 127.0.0.1:6379> zrange myzset3 0 -1 withscores 1) “one” 2) “1” 3) “two” 4) “2” 5) “three” 6) “3” 7) “five” 8) “5” redis 127.0.0.1:6379> zcount myzset3 2 3 (integer) 2 redis 127.0.0.1:6379> 本例中,計算了score在2~3之間的元素數目9、zcard 返回集合中元素個數 redis 127.0.0.1:6379> zrange myzset3 0 -1 withscores 1) “one” 2) “1” 3) “two” 4) “2” 5) “three” 6) “3” 7) “five” 8) “5” redis 127.0.0.1:6379> zcard myzset3 (integer) 4 redis 127.0.0.1:6379> 從本例看出myzset3這個集全的元素數量是410、zscore 返回給定元素對應的score redis 127.0.0.1:6379> zrange myzset3 0 -1 withscores 1) “one” 2) “1” 3) “two” 4) “2” 5) “three” 6) “3” 7) “five” 8) “5” redis 127.0.0.1:6379> zscore myzset3 two “2” redis 127.0.0.1:6379> 此例中我們成功的將two的score取出來了。11、zremrangebyrank 刪除集合中排名在給定區間的元素 redis 127.0.0.1:6379> zrange myzset3 0 -1 withscores 1) “one” 2) “1” 3) “two” 4) “2” 5) “three” 6) “3” 7) “five” 8) “5” redis 127.0.0.1:6379> zremrangebyrank myzset3 3 3 (integer) 1 redis 127.0.0.1:6379> zrange myzset3 0 -1 withscores 1) “one” 2) “1” 3) “two” 4) “2” 5) “three” 6) “3” redis 127.0.0.1:6379> 在本例中我們將myzset3中按從小到大排序結果的下標為3的元素刪除了。12、zremrangebyscore 刪除集合中score在給定區間的元素 redis 127.0.0.1:6379> zrange myzset3 0 -1 withscores 1) “one” 2) “1” 3) “two” 4) “2” 5) “three” 6) “3” redis 127.0.0.1:6379> zremrangebyscore myzset3 1 2 (integer) 2 redis 127.0.0.1:6379> zrange myzset3 0 -1 withscores 1) “three” 2) “3” redis 127.0.0.1:6379> 在本例中我們將myzset3中按從小到大排序結果的score在1~2之間的元素刪除了。