redis教程(The little redis book中文版)

胡海超發表於2018-09-11

許可證

《The Little Redis Book》是經由Attribution-NonCommercial 3.0 Unported license許可的,你不需要為此書付錢。

你可以自由地對此書進行復制,分發,修改或者展示等操作。當然,你必須知道且認可這本書的作者是Karl Seguin,譯者是賴立維,而且不應該將此書用於商業用途。

關於這個許可證詳細描述在這裡:

http://creativecommons.org/licenses/by-nc/3.0/legalcode

關於作者

作者Karl Seguin是一名在多項技術領域浸淫多年的開發者。他是開源軟體計劃的活躍貢獻者,同時也是一名技術作者以及業餘演講者。他寫過若干關於Radis的文章以及一些工具。在他的一個面向業餘遊戲開發者的免費服務裡,Redis為其中的評級和統計功能提供了支援:mogade.com

Karl之前還寫了《The Little MongoDB Book》,這是一本免費且受好評,關於MongoDB的書。

他的部落格是http://openmymind.net,你也可以關注他的Twitter帳號,via @karlseguin

關於譯者

譯者 賴立維 是一名長在天朝的普通程式設計師,對許多技術都有濃厚的興趣,是開源軟體的支持者,Emacs的輕度使用者。

雖然譯者已經很認真地對待這次翻譯,但是限於水平有限,肯定會有不少錯漏,如果發現該書的翻譯有什麼需要修改,可以通過他的郵箱與他聯絡。他的郵箱是jasonlai256@gmail.com

致謝

必須特別感謝Perry Neal一直以來的指導,我的眼界、觸覺以及激情都來源於你。你為我提供了無價的幫助,感謝你。

最新版本

此書的最新有效資源在: http://github.com/karlseguin/the-little-redis-book

中文版是英文版的一個分支,最新的中文版本在:https://github.com/JasonLai256/the-little-redis-book

\clearpage

簡介

最近幾年來,關於持久化和資料查詢的相關技術,其需求已經增長到了讓人驚訝的程度。可以斷言,關係型資料庫再也不是放之四海皆準。換一句話說,圍繞資料的解決方案不可能再只有唯一一種。

對於我來說,在眾多新出現的解決方案和工具裡,最讓人興奮的,無疑是Redis。為什麼?首先是因為其讓人不可思議的容易學習,只需要簡短的幾個小時學習時間,就能對Redis有個大概的認識。還有,Redis在處理一組特定的問題集的同時能保持相當的通用性。更準確地說就是,Redis不會嘗試去解決關於資料的所有事情。在你足夠了解Redis後,事情就會變得越來越清晰,什麼是可行的,什麼是不應該由Redis來處理的。作為一名開發人員,如此的經驗當是相當的美妙。

當你能僅使用Redis去構建一個完整系統時,我想大多數人將會發現,Redis能使得他們的許多資料方案變得更為通用,不論是一個傳統的關係型資料庫,一個面向文件的系統,或是其它更多的東西。這是一種用來實現某些特定特性的解決方法。就類似於一個索引引擎,你不會在Lucene上構建整個程式,但當你需要足夠好的搜尋,為什麼不使用它呢?這對你和你的使用者都有好處。當然,關於Redis和索引引擎之間相似性的討論到此為止。

本書的目的是向讀者傳授掌握Redis所需要的基本知識。我們將會注重於學習Redis的5種資料結構,並研究各種資料建模方法。我們還會接觸到一些主要的管理細節和除錯技巧。

入門

每個人的學習方式都不一樣,有的人喜歡親自實踐學習,有的喜歡觀看教學視訊,還有的喜歡通過閱讀來學習。對於Redis,沒有什麼比親自實踐學習來得效果更好的了。Redis的安裝非常簡單。而且通過隨之安裝的一個簡單的命令解析程式,就能處理我們想做的一切事情。讓我們先花幾分鐘的時間把Redis安裝到我們的機器上。

Windows平臺

Redis並沒有官方支援Windows平臺,但還是可供選擇。你不會想在這裡配置實際的生產環境,不過在我過往的開發經歷裡並沒有感到有什麼限制。

首先進入https://github.com/dmajkic/redis/downloads,然後下載最新的版本(應該會在列表的最上方)。

獲取zip檔案,然後根據你的系統架構,開啟64bit32bit資料夾。

*nix和MacOSX平臺

對於*nix和MacOSX平臺的使用者,從原始檔來安裝是你的最佳選擇。通過最新的版本號來選擇,有效地址於http://redis.io/download。在編寫此書的時候,最新的版本是2.4.6,我們可以執行下面的命令來安裝該版本:

wget http://redis.googlecode.com/files/redis-2.4.6.tar.gz
tar xzf redis-2.4.6.tar.gz
cd redis-2.4.6
make

(當然,Redis同樣可以通過套件管理程式來安裝。例如,使用Homebrew的MaxOSX使用者可以只鍵入brew install redis即可。)

如果你是通過原始檔來安裝,二進位制可執行檔案會被放置在src目錄裡。通過執行cd src可跳轉到src目錄。

執行和連線Redis

如果一切都工作正常,那Redis的二進位制檔案應該已經可以曼妙地跳躍於你的指尖之下。Redis只有少量的可執行檔案,我們將著重於Redis的伺服器和命令列介面(一個類DOS的客戶端)。首先,讓我們來執行伺服器。在Windows平臺,雙擊redis-server,在*nix/MacOSX平臺則執行./redis-server.

如果你仔細看了啟動資訊,你會看到一個警告,指沒能找到redis.conf檔案。Redis將會採用內建的預設設定,這對於我們將要做的已經足夠了。

