随着網際網路業務對性能需求日益強烈,作為Key/Value存儲的Redis具有資料類型豐富和性能表現優異的特點。如果能夠熟練地駕馭它,不管是把它用做緩存還是存儲,對很多大型應用都很多幫助。新浪作為世界上最大的Redis使用者,體會到了Redis為高并發線上業務帶來的好處,但同時也遇到了很多挑戰,新浪為推動Redis這種NoSQL産品在中國網際網路産品技術架構中的使用做出了卓越的貢獻。作為國内第一本推進Redis普及的書,此書比較詳細地介紹了Redis入門必備的基礎知識,同時擁有了一些實踐性方面的章節。如果你對Redis感興趣,推薦你閱讀此書,此書會為你開啟Redis的大門。
---- 楊海朝,新浪首席資料架構師
拿到這本書,看到
豆瓣上8.3分的評價,Redis這個如此廣泛運用的開源技術還是很吸引我的,是以讀完吧,然後寫個讀書筆記,引言是新浪工程師對此書的評價,我看完這本書之後,果然收獲頗豐,記錄如下。
作學習交流之用,非盈利性質
第一章 Redis入門
Redis始于一個意大利創業公司Merzia,創始人Salvatore Sanfilippo以及另外一名主要代碼貢獻者Pieter Noordhuis目前在VMware,全職開發Redis。
Redis代碼托管在Github上。
Redis在性能上是單線程模型,而Memcached支援多線程,是以在多核伺服器上後者的性能更高一些,然而Redis的性能已經足夠優異,在絕大部分場合下其性能都不會成為瓶頸。是以在使用時更應該關心的是二者功能上的差別,如果需要用到進階的資料類型或是持久化等功能,Redis将會是Memcached很好的替代品。
第二章 Redis準備
首先找到linux機器,安裝一個redis,依賴指令wget,gcc
wget http://download.redis.io/redis-stable.tar.gz
tar xzf redis-stable.tar.gz
cd redis-stable
make MALLOC=libc
make install
redis-server (--port 6397) & //啟動redis服務,可以指定端口,背景運作
redis-cli SHUTDOWN //停止Redis,Redis收到SHUTDOWN指令後,會先斷開所有用戶端連接配接,然後根據配置執行持久化,最後完成退出,“kill Redis程序PID”也可以正常結束Redis
redis-cli -h 127.0.0.1 -p 6379 用戶端可以指定連接配接服務端的ip和端口
redis-cli PING
如果接收到PONG說明一切正常,用戶端請求收到服務端的響應。
Redis預設支援16個資料庫,不同的應用應該使用不同的Redis執行個體存儲資料。由于Redis非常輕量級,一個空Redis執行個體占用的記憶體隻有1MB左右,是以不用擔心多個Redis執行個體會占用很多記憶體。
第三章 Redis入門
一些基本指令就不介紹了
HSETNX 原子地實作了HEXISTS和HSET兩個指令以避免競态條件
LRANGE numbers 0 -1可以擷取清單中的所有元素
清單類型可以存儲一個有序的字元串清單,常用的操作是向清單兩端添加元素,或者獲得清單的某一個片段。清單類型内部使用雙向連結清單實作,清單類型适合用來記錄日志,可以保證加入新日志的速度不會受到已有日志數量的影響,但是不适合從中間擷取資料,當清單元素非常多時通路中間元素效率并不高,适合場景是新鮮事這些隻關心最新的内容。
有序集合類型是使用散清單和跳躍表實作的,增删改查都是Log(N),跳表的設計非常精彩,多個level的索引思想,用空間換時間。
場景1.記錄日志時希望隻保留最近的100條日志
LPUSH logs $newLog
LTRIM logs 0 99
場景2.網站監控系統
将元素從一個清單轉到另一個清單 RPOPLPUSH source destination
當source和destination相同時,RPOPLPUSH指令會不斷地将隊尾的元素移到隊首,借助這個特性我們可以實作一個網站監控系統:使用一個隊列存儲需要監控的網址,然後監控程式不斷地使用RPOPLPUSH指令循環取出一個網址來測試其可用性。這裡使用RPOPLPUSH指令的好處在于在程式執行過程中仍然可以不斷地向網址清單中加入新網址,而且整個系統容易擴充,允許多個用戶端同時處理隊列。
場景3.用集合實作反向索引
tag:Redis:posts->3
tag:MySQL:posts->2,3
tag:Java:posts->1,2,3
場景4.分頁實作(按時間,按點選量)
ZRANGEBYSCORE key in min max [WITHSCORES] [LIMIT offset count]
可以用時間+offset作為score,然後就可以根據這個score進行分頁展示了
第四章 Redis進階
事務
MULTI //事務開始
....
EXEC //執行事務
Redis的事務沒有關系資料庫事務提供的復原功能,不過由于Redis不支援復原,也使得Redis在事務上可以保持簡潔和快速,另外如果能夠很好的規劃資料庫,保證鍵名規範的使用,是不會出現如指令與資料類型不比對這樣的運作錯誤的。
因為事務中每個指令的執行結果都是最後一起傳回的,是以無法将前一條指令的結果作為下一條指令的參數,這時候問題出現了:如何原子的實作增1的功能?
watch指令!WATCH指令可以監控一個或多個鍵,一旦其中一個鍵被修改或删除,之後的事務就不會執行。監控一直持續到EXEC指令,通過事務實作incr函數,僞代碼如下:
def incr($key)
WATCH $key
$value = GET $key
if not $value
$value = 0
$value = $value+1 //如果在watch之後有其他線程修改了value,則事務失敗
MULTI
SET $key, $value
result = EXEC //失敗的話,需要重新執行
return result[0]
場景1.實作通路頻率限制
$isKeyExists = EXISTS rate.limiting:$IP
if $isKeyExists is 1
$times = INCR rate.limiting:$IP
if $time > 100
print 通路頻率超過了限制,請稍後再試
exit
else
MULTI
INCR rate.limiting:$IP
EXPIRE $keyName, 60 //這個用事務控制,否則這行如果沒有執行,那使用者最多隻能通路100次部落格了
EXEC
//這段程式有一個臨界問題,如果一個人在前一分鐘的最後通路了99次,新的分鐘開始通路了99次,雖然密集通路了198次,但是代碼判斷不出來,推薦的做法是,縮小時間視窗,比如把一分鐘壓縮成10秒鐘。還有一種方式是記錄每次通路的時間,然後用清單去存儲最近的通路時間,這個方法會占用較多的存儲空間,實際使用時還需要開發者自己去權衡。
場景2.實作緩存
如果命中用redis的值,否則重新計算出新值,兩小時失效,僞代碼如下
$rank = GET cache:rank
if not $rank
$rank = 計算排名...
MULTI
SET cache:rank, $rank
EXPIRE cache:rank, 7200
EXEC
場景3.優先級隊列
當确認郵件和發送通知郵件兩種任務同時存在時,應該優先執行前者。僞代碼如下:
BRPOP key [key ...] timeout 阻塞彈出
loop
$task = BRPOP queue:confirmation.email, queue:notification.email, 0
execute($task[1])
場景4.釋出/訂閱 模式
SUBSCRIBE channel1
PUBLISH channel1 hi
第五章 Redis實踐
列舉了PHP、Ruby、Python、Node.js與Redis的一些應用場景,包括使用者注冊登入功能,自動提示功能(搜尋補足提示功能),線上好友功能,IP位址查詢功能,有興趣的朋友可以再細看。
第六章 Redis腳本
Redis在2.6推出了腳本功能,允許開發者使用Lua語言編寫腳本傳到Redis中執行。使用腳本的好處如下:
- 1.減少網絡開銷:本來5次網絡請求的操作,可以用一個請求完成,原先5次請求的邏輯放在redis伺服器上完成。
- 2.原子操作:Redis會将整個腳本作為一個整體執行,中間不會被其他指令插入。
- 3.複用:用戶端發送的腳本會永久存儲在Redis中,意味着其他用戶端可以複用這一腳本而不需要使用代碼完成同樣的邏輯。
可以看例子:
Lua腳本配合Redis的實踐第七章 Redis管理
Redis支援兩種方式持久化,一種是RDB方式,一種是AOF方式。根據應用場景和對資料可靠性的要求,可以單獨使用其中一種或将二者結合使用。
RDB方式
RDB方式的持久化是通過快照完成的,快照的過程如下
1.Redis使用fork函數複制一份目前程序的副本
2.父程序繼續接手并處理用戶端發來的指令,而子程序開始将記憶體中的資料寫入硬碟的臨時檔案。
3.當子程序寫入完所有資料後會用該臨時檔案替換舊的RDB檔案,至此一次快照操作完成。
在執行fork的時候作業系統(類UNIX作業系統)會使用寫時複制(copy-on-write)政策,即fork函數發生的一刻父子程序共享同一記憶體資料,當父程序要更改其中某片資料時(如執行一個寫指令),作業系統會将該片資料複制一份以保證子程序的資料不受影響,是以新的RDB檔案存儲的是執行fork一刻的記憶體資料。
Redis還支援手動發送SAVE或BGSAVE指令讓Redis執行快照,前者是父程序進行快照,會阻塞其他請求,後者會通過fork子程序執行快照操作。
AOF方式
開啟AOF持久化後每執行一條會更改Redis中的資料的指令,Redis會将指令寫入硬碟中的AOF檔案。按照一定的政策會重寫AOF檔案,因為對于變量的兩次set,隻需要儲存後面的set即可,這樣可以減小AOF檔案的大小。
作業系統緩存的機制,資料并沒有真正寫入到硬碟,而是進入了系統的硬碟緩存,一般設定appendsync everysec每秒執行一次同步操作。flush緩存到硬碟。
叢集,讀寫分離
在常見的場景中,讀的頻率大于寫,當單機Redis無法應付大量的杜請求時(尤其是較耗資源的請求,比如SORT指令等)可以通過複制功能履歷多個從資料庫,主資料庫隻進行寫操作,而從資料庫負責寫操作。
另一個相對耗時的操作是持久化,為了提高性能,可以通過複制功能建立一個(或若幹個)從資料庫,并在從資料庫中啟用持久化,同時在主資料庫中禁用持久化。當從資料庫崩潰時重新開機後主資料庫會自動将資料同步過來,是以無需擔心資料丢失。而當主資料庫崩潰時,需要在從資料庫中使用SLAVEOF NO ONE指令将從資料庫提升成主資料庫繼續服務,并在原來的主資料庫啟動後使用SLAVEOF指令将其設定成新的主資料庫的從資料庫,即可将資料同步回來。perfect!
安全
Redis的安全設計是在”Redis運作在可信環境“這個前提下做出的,在生産環境運作時不能允許外界直接連接配接到Redis伺服器上,雖然Redis支援密碼,但是由于Redis性能極高,并且輸入錯誤密碼後Redis并不會進行主動延遲(考慮到Redis的單線程模型),是以攻擊者可以通過窮舉法破解Redis的密碼(1秒内能夠嘗試十幾萬個密碼)。
管理工具
一般用redis-cli指令行工具就可以,也有一些網頁管理工具,比如phpRedisAdmin,但phpRedisAdmin在擷取鍵清單時使用的是KEYS *指令,當鍵非常多的時候性能并不高,是以對生産環境下擁有大資料量的資料庫來說不适宜使用phpRedisAdmin管理。
附上書封面