摘 要: 隨著互聯(lián)網(wǎng)的發(fā)展,,各種類型的應(yīng)用層出不窮,網(wǎng)站訪問量越來越大,、內(nèi)容越來越多,用戶與系統(tǒng)的交互不斷增強,,訪問集中,造成數(shù)據(jù)庫負載過大,,網(wǎng)站顯示延遲等影響,。緩存技術(shù)就是解決此問題的一種方案,緩存技術(shù)以其簡單的設(shè)計,、高效的存儲性能得到了越來越廣泛的應(yīng)用,,而內(nèi)存數(shù)據(jù)庫則是一種優(yōu)秀的緩存解決方案。主要介紹Redis的特性以及在系統(tǒng)中的應(yīng)用,。
關(guān)鍵詞: Redis,; 高速緩存; 內(nèi)存數(shù)據(jù)庫
隨著 Internet 技術(shù)的快速發(fā)展,,網(wǎng)絡(luò)接入速度不斷提高,,各種類型的應(yīng)用層出不窮,網(wǎng)站訪問量越來越大,、內(nèi)容越來越多,用戶交互不斷增強,訪問集中,,造成數(shù)據(jù)庫負載過大,、網(wǎng)站顯示延遲等影響,而造成影響用戶體驗的主要瓶頸集中在數(shù)據(jù)庫服務(wù)器承載能力方面,。要讓數(shù)據(jù)庫服務(wù)器快速響應(yīng)并能夠承受越來越大的負載, 緩存技術(shù)就是解決此問題的一種方案,。Cache性能高效,設(shè)計簡單,,可以對數(shù)據(jù)庫中的數(shù)據(jù)進行緩存,,降低數(shù)據(jù)庫負載;可以對Web頁面進行緩存,,提高Web頁面響應(yīng)速度,;對復(fù)雜計算結(jié)果進行緩存,可以減少網(wǎng)站服務(wù)器的傳輸負荷和計算速度和對用戶的響應(yīng)速度,有效地提高網(wǎng)站性能及可擴展性,。本文主要介紹開源內(nèi)存數(shù)據(jù)Redis的特性及其應(yīng)用,。
1 Redis簡介
Redis是一種 Key-Value類型的內(nèi)存數(shù)據(jù)庫產(chǎn)品,全名為遠程字典服務(wù)(REmote DIctionary Server),與Memcache 相似,,但Redis 支持更多的數(shù)據(jù)類型,,包括字符串(String)、鏈表(List),、集合(set),、有序集合(Zset)、哈希(Hash)等,。與Memcached一樣,,為了保證效率,Redis數(shù)據(jù)都是緩存在內(nèi)存中,,但與Memcache 只是用來做緩存相比,,Redis適用的場景更多,并且可以直接用于數(shù)據(jù)存儲服務(wù)。
2 Redis特性
雖然Redis與Memcaehed具有很多相似的特征,可替代Memcaehed做為緩存,但它又具有更多優(yōu)秀的特性,如支持多種數(shù)據(jù)結(jié)構(gòu),、支持簡單事務(wù)控制,、支持持久化、支持主從復(fù)制,、Virtual Memory功能等,。
2.1 Redis數(shù)據(jù)類型
Redis除了提供常規(guī)的數(shù)值或字符串外還提供4種數(shù)據(jù)類型:List、Set,、Zset(Sorted Set)和Hset(Hash Set),。
(1)String(字符串)
Strings數(shù)據(jù)結(jié)構(gòu)是簡單的Key-Value類型,Value其實不僅是String,也可以是數(shù)字,。使用Strings類型,,可以完全實現(xiàn)目前 Memcached 的功能,并且效率更高,;還可以享受Redis的定時持久化,、操作日志及Replication等功能。
(2)List(雙向鏈表)
Lists就是鏈表,,使用Lists結(jié)構(gòu)可以輕松地實現(xiàn)最新消息排行等功能,。Lists的另一個應(yīng)用就是消息隊列,可以利用Lists的PUSH操作將任務(wù)存在Lists中,隨后工作線程再用POP操作將任務(wù)取出進行執(zhí)行,。Redis還提供了操作Lists中某一段的API,可以直接查詢、刪除Lists中某一段的元素,。
(3)Set(集合)
Sets是一個集合,,集合的概念就是一堆不重復(fù)值的組合。Set是String類型的無序集合,。Redis還為集合提供了求交集,、并集、差集等操作,,可以非常方便地實現(xiàn)如共同關(guān)注,、共同喜好、二度好友等功能,,對上面的所有集合操作還可以使用不同的命令選擇將結(jié)果返回給客戶端還是存集到一個新的集合中,。
(4)Zset(有序集合)
Zset與Set相似,但在Set的基礎(chǔ)上增加了一個Score的屬性。這一屬性在添加修改元素時進行指定,,每次指定后,,Zset會自動按新的值重新調(diào)整順序。
(5)Hash(hash表)
在Memcached中,,經(jīng)常將一些結(jié)構(gòu)化的信息打包成hashmap,,在客戶端序列化后存儲為一個字符串的值,比如用戶的昵稱,、年齡,、性別、積分等,,在需要修改其中某一項時,,通常需要將所有值取出反序列化后,修改某一項的值,,再序列化存儲回去,。這樣不僅增大了開銷,也不適用于一些可能并發(fā)操作的場合(比如兩個并發(fā)的操作都需要修改積分),。而Redis的Hash結(jié)構(gòu)可以實現(xiàn)像在數(shù)據(jù)庫中Update一個屬性一樣只修改某一項屬性值,。
2.2 Redis事務(wù)控制
Redis可以通過MULTI/EXEC來支持簡單的事物控制,。Redis只能保證事務(wù)中的所有命令串行執(zhí)行,在事務(wù)的執(zhí)行過程中不會為其他客戶端發(fā)起的請求提供服務(wù),。當(dāng)一個client使用multi命令時,,這個連接會進入一個事務(wù)上下文,該連接后續(xù)的命令會放在一個隊列中,,當(dāng)此連接收到exec命令后,,Redis會順序地執(zhí)行隊列中的所有命令,但是如果事務(wù)中的一個命令失敗了,,并不回滾其他命令,。
2.3 Redis持久化機制
Redis是一個能支持持久化的內(nèi)存數(shù)據(jù)庫,它通過將內(nèi)存中的數(shù)據(jù)保存到磁盤來持久化。Redis有兩種持久化方式,,一種是Snapshotting(快照),,另一種是append-only file(縮寫aof)的方式。下面分別介紹:
(1)Snapshotting方式
快照是默認(rèn)的持久化方式,。這種方式是將內(nèi)存中數(shù)據(jù)以快照的方式寫入到二進制文件dump.rdb中,。可以通過配置redis.conf來設(shè)置自動做快照持久化的方式,。
(2)aof方式
如果對數(shù)據(jù)要求很高,,可以采用aof持久化方式。因為在使用aof持久化時,Redis會將每個命令都追加到appendonly.aof文件中,,當(dāng)Redis出現(xiàn)意外關(guān)閉后,,重啟后會通過執(zhí)行appendonly.aof文件中的命令來在內(nèi)存中重建整個數(shù)據(jù)[1]。
2.4 主從復(fù)制
Redis主從復(fù)制配置非常簡單,可以通過配置redis.conf文件中的Replication段來實現(xiàn)主從復(fù)制,。 Redis主從復(fù)制特點:
(1)支持一個master可以擁有多個slave,同時slave還可以接收其他的slave,。
(2)主從復(fù)制不會阻塞master和slave,在同步數(shù)據(jù)時,,master和slaver都可以接收client請求,。
通過主從復(fù)制的特性可以做到以下3個方面:
(1)可以做到讀寫分離,提供系統(tǒng)伸縮性和系統(tǒng)性能,如master主服務(wù)用來寫數(shù)據(jù),,slave服務(wù)用來讀數(shù)據(jù),。
(2)可以做到備份數(shù)據(jù)分離,如slave服務(wù)器群中的一個或兩個服務(wù)器用來備份數(shù)據(jù),。
(3)雖然Redis宣稱主從復(fù)制無阻塞,,但是由于Redis使用單線程服務(wù),而與slave的同步是由線程統(tǒng)一處理,因此,,對性能有影響,。在slave第一次與master做同步時,如果master快照文件較大,相應(yīng)快照文件的傳輸將耗費較長時間,文件傳輸過程中master會造成訪問延遲。
2.5 Virtual Memory 功能
Redis的Virtual Memory(虛擬內(nèi)存)通過配置可以讓用戶設(shè)置最大使用內(nèi)存,當(dāng)超出這個內(nèi)存時,通過LRU類似算法,將一部分?jǐn)?shù)據(jù)存入文件中,在內(nèi)存中只保存使用頻率高的數(shù)據(jù)來提高Redis性能。
3 Redis應(yīng)用分析
通過上述Redis 的特征分析,可以看到當(dāng)作Cache工具時,,Redis與其他Cache相比更多的優(yōu)勢性能和內(nèi)存使用效率相差無幾卻擁有更多的數(shù)據(jù)結(jié)構(gòu)并支持更豐富的數(shù)據(jù)操作,,同時還支持持久化。例如在某博客系統(tǒng)中,,整個系統(tǒng)結(jié)構(gòu)圖如圖1所示,客戶端通過Redis proxy訪問Redis,。目前的Redis本身都不具備分布式集群特性,當(dāng)有大量 Redis時,,通常只能通過客戶端的一些數(shù)據(jù)分配算法(比如一致性哈希)來實現(xiàn)集群存儲,。而 Twemproxy 通過引入一個代理層,可以將其后端的多臺 Redis實例進行統(tǒng)一管理與分配,,使應(yīng)用程序只需要在 Twemproxy 上進行操作,,而不用關(guān)心后面具體有多少個真實的 Redis存儲,這樣就可以通過平行擴展Redis Master服務(wù)器來無限擴展Redis,。
Redis-Master1與Redis-slave11通過主從復(fù)制連接,。同時,Redis-Slaver11通過aof負責(zé)持久化。Twemproxy可以配置結(jié)點故障問題,,當(dāng)某一個結(jié)點出現(xiàn)故障后,用Redis Master相應(yīng)的Redis Slave切換成Master,。
在系統(tǒng)運行中要處理各種業(yè)務(wù)邏輯,,因而需要訪問大量的數(shù)據(jù)庫資源,在數(shù)據(jù)庫上進行讀寫操作,,數(shù)據(jù)庫壓力會較大,,性能較低。通常把更新不頻繁的數(shù)據(jù)進行緩存,。例如下面幾種操作,。
(1)根據(jù)條件得到最新數(shù)據(jù)的操作,以時間為權(quán)重
比較典型的取最新博文的數(shù)據(jù)代碼如圖2所示,??梢詫⒆钚碌? 000條博客的ID放在Redis的雙向鏈表(list)集合中,使用LPUSH globle:blogpostids命令向雙向鏈表(list)集合中插入數(shù)據(jù),插入完成后再用LTRIM global.latest.blogs 0 5000命令使其永遠只保存最近5 000個ID,,然后在客戶端獲取最新博文時可以用下面的邏輯(偽代碼)了,。如果還有其他的取最新數(shù)據(jù)需求,比如“模擬技術(shù)”分類的最新50條數(shù)據(jù),,則可以再建一個按此分類的雙向鏈表(list),將博文的ID插入此雙向鏈表(list)中,。
(2)根據(jù)條件得到排行榜應(yīng)用,相當(dāng)于數(shù)據(jù)中取TOP N操作,,例如訪問量最高的博文,、用戶的相互關(guān)注等。
以某個條件為權(quán)重得到數(shù)據(jù),如果博文按瀏覽的次數(shù)排序,,這時可以使用sorted set將瀏覽次數(shù)值設(shè)置成sorted set的score,,將具體的博文ID設(shè)置成相應(yīng)的value,每次新增博文時只需要執(zhí)行一條ZADD blog:getblogpostrank 0 1000,,每當(dāng)博文被瀏覽時,,使用ZINCRBY blog:getblogpostrank 1 1000,取訪問量最高的博文時就可以使用ZREVRANGE blog:getblogpostrank 0 -1,,來按照瀏覽次數(shù)高低排行了,。
(3)網(wǎng)站計數(shù)的應(yīng)用,比如在博客系統(tǒng)中瀏覽次數(shù),、評論數(shù)等,。
Redis的命令都是原子性的,可以使用INCRBY、ZINCRBY命令來增加計數(shù),,使用DECRBY,、ZINCRBY命令來減少計數(shù)。如果給博文ID號為1 000的增加一次瀏覽,,則使用INCRBY blogpost:1000 1命令,。
(4)構(gòu)建反垃圾信息(spam)系統(tǒng),比如評論系統(tǒng)中的反垃圾信息等,。
在博客系統(tǒng)中評論是必不可少的,,同時各種攻擊spam也少不了(如垃圾評論、廣告,、刷排名等),,可以針對這些spam制定一個規(guī)則,例如一分鐘評論不得超過2次,,5分鐘不能評論多于5次,。這可以使用Redis的Sorted Set來將最近一天用戶操作記起來。使用系統(tǒng)時間為score,這樣執(zhí)行一條ZADD user:1000:operation 54561227854“發(fā)表評論”,,得到用戶最近一分鐘的操作如下ZRANGEBYSCORE user:1000: operation 54561227854 +inf,。這樣如果一分鐘有兩次評論,就可以判定為spam(每個網(wǎng)站的規(guī)則不一樣),。
(5)可以使用Redis系統(tǒng)中Pub/Sub和Ajax長鏈接來構(gòu)建實時消息系統(tǒng),。
Redis的Pub/Sub系統(tǒng)作為一種消息通信模式,類似于設(shè)計模式中的觀察者模式,,發(fā)布和訂閱機制可以很方便地實現(xiàn)簡單的聊天功能,,可以應(yīng)用于實時聊天(通過與Ajax長鏈接)、異步消息處理(如注冊后給用戶郵件通知)等場景,。將不同的頻道分布到不同服務(wù)器上,,也可以很方便地實現(xiàn)服務(wù)器的擴展以應(yīng)對用戶量的增長,。
(6)使用List或者Zset構(gòu)建隊列系統(tǒng)。
可以使用List構(gòu)建隊列系統(tǒng),,加入隊列可以使用rpush global:queue addblogpost,,退出隊列使用lpop global:queue。使用Zset來構(gòu)建有優(yōu)先級的隊列系統(tǒng),,加入普通級別的列隊命令zadd global:zqueue 1 addblogpost,加入高優(yōu)先級的列隊可以通過提高score的值來提高優(yōu)先級,。
為了高速緩存在系統(tǒng)應(yīng)用的不同需求,對Redis 的主要特征進行了分析,,同時對其作為緩存解決方案的應(yīng)用進行了舉列說明,。隨著Redis的發(fā)展,官方roadmap在Redis 3版本中將加入集群功能,,Redis作為內(nèi)存數(shù)據(jù)庫,,可以通過水平擴展來延伸系統(tǒng)的物理內(nèi)存,同時又支持持久化,,完全可以將所有數(shù)據(jù)存放在Redis內(nèi)存數(shù)據(jù)庫中,,這樣才能充分發(fā)揮內(nèi)存數(shù)據(jù)庫的優(yōu)勢。
參考文獻
[1] Xhan.redis學(xué)習(xí)筆記之持久化[EP/OL].[2011-02-07](2013-03-15).http://www.cnblogs.com/xhan/archive/
2011/02/07/1949640.html.