然後,通過雙擊redis-cli(Windows平臺)或者執行./redis-cli(*nix/MacOSX平臺),啟動Redis的控制檯。控制檯將會通過預設的埠(6379)來連線本地執行的伺服器。

可以在命令列介面鍵入info命令來檢視一切是不是都執行正常。你會很樂意看到這麼一大組關鍵字-值(key-value)對的顯示,這為我們檢視伺服器的狀態提供了大量有效資訊。

如果在上面的啟動步驟裡遇到什麼問題,我建議你到Redis的官方支援組裡獲取幫助。

驅動Redis

很快你就會發現,Redis的API就如一組定義明確的函式那般容易理解。Redis具有讓人難以置信的簡單性,其操作過程也同樣如此。這意味著,無論你是使用命令列程式,或是使用你喜歡的語言來驅動,整體的感覺都不會相差多少。因此,相對於命令列程式,如果你更願意通過一種程式語言去驅動Redis,你不會感覺到有任何適應的問題。如果真想如此,可以到Redis的客戶端推薦頁面下載適合的Redis載體。

\clearpage

第1章 - 基礎知識

是什麼使Redis顯得這麼特別?Redis具體能解決什麼型別的問題?要實際應用Redis,開發者必須儲備什麼知識?在我們能回答這麼一些問題之前,我們需要明白Redis到底是什麼。

Redis通常被人們認為是一種持久化的儲存器關鍵字-值型儲存(in-memory persistent key-value store)。我認為這種對Redis的描述並不太準確。Redis的確是將所有的資料存放於儲存器(更多是是按位儲存),而且也確實通過將資料寫入磁碟來實現持久化,但是Redis的實際意義比單純的關鍵字-值型儲存要來得深遠。糾正腦海裡的這種誤解觀點非常關鍵,否則你對於Redis之道以及其應用的洞察力就會變得越發狹義。

事實是,Redis引入了5種不同的資料結構,只有一個是典型的關鍵字-值型結構。理解Redis的關鍵就在於搞清楚這5種資料結構,其工作的原理都是如何,有什麼關聯方法以及你能怎樣應用這些資料結構去構建模型。首先,讓我們來弄明白這些資料結構的實際意義。

應用上面提及的資料結構概念到我們熟悉的關係型資料庫裡,我們可以認為其引入了一個單獨的資料結構——表格。表格既複雜又靈活,基於表格的儲存和管理,沒有多少東西是你不能進行建模的。然而,這種通用性並不是沒有缺點。具體來說就是,事情並不是總能達到假設中的簡單或者快速。相對於這種普遍適用(one-size-fits-all)的結構體系,我們可以使用更為專門化的結構體系。當然,因此可能有些事情我們會完成不了(至少,達不到很好的程度)。但話說回來,這樣做就能確定我們可以獲得想象中的簡單性和速度嗎?

針對特定型別的問題使用特定的資料結構?我們不就是這樣進行程式設計的嗎?你不會使用一個雜湊表去儲存每份資料,也不會使用一個標量變數去儲存。對我來說,這正是Redis的做法。如果你需要處理標量、列表、雜湊或者集合,為什麼不直接就用標量、列表、雜湊和集合去儲存他們?為什麼不是直接呼叫exists(key)去檢測一個已存在的值,而是要呼叫其他比O(1)(常量時間查詢,不會因為待處理元素的增長而變慢)慢的操作?

資料庫(Databases)

與你熟悉的關係型資料庫一致,Redis有著相同的資料庫基本概念,即一個資料庫包含一組資料。典型的資料庫應用案例是,將一個程式的所有資料組織起來,使之與另一個程式的資料保持獨立。

在Redis裡,資料庫簡單的使用一個數字編號來進行辨認,預設資料庫的數字編號是0。如果你想切換到一個不同的資料庫,你可以使用select命令來實現。在命令列介面裡鍵入select 1,Redis應該會回覆一條OK的資訊,然後命令列介面裡的提示符會變成類似redis 127.0.0.1:6379[1]>這樣。如果你想切換回預設資料庫,只要在命令列介面鍵入select 0即可。

命令、關鍵字和值(Commands, Keys and Values)

Redis不僅僅是一種簡單的關鍵字-值型儲存,從其核心概念來看,Redis的5種資料結構中的每一個都至少有一個關鍵字和一個值。在轉入其它關於Redis的有用資訊之前,我們必須理解關鍵字和值的概念。

關鍵字(Keys)是用來標識資料塊。我們將會很常跟關鍵字打交道,不過在現在,明白關鍵字就是類似於users:leto這樣的表述就足夠了。一般都能很好地理解到,這樣關鍵字包含的資訊是一個名為leto的使用者。這個關鍵字裡的冒號沒有任何特殊含義,對於Redis而言,使用分隔符來組織關鍵字是很常見的方法。

值(Values)是關聯於關鍵字的實際值,可以是任何東西。有時候你會儲存字串,有時候是整數,還有時候你會儲存序列化物件(使用JSON、XML或其他格式)。在大多數情況下,Redis會把值看做是一個位元組序列,而不會關注它們實質上是什麼。要注意,不同的Redis載體處理序列化會有所不同(一些會讓你自己決定)。因此,在這本書裡,我們將僅討論字串、整數和JSON。

現在讓我們活動一下手指吧。在命令列介面鍵入下面的命令:

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

這就是Redis命令的基本構成。首先我們要有一個確定的命令,在上面的語句裡就是set。然後就是相應的引數,set命令接受兩個引數,包括要設定的關鍵字,以及相應要設定的值。很多的情況是,命令接受一個關鍵字(當這種情況出現,其經常是第一個引數)。你能想到如何去獲取這個值嗎?我想你會說(當然一時拿不準也沒什麼):

get users:leto

