The Little Redis Book中文版 第二章 - 資料結構

來源:互聯網
上載者:User

現在開始將探究Redis的5種資料結構,我們會解釋每種資料結構都是什麼,包含了什麼有效方法(Method),以及你能用這些資料結構處理哪些類型的特性和資料。

目前為止,我們所知道的Redis構成僅包括命令、關鍵字和值,還沒有接觸到關於資料結構的具體概念。當我們使用set命令時,Redis是怎麼知道我們是在使用哪個資料結構。其解決方案是,每個命令都相對應於一種特定的資料結構。例如,當你使用set命令,你就是將值儲存到一個字串資料結構裡。而當你使用hset命令,你就是將值儲存到一個散列資料結構裡。考慮到Redis的關鍵字集很小,這樣的機制具有相當的可管理性。

Redis的網站裡有著非常優秀的參考文檔,沒有任何理由去重造輪子。但為了搞清楚這些資料結構的作用,我們將會覆蓋那些必須知道的重要命令。

沒有什麼事情比高興的玩和實驗有趣的東西來得更重要的了。在任何時候,你都能通過鍵入flushdb命令將你資料庫裡的所有值清除掉,因此,不要再那麼害羞了,去嘗試做些瘋狂的事情吧。 字串(Strings)

在Redis裡,字串是最基本的資料結構。當你在思索著關鍵字-值對時,你就是在思索著字串資料結構。不要被名字給搞混了,如之前說過的,你的值可以是任何東西。我更喜歡將他們稱作“標量”(Scalars),但也許只有我才這樣想。

我們已經看到了一個常見的字串使用案例,即通過關鍵字儲存物件的執行個體。有時候,你會頻繁地用到這類操作:

set users:leto "{name: leto, planet: dune, likes: [spice]}"

除了這些外,Redis還有一些常用的操作。例如,strlen能用來擷取一個關鍵字對應值的長度;getrange將返回指定範圍內的關鍵字對應值;append會將value附加到已存在的關鍵字對應值中(如果該關鍵字並不存在,則會建立一個新的關鍵字-值對)。不要猶豫,去試試看這些命令吧。下面是我得到的:

> strlen users:leto(integer) 42> getrange users:leto 27 40"likes: [spice]"> append users:leto " OVER 9000!!"(integer) 54

現在你可能會想,這很好,但似乎沒有什麼意義。你不能有效地提取出一段範圍內的JSON檔案,或者為其附加一些值。你是對的,這裡的經驗是,一些命令,尤其是關於字串資料結構的,只有在給定了明確的資料類型後,才會有實際意義。

之前我們知道了,Redis不會去關注你的值是什麼東西。通常情況下,這沒有錯。然而,一些字串命令是專門為一些類型或值的結構而設計的。作為一個有些含糊的用例,我們可以看到,對於一些自訂的空間效率很高的(space-efficient)序列化對象,append和getrange命令將會很有用。對於一個更為具體的用例,我們可以再看一下incr、incrby、decr和decrby命令。這些命令會增長或者縮減一個字串資料結構的值:

> incr stats:page:about(integer) 1> incr stats:page:about(integer) 2> incrby ratings:video:12333 5(integer) 5> incrby ratings:video:12333 3(integer) 8

由此你可以想象到,Redis的字串資料結構能很好地用於分析用途。你還可以去嘗試增長users:leto(一個不是整數的值),然後看看會發生什麼(應該會得到一個錯誤)。

更為進階的用例是setbit和getbit命令。“今天我們有多少個獨立使用者訪問”是個在Web應用裡常見的問題,有一篇精彩的博文,在裡面可以看到Spool是如何使用這兩個命令有效地解決此問題。對於1.28億個使用者,一部膝上型電腦在不到50毫秒的時間裡就給出了回覆,而且只用了16MB的儲存空間。

最重要的事情不是在於你是否明白位元影像(Bitmaps)的工作原理,或者Spool是如何去使用這些命令,而是應該要清楚Redis的字串資料結構比你當初所想的要有用許多。然而,最常見的應用案例還是上面我們給出的:儲存物件(簡單或複雜)和計數。同時,由於通過關鍵字來擷取一個值是如此之快,字串資料結構很常被用來快取資料。 散列(Hashes)

我們已經知道把Redis稱為一種關鍵字-值型儲存是不太準確的,散列資料結構是一個很好的例證。你會看到,在很多方面裡,散列資料結構很像字串資料結構。兩者顯著的區別在於,散列資料結構提供了一個額外的間接層:一個域(Field)。因此,散列資料結構中的set和get是:

hset users:goku powerlevel 9000hget users:goku powerlevel

相關的操作還包括在同一時間設定多個域、同一時間擷取多個域、擷取所有的域和值、列出所有的域或者刪除指定的一個域:

hmset users:goku race saiyan age 737hmget users:goku race powerlevelhgetall users:gokuhkeys users:gokuhdel users:goku age

如你所見,散列資料結構比普通的字串資料結構具有更多的可操作性。我們可以使用一個散列資料結構去獲得更精確的描述,是儲存一個使用者,而不是一個序列化對象。從而得到的好處是能夠提取、更新和刪除具體的資料片段,而不必去擷取或寫入整個值。

