1. 程式人生 > >對Redis的理解,Redis是什麼,Redis和Memcache誰快?

對Redis的理解,Redis是什麼,Redis和Memcache誰快?

轉載時必須以連結形式註明原始出處及本宣告。

前段時間微博發生了一起大的系統故障,結果說是因為Redis叢集的問題,很多技術的朋友都比較關心,其中的原因不會超出James HamiltonOn Designing and Deploying Internet-Scale Service概括的那幾個範圍,James第一條經驗“Design for failure”是所有網際網路架構成功的一個關鍵。網際網路系統的工程理論其實非常簡單,James paper中內容幾乎稱不上理論,而是多條實踐經驗分享,每個公司對這些經驗的理解及執行力決定了架構成敗。

    題外話說完,最近又研究了  Redis

  。去年曾做過一個MemcacheDB, Tokyo Tyrant, Redis performance test,到目前為止,這個benchmark結果依然有效。這1年我們經歷了很多眼花繚亂的key / value儲存產品的誘惑,從Cassandra的淡出(Twitter暫停在主業務使用)到Hbase的興起(Facebook新的郵箱業務選用HBase),當再回頭再去看Redis,發現這個只有1萬多行原始碼的程式充滿了神奇及大量未經挖掘的特性。Redis效能驚人,國內前十大網站的子產品估計用1臺Redis就可以滿足儲存及Cache的需求。除了效能印象之外,業界其實普遍對Redis的認識存在一定誤區。本文提出一些觀點供大家探討。

一、 Redis是什麼?

    這個問題的結果影響了我們怎麼用  Redis  。如果你認為  Redis  是一個key value store, 那可能會用它來代替  MySQL  ;如果認為它是一個可以持久化的cache, 可能只是它儲存一些頻繁訪問的臨時資料。  Redis  是REmote DIctionary Server的縮寫,在  Redis  在官方網站的的副標題是A persistent key-value database with built-in net interface written in ANSI-C for Posix systems,這個定義偏向key value store

。還有一些看法則認為  Redis  是一個memory database,因為它的高效能都是基於記憶體操作的基礎。另外一些人則認為  Redis  是一個data structure server,因為Redis支援複雜的資料特性,比如List, Set等。對Redis的作用的不同解讀決定了你對Redis的使用方式。

    網際網路資料目前基本使用兩種方式來儲存,關係資料庫或者key value。但是這些網際網路業務本身並不屬於這兩種資料型別,比如使用者在社會化平臺中的關係,它是一個list,如果要用關係資料庫儲存就需要轉換成一種多行記錄的形式,這種形式存在很多冗餘資料,每一行需要儲存一些重複資訊。如果用key value儲存則修改和刪除比較麻煩,需要將全部資料讀出再寫入。Redis在記憶體中設計了各種資料型別,讓業務能夠高速原子的訪問這些資料結構,並且不需要關心持久儲存的問題,從架構上解決了前面兩種儲存需要走一些彎路的問題。

二、Redis不可能比Memcache快?

    很多開發者都認為  Redis  不可能比  Memcached  快,  Memcached  完全基於記憶體,而Redis具有持久化儲存特性,即使是非同步的,Redis也不可能比Memcached快。但是測試結果基本是Redis佔絕對優勢。一直在思考這個原因,目前想到的原因有這幾方面。

    LibeventMemcached不同,Redis並沒有選擇LibeventLibevent為了迎合通用性造成程式碼龐大(目前Redis程式碼還不到libevent的1/3)及犧牲了在特定平臺的不少效能。Redis用libevent中兩個檔案修改實現了自己的epoll event loop。業界不少開發者也建議Redis使用另外一個libevent高效能替代libev,但是作者還是堅持Redis應該小巧並去依賴的思路。一個印象深刻的細節是編譯Redis之前並不需要執行./configure

    CAS問題。CASMemcached中比較方便的一種防止競爭修改資源的方法。CAS實現需要為每個cache key設定一個隱藏的cas tokencas相當value版本號,每次settoken需要遞增,因此帶來CPU和記憶體的雙重開銷,雖然這些開銷很小,但是到單機10G+ cache以及QPS上萬之後這些開銷就會給雙方相對帶來一些細微效能差別。