關鍵字和值的是Redis的基本概念,而getset命令是對此最簡單的使用。你可以建立更多的使用者,去嘗試不同型別的關鍵字以及不同的值,看看一些不同的組合。

查詢(Querying)

隨著學習的持續深入,兩件事情將變得清晰起來。對於Redis而言,關鍵字就是一切,而值是沒有任何意義。更通俗來看就是,Redis不允許你通過值來進行查詢。回到上面的例子,我們就不能查詢生活在dune行星上的使用者。

對許多人來說,這會引起一些擔憂。在我們生活的世界裡,資料查詢是如此的靈活和強大,而Redis的方式看起來是這麼的原始和不高效。不要讓這些擾亂你太久。要記住,Redis不是一種普遍使用(one-size-fits-all)的解決方案,確實存在這麼一些事情是不應該由Redis來解決的(因為其查詢的限制)。事實上,在考慮了這些情況後,你會找到新的方法去構建你的資料。

很快,我們就能看到更多實際的用例。很重要的一點是,我們要明白關於Redis的這些基本事實。這能幫助我們弄清楚為什麼值可以是任何東西,因為Redis從來不需要去讀取或理解它們。而且,這也可以幫助我們理清思路,然後去思考如何在這個新世界裡建立模型。

儲存器和持久化(Memory and Persistence)

我們之前提及過,Redis是一種持久化的儲存器記憶體儲(in-memory persistent store)。對於持久化,預設情況下,Redis會根據已變更的關鍵字數量來進行判斷,然後在磁碟裡建立資料庫的快照(snapshot)。你可以對此進行設定,如果X個關鍵字已變更,那麼每隔Y秒儲存資料庫一次。預設情況下,如果1000個或更多的關鍵字已變更,Redis會每隔60秒儲存資料庫;而如果9個或更少的關鍵字已變更,Redis會每隔15分鐘儲存資料庫。

除了建立磁碟快照外,Redis可以在附加模式下執行。任何時候,如果有一個關鍵字變更,一個單一附加(append-only)的檔案會在磁碟裡進行更新。在一些情況裡,雖然硬體或軟體可能發生錯誤,但用那60秒有效資料儲存去換取更好效能是可以接受的。而在另一些情況裡,這種損失就難以讓人接受,Redis為你提供了選擇。在第5章裡,我們將會看到第三種選擇,其將持久化任務減荷到一個從屬資料庫裡。

至於儲存器,Redis會將所有資料都保留在儲存器中。顯而易見,執行Redis具有不低的成本:因為RAM仍然是最昂貴的伺服器硬體部件。

我很清楚有一些開發者對即使是一點點的資料空間都是那麼的敏感。一本《威廉·莎士比亞全集》需要近5.5MB的儲存空間。對於縮放的需求,其它的解決方案趨向於IO-bound或者CPU-bound。這些限制(RAM或者IO)將會需要你去理解更多機器實際依賴的資料型別,以及應該如何去進行儲存和查詢。除非你是儲存大容量的多媒體檔案到Redis中,否則儲存器記憶體儲應該不會是一個問題。如果這對於一個程式是個問題,你就很可能不會用IO-bound的解決方案。

Redis有虛擬儲存器的支援。然而,這個功能已經被認為是失敗的了(通過Redis的開發者),而且它的使用已經被廢棄了。

(從另一個角度來看,一本5.5MB的《威廉·莎士比亞全集》可以通過壓縮減小到近2MB。當然,Redis不會自動對值進行壓縮,但是因為其將所有值都看作是位元組,沒有什麼限制讓你不能對資料進行壓縮/解壓,通過犧牲處理時間來換取儲存空間。)

整體來看(Putting It Together)

我們已經接觸了好幾個高層次的主題。在繼續深入Redis之前,我想做的最後一件事情是將這些主題整合起來。這些主題包括,查詢的限制,資料結構以及Redis在儲存器記憶體儲資料的方法。

當你將這3個主題整合起來,你最終會得出一個絕妙的結論:速度。一些人可能會想,當然Redis會很快速,要知道所有的東西都在儲存器裡。但這僅僅是其中的一部分,讓Redis閃耀的真正原因是其不同於其它解決方案的特殊資料結構。

能有多快速?這依賴於很多東西,包括你正在使用著哪個命令,資料的型別等等。但Redis的效能測試是趨向於數萬或數十萬次操作每秒。你可以通過執行redis-benchmark(就在redis-serverredis-cli的同一個資料夾裡)來進行測試。

我曾經試過將一組使用傳統模型的程式碼轉向使用Redis。在傳統模型裡,執行一個我寫的載入測試,需要超過5分鐘的時間來完成。而在Redis裡,只需要150毫秒就完成了。你不會總能得到這麼好的收穫,但希望這能讓你對我們所談的東西有更清晰的理解。

理解Redis的這個特性很重要,因為這將影響到你如何去與Redis進行互動。擁有SQL背景的程式設計師通常會致力於讓資料庫的資料往返次數減至最小。這對於任何系統都是個好建議,包括Redis。然而,考慮到我們是在處理比較簡單的資料結構,有時候我們還是需要與Redis伺服器頻繁互動,以達到我們的目的。剛開始的時候,可能會對這種資料訪問模式感到不太自然。實際上,相對於我們通過Redis獲得的高效能而言,這僅僅是微不足道的損失。

小結

雖然我們只接觸和擺弄了Redis的冰山一角,但我們討論的主題已然覆蓋了很大範圍內的東西。如果覺得有些事情還是不太清楚(例如查詢),不用為此而擔心,在下一章我們將會繼續深入探討,希望你的問題都能得到解答。

這一章的要點包括:

  • 關鍵字(Keys)是用於標識一段資料的一個字串

  • 值(Values)是一段任意的位元組序列,Redis不會關注它們實質上是什麼

  • Redis展示了(也實現了)5種專門的資料結構

  • 上面的幾點使得Redis快速而且容易使用,但要知道Redis並不適用於所有的應用場景

