Redis數據類型簡介(十分鐘快速學習Redis)

  1. 如何在ubuntu18.04上安裝和保護redis
  2. 如何鏈接到Redis數據庫
  3. 如何管理Redis數據庫和Keys
  4. 如何在Redis中管理副本和客戶端
  5. 如何在Redis中管理字符串
  6. 如何在Redis中管理list
  7. 如何在Redis中管理Hashes
  8. 如何在Redis中管理Sets
  9. 如何在Redis中管理Sorted Sets
  10. 如何在Redis中運行事務
  11. 如何使Redis中的Key失效
  12. 如何解決Redis中的問題
  13. 如何從命令行更改Redis的配置
  14. Redis數據類型簡介

Redis數據類型簡介

Redis不是_簡單的_鍵值存儲,它其實是一個_數據結構服務器_,支持不一樣類型的值。這意味着在傳統鍵值存儲中,您將字符串鍵與字符串值相關聯,而在Redis中,該值不只限於簡單的字符串,還能夠容納更復雜的數據結構。如下是Redis支持的全部數據結構的列表,本教程將分別進行介紹:git

  • 二進制安全字符串。
  • 列表:根據插入順序排序的字符串元素的集合。它們基本上是_鏈表_。
  • 集:惟一,未排序的字符串元素的集合。
  • 相似於Sets的排序集合,但每一個字符串元素都與一個稱爲_score_的浮點值相關聯。元素老是按它們的分數排序,所以與Sets不一樣,能夠檢索一系列元素(例如,您可能會問:給我前10名或後10名)。
  • 哈希,是由與值關聯的字段組成的映射。字段和值都是字符串。這與Ruby或Python哈希很是類似。
  • 位數組(或簡稱爲位圖):可使用特殊命令像位數組同樣處理字符串值:您能夠設置和清除單個位,計數全部設置爲1的位,找到第一個設置或未設置的位,等等。
  • HyperLogLogs:這是一個機率數據結構,用於估計集合的基數。別懼怕,它比看起來更簡單...請參閱本教程的HyperLogLog部分。
  • 流:提供抽象日誌數據類型的類地圖項的僅追加集合。在「 Redis流簡介」中對它們進行了深刻 介紹

命令參考中掌握這些數據類型的工做方式以及使用什麼來解決給定問題並不老是那麼容易,所以,本文檔是有關Redis數據類型及其最多見模式的速成課程。github

對於全部示例,咱們將使用該redis-cli實用程序(一個簡單但方便的命令行實用程序)對Redis服務器發出命令。redis

*Redis keys

Redis密鑰是二進制安全的,這意味着您可使用任何二進制序列做爲密鑰,從「 foo」之類的字符串到JPEG文件的內容。空字符串也是有效的鍵。算法

有關密鑰的其餘一些規則:數據庫

  • 太長的鍵不是一個好主意。例如,1024字節的密鑰不只是內存方面的問題,也是一個壞主意,並且由於在數據集中查找密鑰可能須要進行一些代價高昂的密鑰比較。即便手頭的任務是匹配一個大值的存在,對它進行散列(例如使用SHA1)也是一個更好的主意,尤爲是從內存和帶寬的角度來看。
  • 很是短的鍵一般不是一個好主意。若是您能夠改寫「 user:1000:followers」,那麼將「 u1000flw」寫爲密鑰毫無心義。與鍵對象自己和值對象使用的空間相比,後者更具可讀性,而且添加的空間較小。雖然短鍵顯然會消耗更少的內存,但您的工做是找到合適的平衡。
  • 嘗試堅持使用架構。例如,「 object-type:id」是一個好主意,例如「 user:1000」。點或破折號一般用於多字字段,例如「 comment:1234:reply.to」或「 comment:1234:reply-to」中。
  • 容許的最大密鑰大小爲512 MB。

*Redis Strings

Redis字符串類型是您能夠與Redis鍵關聯的最簡單的值類型。它是Memcached中惟一的數據類型,所以對於新手來講,在Redis中使用它也是很天然的。ubuntu

因爲Redis鍵是字符串,所以當咱們也使用字符串類型做爲值時,咱們會將一個字符串映射到另外一個字符串。字符串數據類型對於許多用例頗有用,例如緩存HTML片斷或頁面。數組

讓咱們使用來處理字符串類型redis-cli(全部示例將redis-cli在本教程中經過來執行)。緩存

> set mykey somevalue
OK
> get mykey
"somevalue"

如您所見,使用SETGET命令是咱們設置和檢索字符串值的方式。請注意,即便鍵已與非字符串值相關聯,SET仍將替換已存儲在鍵中的任何現有值。所以SET執行分配。安全

