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