\clearpage

第2章 - 資料結構

現在開始將探究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 <key>能用來獲取一個關鍵字對應值的長度;getrange <key> <start> <end>將返回指定範圍內的關鍵字對應值;append <key> <value>會將value附加到已存在的關鍵字對應值中(如果該關鍵字並不存在,則會建立一個新的關鍵字-值對)。不要猶豫,去試試看這些命令吧。下面是我得到的:

> strlen users:leto
(integer) 42

> getrange users:leto 27 40
"likes: [spice]"

> append users:leto " OVER 9000!!"
(integer) 54

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

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

> 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(一個不是整數的值),然後看看會發生什麼(應該會得到一個錯誤)。

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

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

雜湊(Hashes)

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

hset users:goku powerlevel 9000
hget users:goku powerlevel

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

hmset users:goku race saiyan age 737
hmget users:goku race powerlevel
hgetall users:goku
hkeys users:goku
hdel users:goku age

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

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

列表(Lists)

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

lpush newusers goku
ltrim 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 jessica
sadd friends:duncan paul jessica alia

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

sismember friends:leto jessica
sismember 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種資料結構和相應的各種方法,就認為你必須要把所有的東西都用上。只使用一些命令去構建一個特性是很常見的。

\clearpage

第3章 - 使用資料結構

在上一章裡,我們談論了Redis的5種資料結構,對於一些可能的用途也給出了用例。現在是時候來看看一些更高階,但依然很常見的主題和設計模式。

大O表示法(Big O Notation)

在本書中,我們之前就已經看到過大O表示法,包括O(1)和O(N)的表示。大O表示法的慣常用途是,描述一些用於處理一定數量元素的行為的綜合表現。在Redis裡,對於一個要處理一定數量元素的命令,大O表示法讓我們能瞭解該命令的大概執行速度。

在Redis的文件裡,每一個命令的時間複雜度都用大O表示法進行了描述,還能知道各命令的具體效能會受什麼因素影響。讓我們來看看一些用例。

常數時間複雜度O(1)被認為是最快速的,無論我們是在處理5個元素還是5百萬個元素,最終都能得到相同的效能。對於sismember命令,其作用是告訴我們一個值是否屬於一個集合,時間複雜度為O(1)。sismember命令很強大,很大部分的原因是其高效的效能特徵。許多Redis命令都具有O(1)的時間複雜度。

對數時間複雜度O(log(N))被認為是第二快速的,其通過使需掃描的區間不斷皺縮來快速完成處理。使用這種“分而治之”的方式,大量的元素能在幾個迭代過程裡被快速分解完整。zadd命令的時間複雜度就是O(log(N)),其中N是在分類集合中的元素數量。

再下來就是線性時間複雜度O(N),在一個表格的非索引列裡進行查詢就需要O(N)次操作。ltrim命令具有O(N)的時間複雜度,但是,在ltrim命令裡,N不是列表所擁有的元素數量,而是被刪除的元素數量。從一個具有百萬元素的列表裡用ltrim命令刪除1個元素,要比從一個具有一千個元素的列表裡用ltrim命令刪除10個元素來的快速(實際上,兩者很可能會是一樣快,因為兩個時間都非常的小)。

根據給定的最小和最大的值的標記,zremrangebyscore命令會在一個分類集合裡進行刪除元素操作,其時間複雜度是O(log(N)+M)。這看起來似乎有點兒雜亂,通過閱讀文件可以知道,這裡的N指的是在分類集合裡的總元素數量,而M則是被刪除的元素數量。可以看出,對於效能而言,被刪除的元素數量很可能會比分類集合裡的總元素數量更為重要。

(譯註:zremrangebyscore命令的具體構成是ZREMRANGEBYSCORE Key max mix。)

對於sort命令,其時間複雜度為O(N+M*log(M)),我們將會在下一章談論更多的相關細節。從sort命令的效能特徵來看,可以說這是Redis裡最複雜的一個命令。

還存在其他的時間複雜度描述,包括O(N^2)和O(C^N)。隨著N的增大,其效能將急速下降。在Redis裡,沒有任何一個命令具有這些型別的時間複雜度。

值得指出的一點是,在Redis裡,當我們發現一些操作具有O(N)的時間複雜度時,我們可能可以找到更為好的方法去處理。

(譯註:對於Big O Notation,相信大家都非常的熟悉,雖然原文僅僅是對該表示法進行簡單的介紹,但限於個人的演算法知識和文筆水平實在有限,此小節的翻譯讓我頭痛頗久,最終成果也確實難以讓人滿意,望見諒。)

仿多關鍵字查詢(Pseudo Multi Key Queries)

時常,你會想通過不同的關鍵字去查詢相同的值。例如,你會想通過電子郵件(當使用者開始登入時)去獲取使用者的具體資訊,或者通過使用者id(在使用者登入後)去獲取。有一種很不實效的解決方法,其將使用者物件分別放置到兩個字串值裡去:

set users:leto@dune.gov "{id: 9001, email: 'leto@dune.gov', ...}"
set users:9001 "{id: 9001, email: 'leto@dune.gov', ...}"

這種方法很糟糕,如此不但會產生兩倍數量的記憶體,而且這將會成為資料管理的惡夢。

如果Redis允許你將一個關鍵字連結到另一個的話,可能情況會好很多,可惜Redis並沒有提供這樣的功能(而且很可能永遠都不會提供)。Redis發展到現在,其開發的首要目的是要保持程式碼和API的整潔簡單,關鍵字連結功能的內部實現並不符合這個前提(對於關鍵字,我們還有很多相關方法沒有談論到)。其實,Redis已經提供瞭解決的方法:雜湊。

