Redis數(shù)據(jù)類型簡介(十分鐘快速學(xué)習(xí)Redis)

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

Redis數(shù)據(jù)類型簡介

Redis不是簡單的鍵值存儲,它實(shí)際上是一個數(shù)據(jù)結(jié)構(gòu)服務(wù)器,支持不同類型的值。這意味著在傳統(tǒng)鍵值存儲中,您將字符串鍵與字符串值相關(guān)聯(lián),而在Redis中,該值不僅限于簡單的字符串,還可以容納更復(fù)雜的數(shù)據(jù)結(jié)構(gòu)。以下是Redis支持的所有數(shù)據(jù)結(jié)構(gòu)的列表,本教程將分別進(jìn)行介紹:

  • 二進(jìn)制安全字符串。
  • 列表:根據(jù)插入順序排序的字符串元素的集合。它們基本上是鏈表。
  • 集:唯一,未排序的字符串元素的集合。
  • 類似于Sets的排序集合,但每個字符串元素都與一個稱為score的浮點(diǎn)值相關(guān)聯(lián)。元素總是按它們的分?jǐn)?shù)排序,因此與Sets不同,可以檢索一系列元素(例如,您可能會問:給我前10名或后10名)。
  • 哈希,是由與值關(guān)聯(lián)的字段組成的映射。字段和值都是字符串。這與Ruby或Python哈希非常相似。
  • 位數(shù)組(或簡稱為位圖):可以使用特殊命令像位數(shù)組一樣處理字符串值:您可以設(shè)置和清除單個位,計數(shù)所有設(shè)置為1的位,找到第一個設(shè)置或未設(shè)置的位,等等。
  • HyperLogLogs:這是一個概率數(shù)據(jù)結(jié)構(gòu),用于估計集合的基數(shù)。別害怕,它比看起來更簡單...請參閱本教程的HyperLogLog部分。
  • 流:提供抽象日志數(shù)據(jù)類型的類地圖項(xiàng)的僅追加集合。在“ Redis流簡介”中對它們進(jìn)行了深入 介紹

命令參考中掌握這些數(shù)據(jù)類型的工作方式以及使用什么來解決給定問題并不總是那么容易,因此,本文檔是有關(guān)Redis數(shù)據(jù)類型及其最常見模式的速成課程。

對于所有示例,我們將使用該redis-cli實(shí)用程序(一個簡單但方便的命令行實(shí)用程序)對Redis服務(wù)器發(fā)出命令。

*Redis keys

Redis密鑰是二進(jìn)制安全的,這意味著您可以使用任何二進(jìn)制序列作為密鑰,從“ foo”之類的字符串到JPEG文件的內(nèi)容??兆址彩怯行У逆I。

有關(guān)密鑰的其他一些規(guī)則:

  • 太長的鍵不是一個好主意。例如,1024字節(jié)的密鑰不僅是內(nèi)存方面的問題,也是一個壞主意,而且因?yàn)樵跀?shù)據(jù)集中查找密鑰可能需要進(jìn)行一些代價高昂的密鑰比較。即使手頭的任務(wù)是匹配一個大值的存在,對它進(jìn)行散列(例如使用SHA1)也是一個更好的主意,尤其是從內(nèi)存和帶寬的角度來看。
  • 非常短的鍵通常不是一個好主意。如果您可以改寫“ user:1000:followers”,那么將“ u1000flw”寫為密鑰毫無意義。與鍵對象本身和值對象使用的空間相比,后者更具可讀性,并且添加的空間較小。雖然短鍵顯然會消耗更少的內(nèi)存,但您的工作是找到合適的平衡。
  • 嘗試堅持使用架構(gòu)。例如,“ object-type:id”是一個好主意,例如“ user:1000”。點(diǎn)或破折號通常用于多字字段,例如“ comment:1234:reply.to”或“ comment:1234:reply-to”中。
  • 允許的最大密鑰大小為512 MB。

*Redis Strings

Redis字符串類型是您可以與Redis鍵關(guān)聯(lián)的最簡單的值類型。它是Memcached中唯一的數(shù)據(jù)類型,因此對于新手來說,在Redis中使用它也是很自然的。

由于Redis鍵是字符串,因此當(dāng)我們也使用字符串類型作為值時,我們會將一個字符串映射到另一個字符串。字符串?dāng)?shù)據(jù)類型對于許多用例很有用,例如緩存HTML片段或頁面。