三、單臺Redis的存放資料必須比實體記憶體小

  Redis  的資料全部放在記憶體帶來了高速的效能,但是也帶來一些不合理之處。比如一箇中型網站有100萬註冊使用者,如果這些資料要用Redis來儲存,記憶體的容量必須能夠容納這100萬用戶。但是業務實際情況是100萬用戶只有5萬活躍使用者,1周來訪問過1次的也只有15萬用戶,因此全部100萬用戶的資料都放在記憶體有不合理之處,RAM需要為冷資料買單。

這跟作業系統非常相似,作業系統所有應用訪問的資料都在記憶體,但是如果實體記憶體容納不下新的資料,作業系統會智慧將部分長期沒有訪問的資料交換到磁碟,為新的應用留出空間。現代作業系統給應用提供的並不是實體記憶體,而是虛擬記憶體(Virtual Memory)的概念。

基於相同的考慮,Redis 2.0也增加了VM特性。讓Redis資料容量突破了實體記憶體的限制。並實現了資料冷熱分離。

四、Redis的VM實現是重複造輪子

Redis的VM依照之前的epoll實現思路依舊是自己實現。但是在前面作業系統的介紹提到OS也可以自動幫程式實現冷熱資料分離,Redis只需要OS申請一塊大記憶體,OS會自動將熱資料放入實體記憶體,冷資料交換到硬碟,另外一個知名的“理解了現代作業系統(3)”的Varnish就是這樣實現,也取得了非常成功的效果。

作者antirez在解釋為什麼要自己實現VM中提到幾個原因。主要OS的VM換入換出是基於Page概念,比如OS VM1Page是4K, 4K中只要還有一個元素即使只有1個位元組被訪問,這個頁也不會被SWAP, 換入也同樣道理,讀到一個位元組可能會換入4K無用的記憶體。而Redis自己實現則可以達到控制換入的粒度。另外訪問作業系統SWAP記憶體區域時block程序,也是導致Redis要自己實現VM原因之一。

五、用get / set方式使用Redis

作為一個key / value存在,很多開發者自然的使用set/get方式來使用  Redis  ,實際上這並不是最優化的使用方法。尤其在未啟用VM情況下,Redis全部資料需要放入記憶體,節約記憶體尤其重要。

假如一個key-value單元需要最小佔用512位元組,即使只存一個位元組也佔了512位元組。這時候就有一個設計模式,可以把key複用,幾個key-value放入一個key中,value再作為一個set存入,這樣同樣512位元組就會存放10-100倍的容量。

這就是為了節約記憶體,建議使用hashset而不是set/get的方式來使用Redis。

六、使用aof代替snapshot

  Redis  有兩種儲存方式,預設是snapshot方式,實現方法是定時將記憶體的快照(snapshot)持久化到硬碟,這種方法缺點是持久化之後如果出現crash則會丟失一段資料。因此在完美主義者的推動下作者增加了aof方式。aofappend only mode,在寫入記憶體資料的同時將操作命令儲存到日誌檔案,在一個併發更改上萬的系統中,命令日誌是一個非常龐大的資料,管理維護成本非常高,恢復重建時間會非常長,這樣導致失去aof高可用性本意。另外更重要的是Redis是一個記憶體資料結構模型,所有的優勢都是建立在對記憶體複雜資料結構高效的原子操作上,這樣就看出aof是一個非常不協調的部分。

其實aof目的主要是資料可靠性及高可用性,在  Redis  中有另外一種方法來達到目的:Replication。由於  Redis  的高效能,複製基本沒有延遲。這樣達到了防止單點故障及實現了高可用。

本文主題

如果本文對你有幫助,那麼請你贊助我,讓我更有激情的寫下去,幫助更多的人。