使用雜湊資料結構,我們可以擺脫重複的纏繞:

set users:9001 "{id: 9001, email: leto@dune.gov, ...}"
hset users:lookup:email leto@dune.gov 9001

我們所做的是,使用域來作為一個二級索引,然後去引用單個使用者物件。要通過id來獲取使用者資訊,我們可以使用一個普通的get命令:

get users:9001

而如果想通過電子郵箱來獲取使用者資訊,我們可以使用hget命令再配合使用get命令(Ruby程式碼):

id = redis.hget('users:lookup:email', 'leto@dune.gov')
user = redis.get("users:#{id}")

你很可能將會經常使用這類用法。在我看來,這就是雜湊真正耀眼的地方。在你瞭解這類用法之前,這可能不是一個明顯的用例。

引用和索引(References and Indexes)

我們已經看過幾個關於值引用的用例,包括介紹列表資料結構時的用例,以及在上面使用雜湊資料結構來使查詢更靈活一些。進行歸納後會發現,對於那些值與值間的索引和引用,我們都必須手動的去管理。誠實來講,這確實會讓人有點沮喪,尤其是當你想到那些引用相關的操作,如管理、更新和刪除等,都必須手動的進行時。在Redis裡,這個問題還沒有很好的解決方法。

我們已經看到,集合資料結構很常被用來實現這類索引:

sadd friends:leto ghanima paul chani jessica

這個集合裡的每一個成員都是一個Redis字串資料結構的引用,而每一個引用的值則包含著使用者物件的具體資訊。那麼如果chani改變了她的名字,或者刪除了她的帳號,應該如何處理?從整個朋友圈的關係結構來看可能會更好理解,我們知道,chani也有她的朋友:

sadd friends_of:chani leto paul

如果你有什麼待處理情況像上面那樣,那在維護成本之外,還會有對於額外索引值的處理和儲存空間的成本。這可能會令你感到有點退縮。在下一小節裡,我們將會談論減少使用額外資料互動的效能成本的一些方法(在第1章我們粗略地討論了下)。

如果你確實在擔憂著這些情況,其實,關係型資料庫也有同樣的開銷。索引需要一定的儲存空間,必須通過掃描或查詢,然後才能找到相應的記錄。其開銷也是存在的,當然他們對此做了很多的優化工作,使之變得更為有效。

再次說明,需要在Redis裡手動地管理引用確實是頗為棘手。但是,對於你關心的那些問題,包括效能或儲存空間等,應該在經過測試後,才會有真正的理解。我想你會發現這不會是一個大問題。

資料互動和流水線(Round Trips and Pipelining)

我們已經提到過,與伺服器頻繁互動是Redis的一種常見模式。這類情況可能很常出現,為了使我們能獲益更多,值得仔細去看看我們能利用哪些特性。

許多命令能接受一個或更多的引數,也有一種關聯命令(sister-command)可以接受多個引數。例如早前我們看到過mget命令,接受多個關鍵字,然後返回值:

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

或者是sadd命令,能新增一個或多個成員到集合裡:

sadd friends:vladimir piter
sadd friends:paul jessica leto "leto II" chani

Redis還支援流水線功能。通常情況下,當一個客戶端傳送請求到Redis後,在傳送下一個請求之前必須等待Redis的答覆。使用流水線功能,你可以傳送多個請求,而不需要等待Redis響應。這不但減少了網路開銷,還能獲得效能上的顯著提高。

值得一提的是,Redis會使用儲存器去排列命令,因此批量執行命令是一個好主意。至於具體要多大的批量,將取決於你要使用什麼命令(更明確來說,該引數有多大)。另一方面來看,如果你要執行的命令需要差不多50個字元的關鍵字,你大概可以對此進行數千或數萬的批量操作。

對於不同的Redis載體,在流水線裡執行命令的方式會有所差異。在Ruby裡,你傳遞一個程式碼塊到pipelined方法:

redis.pipelined do
  9001.times do
	redis.incr('powerlevel')
  end
end

正如你可能猜想到的,流水線功能可以實際地加速一連串命令的處理。

事務(Transactions)

每一個Redis命令都具有原子性,包括那些一次處理多項事情的命令。此外,對於使用多個命令,Redis支援事務功能。

你可能不知道,但Redis實際上是單執行緒執行的,這就是為什麼每一個Redis命令都能夠保證具有原子性。當一個命令在執行時,沒有其他命令會執行(我們會在往後的章節裡簡略談論一下Scaling)。在你考慮到一些命令去做多項事情時,這會特別的有用。例如:

incr命令實際上就是一個get命令然後緊隨一個set命令。

getset命令設定一個新的值然後返回原始值。

setnx命令首先測試關鍵字是否存在,只有當關鍵字不存在時才設定值

雖然這些都很有用,但在實際開發時,往往會需要執行具有原子性的一組命令。若要這樣做,首先要執行multi命令,緊隨其後的是所有你想要執行的命令(作為事務的一部分),最後執行exec命令去實際執行命令,或者使用discard命令放棄執行命令。Redis的事務功能保證了什麼?

  • 事務中的命令將會按順序地被執行

  • 事務中的命令將會如單個原子操作般被執行(沒有其它的客戶端命令會在中途被執行)

  • 事務中的命令要麼全部被執行,要麼不會執行

你可以(也應該)在命令列介面對事務功能進行一下測試。還有一點要注意到,沒有什麼理由不能結合流水線功能和事務功能。

multi
hincrby groups:1percent balance -9000000000
hincrby groups:99percent balance 9000000000
exec