讓我們使用來處理字符串類型redis-cli(所有示例將redis-cli在本教程中通過來執(zhí)行)。

> set mykey somevalue
OK
> get mykey
"somevalue"

如您所見,使用SETGET命令是我們設(shè)置和檢索字符串值的方式。請注意,即使鍵已與非字符串值相關(guān)聯(lián),SET仍將替換已存儲在鍵中的任何現(xiàn)有值。因此SET執(zhí)行分配。

值可以是每種類型的字符串(包括二進(jìn)制數(shù)據(jù)),例如,您可以在值內(nèi)存儲jpeg圖像。值不能大于512 MB。

SET命令有有趣的選項(xiàng),這是作為附加參數(shù)。例如,如果密鑰已經(jīng)存在,我可能會要求SET失敗,或者相反,只有密鑰已經(jīng)存在時,它才會成功:

> set mykey newval nx
(nil)
> set mykey newval xx
OK

即使字符串是Redis的基本值,您也可以使用它們執(zhí)行一些有趣的操作。例如,一個是原子增量:

> set counter 100
OK
> incr counter
(integer) 101
> incr counter
(integer) 102
> incrby counter 50
(integer) 152

INCR命令由一個解析字符串值作為一個整數(shù),它的增量,并最終將獲得的值作為新的值。還有其他類似的命令,例如INCRBY, DECRDECRBY。在內(nèi)部,它始終是相同的命令,其執(zhí)行方式略有不同。

INCR是原子的意味著什么?即使使用相同密鑰發(fā)出INCR的多個客戶也永遠(yuǎn)不會進(jìn)入競爭狀態(tài)。例如,客戶端1不會同時讀取“ 10”,客戶端2會同時讀取“ 10”,都遞增為11,并將新值設(shè)置為11。最終值將始終為12,而在所有其他客戶端未同時執(zhí)行命令時執(zhí)行增量設(shè)置操作。

有許多用于操作字符串的命令。例如,GETSET命令將鍵設(shè)置為新值,并返回舊值作為結(jié)果。例如,如果您的系統(tǒng)在 每次網(wǎng)站接收新訪客時使用INCR遞增Redis密鑰,則可以使用此命令。您可能希望每小時收集一次此信息,而又不會丟失任何增量。您可以GETSET鍵,為其分配新值“ 0”,然后回讀舊值。

在單個命令中設(shè)置或檢索多個鍵的值的功能對于減少延遲也很有用。因此,有MSETMGET命令:

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

使用MGET時,Redis返回一個值數(shù)組。

*Altering and querying the key space

有些命令未在特定類型上定義,但是在與鍵的空間進(jìn)行交互時很有用,因此可以與任何類型的鍵一起使用。

例如,EXISTS命令返回1或0表示數(shù)據(jù)庫中是否存在給定的鍵,而DEL命令則刪除鍵和關(guān)聯(lián)的值(無論該值是什么)。

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

從示例中,您還可以看到DEL本身如何返回1或0,具體取決于密鑰是否已刪除(存在)(不存在具有該名稱的此類密鑰)。

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

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

*Redis expires: keys with limited time to live

在繼續(xù)使用更復(fù)雜的數(shù)據(jù)結(jié)構(gòu)之前,我們需要討論另一個功能,該功能不管值類型如何都可以工作,并且稱為Redis expires。基本上,您可以為密鑰設(shè)置一個超時時間,這是有限的生存時間。生存時間過去后,該密鑰將自動銷毀,就像用戶使用該密鑰調(diào)用DEL命令一樣。

有關(guān)Redis的一些快速信息將過期:

  • 可以使用秒或毫秒精度進(jìn)行設(shè)置。
  • 但是,到期時間分辨率始終為1毫秒。
  • 有關(guān)過期的信息被復(fù)制并保留在磁盤上,實(shí)際上Redis服務(wù)器保持停止?fàn)顟B(tài)的時間已經(jīng)過去(這意味著Redis保存了密鑰過期的日期)。

設(shè)置過期時間很簡單:

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

由于第二次呼叫延遲了5秒鐘以上,因此在兩次GET呼叫之間密鑰消失了。在上面的示例中,我們使用EXPIRE來設(shè)置過期時間(也可以使用它來為已經(jīng)具有密鑰的密鑰設(shè)置不同的過期時間,例如可以使用PERSIST來刪除過期并使密鑰永久持久化)。但是,我們也可以使用其他Redis命令來創(chuàng)建具有過期密鑰。例如,使用SET選項(xiàng):

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

