隨着互聯網業務對性能需求日益強烈,作為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管理。
附上書封面