最後,Redis能讓你指定一個關鍵字(或多個關鍵字),當關鍵字有改變時,可以檢視或者有條件地應用一個事務。這是用於當你需要獲取值,且待執行的命令基於那些值時,所有都在一個事務裡。對於上面展示的程式碼,我們不能去實現自己的incr命令,因為一旦exec命令被呼叫,他們會全部被執行在一塊。我們不能這麼做:

redis.multi()
current = redis.get('powerlevel')
redis.set('powerlevel', current + 1)
redis.exec()

(譯註:雖然Redis是單執行緒執行的,但是我們可以同時執行多個Redis客戶端程式,常見的併發問題還是會出現。像上面的程式碼,在get執行之後,set執行之前,powerlevel的值可能會被另一個Redis客戶端給改變,從而造成錯誤。)

這些不是Redis的事務功能的工作。但是,如果我們增加一個watchpowerlevel,我們可以這樣做:

redis.watch('powerlevel')
current = redis.get('powerlevel')
redis.multi()
redis.set('powerlevel', current + 1)
redis.exec()

在我們呼叫watch後,如果另一個客戶端改變了powerlevel的值,我們的事務將會執行失敗。如果沒有客戶端改變powerlevel的值,那麼事務會繼續工作。我們可以在一個迴圈裡執行這些程式碼,直到其能正常工作。

關鍵字反模式(Keys Anti-Pattern)

在下一章中,我們將會談論那些沒有確切關聯到資料結構的命令,其中的一些是管理或除錯工具。然而有一個命令我想特別地在這裡進行談論:keys命令。這個命令需要一個模式,然後查詢所有匹配的關鍵字。這個命令看起來很適合一些任務,但這不應該用在實際的產品程式碼裡。為什麼?因為這個命令通過線性掃描所有的關鍵字來進行匹配。或者,簡單地說,這個命令太慢了。

人們會如此去使用這個命令?一般會用來構建一個本地的Bug追蹤服務。每一個帳號都有一個id,你可能會通過一個看起來像bug:account_id:bug_id的關鍵字,把每一個Bug儲存到一個字串資料結構值中去。如果你在任何時候需要查詢一個帳號的Bug(顯示它們,或者當使用者刪除了帳號時刪除掉這些Bugs),你可能會嘗試去使用keys命令:

keys bug:1233:*

更好的解決方法應該使用一個雜湊資料結構,就像我們可以使用雜湊資料結構來提供一種方法去展示二級索引,因此我們可以使用域來組織資料:

hset bugs:1233 1 "{id:1, account: 1233, subject: '...'}"
hset bugs:1233 2 "{id:2, account: 1233, subject: '...'}"

從一個帳號裡獲取所有的Bug標識,可以簡單地呼叫hkeys bugs:1233。去刪除一個指定的Bug,可以呼叫hdel bugs:1233 2。如果要刪除了一個帳號,可以通過del bugs:1233把關鍵字刪除掉。

小結

結合這一章以及前一章,希望能讓你得到一些洞察力,瞭解如何使用Redis去支援(Power)實際專案。還有其他的模式可以讓你去構建各種型別的東西,但真正的關鍵是要理解基本的資料結構。你將能領悟到,這些資料結構是如何能夠實現你最初視角之外的東西。

\clearpage

第4章 超越資料結構

5種資料結構組成了Redis的基礎,其他沒有關聯特定資料結構的命令也有很多。我們已經看過一些這樣的命令:infoselectflushdbmultiexecdiscardwatchkeys。這一章將看看其他的一些重要命令。

使用期限(Expiration)

Redis允許你標記一個關鍵字的使用期限。你可以給予一個Unix時間戳形式(自1970年1月1日起)的絕對時間,或者一個基於秒的存活時間。這是一個基於關鍵字的命令,因此其不在乎關鍵字表示的是哪種型別的資料結構。

expire pages:about 30
expireat pages:about 1356933600

第一個命令將會在30秒後刪除掉關鍵字(包括其關聯的值)。第二個命令則會在2012年12月31日上午12點刪除掉關鍵字。

這讓Redis能成為一個理想的緩衝引擎。通過ttl命令,你可以知道一個關鍵字還能夠存活多久。而通過persist命令,你可以把一個關鍵字的使用期限刪除掉。

ttl pages:about
persist pages:about

最後,有個特殊的字串命令,setex命令讓你可以在一個單獨的原子命令裡設定一個字串值,同時裡指定一個生存期(這比任何事情都要方便)。

setex pages:about 30 '<h1>about us</h1>....'

釋出和訂閱(Publication and Subscriptions)

Redis的列表資料結構有blpopbrpop命令,能從列表裡返回且刪除第一個(或最後一個)元素,或者被堵塞,直到有一個元素可供操作。這可以用來實現一個簡單的佇列。

(譯註:對於blpopbrpop命令,如果列表裡沒有關鍵字可供操作,連線將被堵塞,直到有另外的Redis客戶端使用lpushrpush命令推入關鍵字為止。)

此外,Redis對於訊息釋出和頻道訂閱有著一流的支援。你可以開啟第二個redis-cli視窗,去嘗試一下這些功能。在第一個視窗裡訂閱一個頻道(我們會稱它為warnings):

subscribe warnings

其將會答覆你訂閱的資訊。現在,在另一個視窗,釋出一條訊息到warnings頻道:

publish warnings "it's over 9000!"

如果你回到第一個視窗,你應該已經接收到warnings頻道發來的訊息。

你可以訂閱多個頻道(subscribe channel1 channel2 ...),訂閱一組基於模式的頻道(psubscribe warnings:*),以及使用unsubscribepunsubscribe命令停止監聽一個或多個頻道,或一個頻道模式。

最後,可以注意到publish命令的返回值是1,這指出了接收到訊息的客戶端數量。

監控和延遲日誌(Monitor and Slow Log)