上面的示例使用一個字符串值設(shè)置一個密鑰,該密鑰100的到期時間為十秒鐘。稍后調(diào)用TTL命令以檢查密鑰的剩余生存時間。

為了設(shè)置和檢查以毫秒為單位到期,檢查PEXPIRE熱釋光的命令,以及完整列表SET選項(xiàng)。

*Redis Lists

為了解釋List數(shù)據(jù)類型,最好從理論上入手,因?yàn)?em>List一詞經(jīng)常被信息技術(shù)人員以不正當(dāng)?shù)姆绞绞褂?。例如,?Python列表”并不是名稱(鏈接列表)所建議的,而是數(shù)組(在Ruby中,相同的數(shù)據(jù)類型實(shí)際上稱為數(shù)組)。

從非常普遍的角度來看,列表只是一系列有序元素:10,20,1,2,3是一個列表。但是,使用Array實(shí)現(xiàn)的List的屬性與使用Linked List實(shí)現(xiàn)的List的屬性非常不同 。

Redis列表是通過鏈接列表實(shí)現(xiàn)的。這意味著即使您在列表中有數(shù)百萬個元素,在列表的開頭或結(jié)尾添加新元素的操作也會在固定時間內(nèi)執(zhí)行。使用LPUSH命令將新元素添加到具有10個元素的列表的開頭的速度與將元素添加到具有1000萬個元素的列表的開頭的速度相同。

缺點(diǎn)是什么?在使用Array實(shí)現(xiàn)的列表中,按索引訪問元素速度非??欤ê愣〞r間索引訪問),而在通過鏈接列表實(shí)現(xiàn)的列表中訪問速度不是那么快(其中操作需要的工作量與所訪問元素的索引成比例)。

Redis列表是通過鏈接列表實(shí)現(xiàn)的,因?yàn)閷τ跀?shù)據(jù)庫系統(tǒng)而言,至關(guān)重要的是能夠以非??斓姆绞綄⒃靥砑拥胶荛L的列表中。稍后您將看到,另一個強(qiáng)大的優(yōu)勢是Redis列表可以在恒定的時間內(nèi)以恒定的長度獲取。

當(dāng)快速訪問大量元素的中間位置很重要時,可以使用另一種稱為排序集的數(shù)據(jù)結(jié)構(gòu)。排序的集將在本教程的后面部分介紹。

*First steps with Redis Lists

所述LPUSH命令將一個新元素到一個列表,在左側(cè)(在頭部),而RPUSH命令將一個新元素到一個列表,在右側(cè)(在尾部)。最后, 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需要兩個索引,要返回的范圍的第一個和最后一個元素。兩個索引都可以為負(fù),告訴Redis從末尾開始計數(shù):因此-1是列表的最后一個元素,-2是列表的倒數(shù)第二個元素,依此類推。

如您所見,RPUSH在列表的右側(cè)附加了元素,而最后的LPUSH在列表的左側(cè)附加了元素。

這兩個命令都是可變參數(shù)命令,這意味著您可以在單個調(diào)用中隨意將多個元素推入列表中:

> 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列表上定義的一項(xiàng)重要操作是彈出元素的能力。彈出元素是同時從列表中檢索元素并將其從列表中刪除的操作。您可以從左側(cè)和右側(cè)彈出元素,類似于在列表兩邊推送元素的方式:

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

我們添加了三個元素并彈出了三個元素,因此在此命令序列的末尾,列表為空,沒有其他要彈出的元素。如果我們嘗試彈出另一個元素,則會得到以下結(jié)果:

> rpop mylist
(nil)

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

*Common use cases for lists

列表對于許多任務(wù)很有用,以下是兩個非常有代表性的用例:

  • 記住用戶發(fā)布到社交網(wǎng)絡(luò)上的最新更新。
  • 使用生產(chǎn)者將項(xiàng)目推送到列表中的消費(fèi)者與生產(chǎn)者模式進(jìn)行流程之間的通信,而消費(fèi)者(通常是worker)消耗這些項(xiàng)目和已執(zhí)行的動作。Redis具有特殊的列表命令,以使此用例更加可靠和高效。