值能夠是每種類型的字符串(包括二進制數據),例如,您能夠在值內存儲jpeg圖像。值不能大於512 MB。服務器

SET命令有有趣的選項,這是做爲附加參數。例如,若是密鑰已經存在,我可能會要求SET失敗,或者相反,只有密鑰已經存在時,它纔會成功:

> 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命令由一個解析字符串值做爲一個整數,它的增量,並最終將得到的值做爲新的值。還有其餘相似的命令,例如INCRBYDECRDECRBY。在內部,它始終是相同的命令,其執行方式略有不一樣。

INCR是原子的意味着什麼?即便使用相同密鑰發出INCR的多個客戶也永遠不會進入競爭狀態。例如,客戶端1不會同時讀取「 10」,客戶端2會同時讀取「 10」,都遞增爲11,並將新值設置爲11。最終值將始終爲12,而在全部其餘客戶端未同時執行命令時執行增量設置操做。

有許多用於操做字符串的命令。例如,GETSET命令將鍵設置爲新值,並返回舊值做爲結果。例如,若是您的系統在 每次網站接收新訪客時使用INCR遞增Redis密鑰,則可使用此命令。您可能但願每小時收集一次此信息,而又不會丟失任何增量。您能夠GETSET鍵,爲其分配新值「 0」,而後回讀舊值。

在單個命令中設置或檢索多個鍵的值的功能對於減小延遲也頗有用。所以,有MSETMGET命令:

> mset a 10 b 20 c 30
OK
> mget a b c
1) "10"
2) "20"
3) "30"

使用MGET時,Redis返回一個值數組。

*Altering and querying the key space

有些命令未在特定類型上定義,可是在與鍵的空間進行交互時頗有用,所以能夠與任何類型的鍵一塊兒使用。

例如,EXISTS命令返回1或0表示數據庫中是否存在給定的鍵,而DEL命令則刪除鍵和關聯的值(不管該值是什麼)。

> set mykey hello
OK
> exists mykey
(integer) 1
> del mykey
(integer) 1
> exists mykey
(integer) 0

從示例中,您還能夠看到DEL自己如何返回1或0,具體取決於密鑰是否已刪除(存在)(不存在具備該名稱的此類密鑰)。

有許多與密鑰空間相關的命令,可是以上兩個命令與TYPE命令一塊兒是必不可少的,TYPE命令返回存儲在指定密鑰處的值的類型:

> set mykey x
OK
> type mykey
string
> del mykey
(integer) 1
> type mykey
none

*Redis expires: keys with limited time to live

在繼續使用更復雜的數據結構以前,咱們須要討論另外一個功能,該功能無論值類型如何均可以工做,而且稱爲Redis expires。基本上,您能夠爲密鑰設置一個超時時間,這是有限的生存時間。生存時間過去後,該密鑰將自動銷燬,就像用戶使用該密鑰調用DEL命令同樣。

有關Redis的一些快速信息將過時:

  • 可使用秒或毫秒精度進行設置。
  • 可是,到期時間分辨率始終爲1毫秒。
  • 有關過時的信息被複制並保留在磁盤上,實際上Redis服務器保持中止狀態的時間已通過去(這意味着Redis保存了密鑰過時的日期)。

設置過時時間很簡單:

> set key some-value
OK
> expire key 5
(integer) 1
> get key (immediately)
"some-value"
> get key (after some time)
(nil)

因爲第二次呼叫延遲了5秒鐘以上,所以在兩次GET呼叫之間密鑰消失了。在上面的示例中,咱們使用EXPIRE來設置過時時間(也可使用它來爲已經具備密鑰的密鑰設置不一樣的過時時間,例如可使用PERSIST來刪除過時並使密鑰永久持久化)。可是,咱們也可使用其餘Redis命令來建立具備過時密鑰。例如,使用SET選項:

> set key 100 ex 10
OK
> ttl key
(integer) 9

上面的示例使用一個字符串值設置一個密鑰,該密鑰100的到期時間爲十秒鐘。稍後調用TTL命令以檢查密鑰的剩餘生存時間。

爲了設置和檢查以毫秒爲單位到期,檢查PEXPIRE熱釋光的命令,以及完整列表SET選項。

*Redis Lists

爲了解釋List數據類型,最好從理論上入手,由於_List_一詞常常被信息技術人員以不正當的方式使用。例如,「 Python列表」並非名稱(連接列表)所建議的,而是數組(在Ruby中,相同的數據類型實際上稱爲數組)。

