Redis
不是一個簡單鍵值對存儲器,而是一個數據結構服務,它支持不一樣類型的值。這意味着傳統的鍵值對存儲器將字符串鍵和字符串值關聯起來,在Redis
中,值的類型不只僅侷限於字符串,還能夠是更加複雜的數據結構,下面是Redis
支持的數據結構,將會在各個章節接觸到:redis
map
,鍵和值都是字符串。對於Ruby
和Python
很是的友好。知道這些數據類型和怎樣使用對於解決命令索引給出的問題並不老是微不足道的。知道這些數據類型和怎樣使用對於解決命令索引給出的問題是很重要的,因此這個文檔將做爲了解Redis
數據類型和他們基本模式的一個入門課程。數據庫
對於全部的案例咱們將使用redis-cli
工具,這是一個很簡單可是很便利的命令行工具,用來和Redis
服務端作交互。數組
Redis
的key
Redis
的key
是比特安全的,這意味着你可使用任何的二進制序列做爲key
,從像foo
的字符串到一個JPEG
文件的內容。甚至空字符串也是能夠的。緩存
關於key
有一些其餘的規則:安全
key
是不推薦的。一個1024 bytes
是一個很是壞的注意,不只僅是由於內存浪費,更是由於在數據集中搜索對比的時候須要耗費更多的成本。當要處理的是匹配一個很是大的值,從內存和帶寬的角度來看,使用這個值的hash
值是更好的辦法(好比使用SHA1
)。key
一般也是不推薦的。在寫像u100flw
這樣的鍵的時候,有一個小小的要點,咱們能夠用user:1000:followers
代替。可讀性更好,對於key
對象和value
對象增長的空間佔用與此相比來講卻是次要的。當短的key
能夠很明顯減小空間佔用的時候,你的工做就是找到正確的平衡object-type:id
是一個好主意,-
和.
一般用於多個字符的域,就像comment:1234:reply.to
,或者comment:1234:reply-to
。key
容許512MB
Redis
字符串Redi
字符串類型是Redis
的key
能夠關聯的的最簡單的數據類型。這是Mmcached
惟一的數據類型,因此對於Redis
的使用新手來講,這是很是天然的。網絡
由於Redis
的key
是字符串,當咱們使用字符串類型做爲值的時候,咱們是將一個字符串映射到另外一個字符串。字符串類型在不少場景中是很是有用的,好比緩存HTML
片斷或者頁面。數據結構
接下來使用redis-cli
使用一下字符串類型(在這個文章中全部的示例都經過使用redis-cli
):app
> set mykey somevalue OK > get mykey "somevalue"
正如你看到的,使用SET
和GET
命令能夠設置和獲取一個字符串值。值得注意的是SET
將會覆蓋key
已經存在的值,即便這個key
關聯了一個不是字符串的值。因此SET
表現爲一個任務。ide
值能夠是任意類型的字符串(包括二進制數據),好比你能夠存儲jpeg
圖片。一個值不能超過512MB
。工具
SET
命令有一些有趣的選項,做爲而外的參數。好比。我可讓SET
在key
已經存在的時候失敗,或者相反,只有在key
存在的時候才成功:
> set mykey newval nx (nil) > set mykey newval xx OK
即便字符串是Redis
最基本的值,依舊有不少有趣的操做可使用。好比,原子增加:
> set counter 100 OK > incr counter (integer) 101 > incr counter (integer) 102 > incrby counter 50 (integer) 152
INCR
命令將字符串轉化爲integer
,自增1,而後保存成新的值,還有其餘相似的命令,好比INCRBY
、DECR
、DECRBY
。在內部他們實際上是同樣的命令,只是執行的時候有一點小差異而已。
INCR
是原子的意味着什麼?這意味着即便多個客戶端發送INCR
獲取同一個key,將不會進入競爭狀態,例如,客戶端1獲取到10
,同時客戶端2也獲取到10
是不可能的,所有獲取的都是11
,而且將11
保存成新的值。最懂的值將會是12
,讀取-自增-設置
三個操做將在其餘客戶端還沒執行命令的時候同時完成。
有不少的命令能夠操做字符串。好比GETSET
命令給一個key
設置一個新的個值,同時返回舊的值做爲結果。好比,你的系統在你的網站有一個新的訪客到來的時候,使用INCR
自增一個Redis
的key
,你可使用這個命令。你可能須要每小時收集全部的信息,甚至不錯過每一次增加,你能夠GETSET
一個key
,將它的值設置爲0的同時獲取新的值。
使用一個命令同時設置或者獲取多個key
的能力是下降延遲的好方法。MSET
和MGET
命令能夠作到:
> mset a 10 b 20 c 30 OK > mget a b c 1) "10" 2) "20" 3) "30"
當MGET
使用的時候,Redis
將會返回一個值。
key
空間有一些命令在部分類型中並無定義,可是和key
空間交互的時候是很是有用的,因此,可使用在任意類型的key
之上。
好比,當DEL
命令刪了吃了一個key和他所關聯的值的時候,EXISTS
命令返回1
或者0
去標記一個key
是否存在在數據庫,無論這個key
關聯的值是什麼類型。
> set mykey hello OK > exists mykey (integer) 1 > del mykey (integer) 1 > exists mykey (integer) 0
從例子中能夠看出,DEL
命令返回1
或者0
取決與key
是否被移除了(存在,或者沒有這個名字的key
)。
From the examples you can also see how DEL itself returns 1 or 0 depending on whether the key was removed (it existed) or not (there was no such key with that name).
有很過key
空間相關的命令,上面的兩個命令和TYPE
命令是最主要的,TYPE
命令返回的是存儲在這個key
中的類型。
> set mykey x OK > type mykey string > del mykey (integer) 1 > type mykey none
Redis
期限:key
的生存時間在繼續瞭解更多複雜的數據類型以前,咱們須要先討論另外一個無視值類型的特性,咱們稱之爲Redis
生存時間。簡單來講你能夠爲一個key
設置一個過時時間,這個就是key
能夠存在的時間。當能夠存在的時間過了,這個key
就會自動銷燬,就像用戶使用DEL
命令刪除了這個key。
關於Redis
期限的一些簡單信息:
Redis
服務端中止的時候,時間也會過去(這意味着Redis
將會保存一個key
的過時日期)。設置一個過時時間是很簡單的
> set key some-value OK > expire key 5 (integer) 1 > get key (immediately) "some-value" > get key (after some time) (nil)
在兩次相隔5s的GET
調用中,key
徹底消失了。在上面的例子中,咱們用EXPIRE
去設置過時時間(固然也能夠用來給一個已經存在過時時間的key
設置一個不一樣的過時時間,好比PERSIST
能夠用來移除過時時間,使這個key
永久持久化)。固然咱們也可使用其餘Redis
命令建立一個有過時時間的key
。好比,使用SET
命令的選項:
> set key 100 ex 10 OK > ttl key (integer) 9
的絎棉這個栗子設置了一個值爲100,過時時間爲10秒的key
,接下來的TTL
命令用來檢查這個key
剩下的生存時間。
爲了用毫秒設置和檢查生存時間,可使用PEXPIRE
和PTTL
命令,和完整的SET
命令選項。
Redis List
爲了解釋List
這種數據類型,最好先來點理論知識做爲開胃菜,其實術語List
在信息技術領域的使用是常常是不恰單的。好比Python Lists
並不像名字所體現的(Linked Lists
),更像Arrays
(實際上相同的數據類型在Ruby
中稱爲Array
)。
從通常的觀點看,一個List
只是一個由一系列有序元素組成的列表:10,20,1,2,3
。可是使用Array
實現的List
和用Linked List
實現的List
在特性上有很大的不一樣。Redis List
是經過Linked List
實現的。這意味着即便你有百萬個元素在列表內,添加一個元素的操做到頭部或者尾部的操做的時間是一個常量。使用LPUSH
命令添加一個新元素到一個有10個元素的列表的頭部所耗費的時間是和添加一個元素到一個有10000000萬元素的列表的頭部是同樣的。
不利的一面是什麼呢?使用Arrays
實現的List
經過索引訪問一個元素是很是迅速的(常量時間),然而用Linked List
實現的則不會這麼快(這個操做須要的時間是和要訪問的索引成正比的)。
Redis List
使用Linked List
實現是由於對於數據庫系統來講,它須要可以經過很是快的方式添加元素到一個很是長的列表。接下來你將看到一個很是強的優點,那就是Redis Lists
能夠採起常量長度在常量時間內。
當須要很是快的訪問一個巨大聚合元素的其中一個數據時候,有另外一種數據結構可供選擇,那就是Sorted Sets
,Sorted Set
將在下面的章節涉及。
Redis Lists
使用第一步LPUSH
命令添加一個新的元素到一個列表的左邊(頭部),RPUSH
命令添加一個新的元素到一個列表的右邊(尾部)。LRAGE
命令從列表中提取元素。
> rpush mylist A (integer) 1 > rpush mylist B (integer) 2 > lpush mylist first (integer) 3 > lrange mylist 0 -1 1) "first" 2) "A" 3) "B"
注意:LRAGE
須要兩個索引,要返回的第一個元素的索引和最後一個元素的索引。兩個因此均可以被導航,告訴Redis
從開始統計到結束:因此,-1是列表的最後一個元素,-2是列表的倒數第一個元素,以此類推。
就像你看到的RPISH
添加元素到列表的右邊,LPUSH
添加元素到列表的左邊。
As you can see RPUSH appended the elements on the right of the list, while the final LPUSH appended the element on the left.
兩個命令都是可變參數長度的命令,,這意味着你能夠在一次執行中自由的推入多個元素到一個列表:
> rpush mylist 1 2 3 4 5 "foo bar" (integer) 9 > lrange mylist 0 -1 1) "first" 2) "A" 3) "B" 4) "1" 5) "2" 6) "3" 7) "4" 8) "5" 9) "foo bar"
定義在Redis Lists
中的一個重要操做是pop
的能力。彈出元素是從列表獲取元素而且淘汰元素的操做。你能夠從左邊或則右邊彈出元素,就像你能夠從列表兩邊推入元素同樣:
> rpush mylist a b c (integer) 3 > rpop mylist "c" > rpop mylist "b" > rpop mylist "a"
咱們推入了三個元素而且彈出了三個元素,因此執行完這一系列命令,這個列表最終變成空的,而且將再也不有數據彈出。若是咱們依舊嘗試彈出其餘元素,咱們將會獲得以下結果:
> rpop mylist (nil) Redis returned a NULL value to signal that there are no elements in the list.
Redis Lists
的應用場景Lists
對一系列任務都頗有幫助,下面是兩個典型應用場景:
Redis
有特殊的列表命令去保證這種用戶場景更加可靠和有效。好比,Ruby
的庫resque
和sidekiq
在底層使用Redis Lists
去實現後臺任務。
流行的社交網絡Twitter
將最新的Twitter
用戶文章推入Redis Lists
。
爲了一步一步歸納一個普通的用戶場景,想一想你的主頁顯示了發佈在一個照片分享社交網絡的最新的照片,你想要很快的訪問。
每次一個用戶發佈一張新的照片,咱們使用LPUSH
將照片的ID放入一個列表。當用戶訪問主頁的時候,咱們使用LRANGE 0 9
去獲取最新的10張照片。
List
在不少應用場景下,咱們只是想使用列表去存儲最新的項目,好比:社交網絡更新,日誌,諸如此類。Redis
容許咱們像使用有限集合同樣使用列表,只記住最新的N條數據並使用LTRIM
拋棄掉最舊的數據。
LTRIM
和LRANGE
很像,可是它不是現實指定的元素範圍,而是設置指定的方位爲新的列表值。全部不在這個範圍以內的元素將被移除:
An example will make it more clear:
> rpush mylist 1 2 3 4 5 (integer) 5 > ltrim mylist 0 2 OK > lrange mylist 0 -1 1) "1" 2) "2" 3) "3"
TRIM
命令告訴Redis
只獲取列中中索引0到2的元素,其餘不在這個範圍內的元素所有拋棄。這讓一個簡單可是有用的模式獲得實現:向列表推入數據操做+修剪操做一塊兒,實現了添加一個新元素並拋棄超出範圍的元素:
LPUSH mylist <some element> LTRIM mylist 0 999
上面的命令結合起來實現了添加一個新的元素到列表並獲取列表前1000條最新的元素。LRANGE
命令讓你能夠獲取到定模的元素而且不準要記住每個舊的數據
注意:儘管LRANGE
命令技術上是一個O(N)
的命令,獲取列表頭部或者尾部很小範圍的的數據依舊是一個常量時間操做。
List
會阻塞的操做列表有一個很特別的特性讓它能夠很適合用來實現隊列,通常做爲內部進程通訊系統的構建塊:阻塞操做。
想一想你的一個進程想要將一個元素推入列表,另外一個進程想要對這些元素進行某些操做。這就是一般說的生產者/消費者模式,能夠用下面的方式簡單的實現:
LPUSH
向列表推入數據。RPOP
從列表消費數據。然而,有時列表有可能時空的,沒有什麼好執行的,因此RPOP
將會返回NULL
,這種狀況下,消費者強制等待一些時間而後從新
Redis
和客戶端去執行無效的命令(當列表是空的的時候,針對全部的請求其實沒有作任何的工做,只是簡單的返回NULL
)。NULL
後會等待一些時間。讓延遲更小,咱們在能夠在兩個執行RPOP
命令之間等待更少的時間,可是會引發問題1,也就是更多的無效請求。因此Redis
實現了BRPOP
和BLPOP
命令,這個命苦可讓RPOP
和LPOP
能夠在列表爲空的時候堵塞:他們將只在一個新的元素添加進列表的時候執行,或者當用戶指定的超時時間到了。
這是一個關於咱們可使用的BRPOP
命令示例:
> brpop tasks 5 1) "tasks" 2) "do_something"
這意味着:等待列表中的元素,可是若是5s以後沒有元素就返回。
值得注意的是,你能夠設置超時時間爲0,從而讓線程永遠等待,固然你也能夠指定多個列表,而不是一個,同一時間等待多個列表,將會收到第一個收到新元素列表的通知。
一些關於BRPOP
的筆記:
RPOP
不同:是一個包含兩個元素的數組,他包含了key
的名字,由於BRPOP
和BLPOP
能夠作到堵塞等待多個列表的元素。NULL
。關於列表和堵塞操做還有更多的星系你須要知道。咱們推薦你能夠閱讀下面的更多內容:
RPOPLPUSH
命令能夠構建一個更安全的隊列或者旋轉隊列。BRPOPLPUSH
命令是RPOPLPUSH
命令堵塞的變形。