例如,流行的Ruby庫resquesidekiq都在后臺使用Redis列表,以實(shí)現(xiàn)后臺作業(yè)。

流行的Twitter社交網(wǎng)絡(luò) 用戶發(fā)布的最新推文放入Redis列表中。

為了逐步描述一個常見的用例,假設(shè)您的主頁顯示了在照片共享社交網(wǎng)絡(luò)中發(fā)布的最新照片,并且您想加快訪問速度。

  • 每次用戶發(fā)布新照片時,我們都會使用LPUSH將其ID添加到列表中。
  • 當(dāng)用戶訪問主頁時,我們LRANGE 0 9為了獲取最新發(fā)布的10個項(xiàng)目。

*Capped lists

在許多用例中,我們只想使用列表來存儲最新項(xiàng)目,無論它們是什么:社交網(wǎng)絡(luò)更新,日志或其他任何內(nèi)容。

Redis允許我們使用列表作為上限集合,僅使用LTRIM命令記住最新的N個項(xiàng)目并丟棄所有最舊的項(xiàng)目。

LTRIM命令類似于LRANGE,但是,而不是顯示元件的規(guī)定的范圍內(nèi)將其設(shè)置在該范圍作為新的列表值。給定范圍之外的所有元素都將被刪除。

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

> 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列出列表元素,其他所有內(nèi)容都將被丟棄。這允許一個非常簡單但有用的模式:一起執(zhí)行List推操作+ List修剪操作,以便添加新元素并丟棄超出限制的元素:

LPUSH mylist <some element>
LTRIM mylist 0 999

上面的組合添加了一個新元素,并且僅將1000個最新元素納入列表。使用LRANGE,您可以訪問最重要的項(xiàng)目,而無需記住非常舊的數(shù)據(jù)。

注意:雖然LRANGE從技術(shù)上講是O(N)命令,但朝列表的開頭或結(jié)尾訪問較小范圍是恒定時間操作。

*Blocking operations on lists

列表具有一項(xiàng)特殊功能,使其適合于實(shí)現(xiàn)隊列,并且通常用作進(jìn)程間通信系統(tǒng)的構(gòu)建塊:阻止操作。

想象一下,您想通過一個流程將項(xiàng)目推入列表,然后使用不同的流程來對這些項(xiàng)目進(jìn)行某種工作。這是通常的生產(chǎn)者/使用者設(shè)置,可以通過以下簡單方式實(shí)現(xiàn):

  • 為了將項(xiàng)目推送到列表中,生產(chǎn)者調(diào)用LPUSH。
  • 為了從列表中提取/處理項(xiàng)目,消費(fèi)者調(diào)用RPOP。

但是,有時列表可能為空,沒有任何要處理的內(nèi)容,因此RPOP僅返回NULL。在這種情況下,消費(fèi)者被迫等待一段時間,然后使用RPOP重試。這稱為輪詢,在這種情況下不是一個好主意,因?yàn)樗袔讉€缺點(diǎn):

  1. 強(qiáng)制Redis和客戶端處理無用的命令(列表為空時的所有請求將無法完成任何實(shí)際工作,它們只會返回NULL)。
  2. 由于工作人員在收到NULL之后會等待一段時間,因此會增加項(xiàng)目處理的延遲。為了使延遲更小,我們可以在兩次調(diào)用RPOP之間等待的時間更少,從而擴(kuò)大了問題編號1,即對Redis的調(diào)用更加無用。

所以,所謂的Redis命令工具BRPOPBLPOP它們的版本RPOPLPOP能夠阻止如果列表是空的:他們將回到只有當(dāng)新的元素添加到列表中的來電者,或在用戶指定的超時到達(dá)。

這是我們可以在worker中使用的BRPOP調(diào)用的示例:

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

這意味著:“等待列表中的元素tasks,但如果5秒鐘后沒有可用元素,則返回”。

請注意,您可以將0用作超時來永遠(yuǎn)等待元素,還可以指定多個列表,而不僅僅是一個列表,以便同時等待多個列表,并在第一個列表收到一個元素時得到通知。