從很是廣泛的角度來看,列表只是一系列有序元素:10,20,1,2,3是一個列表。可是,使用Array實現的List的屬性與使用_Linked List_實現的List的屬性很是不一樣 。

Redis列表是經過連接列表實現的。這意味着即便您在列表中有數百萬個元素,在列表的開頭或結尾添加新元素的操做也會_在固定時間內_執行。使用LPUSH命令將新元素添加到具備10個元素的列表的開頭的速度與將元素添加到具備1000萬個元素的列表的開頭的速度相同。

缺點是什麼?在使用Array實現的列表中,_按索引_訪問元素_的_速度很是快(恆定時間索引訪問),而在經過連接列表實現的列表中訪問速度不是那麼快(其中操做須要的工做量與所訪問元素的索引成比例)。

Redis列表是經過連接列表實現的,由於對於數據庫系統而言,相當重要的是可以以很是快的方式將元素添加到很長的列表中。稍後您將看到,另外一個強大的優點是Redis列表能夠在恆定的時間內以恆定的長度獲取。

當快速訪問大量元素的中間位置很重要時,可使用另外一種稱爲排序集的數據結構。排序的集將在本教程的後面部分介紹。

*First steps with Redis Lists

所述LPUSH命令將一個新元素到一個列表,在左側(在頭部),而RPUSH命令將一個新元素到一個列表,在右側(在尾部)。最後, LRANGE命令從列表中提取元素範圍:

> 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"

請注意,LRANGE須要兩個索引,要返回的範圍的第一個和最後一個元素。兩個索引均可覺得負,告訴Redis從末尾開始計數:所以-1是列表的最後一個元素,-2是列表的倒數第二個元素,依此類推。

如您所見,RPUSH在列表的右側附加了元素,而最後的LPUSH在列表的左側附加了元素。

這兩個命令都是_可變參數命令_,這意味着您能夠在單個調用中隨意將多個元素推入列表中:

> 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列表上定義的一項重要操做是_彈出元素_的能力。彈出元素是同時從列表中檢索元素並將其從列表中刪除的操做。您能夠從左側和右側彈出元素,相似於在列表兩邊推送元素的方式:

> rpush mylist a b c
(integer) 3
> rpop mylist
"c"
> rpop mylist
"b"
> rpop mylist
"a"

咱們添加了三個元素並彈出了三個元素,所以在此命令序列的末尾,列表爲空,沒有其餘要彈出的元素。若是咱們嘗試彈出另外一個元素,則會獲得如下結果:

> rpop mylist
(nil)

Redis返回NULL值,以指示列表中沒有元素。

*Common use cases for lists

列表對於許多任務頗有用,如下是兩個很是有表明性的用例:

  • 記住用戶發佈到社交網絡上的最新更新。
  • 使用生產者將項目推送到列表中的消費者與生產者模式進行流程之間的通訊,而消費者(一般是_worker_)消耗這些項目和已執行的動做。Redis具備特殊的列表命令,以使此用例更加可靠和高效。

例如,流行的Ruby庫resquesidekiq都在後臺使用Redis列表,以實現後臺做業。

流行的Twitter社交網絡 用戶發佈的最新推文放入Redis列表中。

爲了逐步描述一個常見的用例,假設您的主頁顯示了在照片共享社交網絡中發佈的最新照片,而且您想加快訪問速度。

  • 每次用戶發佈新照片時,咱們都會使用LPUSH將其ID添加到列表中。
  • 當用戶訪問主頁時,咱們LRANGE 0 9爲了獲取最新發布的10個項目。

*Capped lists

在許多用例中,咱們只想使用列表來存儲_最新項目_,不管它們是什麼:社交網絡更新,日誌或其餘任何內容。

Redis容許咱們使用列表做爲上限集合,僅使用LTRIM命令記住最新的N個項目並丟棄全部最舊的項目。

LTRIM命令相似於LRANGE,可是,而不是顯示元件的規定的範圍內將其設置在該範圍做爲新的列表值。給定範圍以外的全部元素都將被刪除。

一個例子將使其更加清楚:

> rpush mylist 1 2 3 4 5
(integer) 5
> ltrim mylist 0 2
OK
> lrange mylist 0 -1
1) "1"
2) "2"
3) "3"

上面的LTRIM命令告訴Redis僅從索引0到2列出列表元素,其餘全部內容都將被丟棄。這容許一個很是簡單但有用的模式:一塊兒執行List推操做+ List修剪操做,以便添加新元素並丟棄超出限制的元素:

LPUSH mylist <some element>
LTRIM mylist 0 999

上面的組合添加了一個新元素,而且僅將1000個最新元素歸入列表。使用LRANGE,您能夠訪問最重要的項目,而無需記住很是舊的數據。

注意:雖然LRANGE從技術上講是O(N)命令,但朝列表的開頭或結尾訪問較小範圍是恆定時間操做。

*Blocking operations on lists

列表具備一項特殊功能,使其適合於實現隊列,而且一般用做進程間通訊系統的構建塊:阻止操做。

想象一下,您想經過一個流程將項目推入列表,而後使用不一樣的流程來對這些項目進行某種工做。這是一般的生產者/使用者設置,能夠經過如下簡單方式實現:

  • 爲了將項目推送到列表中,生產者調用LPUSH
  • 爲了從列表中提取/處理項目,消費者調用RPOP

可是,有時列表可能爲空,沒有任何要處理的內容,所以RPOP僅返回NULL。在這種狀況下,消費者被迫等待一段時間,而後使用RPOP重試。這稱爲_輪詢_,在這種狀況下不是一個好主意,由於它有幾個缺點:

  1. 強制Redis和客戶端處理無用的命令(列表爲空時的全部請求將沒法完成任何實際工做,它們只會返回NULL)。
  2. 因爲工做人員在收到NULL以後會等待一段時間,所以會增長項目處理的延遲。爲了使延遲更小,咱們能夠在兩次調用RPOP之間等待的時間更少,從而擴大了問題編號1,即對Redis的調用更加無用。

因此,所謂的Redis命令工具BRPOPBLPOP它們的版本RPOPLPOP可以阻止若是列表是空的:他們將回到只有當新的元素添加到列表中的來電者,或在用戶指定的超時到達。

這是咱們能夠在worker中使用的BRPOP調用的示例:

> brpop tasks 5
1) "tasks"
2) "do_something"

這意味着:「等待列表中的元素tasks,但若是5秒鐘後沒有可用元素,則返回」。

請注意,您能夠將0用做超時來永遠等待元素,還能夠指定多個列表,而不只僅是一個列表,以便同時等待多個列表,並在第一個列表收到一個元素時獲得通知。

有關BRPOP的幾點注意事項

  1. 客戶端以有序方式提供服務:第一個阻塞等待列表的客戶端,在某個元素被其餘客戶端推送時首先提供服務,依此類推。
  2. 返回值與RPOP相比有所不一樣:它是一個包含兩個元素的數組,由於它還包含鍵的名稱,由於BRPOPBLPOP可以阻止等待來自多個列表的元素。
  3. 若是達到超時,則返回NULL。

關於列表和阻止操做,您應該瞭解更多信息。咱們建議您閱讀如下內容:

  • 使用RPOPLPUSH能夠構建更安全的隊列或輪換隊列。
  • 該命令還有一個阻塞變體,稱爲BRPOPLPUSH

*Automatic creation and removal of keys

到目前爲止,在咱們的示例中,咱們無需在推入元素以前建立空列表,也無需在內部再也不包含元素時刪除空列表。Redis的責任是在列表爲空時刪除鍵,或者在鍵不存在而且咱們試圖向其添加元素(例如,使用LPUSH)時建立一個空列表。

這不是特定於列表的,它適用於由多個元素組成的全部Redis數據類型-流,集合,排序集合和哈希。

基本上,咱們能夠用三個規則來總結行爲:

  1. 當咱們將元素添加到聚合數據類型時,若是目標鍵不存在,則在添加元素以前會建立一個空的聚合數據類型。
  2. 當咱們從聚合數據類型中刪除元素時,若是該值保持爲空,則鍵將自動銷燬。流數據類型是此規則的惟一例外。
  3. 調用帶有空鍵的只讀命令(例如LLEN(返回列表的長度))或寫命令刪除元素,總會產生與鍵保持空的聚合類型相同的結果。命令但願找到。

規則1的示例:

> del mylist
(integer) 1
> lpush mylist 1 2 3
(integer) 3

可是,若是密鑰存在,咱們將沒法對錯誤的類型執行操做:

> set foo bar
OK
> lpush foo 1 2 3
(error) WRONGTYPE Operation against a key holding the wrong kind of value
> type foo
string

規則2的示例:

> lpush mylist 1 2 3
(integer) 3
> exists mylist
(integer) 1
> lpop mylist
"3"
> lpop mylist
"2"
> lpop mylist
"1"
> exists mylist
(integer) 0

彈出全部元素後,鍵再也不存在。

規則3的示例:

> del mylist
(integer) 0
> llen mylist
(integer) 0
> lpop mylist
(nil)

*Redis Hashes

Redis散列與字段值對看起來徹底同樣,多是人們指望的「散列」外觀:

> hmset user:1000 username antirez birthyear 1977 verified 1
OK
> hget user:1000 username
"antirez"
> hget user:1000 birthyear
"1977"
> hgetall user:1000
1) "username"
2) "antirez"
3) "birthyear"
4) "1977"
5) "verified"
6) "1"

儘管哈希能夠方便地表示_對象_,可是實際上能夠放入哈希中的字段數沒有實際限制(可用內存除外),所以您能夠在應用程序內部以多種不一樣方式使用哈希。

HMSET命令設置哈希的多個字段,而HGET檢索單個字段。HMGET相似於HGET但返回值的數組:

> hmget user:1000 username birthyear no-such-field
1) "antirez"
2) "1977"
3) (nil)

有些命令也能夠對單個字段執行操做,例如HINCRBY

> hincrby user:1000 birthyear 10
(integer) 1987
> hincrby user:1000 birthyear 10
(integer) 1997

您能夠在文檔中找到哈希命令完整列表

值得注意的是,小哈希(即,一些具備較小值的元素)以特殊方式在內存中進行編碼,從而使它們具備很高的內存效率。

*Redis Sets

Redis集是字符串的無序集合。該 SADD命令添加新的元素,一組。還能夠對集合進行許多其餘操做,例如測試給定元素是否已存在,執行多個集合之間的交集,並集或求差等等。

> sadd myset 1 2 3
(integer) 3
> smembers myset
1. 3
2. 1
3. 2

在這裏,我在集合中添加了三個元素,並告訴Redis返回全部元素。如您所見,它們沒有排序-Redis能夠在每次調用時隨意以任何順序返回元素,由於與用戶之間沒有關於元素順序的約定。

Redis具備用於測試成員資格的命令。例如,檢查元素是否存在:

> sismember myset 3
(integer) 1
> sismember myset 30
(integer) 0

「 3」是集合的成員,而「 30」不是集合的成員。

集合很是適合表示對象之間的關係。例如,咱們能夠輕鬆地使用集合來實現標籤。

對這個問題進行建模的一種簡單方法是爲咱們要標記的每一個對象設置一個集合。該集合包含與對象關聯的標籤的ID。

一個例證是標記新聞文章。若是商品ID 1000帶有標籤一、二、5和77進行標記,則集合能夠將這些標籤ID與新聞項相關聯:

> sadd news:1000:tags 1 2 5 77
(integer) 4

咱們可能還須要逆關係:用給定標籤標記的全部新聞的列表:

> sadd tag:1:news 1000
(integer) 1
> sadd tag:2:news 1000
(integer) 1
> sadd tag:5:news 1000
(integer) 1
> sadd tag:77:news 1000
(integer) 1

要獲取給定對象的全部標籤很簡單:

> smembers news:1000:tags
1. 5
2. 1
3. 77
4. 2

注意:在示例中,咱們假設您具備另外一個數據結構,例如Redis哈希,它將標籤ID映射到標籤名稱。

還有其餘一些很是簡單的操做,使用正確的Redis命令仍然很容易實現。例如,咱們可能須要包含標籤一、二、10和27的全部對象的列表。咱們可使用SINTER命令執行此操做,該命令執行不一樣集合之間的交集。咱們能夠用:

> sinter tag:1:news tag:2:news tag:10:news tag:27:news
... results here ...

除了交集以外,您還能夠執行並集,求差,提取隨機元素等等。

提取元素的命令稱爲SPOP,對於建模某些問題很是方便。例如,爲了實現基於Web的撲克遊戲,您可能須要用一組來表明您的套牌。假設咱們對(C)lubs,(D)鑽石,(H)耳釘,(S)墊使用一個單字符前綴:

>  sadd deck C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 CJ CQ CK
   D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 DJ DQ DK H1 H2 H3
   H4 H5 H6 H7 H8 H9 H10 HJ HQ HK S1 S2 S3 S4 S5 S6
   S7 S8 S9 S10 SJ SQ SK
   (integer) 52

如今咱們要爲每一個玩家提供5張卡片。該SPOP命令刪除一個隨機元素,將其返回到客戶端,因此在這種狀況下完美運行。

可是,若是咱們直接在甲板上對其進行稱呼,那麼在遊戲的下一場比賽中,咱們將須要再次填充紙牌,這可能並不理想。所以,首先,咱們能夠將存儲在deck密鑰中的集合複製到game:1:deck密鑰中。

