1. 程式人生 > 資料庫 >Redis【有與無】【T1】Redis資料型別和抽象簡介

Redis【有與無】【T1】Redis資料型別和抽象簡介

本文基於Redis 6.0.9版本,前提至少 Redis 3.0或更高版本。

目錄


1.Redis資料型別和抽象簡介

Redis不是簡單的key-value儲存,它實際上是一個數據結構伺服器,支援各種型別的值。 這意味著,儘管在傳統的key-value儲存中,你將string key與string value相關聯,但是在Redis中,該值不僅限於簡單的string,還可以容納更復雜的資料結構。

  • 二進位制安全字串
  • Lists: 根據插入順序排序的字串元素集合。 它們基本上是連結列表。
  • Sets: 唯一、未排序的字串元素的集合。
  • Sorted sets: 與Sets類似,但每個字串元素都與一個稱為分數的浮點值相關聯。 元素總是按它們的分數排序,因此與Sets不同,可以檢索一系列元素(例如,檢視前10名或後10名)。
  • Hashes: 由與值相關聯的欄位組成的map。 欄位和值都是字串。 這與Ruby或Python雜湊非常相似。
  • Bit arrays (or simply bitmaps): 可以使用特殊命令像位陣列一樣處理字串值:你可以設定和清除單個bits,計數所有設定為1的bits,找到第一個設定或未設定的位,依此類推。
  • HyperLogLogs:這是一個概率資料結構,用於估計集合的基數。
  • Streams: 提供抽象日誌資料型別的map-like entries 的僅追加集合。

從命令參考中掌握這些資料型別的工作方式以及使用什麼來解決給定問題並不總是那麼容易,因此,本文件是有關及其最常見模式的速成教程。

對於所有示例,我們將使用redis-cli實用程式(一個簡單但方便的命令列實用程式)對Redis伺服器發出命令。

1.1.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。

1.2.Redis Strings

Redis字串型別是你可以與Redis鍵關聯的最簡單的值型別。 它是Memcached(分散式的快取記憶體)中唯一的資料型別,因此對於新手來說,在Redis中使用它也是很自然的。

由於Redis鍵是字串,因此當我們也使用字串型別作為值時,我們會將一個字串對映到另一個字串。 字串資料型別對於許多用例很有用,例如快取HTML片段或頁面。

讓我們使用redis-cli來研究字串型別(在本文中,所有示例都將通過redis-cli執行)。

> set mykey somevalue
OK
> get mykey
"somevalue"

如你所見,使用和命令是設定和檢索字串值的方式。 請注意,即使鍵已與非字串值相關聯,SET仍將替換已儲存在鍵中的任何現有值。

值可以是每種型別的字串(包括二進位制資料),例如,你可以在值記憶體儲jpeg圖片。 值不能大於512 MB。

命令具有有趣的選項,這些選項作為附加引數提供。 例如,如果鍵已經存在,我可能會要求失敗,反之,只有鍵已經存在,才能成功:

> 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

 命令將字串值解析為整數,將其遞增1,最後將獲得的值設定為新值。 還有其他類似的命令,例如, 和。 在內部,它始終是相同的命令,其執行方式略有不同。

INCR是原子的意味著什麼? 即使多個客戶端使用相同鍵發出INCR也永遠不會進入競爭狀態。 例如,客戶端1不會同時讀取“10”,客戶端2會同時讀取“10”,都遞增為11,並將新值設定為11。最終值將始終為12,而 在所有其他客戶端未同時執行命令時執行增量設定操作。

有許多用於操作字串的命令。 例如, 命令將鍵設定為新值,並返回舊值作為結果。 例如,如果你的系統在每次網站接收新訪客時使用 來增加Redis鍵,則可以使用此命令。 你可能希望每小時收集一次此資訊,而又不會丟失任何增量。 你可以 鍵,為其分配新值“0”,然後回讀舊值。

在單個命令中設定或檢索多個鍵的值的功能對於減少延遲也很有用。 因此,有 和 命令:

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

使用 時,Redis返回一個值陣列。

1.3.更改和查詢鍵空間

有些命令沒有在特定型別上定義,但是在與鍵空間互動時很有用,因此可以與任何型別的鍵一起使用。