有關(guān)BRPOP的幾點(diǎn)注意事項(xiàng)

  1. 客戶端以有序方式提供服務(wù):第一個阻塞等待列表的客戶端,在某個元素被其他客戶端推送時首先提供服務(wù),依此類推。
  2. 返回值與RPOP相比有所不同:它是一個包含兩個元素的數(shù)組,因?yàn)樗€包含鍵的名稱,因?yàn)?a href="/commands/brpop" target="_blank">BRPOP和BLPOP能夠阻止等待來自多個列表的元素。
  3. 如果達(dá)到超時,則返回NULL。

關(guān)于列表和阻止操作,您應(yīng)該了解更多信息。我們建議您閱讀以下內(nèi)容:

  • 使用RPOPLPUSH可以構(gòu)建更安全的隊列或輪換隊列。
  • 該命令還有一個阻塞變體,稱為BRPOPLPUSH

*Automatic creation and removal of keys

到目前為止,在我們的示例中,我們無需在推入元素之前創(chuàng)建空列表,也無需在內(nèi)部不再包含元素時刪除空列表。Redis的責(zé)任是在列表為空時刪除鍵,或者在鍵不存在并且我們試圖向其添加元素(例如,使用LPUSH)時創(chuàng)建一個空列表。

這不是特定于列表的,它適用于由多個元素組成的所有Redis數(shù)據(jù)類型-流,集合,排序集合和哈希。

基本上,我們可以用三個規(guī)則來總結(jié)行為:

  1. 當(dāng)我們將元素添加到聚合數(shù)據(jù)類型時,如果目標(biāo)鍵不存在,則在添加元素之前會創(chuàng)建一個空的聚合數(shù)據(jù)類型。
  2. 當(dāng)我們從聚合數(shù)據(jù)類型中刪除元素時,如果該值保持為空,則鍵將自動銷毀。流數(shù)據(jù)類型是此規(guī)則的唯一例外。
  3. 調(diào)用帶有空鍵的只讀命令(例如LLEN(返回列表的長度))或?qū)懨顒h除元素,總會產(chǎn)生與鍵保持空的聚合類型相同的結(jié)果。命令希望找到。

規(guī)則1的示例:

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

但是,如果密鑰存在,我們將無法對錯誤的類型執(zhí)行操作:

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

規(guī)則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

彈出所有元素后,鍵不再存在。

規(guī)則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"

盡管哈希可以方便地表示對象,但是實(shí)際上可以放入哈希中的字段數(shù)沒有實(shí)際限制(可用內(nèi)存除外),因此您可以在應(yīng)用程序內(nèi)部以多種不同方式使用哈希。

HMSET命令設(shè)置哈希的多個字段,而HGET檢索單個字段。HMGET類似于HGET但返回值的數(shù)組:

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

有些命令也可以對單個字段執(zhí)行操作,例如HINCRBY

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

您可以在文檔中找到哈希命令完整列表。

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

*Redis Sets

Redis集是字符串的無序集合。該 SADD命令添加新的元素,一組。還可以對集合進(jìn)行許多其他操作,例如測試給定元素是否已存在,執(zhí)行多個集合之間的交集,并集或求差等等。

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

在這里,我在集合中添加了三個元素,并告訴Redis返回所有元素。如您所見,它們沒有排序-Redis可以在每次調(diào)用時隨意以任何順序返回元素,因?yàn)榕c用戶之間沒有關(guān)于元素順序的約定。

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

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

“ 3”是集合的成員,而“ 30”不是集合的成員。

集合非常適合表示對象之間的關(guān)系。例如,我們可以輕松地使用集合來實(shí)現(xiàn)標(biāo)簽。

對這個問題進(jìn)行建模的一種簡單方法是為我們要標(biāo)記的每個對象設(shè)置一個集合。該集合包含與對象關(guān)聯(lián)的標(biāo)簽的ID。

一個例證是標(biāo)記新聞文章。如果商品ID 1000帶有標(biāo)簽1、2、5和77進(jìn)行標(biāo)記,則集合可以將這些標(biāo)簽ID與新聞項(xiàng)相關(guān)聯(lián):

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

我們可能還需要逆關(guān)系:用給定標(biāo)簽標(biāo)記的所有新聞的列表:

> 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

要獲取給定對象的所有標(biāo)簽很簡單:

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

注意:在示例中,我們假設(shè)您具有另一個數(shù)據(jù)結(jié)構(gòu),例如Redis哈希,它將標(biāo)簽ID映射到標(biāo)簽名稱。