這可使用SUNIONSTORE來完成,SUNIONSTORE一般執行多個集合之間的聯合,並將結果存儲到另外一個集合中。可是,因爲單個集合的並集自己,我可使用如下命令複製個人卡組:

> sunionstore game:1:deck deck
(integer) 52

如今,我準備爲第一位玩家提供五張牌:

> spop game:1:deck
"C6"
> spop game:1:deck
"CQ"
> spop game:1:deck
"D1"
> spop game:1:deck
"CJ"
> spop game:1:deck
"SJ"

一副千斤頂,不是很好...

如今是引入set命令的好時機,該命令提供集合中元素的數量。 在集合理論的上下文中,這一般稱爲_集合_的_基數_,所以Redis命令稱爲SCARD

> scard game:1:deck
(integer) 47

數學原理:52-5 = 47。

當您只須要獲取隨機元素而不將其從集合中刪除時,可使用適合該任務的SRANDMEMBER命令。它還具備返回重複元素和非重複元素的功能。

*Redis Sorted sets

排序集是一種數據類型,相似於集合和哈希之間的混合。像集合同樣,排序集合由惟一的,非重複的字符串元素組成,所以從某種意義上說,排序集合也是一個集合。

可是,雖然集內的元素沒有排序,但排序後的集合中的每一個元素都與一個稱爲_得分_的浮點值相關聯 (這就是爲何該類型也相似於哈希的緣由,由於每一個元素都映射到一個值)。

此外,已排序集合中的元素是按_順序進行的_(所以,它們不是應請求而排序的,順序是用於表示已排序集合的數據結構的特殊性)。它們按照如下規則排序:

  • 若是A和B是兩個分數不一樣的元素,則若是A.score是> B.score,則A>B。
  • 若是A和B的分數徹底相同,那麼若是A字符串在字典上大於B字符串,則A>B。A和B字符串不能相等,由於排序集僅具備惟一元素。

讓咱們從一個簡單的示例開始,添加一些選定的黑客名稱做爲排序的集合元素,並以其出生年份爲「得分」。

> zadd hackers 1940 "Alan Kay"
(integer) 1
> zadd hackers 1957 "Sophie Wilson"
(integer) 1
> zadd hackers 1953 "Richard Stallman"
(integer) 1
> zadd hackers 1949 "Anita Borg"
(integer) 1
> zadd hackers 1965 "Yukihiro Matsumoto"
(integer) 1
> zadd hackers 1914 "Hedy Lamarr"
(integer) 1
> zadd hackers 1916 "Claude Shannon"
(integer) 1
> zadd hackers 1969 "Linus Torvalds"
(integer) 1
> zadd hackers 1912 "Alan Turing"
(integer) 1

如您所見,ZADDSADD類似,可是使用一個額外的參數(放置在要添加的元素以前)做爲得分。 ZADD也是可變參數,所以即便上面的示例中未使用它,您也能夠自由指定多個得分-值對。

使用排序集,返回按其出生年份排序的黑客列表很簡單,由於實際上_他們已經被排序了_。

實施說明:排序集是經過包含跳過列表和哈希表的雙端口數據結構實現的,所以,每次添加元素時,Redis都會執行O(log(N))操做。很好,可是當咱們要求排序元素時,Redis根本不須要作任何工做,它已經所有排序了:

> zrange hackers 0 -1
1) "Alan Turing"
2) "Hedy Lamarr"
3) "Claude Shannon"
4) "Alan Kay"
5) "Anita Borg"
6) "Richard Stallman"
7) "Sophie Wilson"
8) "Yukihiro Matsumoto"
9) "Linus Torvalds"

注意:0和-1表示從元素索引0到最後一個元素(-1的工做方式與LRANGE命令的狀況相同)。

若是我想按相反的順序訂購(最小到最大)怎麼辦?使用ZREVRANGE而不是ZRANGE

> zrevrange hackers 0 -1
1) "Linus Torvalds"
2) "Yukihiro Matsumoto"
3) "Sophie Wilson"
4) "Richard Stallman"
5) "Anita Borg"
6) "Alan Kay"
7) "Claude Shannon"
8) "Hedy Lamarr"
9) "Alan Turing"

也可使用如下WITHSCORES參數返回分數:

> zrange hackers 0 -1 withscores
1) "Alan Turing"
2) "1912"
3) "Hedy Lamarr"
4) "1914"
5) "Claude Shannon"
6) "1916"
7) "Alan Kay"
8) "1940"
9) "Anita Borg"
10) "1949"
11) "Richard Stallman"
12) "1953"
13) "Sophie Wilson"
14) "1957"
15) "Yukihiro Matsumoto"
16) "1965"
17) "Linus Torvalds"
18) "1969"