monitor命令可以讓你檢視Redis正在做什麼。這是一個優秀的除錯工具,能讓你瞭解你的程式如何與Redis進行互動。在兩個redis-cli視窗中選一個(如果其中一個還處於訂閱狀態,你可以使用unsubscribe命令退訂,或者直接關掉視窗再重新開啟一個新視窗)鍵入monitor命令。在另一個視窗,執行任何其他型別的命令(例如getset命令)。在第一個視窗裡,你應該可以看到這些命令,包括他們的引數。

在實際生產環境裡,你應該謹慎執行monitor命令,這真的僅僅就是一個很有用的除錯和開發工具。除此之外,沒有更多要說的了。

隨同monitor命令一起,Redis擁有一個slowlog命令,這是一個優秀的效能剖析工具。其會記錄執行時間超過一定數量微秒的命令。在下一章節,我們會簡略地涉及如何配置Redis,現在你可以按下面的輸入配置Redis去記錄所有的命令:

config set slowlog-log-slower-than 0

然後,執行一些命令。最後,你可以檢索到所有日誌,或者檢索最近的那些日誌:

slowlog get
slowlog get 10

通過鍵入slowlog len,你可以獲取延遲日誌裡的日誌數量。

對於每個被你鍵入的命令,你應該檢視4個引數:

  • 一個自動遞增的id

  • 一個Unix時間戳,表示命令開始執行的時間

  • 一個微妙級的時間,顯示命令執行的總時間

  • 該命令以及所帶引數

延遲日誌儲存在儲存器中,因此在生產環境中執行(即使有一個低閥值)也應該不是一個問題。預設情況下,它將會追蹤最近的1024個日誌。

排序(Sort)

sort命令是Redis最強大的命令之一。它讓你可以在一個列表、集合或者分類集合裡對值進行排序(分類集合是通過標記來進行排序,而不是集合裡的成員)。下面是一個sort命令的簡單用例:

rpush users:leto:guesses 5 9 10 2 4 10 19 2
sort users:leto:guesses

這將返回進行升序排序後的值。這裡有一個更高階的例子:

sadd friends:ghanima leto paul chani jessica alia duncan
sort friends:ghanima limit 0 3 desc alpha

上面的命令向我們展示了,如何對已排序的記錄進行分頁(通過limit),如何返回降序排序的結果(通過desc),以及如何用字典序排序代替數值序排序(通過alpha)。

sort命令的真正力量是其基於引用物件來進行排序的能力。早先的時候,我們說明了列表、集合和分類集合很常被用於引用其他的Redis物件,sort命令能夠解引用這些關係,而且通過潛在值來進行排序。例如,假設我們有一個Bug追蹤器能讓使用者看到各類已存在問題。我們可能使用一個集合資料結構去追蹤正在被監視的問題:

sadd watch:leto 12339 1382 338 9338

你可能會有強烈的感覺,想要通過id來排序這些問題(預設的排序就是這樣的),但是,我們更可能是通過問題的嚴重性來對這些問題進行排序。為此,我們要告訴Redis將使用什麼模式來進行排序。首先,為了可以看到一個有意義的結果,讓我們新增多一點資料:

set severity:12339 3
set severity:1382 2
set severity:338 5
set severity:9338 4

要通過問題的嚴重性來降序排序這些Bug,你可以這樣做:

sort watch:leto by severity:* desc

Redis將會用儲存在列表(集合或分類集合)中的值去替代模式中的*(通過by)。這會建立出關鍵字名字,Redis將通過查詢其實際值來排序。

在Redis裡,雖然你可以有成千上萬個關鍵字,類似上面展示的關係還是會引起一些混亂。幸好,sort命令也可以工作在雜湊資料結構及其相關域裡。相對於擁有大量的高層次關鍵字,你可以利用雜湊:

hset bug:12339 severity 3
hset bug:12339 priority 1
hset bug:12339 details "{id: 12339, ....}"

hset bug:1382 severity 2
hset bug:1382 priority 2
hset bug:1382 details "{id: 1382, ....}"

hset bug:338 severity 5
hset bug:338 priority 3
hset bug:338 details "{id: 338, ....}"

hset bug:9338 severity 4
hset bug:9338 priority 2
hset bug:9338 details "{id: 9338, ....}"

所有的事情不僅變得更為容易管理,而且我們能通過severitypriority來進行排序,還可以告訴sort命令具體要檢索出哪一個域的資料:

sort watch:leto by bug:*->priority get bug:*->details

相同的值替代出現了,但Redis還能識別->符號,用它來檢視雜湊中指定的域。裡面還包括了get引數,這裡也會進行值替代和域檢視,從而檢索出Bug的細節(details域的資料)。

對於太大的集合,sort命令的執行可能會變得很慢。好訊息是,sort命令的輸出可以被儲存起來:

sort watch:leto by bug:*->priority get bug:*->details store watch_by_priority:leto

使用我們已經看過的expiration命令,再結合sort命令的store能力,這是一個美妙的組合。

小結

這一章主要關注那些非特定資料結構關聯的命令。和其他事情一樣,它們的使用依情況而定。構建一個程式或特性時,可能不會用到使用期限、釋出和訂閱或者排序等功能。但知道這些功能的存在是很好的。而且,我們也只接觸到了一些命令。還有更多的命令,當你消化理解完這本書後,非常值得去瀏覽一下完整的命令列表

\clearpage

第5章 - 管理

在最後一章裡,我們將集中談論Redis執行中的一些管理方面內容。這是一個不完整的Redis管理指南,我們將會回答一些基本的問題,初接觸Redis的新使用者可能會很感興趣。

配置(Configuration)