還有其他一些非常簡單的操作,使用正確的Redis命令仍然很容易實(shí)現(xiàn)。例如,我們可能需要包含標(biāo)簽1、2、10和27的所有對象的列表。我們可以使用SINTER命令執(zhí)行此操作,該命令執(zhí)行不同集合之間的交集。我們可以用:

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

除了交集之外,您還可以執(zhí)行并集,求差,提取隨機(jī)元素等等。

提取元素的命令稱為SPOP,對于建模某些問題非常方便。例如,為了實(shí)現(xiàn)基于Web的撲克游戲,您可能需要用一組來代表您的套牌。假設(shè)我們對(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

現(xiàn)在我們要為每個玩家提供5張卡片。該SPOP命令刪除一個隨機(jī)元素,將其返回到客戶端,所以在這種情況下完美運(yùn)行。

但是,如果我們直接在甲板上對其進(jìn)行稱呼,那么在游戲的下一場比賽中,我們將需要再次填充紙牌,這可能并不理想。因此,首先,我們可以將存儲在deck密鑰中的集合復(fù)制到game:1:deck密鑰中。

這可以使用SUNIONSTORE來完成,SUNIONSTORE通常執(zhí)行多個集合之間的聯(lián)合,并將結(jié)果存儲到另一個集合中。但是,由于單個集合的并集本身,我可以使用以下命令復(fù)制我的卡組:

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

現(xiàn)在,我準(zhǔn)備為第一位玩家提供五張牌:

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

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

現(xiàn)在是引入set命令的好時機(jī),該命令提供集合中元素的數(shù)量。 在集合理論的上下文中,這通常稱為集合基數(shù),因此Redis命令稱為SCARD。

> scard game:1:deck
(integer) 47

數(shù)學(xué)原理:52-5 = 47。

當(dāng)您只需要獲取隨機(jī)元素而不將其從集合中刪除時,可以使用適合該任務(wù)的SRANDMEMBER命令。它還具有返回重復(fù)元素和非重復(fù)元素的功能。

*Redis Sorted sets

排序集是一種數(shù)據(jù)類型,類似于集合和哈希之間的混合。像集合一樣,排序集合由唯一的,非重復(fù)的字符串元素組成,因此從某種意義上說,排序集合也是一個集合。

但是,雖然集內(nèi)的元素沒有排序,但排序后的集合中的每個元素都與一個稱為得分的浮點(diǎn)值相關(guān)聯(lián) (這就是為什么該類型也類似于哈希的原因,因?yàn)槊總€元素都映射到一個值)。

此外,已排序集合中的元素是按順序進(jìn)行的(因此,它們不是應(yīng)請求而排序的,順序是用于表示已排序集合的數(shù)據(jù)結(jié)構(gòu)的特殊性)。它們按照以下規(guī)則排序:

  • 如果A和B是兩個分?jǐn)?shù)不同的元素,則如果A.score是> B.score,則A>B。
  • 如果A和B的分?jǐn)?shù)完全相同,那么如果A字符串在字典上大于B字符串,則A>B。A和B字符串不能相等,因?yàn)榕判蚣瘍H具有唯一元素。

讓我們從一個簡單的示例開始,添加一些選定的黑客名稱作為排序的集合元素,并以其出生年份為“得分”。

> 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相似,但是使用一個額外的參數(shù)(放置在要添加的元素之前)作為得分。 ZADD也是可變參數(shù),因此即使上面的示例中未使用它,您也可以自由指定多個得分-值對。

使用排序集,返回按其出生年份排序的黑客列表很簡單,因?yàn)閷?shí)際上他們已經(jīng)被排序了

實(shí)施說明:排序集是通過包含跳過列表和哈希表的雙端口數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)的,因此,每次添加元素時,Redis都會執(zhí)行O(log(N))操作。很好,但是當(dāng)我們要求排序元素時,Redis根本不需要做任何工作,它已經(jīng)全部排序了:

> 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參數(shù)返回分?jǐn)?shù):

> 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

排序集比這更強(qiáng)大。它們可以在范圍內(nèi)操作。讓我們獲取所有在1950年(含)之前出生的人。我們使用ZRANGEBYSCORE命令來做到這一點(diǎn):

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

我們要求Redis返回分?jǐn)?shù)在負(fù)無窮大和1950之間的所有元素(包括兩個極端)。

也可以刪除元素范圍。讓我們從排序集中刪除所有1940年至1960年之間出生的黑客:

> zremrangebyscore hackers 1940 1960
(integer) 4

ZREMRANGEBYSCORE可能不是最好的命令名稱,但是它可能非常有用,并返回已刪除元素的數(shù)量。

為排序的集合元素定義的另一個極其有用的操作是get-rank操作??梢詥栆粋€元素在有序元素集合中的位置是什么。

> zrank hackers "Anita Borg"
(integer) 4

ZREVRANK命令也可以為了獲得軍銜,考慮的要素排序的下降方式。

*Lexicographical scores

在最新版本的Redis 2.8中,引入了一項(xiàng)新功能,該功能允許按字典順序獲取范圍,假設(shè)已排序集中的元素都以相同的相同分?jǐn)?shù)插入(將元素與C memcmp函數(shù)進(jìn)行比較 ,因此可以確保沒有排序規(guī)則) ,并且每個Redis實(shí)例將以相同的輸出進(jìn)行回復(fù))。

用于按字典順序操作的主要命令是ZRANGEBYLEX, ZREVRANGEBYLEX,ZREMRANGEBYLEXZLEXCOUNT。

例如,讓我們再次添加我們的著名黑客列表,但是這次對所有元素使用零分:

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

由于排序集的排序規(guī)則,它們已經(jīng)按字典順序排序:

> 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)(取決于第一個字符),字符串無限和負(fù)無限分別用+-字符串指定。有關(guān)更多信息,請參見文檔。

此功能非常重要,因?yàn)樗试S我們將排序后的集合用作通用索引。例如,如果要通過128位無符號整數(shù)參數(shù)索引元素,則只需將元素添加到具有相同分?jǐn)?shù)(例如0)但具有由128個字節(jié)組成的16字節(jié)前綴的排序集中大尾數(shù)中的位數(shù)。由于big endian中的數(shù)字實(shí)際上按數(shù)字順序也按字典順序(以原始字節(jié)順序)排序,因此您可以要求128位空間中的范圍,并獲得丟棄前綴的元素值。

如果要在更嚴(yán)重的演示環(huán)境中查看該功能,請檢查Redis自動完成演示

*Updating the score: leader boards

在切換到下一個主題之前,請只對已排序集做最后的說明。排序集的分?jǐn)?shù)可以隨時更新。只需對已包含在排序集中的元素調(diào)用ZADD,將以O(shè)(log(N))時間復(fù)雜度更新其得分(和位置)。這樣,當(dāng)有大量更新時,排序集是合適的。

由于這種特性,常見的用例是排行榜。典型的應(yīng)用是Facebook游戲,您可以將按高分對用戶進(jìn)行排序的能力與獲得排名的操作結(jié)合起來,以顯示前N名的用戶以及排行榜中的用戶排名(例如,“您是這里的#4932最佳成績”)。

*Bitmaps

位圖不是實(shí)際的數(shù)據(jù)類型,而是在String類型上定義的一組面向位的操作。由于字符串是二進(jìn)制安全Blob,并且最大長度為512 MB,因此它們適合設(shè)置多達(dá)2 32個不同的位。

位操作分為兩類:固定時間的單個位操作(如將一個位設(shè)置為1或0或獲取其值),以及對位組的操作,例如計算給定位范圍內(nèi)設(shè)置的位的數(shù)量(例如,人口計數(shù))。

位圖的最大優(yōu)點(diǎn)之一是,它們在存儲信息時通??梢怨?jié)省大量空間。例如,在以增量用戶ID表示不同用戶的系統(tǒng)中,僅使用512 MB內(nèi)存就可以記住40億用戶的一位信息(例如,知道用戶是否要接收新聞通訊)。

使用SETBITGETBIT命令設(shè)置和檢索位:

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

所述SETBIT命令采用作為第一個參數(shù)的比特數(shù),和作為第二個參數(shù)的值以設(shè)置所述位,其為1或0的命令自動放大字符串,如果尋址位是當(dāng)前字符串長度之外。

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

在位組上有三個命令:

  1. BITOP在不同的字符串之間執(zhí)行按位運(yùn)算。提供的運(yùn)算為AND,OR,XOR和NOT。
  2. BITCOUNT執(zhí)行填充計數(shù),報告設(shè)置為1的位數(shù)。
  3. BITPOS查找指定值為0或1的第一位。