*Operating on ranges

排序集比這更強大。它們能夠在範圍內操做。讓咱們獲取全部在1950年(含)以前出生的人。咱們使用ZRANGEBYSCORE命令來作到這一點:

> zrangebyscore hackers -inf 1950
1) "Alan Turing"
2) "Hedy Lamarr"
3) "Claude Shannon"
4) "Alan Kay"
5) "Anita Borg"

咱們要求Redis返回分數在負無窮大和1950之間的全部元素(包括兩個極端)。

也能夠刪除元素範圍。讓咱們從排序集中刪除全部1940年至1960年之間出生的黑客:

> zremrangebyscore hackers 1940 1960
(integer) 4

ZREMRANGEBYSCORE可能不是最好的命令名稱,可是它可能很是有用,並返回已刪除元素的數量。

爲排序的集合元素定義的另外一個極其有用的操做是get-rank操做。能夠問一個元素在有序元素集合中的位置是什麼。

> zrank hackers "Anita Borg"
(integer) 4

ZREVRANK命令也能夠爲了得到軍銜,考慮的要素排序的降低方式。

*Lexicographical scores

在最新版本的Redis 2.8中,引入了一項新功能,該功能容許按字典順序獲取範圍,假設已排序集中的元素都以相同的相同分數插入(將元素與C memcmp函數進行比較 ,所以能夠確保沒有排序規則) ,而且每一個Redis實例將以相同的輸出進行回覆)。

用於按字典順序操做的主要命令是ZRANGEBYLEXZREVRANGEBYLEXZREMRANGEBYLEXZLEXCOUNT

例如,讓咱們再次添加咱們的著名黑客列表,可是此次對全部元素使用零分:

> zadd hackers 0 "Alan Kay" 0 "Sophie Wilson" 0 "Richard Stallman" 0
  "Anita Borg" 0 "Yukihiro Matsumoto" 0 "Hedy Lamarr" 0 "Claude Shannon"
  0 "Linus Torvalds" 0 "Alan Turing"

因爲排序集的排序規則,它們已經按字典順序排序:

> zrange hackers 0 -1
1) "Alan Kay"
2) "Alan Turing"
3) "Anita Borg"
4) "Claude Shannon"
5) "Hedy Lamarr"
6) "Linus Torvalds"
7) "Richard Stallman"
8) "Sophie Wilson"
9) "Yukihiro Matsumoto"

使用ZRANGEBYLEX咱們能夠要求詞典範圍:

> zrangebylex hackers [B [P
1) "Claude Shannon"
2) "Hedy Lamarr"
3) "Linus Torvalds"

範圍能夠是包含(inclusive)或排除(exclusive)(取決於第一個字符),字符串無限和負無限分別用+-字符串指定。有關更多信息,請參見文檔。

此功能很是重要,由於它容許咱們將排序後的集合用做通用索引。例如,若是要經過128位無符號整數參數索引元素,則只需將元素添加到具備相同分數(例如0)但具備由128個字節組成的16字節前綴的排序集中大尾數中的位數。因爲big endian中的數字實際上按數字順序也按字典順序(以原始字節順序)排序,所以您能夠要求128位空間中的範圍,並得到丟棄前綴的元素值。

若是要在更嚴重的演示環境中查看該功能,請檢查Redis自動完成演示

*Updating the score: leader boards

在切換到下一個主題以前,請只對已排序集作最後的說明。排序集的分數能夠隨時更新。只需對已包含在排序集中的元素調用ZADD,將以O(log(N))時間複雜度更新其得分(和位置)。這樣,當有大量更新時,排序集是合適的。