例如, 命令返回1或0表示資料庫中是否存在給定的鍵,而 命令則刪除鍵和關聯的值(無論該值是什麼)。

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

從示例中,你還可以看到 本身如何返回1或0,具體取決於是否刪除了該鍵(它已經存在)(沒有該名稱的鍵)。

有許多與鍵空間相關的命令,但是以上兩個命令與命令一起是必不可少的,命令返回儲存在指定鍵處的值的型別:

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

1.4.Redis到期:鍵有限的生存時間

在繼續使用更復雜的資料結構之前,我們需要討論另一個功能,該功能無論值型別如何都可以工作,並且稱為Redis expires。 基本上,你可以為鍵設定一個超時時間,這是有限的生存時間。

生存時間過去後,該鍵會自動銷燬,就像使用者使用該鍵呼叫命令一樣。

有關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秒以上,因此兩次之間的鍵消失了。在上面的示例中,我們使用來設定過期時間(也可以用於為已經存在的鍵設定不同的過期時間,例如可以使用來刪除過期並使鍵永久持久化 )。但是,我們也可以使用其他Redis命令來建立具有到期時間的鍵。 例如,使用選項:

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

上面的示例設定了一個字串值100的鍵,該鍵的到期時間為10秒。 稍後呼叫命令以檢查鍵的剩餘生存時間。

為了設定並檢查以毫秒為單位的到期時間,請檢查和命令以及選項的完整列表。

1.5.Redis Lists

為了解釋List資料型別,最好從理論上入手,因為List一詞經常被資訊科技人員以不正當的方式使用。 例如,“Python Lists”並不是名稱(Linked Lists)的名字,而是陣列(在Ruby中,相同的資料型別實際上稱為陣列)。

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

Redis列表是通過連結列表(Linked Lists)實現的。 這意味著即使你在列表中有數百萬個元素,在列表的開頭或結尾新增新元素的操作也會在恆定時間內執行。 使用命令將新元素新增到具有10個元素的列表的開頭的速度與將元素新增到具有1000萬個元素的列表的開頭的速度相同。

有什麼缺點? 在使用Array實現的列表中,按索引訪問元素的速度非常快(恆定時間索引訪問),而在通過連結列表實現的列表中訪問速度不是那麼快(其中操作需要的工作量與所訪問元素的索引成比例)。

Redis列表是使用連結列表實現的,因為對於資料庫系統而言,至關重要的是能夠以非常快的方式將元素新增到很長的列表中。 稍後你將看到,另一個強大的優勢是Redis列表可以在恆定的時間內以恆定的長度獲取。

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

1.6.Redis Lists的第一步

 命令將一個新元素新增到列表的左側(頂部),而 命令將一個新元素新增到列表的右側(頂部)。 最後,命令從列表中提取元素範圍:

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

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

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

這兩個命令都是可變引數命令,這意味著你可以在單個呼叫中隨意將多個元素推入列表中:

> 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值以表示列表中沒有元素。

1.7.列表的常見用例

列表對於許多工很有用,以下是兩個非常有代表性的用例:

  • 檢視使用者釋出到社交網路上的最近的更新。
  • 使用生產者將專案推送到列表中的消費者與生產者模式進行流程之間的通訊,而消費者(通常是worker)則消耗這些專案和已執行的動作。 Redis具有特殊的列表命令,以使此用例更加可靠和高效。

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

流行的Twitter社交網路將使用者釋出的放入Redis列表中。

為了逐步描述一個常見的用例,假設你的主頁顯示了在照片共享社交網路中釋出的最新照片,並且你想加快訪問速度。

  • 每次使用者釋出新照片時,我們都會使用將其ID新增到列表中。
  • 當用戶訪問主頁時,我們使用LRANGE 0 9來獲取最新發布的10個專案。

1.8.封頂列表

在許多用例中,我們只想使用列表來儲存最新專案,無論它們是什麼:社交網路更新,日誌或其他任何內容。

Redis允許我們使用列表作為上限集合,僅使用 命令記住最近的N個專案並丟棄所有最舊的專案。

 命令類似於,但是不顯示指定的元素範圍,而是將範圍設定為新列表值。 給定範圍之外的所有元素都將被刪除。

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

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

上面的命令告訴Redis僅從索引0到2列出列表元素,其他所有內容都將被丟棄。 這允許一個非常簡單但有用的模式:一起執行列表推送操作和列表修剪操作,以新增新元素並丟棄超出限制的元素:

LPUSH mylist <some element>
LTRIM mylist 0 999

上面的組合添加了一個新元素,並且僅將1000個最新元素納入列表。 使用,你可以訪問最重要的專案,而無需記住非常舊的資料。

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

1.9.阻止列表上的操作

列表具有一項特殊功能,使其適合於實現佇列,並且通常用作程序間通訊系統的構建塊:阻止操作(blocking operations)。

想象一下,你想通過一個流程將專案推入列表,然後使用不同的流程來對這些專案進行某種工作。 這是通常的生產者/使用者設定,可以通過以下簡單方式實現:

  • 為了將專案推送到列表中,生產者呼叫。
  • 為了從列表中提取/處理專案,消費者呼叫。

但是,有時列表可能為空,沒有任何要處理的內容,因此僅返回NULL。 在這種情況下,消費者被迫等待一段時間,然後使用重試。這被稱為輪詢(polling),在這種情況下不是一個好主意,因為它有幾個缺點:

  • 強制Redis和客戶端處理無用的命令(列表為空時的所有請求將無法完成任何實際工作,它們只會返回NULL)。
  • 由於工人(worker)在收到NULL之後會等待一段時間,因此會增加專案處理的延遲。 為了減小延遲,我們可以在兩次呼叫之間等待更少的時間,從而擴大了問題編號1,即對Redis的呼叫更多了。

因此,Redis實現了稱為和的命令,它們是和的版本,如果列表為空,它們可以阻止:僅當將新元素新增到列表中或達到使用者指定的超時時,它們才會返回到呼叫方。

這是我們可以在worker中使用的呼叫的示例:

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

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

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

有關的幾點注意事項:

  • 客戶端以有序的方式服務:第一個阻塞等待列表的客戶端,當元素被其他客戶端推送時首先服務,依此類推。
  • 返回值與相比有所不同:它是一個包含兩個元素的陣列,因為它還包含鍵的名稱,因為和能夠阻止等待來自多個列表的元素。
  • 如果達到超時,則返回NULL。

你還應該瞭解有關列表和阻止操作的更多資訊。 我們建議你閱讀以下內容:

  • 使用可以建立更安全的佇列或輪詢佇列。
  • 該命令還有一個阻塞變體,稱為。

1.10.自動建立和刪除鍵

到目前為止,在我們的示例中,我們無需在推送元素之前建立空列表,也無需在內部不再包含元素時刪除空列表。 Redis的責任是在列表為空時刪除鍵,或者在鍵不存在的情況下建立一個空列表,並且我們正在嘗試向其中新增元素,例如使用。

這不是特定於列表的,它適用於由多個元素組成的所有Redis資料型別-Streams, Sets, Sorted Sets, Hashes。

基本上,我們可以用三個規則來總結行為:

  1. 當我們將元素新增到聚合資料型別時,如果目標鍵不存在,則會在新增元素之前建立一個空的聚合資料型別。
  2. 當我們從聚合資料型別中刪除元素時,如果該值保持為空,則鍵將自動銷燬。 Stream資料型別是此規則的唯一例外。
  3. 呼叫帶有空鍵的只讀命令(例如(返回列表的長度))或寫命令刪除元素時,總是會產生與鍵保持空的聚合型別相同的結果。

規則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)

1.11.Redis Hashes

Redis hashe 與field-value對看起來完全一樣,可能是人們期望的“hash”外觀:

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

儘管hashes很容易表示物件,但是實際上可以放入hashes中的欄位數沒有實際限制(可用記憶體除外),因此你可以在應用程式內部以多種不同方式使用hashes。

命令設定hash的多個欄位,而檢索單個欄位。 與相似,但返回一個值陣列:

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

還有一些命令也可以對單個欄位執行操作,例如:

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

你可以在文件中找到。

值得注意的是,小hashes(即一些具有較小值的元素)在記憶體中以特殊方式進行了編碼,從而使其具有很高的記憶體效率。

1.12.Redis Sets

Redis集是字串的無序集合。 命令將新元素新增到集合中。 還可以對集合進行許多其他操作,例如測試給定元素是否已存在,執行多個集合之間的交集,並集或差等。

