馬 冰 姜學(xué)軍 史海洋
(沈陽(yáng)理工大學(xué),遼寧 沈陽(yáng)110159)
Redis是一個(gè)高性能的key-value數(shù)據(jù)庫(kù)。redis的出現(xiàn),很大程度補(bǔ)償了memcached這類key/value存儲(chǔ)的不足,在部分場(chǎng)合可以對(duì)關(guān)系數(shù)據(jù)庫(kù)起到很好的補(bǔ)充作用。它提供了Java,C/C++,C#,PHP,JavaScript,Perl,Object-C,Python,Ruby,Erlang等客戶端,使用很方便[1]。它有以下幾個(gè)特點(diǎn):
Redis使用標(biāo)準(zhǔn)C編寫實(shí)現(xiàn),而且數(shù)據(jù)加載到內(nèi)存上,所以讀寫速度非???。官方提供的數(shù)據(jù)表明,普通的Linux機(jī)器上,Redis的讀寫速度分別達(dá)到81000/s和110000/s。
Redis使用單線程的IO復(fù)用模型。自己封裝了一個(gè)簡(jiǎn)單的AeEvent事件處理框架,主要實(shí)現(xiàn)了epoll,kqueue和select,對(duì)于單純只有IO操作來(lái)說(shuō),單線程可以將速度優(yōu)勢(shì)發(fā)揮到最大。
由于所有數(shù)據(jù)保存于內(nèi)存中,所以對(duì)數(shù)據(jù)的更新將異步地保存到磁盤上。
相比于其他內(nèi)存型數(shù)據(jù)庫(kù),Redis不只支持字符串類型,而是支持多種數(shù)據(jù)結(jié)構(gòu)。目前支持5種。分別為string(字符串)、hash(hash表)、list(雙向鏈表)、set(無(wú)序集合)、zset(有序集合)。
Redis對(duì)不同數(shù)據(jù)類型操作時(shí)自動(dòng)的,因此設(shè)置或增加key,從一個(gè)集合中增加或刪除一個(gè)元素都能安全的操作。
Redis支持多種語(yǔ)言,諸如Ruby,Python,PHP,ErLang,Perl,Lua,Java,Scala等。
Redis支持簡(jiǎn)單而快速的主從復(fù)制。官方提供數(shù)據(jù),Slave在21秒內(nèi)即完成了Amazon網(wǎng)站10Gkey Set的復(fù)制。
目前只支持PHP,Ruby,Scala語(yǔ)言的Sharding功能。
1)小數(shù)據(jù)量,高速讀寫訪問(wèn)。
2)大數(shù)據(jù)量,有明顯熱點(diǎn)數(shù)據(jù)。
3)大數(shù)據(jù)量,無(wú)熱點(diǎn)數(shù)據(jù)。
4)所有數(shù)據(jù)in-memory。
(代碼都是用java語(yǔ)言完成,數(shù)據(jù)庫(kù)操作采用MyBatis框架,Redis操作使用Spring提供的方法)
表1 執(zhí)行五次來(lái)進(jìn)行比較兩者的時(shí)間(毫秒)
可見,redis的存取性能要極大程度優(yōu)于數(shù)據(jù)庫(kù),所以在應(yīng)用中合適的使用redis,能極大提升產(chǎn)品性能。
Redis存取速度快的原因分析如下:
4.3.1 純內(nèi)存操作
Redis所有數(shù)據(jù)存儲(chǔ)于內(nèi)存中,內(nèi)存中的操作速度比磁盤快的多。
4.3.2 Redis的存取底層使用異步非阻塞IO
系統(tǒng)I/O可分為阻塞型,非阻塞同步型以及非阻塞異步型。
阻塞型I/O意味著控制權(quán)只到調(diào)用操作結(jié)束了才會(huì)回到調(diào)用者手里.結(jié)果調(diào)用者被阻塞了,這段時(shí)間了做不了任何其它事情。
非阻塞同步是會(huì)立即返回控制權(quán)給調(diào)用者的。調(diào)用者不需要等等,它從調(diào)用的函數(shù)獲取兩種結(jié)果:要么此次調(diào)用成功進(jìn)行了;要么系統(tǒng)返回錯(cuò)誤標(biāo)識(shí)告訴調(diào)用者當(dāng)前資源不可用,你再等等或者再試度看吧。
在非阻塞異步調(diào)用中,稍有不同。調(diào)用函數(shù)在立即返回時(shí),還告訴調(diào)用者,這次請(qǐng)求已經(jīng)開始了。系統(tǒng)會(huì)使用另外的資源或者線程來(lái)完成這次調(diào)用操作,并在完成的時(shí)候知會(huì)調(diào)用者(比如通過(guò)回調(diào)函數(shù))[2]。
In a non-blocking asynchronous call,the calling function returns control to the caller immediately,reporting that the requested action was started.The calling system will execute the caller’s request using additional system resources/threads and will notify the caller(by callback for example),when the result is ready for processing。[3]
在以上三種IO形式中,非阻塞異步是性能最高、伸縮性最好的。
4.3.3 采用Epoll接口實(shí)現(xiàn)IO復(fù)用
與epoll模式相比較,有select模式,select模式是采用輪詢的方式,來(lái)檢測(cè)所有socket的狀態(tài),當(dāng)有很大的socket集時(shí),即使有小部分是活躍的,內(nèi)核也需要把整個(gè)集合輪詢才能知道所有socket的狀態(tài)變化,而epoll模式是在創(chuàng)建時(shí)即對(duì)需要關(guān)注的socket注冊(cè)事件,當(dāng)某一個(gè)監(jiān)聽的socket有狀態(tài)變化,就將其保存到一個(gè)內(nèi)部數(shù)組中,當(dāng)應(yīng)用層需要檢測(cè)時(shí)直接返回該數(shù)組,不需要對(duì)所有socket逐一檢查,所以epoll模式的性能優(yōu)于select模式。
需要設(shè)置數(shù)據(jù)的一定存活時(shí)間。例如找回密碼功能,用戶需要填寫郵箱,然后發(fā)送一個(gè)連接到用戶郵箱中,該連接是有時(shí)間限制,半小時(shí)后即失效。此時(shí)就可以利用redis存儲(chǔ)數(shù)據(jù)可以設(shè)置有效時(shí)間的功能。并且不需要存儲(chǔ)于數(shù)據(jù)庫(kù),讀取redis的效率更高。
讀取時(shí)直接在redis中查詢key為"redis.global.forgetPass",如果有值則說(shuō)明未超時(shí),為空則說(shuō)明連接失效。
一些全局的系統(tǒng)設(shè)置,基本上都是在第一次啟動(dòng)應(yīng)用的時(shí)候設(shè)置,以后就不會(huì)再修改的數(shù)據(jù),可以存于redis中,設(shè)置不自動(dòng)失效。
所有業(yè)務(wù)數(shù)據(jù),可以通過(guò)數(shù)據(jù)庫(kù)主鍵ID當(dāng)成key或者一些層級(jí)關(guān)系生成redis存儲(chǔ)的key,將整個(gè)數(shù)據(jù)對(duì)象存儲(chǔ)于redis,極大的減少數(shù)據(jù)庫(kù)查詢次數(shù),提高應(yīng)用性能。
例如:數(shù)據(jù)的層級(jí)為 代理商→租戶→用戶 則用戶可以在redis中存儲(chǔ)的數(shù)據(jù)為
Key:redis.user.partnerId.customerId.userId
Value:user的數(shù)據(jù)對(duì)象
e.g.redis.user.1.33.21 redis.user.3.42.13。
[1]百度百科.Redis[EB/OL].http://baike.baidu.com/view/4595959.htm,2014.
[2]veryDemo.兩種高性能I/O設(shè)計(jì)模式(Reactor/Proactor)的比較[Z].2005.
[3]Artima developer.Comparing Two High-Performance I/ODesign Patterns[Z].2005.