因爲這種特性,常見的用例是排行榜。典型的應用是Facebook遊戲,您能夠將按高分對用戶進行排序的能力與得到排名的操做結合起來,以顯示前N名的用戶以及排行榜中的用戶排名(例如,「您是這裏的#4932最佳成績」)。

*Bitmaps

位圖不是實際的數據類型,而是在String類型上定義的一組面向位的操做。因爲字符串是二進制安全Blob,而且最大長度爲512 MB,所以它們適合設置多達2 32個不一樣的位。

位操做分爲兩類:固定時間的單個位操做(如將一個位設置爲1或0或獲取其值),以及對位組的操做,例如計算給定位範圍內設置的位的數量(例如,人口計數)。

位圖的最大優勢之一是,它們在存儲信息時一般能夠節省大量空間。例如,在以增量用戶ID表示不一樣用戶的系統中,僅使用512 MB內存就能夠記住40億用戶的一位信息(例如,知道用戶是否要接收新聞通信)。

使用SETBITGETBIT命令設置和檢索位:

> setbit key 10 1
(integer) 1
> getbit key 10
(integer) 1
> getbit key 11
(integer) 0

所述SETBIT命令採用做爲第一個參數的比特數,和做爲第二個參數的值以設置所述位,其爲1或0的命令自動放大字符串,若是尋址位是當前字符串長度以外。

GETBIT只是返回指定索引處的位的值。超出範圍的位(尋址超出存儲在目標鍵中的字符串長度的位)始終被視爲零。

在位組上有三個命令:

  1. BITOP在不一樣的字符串之間執行按位運算。提供的運算爲AND,OR,XOR和NOT。
  2. BITCOUNT執行填充計數,報告設置爲1的位數。
  3. BITPOS查找指定值爲0或1的第一位。

不管BITPOS比特計數可以與字符串的字節範圍進行操做,而不是該字符串的整個長度運行。如下是BITCOUNT調用的一個簡單示例:

> setbit key 0 1
(integer) 0
> setbit key 100 1
(integer) 0
> bitcount key
(integer) 2

位圖的常見用例是:

  • 各類實時分析。
  • 存儲與對象ID相關的空間高效但高性能的布爾信息。

例如,假設您想知道網站用戶天天訪問量最長的時間。您從零開始計算天數,即從您公開網站的那一天開始,並在用戶每次訪問該網站時對SETBIT進行設置。做爲位索引,您只需花費當前的unix時間,減去初始偏移量,而後除以一天中的秒數(一般爲3600 * 24)。

這樣,對於每一個用戶,您都有一個小的字符串,其中包含天天的訪問信息。使用BITCOUNT,能夠輕鬆得到給定用戶訪問網站的天數,而只需幾個BITPOS調用,或者僅獲取和分析客戶端的位圖,就能夠輕鬆計算最長的連勝記錄。

位圖很容易分紅多個鍵,例如,爲了分片數據集,而且由於一般最好避免使用大鍵。要在不一樣的密鑰上拆分位圖,而不是將全部位都設置爲密鑰,一個簡單的策略就是爲每一個密鑰存儲M位,並使用來獲取密鑰名稱,使用來獲取bit-number/M第N位bit-number MOD M

*HyperLogLogs

HyperLogLog是一種機率數據結構,用於對惟一事物進行計數(從技術上講,這是指估計集合的基數)。一般,對惟一項目進行計數須要使用與要計數的項目數量成比例的內存量,由於您須要記住過去已經看到的元素,以免屢次對其進行計數。可是,有一組算法會之內存爲代價來交換精度:您最終會獲得帶有標準偏差的估計量度,在Redis實現的狀況下,該偏差小於1%。這種算法的神奇之處在於,您再也不須要使用與所計數項目數量成正比的內存量,而是可使用恆定數量的內存!在最壞的狀況下爲12k字節,若是您的HyperLogLog(從如今開始將它們稱爲HLL)看到的元素不多,則少得多。

Redis中的HLL儘管在技術上是不一樣的數據結構,但被編碼爲Redis字符串,所以您能夠調用GET來序列化HLL,而後調用SET 來將其反序列化回服務器。

從概念上講,HLL API就像使用Set來執行相同的任務。你會 薩德每一個觀測元素爲一組,而且將使用SCARD檢查組中的元素,這是惟一的數量自SADD不會再添加一個現有的元素。

儘管您並未真正_將項目添加_到HLL中,但因爲數據結構僅包含不包含實際元素的狀態,所以API相同:

  • 每次看到新元素時,均可以使用PFADD將其添加到計數中。

  • 到目前爲止,每次您要檢索_添加_到PFADD的惟一元素的當前近似值時,均可以使用PFCOUNT

    > pfadd hll a b c d
    (integer) 1
    > pfcount hll
    (integer) 4

該數據結構用例的一個例子是天天計算用戶在搜索表單中執行的惟一查詢。

Redis也可以執行HLL的合併,請查看 完整的文檔以獲取更多信息。

*Other notable features

Redis API中還有其餘重要內容,在本文檔的上下文中沒法探討,但值得您注意:

*Learn more

本教程毫不完整,僅涵蓋了API的基礎知識。閱讀命令參考以發現更多內容。

感謝您的閱讀,並祝您使用Redis玩得開心!

相關文章
相關標籤/搜索