> 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帶有標籤1、2、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 hash,它將標籤ID對映到標籤名稱。

使用正確的Redis命令,仍然可以輕鬆實現其他一些重要的操作。 例如,我們可能需要包含標籤1、2、10和27的所有物件的列表。 我們可以使用 命令執行此操作,該命令執行不同集合之間的交集。 我們可以用:

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

除了交集之外,你還可以執行並集,求差,提取隨機元素等等。

提取元素的命令稱為,它對某些問題的建模非常方便。 例如,為了實現基於Web的撲克遊戲,你可能需要用一組來代表你的套牌。 假設我們對(C)lubs, (D)iamonds, (H)earts, (S)pades使用一個單字元字首:

>  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張卡片。 命令刪除了一個隨機元素,並將其返回給客戶端,因此在這種情況下,它是完美的操作。

但是,如果我們直接在deck上對其進行稱呼,那麼在遊戲的下一場比賽中,我們將需要再次填充卡片組,這可能並不理想。 首先,我們可以將儲存在套牌deck鍵中複製到game:1:deck 鍵。

這可以使用來完成,通常執行多個集合之間的聯合,並將結果儲存到另一個集合中。 但是,由於單個集合的並集本身,我可以使用以下命令複製我的卡組:

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

一對jacks,不是很好...

現在是引入set命令的好時機,該命令提供集合中元素的數量。 在集合論的上下文中,這通常稱為集合的基數(cardinality of a set),因此Redis命令稱為。

> scard game:1:deck
(integer) 47

數學計算:52-5 = 47。

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

1.13.Redis Sorted sets

排序的集合是一種資料型別,類似於集合和雜湊之間的混合。 像集一樣,排序集由唯一的,非重複的字串元素組成,因此從某種意義上說,排序集也是一個集。

但是,雖然集內的元素沒有排序,但是排序後的集合中的每個元素都與一個稱為得分的浮點值相關聯(這就是為什麼該型別也類似於雜湊的原因,因為每個元素都對映到一個值)。

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

  • 如果A和B是兩個具有不同分數的元素,則如果A.score是> B.score,則A>B。
  • 如果A和B的分數完全相同,那麼如果A字串在字典上大於B字串,則A>B。 A和B字串不能相等,因為排序集僅具有唯一元素。

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

 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

如你所見,與相似,但是採用了一個額外的引數(放置在要新增的元素之前)作為得分。 也是可變引數,因此即使在上面的示例中未使用它,你也可以自由指定多個score-value對。

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

實施注意事項:排序集是通過包含跳過列表和雜湊表的雙埠資料結構實現的,因此,每次新增元素時,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的工作方式與命令的情況相同)。

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

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

也可以使用 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"

1.14.在範圍內操作

排序集比這更強大。 它們可以在範圍內操作。 讓我們獲取所有出生到1950年(含)的個人。 我們使用 命令來做到這一點:

> 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

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

為排序的集合元素定義的另一個極其有用的操作是get-rank操作。 可以問一個元素在有序元素集合中的位置是什麼。

> zrank hackers "Anita Borg"
(integer) 4

命令也可用於獲得排名,考慮到元素按降序排序。

1.15.Lexicographical分數

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

使用詞典範圍操作的主要命令是, , , 。

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

> 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 hackers [B [P
1) "Claude Shannon"
2) "Hedy Lamarr"
3) "Linus Torvalds"

範圍可以是包含(inclusive)或排除(exclusive)(取決於第一個字元),字串無限和負無限分別用+和-字串指定。 請參閱文件以獲取更多資訊。

此功能很重要,因為它允許我們將排序後的集合用作通用索引。 例如,如果要使用128位無符號整數引數索引元素,則只需將元素新增到具有相同分數(例如0)但具有,在大尾數中,由128個字元組成的16位元組字首的sorted set。由於在大尾數中的數字實際上按數字順序也按字典順序(以原始位元組順序)排序,因此你可以要求128位空間中的範圍,並獲得丟棄字首的元素值。

如果要在更嚴重的演示環境中檢視該功能,請。

1.16.更新分數:排行榜

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