當你第一次執行Redis的伺服器,它會向你顯示一個警告,指redis.conf檔案沒有被找到。這個檔案可以被用來配置Redis的各個方面。一個充分定義(well-documented)的redis.conf檔案對各個版本的Redis都有效。範例檔案包含了預設的配置選項,因此,對於想要了解設定在幹什麼,或預設設定是什麼,都會很有用。你可以在https://github.com/antirez/redis/raw/2.4.6/redis.conf找到這個檔案。

這個配置檔案針對的是Redis 2.4.6,你應該用你的版本號替代上面URL裡的"2.4.6"。執行info命令,其顯示的第一個值就是Redis的版本號。

因為這個檔案已經是充分定義(well-documented),我們就不去再進行設定了。

除了通過redis.conf檔案來配置Redis,config set命令可以用來對個別值進行設定。實際上,在將slowlog-log-slower-than設定為0時,我們就已經使用過這個命令了。

還有一個config get命令能顯示一個設定值。這個命令支援模式匹配,因此如果我們想要顯示關聯於日誌(logging)的所有設定,我們可以這樣做:

config get *log*

驗證(Authentication)

通過設定requirepass(使用config set命令或redis.conf檔案),可以讓Redis需要一個密碼驗證。當requirepass被設定了一個值(就是待用的密碼),客戶端將需要執行一個auth password命令。

一旦一個客戶端通過了驗證,就可以在任意資料庫裡執行任何一條命令,包括flushall命令,這將會清除掉每一個資料庫裡的所有關鍵字。通過配置,你可以重新命名一些重要命令為混亂的字串,從而獲得一些安全性。

rename-command CONFIG 5ec4db169f9d4dddacbfb0c26ea7e5ef
rename-command FLUSHALL 1041285018a942a4922cbf76623b741e

或者,你可以將新名字設定為一個空字串,從而禁用掉一個命令。

大小限制(Size Limitations)

當你開始使用Redis,你可能會想知道,我能使用多少個關鍵字?還可能想知道,一個雜湊資料結構能有多少個域(尤其是當你用它來組織資料時),或者是,一個列表資料結構或集合資料結構能有多少個元素?對於每一個例項,實際限制都能達到億萬級別(hundreds of millions)。

複製(Replication)

Redis支援複製功能,這意味著當你向一個Redis例項(Master)進行寫入時,一個或多個其他例項(Slaves)能通過Master例項來保持更新。可以在配置檔案裡設定slaveof,或使用slaveof命令來配置一個Slave例項。對於那些沒有進行這些設定的Redis例項,就可能一個Master例項。

為了更好保護你的資料,複製功能拷貝資料到不同的伺服器。複製功能還能用於改善效能,因為讀取請求可以被髮送到Slave例項。他們可能會返回一些稍微滯後的資料,但對於大多數程式來說,這是一個值得做的折衷。

遺憾的是,Redis的複製功能還沒有提供自動故障恢復。如果Master例項崩潰了,一個Slave例項需要手動的進行升級。如果你想使用Redis去達到某種高可用性,對於使用心跳監控(heartbeat monitoring)和指令碼自動開關(scripts to automate the switch)的傳統高可用性工具來說,現在還是一個棘手的難題。

備份檔案(Backups)

備份Redis非常簡單,你可以將Redis的快照(snapshot)拷貝到任何地方,包括S3、FTP等。預設情況下,Redis會把快照儲存為一個名為dump.rdb的檔案。在任何時候,你都可以對這個檔案執行scpftpcp等常用命令。

有一種常見情況,在Master例項上會停用快照以及單一附加檔案(aof),然後讓一個Slave例項去處理備份事宜。這可以幫助減少Master例項的載荷。在不損害整體系統響應性的情況下,你還可以在Slave例項上設定更多主動儲存的引數。

縮放和Redis叢集(Scaling and Redis Cluster)

複製功能(Replication)是一個成長中的網站可以利用的第一個工具。有一些命令會比另外一些來的昂貴(例如sort命令),將這些執行載荷轉移到一個Slave例項裡,可以保持整體系統對於查詢的快速響應。

此外,通過分發你的關鍵字到多個Redis例項裡,可以達到真正的縮放Redis(記住,Redis是單執行緒的,這些可以執行在同一個邏輯框裡)。隨著時間的推移,你將需要特別注意這些事情(儘管許多的Redis載體都提供了consistent-hashing演算法)。對於資料水平分佈(horizontal distribution)的考慮不在這本書所討論的範圍內。這些東西你也很可能不需要去擔心,但是,無論你使用哪一種解決方案,有一些事情你還是必須意識到。

好訊息是,這些工作都可在Redis叢集下進行。不僅提供水平縮放(包括均衡),為了高可用性,還提供了自動故障恢復。

高可用性和縮放是可以達到的,只要你願意為此付出時間和精力,Redis叢集也使事情變得簡單多了。

小結

在過去的一段時間裡,已經有許多的計劃和網站使用了Redis,毫無疑問,Redis已經可以應用於實際生產中了。然而,一些工具還是不夠成熟,尤其是一些安全性和可用性相關的工具。對於Redis叢集,我們希望很快就能看到其實現,這應該能為一些現有的管理挑戰提供處理幫忙。

\clearpage

總結

在許多方面,Redis體現了一種簡易的資料處理方式,其剝離掉了大部分的複雜性和抽象,並可有效的在不同系統裡執行。不少情況下,選擇Redis不是最佳的選擇。在另一些情況裡,Redis就像是為你的資料提供了特別定製的解決方案。

最終,回到我最開始所說的:Redis很容易學習。現在有許多的新技術,很難弄清楚哪些才真正值得我們花時間去學習。如果你從實際好處來考慮,Redis提供了他的簡單性。我堅信,對於你和你的團隊,學習Redis是最好的技術投資之一。

相關文章