許可證
《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檔案,然後根據你的系統架構,開啟64bit
或32bit
資料夾。
*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的基本概念,而get
和set
命令是對此最簡單的使用。你可以建立更多的使用者,去嘗試不同型別的關鍵字以及不同的值,看看一些不同的組合。
查詢(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-server
和redis-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)序列化物件,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 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的事務功能的工作。但是,如果我們增加一個watch
到powerlevel
,我們可以這樣做:
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的基礎,其他沒有關聯特定資料結構的命令也有很多。我們已經看過一些這樣的命令:info
, select
, flushdb
, multi
, exec
, discard
, watch
和keys
。這一章將看看其他的一些重要命令。
使用期限(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的列表資料結構有blpop
和brpop
命令,能從列表裡返回且刪除第一個(或最後一個)元素,或者被堵塞,直到有一個元素可供操作。這可以用來實現一個簡單的佇列。
(譯註:對於blpop
和brpop
命令,如果列表裡沒有關鍵字可供操作,連線將被堵塞,直到有另外的Redis客戶端使用lpush
或rpush
命令推入關鍵字為止。)
此外,Redis對於訊息釋出和頻道訂閱有著一流的支援。你可以開啟第二個redis-cli
視窗,去嘗試一下這些功能。在第一個視窗裡訂閱一個頻道(我們會稱它為warnings
):
subscribe warnings
其將會答覆你訂閱的資訊。現在,在另一個視窗,釋出一條訊息到warnings
頻道:
publish warnings "it's over 9000!"
如果你回到第一個視窗,你應該已經接收到warnings
頻道發來的訊息。
你可以訂閱多個頻道(subscribe channel1 channel2 ...
),訂閱一組基於模式的頻道(psubscribe warnings:*
),以及使用unsubscribe
和punsubscribe
命令停止監聽一個或多個頻道,或一個頻道模式。
最後,可以注意到publish
命令的返回值是1,這指出了接收到訊息的客戶端數量。
監控和延遲日誌(Monitor and Slow Log)
monitor
命令可以讓你檢視Redis正在做什麼。這是一個優秀的除錯工具,能讓你瞭解你的程式如何與Redis進行互動。在兩個redis-cli
視窗中選一個(如果其中一個還處於訂閱狀態,你可以使用unsubscribe
命令退訂,或者直接關掉視窗再重新開啟一個新視窗)鍵入monitor
命令。在另一個視窗,執行任何其他型別的命令(例如get
或set
命令)。在第一個視窗裡,你應該可以看到這些命令,包括他們的引數。
在實際生產環境裡,你應該謹慎執行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, ....}"
所有的事情不僅變得更為容易管理,而且我們能通過severity
或priority
來進行排序,還可以告訴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
的檔案。在任何時候,你都可以對這個檔案執行scp
、ftp
或cp
等常用命令。
有一種常見情況,在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是最好的技術投資之一。