由於此特性,常見的用例是排行榜。 典型的應用是Facebook遊戲,你可以結合使用按照高分對使用者進行排序的能力以及排名操作,以顯示排名前N位的使用者以及排行榜中的使用者排名(例如,“你是這裡的#4932最佳成績”)。

1.17.Bitmaps

Bitmaps不是實際的資料型別,而是在String型別上定義的一組bit-oriented的操作。 由於字串是二進位制安全Blob,並且最大長度為512 MB,因此它們適合設定多達2^{32}個不同的bits。

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

bitmaps的最大優點之一是,它們在儲存資訊時通常可以節省大量空間。 例如,在以增量使用者ID表示不同使用者的系統中,僅使用512 MB記憶體就可以記住40億使用者的一位資訊(例如,知道使用者是否要接收新聞通訊)。

使用和命令設定和檢索位:

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

命令將位號作為其第一個引數,將其設定為1或0的值作為其第二個引數。如果定址的位超出當前字串長度,則該命令將自動放大字串。

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

在bits組上有三個命令:

  1. 在不同的字串之間執行bit-wise運算。 提供的運算為AND,OR,XOR和NOT。
  2. 執行填充計數,報告設定為1的位數。
  3.  查詢指定值為0或1的第一位。

和都可以在字串的位元組範圍內執行,而不是在字串的整個長度上執行。 以下是呼叫的一個簡單示例:

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

bitmaps的常見用例是:

  • 各種實時分析。
  • 儲存與物件ID相關聯的空間高效但高效能的布林資訊。

例如,假設你想知道網站使用者每天訪問量最長的時間。 你開始從零開始計數,即從你公開網站的那一天開始,並在使用者每次訪問該網站時對進行設定。 作為位索引,你只需花費當前的unix時間,減去初始偏移量,然後除以一天中的秒數(通常為3600*24)。

這樣,對於每個使用者,你都有一個小的字串,其中包含每天的訪問資訊。 使用,可以輕鬆獲得給定使用者訪問該網站的天數,而只需幾個呼叫,或者僅獲取並分析客戶端的bitmap,就可以輕鬆計算出最長的軌跡。

Bitmaps很容易拆分成多個鍵,例如,為了分片資料集,因為通常最好避免使用大鍵。 要在不同的鍵上拆分點陣圖,而不是將所有位都設定為鍵,一個簡單的策略是每個鍵儲存M位,並獲得具有bit-number/M的鍵名,並使用bit獲得第N位以 bit-number MOD M 內部定址鍵。

1.18.HyperLogLogs

HyperLogLog是一種概率資料結構,用於對唯一事物進行計數(從技術上講,這是指估計集合的基數)。 通常,對唯一專案進行計數需要使用與要計數的專案數量成比例的記憶體量,因為你需要記住過去已經看到的元素,以避免多次對其進行計數。 但是,有一組演算法會以記憶體為代價來交換精度:你最終會得到帶有標準誤差的估計量,在Redis實現的情況下,該誤差小於1%。 該演算法的神奇之處在於,你不再需要使用與所計數專案數成正比的記憶體量,而是可以使用恆定數量的記憶體! 在最壞的情況下為12k bytes,如果你的HyperLogLog(從現在開始將它們稱為HLL)看到的元素很少,則少得多。

Redis中的HLL儘管在技術上是不同的資料結構,但是被編碼為Redis字串,因此你可以呼叫來序列化HLL,然後呼叫來將其反序列化回伺服器。

從概念上講,HLL API就像使用Set來完成相同的任務。 你可以將每個觀察到的元素新增到集合中,並使用檢查集合中的元素數量,這是唯一的,因為不會重新新增現有元素。

儘管你並未真正將專案新增到HLL中,但由於資料結構僅包含不包含實際元素的狀態,因此API相同:

  • 每次看到新元素時,都可以使用將其新增到計數中。
  • 到目前為止,每次你要檢索新增到中的唯一元素的當前近似值時,都使用。
> pfadd hll a b c d
(integer) 1
> pfcount hll
(integer) 4

此資料結構用例的一個例子是,每天計算使用者在搜尋表單中執行的唯一查詢。

Redis也可以執行HLL的合併,請。

1.19.其他顯著特徵

Redis API中還有其他重要內容,在本文件的上下文中無法探討,但值得你注意:

  • 可以在伺服器端以改善延遲和頻寬。
  • Redis還是。

1.20.學到更多

本教程絕不完整,僅介紹了API的基礎知識。 。

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