無論BITPOS比特計數(shù)能夠與字符串的字節(jié)范圍進(jìn)行操作,而不是該字符串的整個長度運(yùn)行。以下是BITCOUNT調(diào)用的一個簡單示例:

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

位圖的常見用例是:

  • 各種實(shí)時分析。
  • 存儲與對象ID相關(guān)的空間高效但高性能的布爾信息。

例如,假設(shè)您想知道網(wǎng)站用戶每天訪問量最長的時間。您從零開始計算天數(shù),即從您公開網(wǎng)站的那一天開始,并在用戶每次訪問該網(wǎng)站時對SETBIT進(jìn)行設(shè)置。作為位索引,您只需花費(fèi)當(dāng)前的unix時間,減去初始偏移量,然后除以一天中的秒數(shù)(通常為3600 * 24)。

這樣,對于每個用戶,您都有一個小的字符串,其中包含每天的訪問信息。使用BITCOUNT,可以輕松獲得給定用戶訪問網(wǎng)站的天數(shù),而只需幾個BITPOS調(diào)用,或者僅獲取和分析客戶端的位圖,就可以輕松計算最長的連勝記錄。

位圖很容易分成多個鍵,例如,為了分片數(shù)據(jù)集,并且因?yàn)橥ǔW詈帽苊馐褂么箧I。要在不同的密鑰上拆分位圖,而不是將所有位都設(shè)置為密鑰,一個簡單的策略就是為每個密鑰存儲M位,并使用來獲取密鑰名稱,使用來獲取bit-number/M第N位bit-number MOD M。

*HyperLogLogs

HyperLogLog是一種概率數(shù)據(jù)結(jié)構(gòu),用于對唯一事物進(jìn)行計數(shù)(從技術(shù)上講,這是指估計集合的基數(shù))。通常,對唯一項(xiàng)目進(jìn)行計數(shù)需要使用與要計數(shù)的項(xiàng)目數(shù)量成比例的內(nèi)存量,因?yàn)槟枰涀∵^去已經(jīng)看到的元素,以避免多次對其進(jìn)行計數(shù)。但是,有一組算法會以內(nèi)存為代價來交換精度:您最終會得到帶有標(biāo)準(zhǔn)誤差的估計量度,在Redis實(shí)現(xiàn)的情況下,該誤差小于1%。這種算法的神奇之處在于,您不再需要使用與所計數(shù)項(xiàng)目數(shù)量成正比的內(nèi)存量,而是可以使用恒定數(shù)量的內(nèi)存!在最壞的情況下為12k字節(jié),如果您的HyperLogLog(從現(xiàn)在開始將它們稱為HLL)看到的元素很少,則少得多。

Redis中的HLL盡管在技術(shù)上是不同的數(shù)據(jù)結(jié)構(gòu),但被編碼為Redis字符串,因此您可以調(diào)用GET來序列化HLL,然后調(diào)用SET 來將其反序列化回服務(wù)器。

從概念上講,HLL API就像使用Set來執(zhí)行相同的任務(wù)。你會 薩德每個觀測元素為一組,并且將使用SCARD檢查組中的元素,這是唯一的數(shù)量自SADD不會再添加一個現(xiàn)有的元素。

盡管您并未真正將項(xiàng)目添加到HLL中,但由于數(shù)據(jù)結(jié)構(gòu)僅包含不包含實(shí)際元素的狀態(tài),因此API相同:

  • 每次看到新元素時,都可以使用PFADD將其添加到計數(shù)中。

  • 到目前為止,每次您要檢索添加PFADD的唯一元素的當(dāng)前近似值時,都可以使用PFCOUNT

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

該數(shù)據(jù)結(jié)構(gòu)用例的一個例子是每天計算用戶在搜索表單中執(zhí)行的唯一查詢。

Redis也能夠執(zhí)行HLL的合并,請查看 完整的文檔以獲取更多信息。

*Other notable features

Redis API中還有其他重要內(nèi)容,在本文檔的上下文中無法探討,但值得您注意:

*Learn more

本教程絕不完整,僅涵蓋了API的基礎(chǔ)知識。閱讀命令參考以發(fā)現(xiàn)更多內(nèi)容。

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

如果你喜歡本文,
請關(guān)注公眾號 分布式編程
原文地址:https://zthinker.com/archives/redis%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%E7%AE%80%E4%BB%8B

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容