對於散列資料結構,可以從一個經過明確定義的對象的角度來考慮,例如一個使用者,關鍵之處在於要理解他們是如何工作的。從效能上的原因來看,這是正確的,更具粒度化的控制可能會相當有用。在下一章我們將會看到,如何用散列資料結構去組織你的資料,使查詢變得更為實效。在我看來,這是散列真正耀眼的地方。 列表(Lists)

對於一個給定的關鍵字,列表資料結構讓你可以儲存和處理一組值。你可以添加一個值到列表裡、擷取列表的第一個值或最後一個值以及用給定的索引來處理值。列表資料結構維護了值的順序,提供了基於索引的高效操作。為了跟蹤在網站裡註冊的最新使用者,我們可以維護一個newusers的列表:

lpush newusers gokultrim newusers 0 50

(譯註:ltrim命令的具體構成是LTRIM Key start stop。要理解ltrim命令,首先要明白Key所儲存的值是一個列表,理論上列表可以存放任意個值。對於指定的列表,根據所提供的兩個範圍參數start和stop,ltrim命令會將指定範圍外的值都刪除掉,只留下範圍內的值。)

首先,我們將一個新使用者推入到列表的前端,然後對列表進行調整,使得該列表只包含50個最近被推入的使用者。這是一種常見的模式。ltrim是一個具有O(N)時間複雜度的操作,N是被刪除的值的數量。從上面的例子來看,我們總是在插入了一個使用者後再進行列表調整,實際上,其將具有O(1)的時間複雜度(因為N將永遠等於1)的常數效能。

這是我們第一次看到一個關鍵字的對應值索引另一個值。如果我們想要擷取最近的10個使用者的詳細資料,我們可以運行下面的組合操作:

keys = redis.lrange('newusers', 0, 10)redis.mget(*keys.map {|u| "users:#{u}"})

我們之前談論過關於多次往返資料的模式,上面的兩行Ruby代碼為我們進行了很好的示範。

當然,對於儲存和索引關鍵字的功能,並不是只有列表資料結構這種方式。值可以是任意的東西,你可以使用列表資料結構去儲存日誌,也可以用來跟蹤使用者瀏覽網站時的路徑。如果你過往曾構建過遊戲,你可能會使用列表資料結構去跟蹤使用者的排隊活動。 集合(Sets)

集合資料結構常常被用來儲存只能唯一存在的值,並提供了許多的基於集合的操作,例如並集。集合資料結構沒有對值進行排序,但是其提供了高效的基於值的操作。使用集合資料結構的典型用例是朋友名單的實現:

sadd friends:leto ghanima paul chani jessicasadd friends:duncan paul jessica alia

不管一個使用者有多少個朋友,我們都能高效地(O(1)時間複雜度)識別出使用者X是不是使用者Y的朋友:

sismember friends:leto jessicasismember friends:leto vladimir

而且,我們可以查看兩個或更多的人是不是有共同的朋友:

sinter friends:leto friends:duncan

甚至可以在一個新的關鍵字裡儲存結果:

sinterstore friends:leto_duncan friends:leto friends:duncan

有時候需要對值的屬性進行標記和跟蹤處理,但不能通過簡單的複製操作完成,集合資料結構是解決此類問題的最好方法之一。當然,對於那些需要運用集合操作的地方(例如交集和並集),集合資料結構就是最好的選擇。 分類集合(Sorted Sets)

最後也是最強大的資料結構是分類集合資料結構。如果說散列資料結構類似於字串資料結構,主要區分是域(field)的概念;那麼分類集合資料結構就類似於集合資料結構,主要區分是標記(score)的概念。標記提供了排序(sorting)和秩劃分(ranking)的功能。如果我們想要一個秩分類的朋友名單,可以這樣做:

zadd friends:duncan 70 ghanima 95 paul 95 chani 75 jessica 1 vladimir

對於duncan的朋友,要怎樣計算出標記(score)為90或更高的人數。

zcount friends:duncan 90 100

如何擷取chani在名單裡的秩(rank)。

zrevrank friends:duncan chani

(譯註:zrank命令的具體構成是ZRANK Key menber,要知道Key儲存的Sorted Set預設是根據Score對各個menber進行升序的排列,該命令就是用來擷取menber在該排列裡的次序,這就是所謂的秩。)

我們使用了zrevrank命令而不是zrank命令,這是因為Redis的預設排序是從低到高,但是在這個例子裡我們的秩劃分是從高到低。對於分類集合資料結構,最常見的應用案例是用來實現熱門排行榜系統。事實上,對於一些基於整數排序,且能以標記(score)來進行有效操作的東西,使用分類集合資料結構來處理應該都是不錯的選擇。 小結

對於Redis的5種資料結構,我們進行了高層次的概述。一件有趣的事情是,相對於最初構建時的想法,你經常能用Redis創造出一些更具實效的事情。對於字串資料結構和分類集合資料結構的使用,很有可能存在一些構建方法是還沒有人想到的。當你理解了那些常用的應用案例後,你將發現Redis對於許多類型的問題,都是很理想的選擇。還有,不要因為Redis展示了5種資料結構和相應的各種方法,就認為你必須要把所有的東西都用上。只使用一些命令去構建一個特性是很常見的。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.