關於Redis的應用

寫這篇文章主要是在開發www.ximalaya.com的feed(登陸首頁看到的好友動態,未登陸是看不到的哭)模塊使用Redis的一些經驗。(www.ximalaya.com是音頻爲傳播介質的SNS網站,喜歡的同窗不妨用一用,也有APP的,仍是一款很是不錯的產品。音樂,相聲,有聲小說等等 一網打盡)。關於Feed自己的討論之後再專門發博,有興趣的同窗也能夠先開博,而後共同討論。 java

Redis這個神器已經在互聯網名聲大做了,主要是對關係型數據庫的補充。由於在大併發下,IO每每是性能的瓶頸,因此把數據儘可能放到離cpu近的地方,是解決IO瓶頸的有效途徑。呵呵,誰都想把數據放到寄存器,L1 L2 L3cache上,由於這些都是離cpu很近的地方,在一到幾十個cpu時鐘週期內能讀到數據,可是這些緩存空間實在過小了。因此只能把最熱的數據去徵用這部分緩存。那麼比這些數據稍微"冷"一點,但又常常訪問的數據怎麼辦呢?目前最好的途徑就是放在內存中。redis,memcache這類的內存數據庫就是這樣作的。放到內存中就意味着cpu能夠在幾百個時鐘週期內能讀到數據。通常服務器配置也是16個G內存吧,在64位系統下,理論可讓你的程序徹底佔用這16G內存。既然能夠在這麼快速的讀到數據,那麼採用什麼算法讀數據呢?線性查找?那又是浪費cpu時鐘週期的。查找最快的莫過於hash表,它的時間複雜度是O(1)。因此本質來講Redis,memcache都是在內存上的hash數據庫。Redis和memcache在互聯網的應用都比較多,如何選擇呢?固然兩個都是解決緩存問題的神器,我更趨向於Redis這個神器。緣由以下:  redis

1,豐富的數據結構,string,hash,list,set,sortset等數據結構都支持,而memcache僅支持string(至關於key-value結構,這裏說string是說的redis中的string),而其餘數據結構也是在互聯網中經常使用的。若是你想用就只能選Redis了。 算法

2,可持久化,memcache是不支持持久化的。那麼對於緩存的應用,一旦memcache服務掛掉了,內存的數據就丟失了,那麼能夠快速重啓是吧,內存的數據全沒了,這時候就有一個熱點故障。或許你說能夠經過預熱來解決這個問題。可是Redis能夠不須要預熱,他會從rdb或aof中自動重建內存。以前的Redis版本中重啓是須要花費很長時間的,可是在2.6版本後這個過程很是快了。即便是開啓aof的狀況下重啓,9G的數據也只須要六分半的時間(www.ximalaya.com的真實案例)。這主要是Redis做者對aof的命令進行了合併,加快了重啓的速度。對於須要持久化的應用,memcache就無能爲力了,或許你能夠選擇memcached,固然Redis任然是更好的方案。 數據庫

3,至於速度方面,Redis不會差於memcache,主要是Redis採用了epoll進行通訊吧。 數組

4,memcache是多線程的,Redis是單線程的,雖然更喜歡多線程的應用。但貌似Redis在單線程的操做下速度一點也不遜色,並且讓操做更安全了,不須要鎖,也不須要線程切換的開銷了。Redis在備份rdb的時候會fork一個進程出來,讓主進程不受任何影響。 緩存

Redis這個服務器,對於高併發快速讀寫,原子計數,消息隊列,實時排名都是實用的。對於互聯網網站來講這些都是頻繁使用,也是關係型數據庫比較頭疼的。剛好以上提到的用途都在生成環境中有用到。根據實際經驗來看,Redis仍是實用於緩存,原子計數。對於持久化存儲真的不太適合。由於它會把數據都加載到內存中(若是內存不夠用會load到虛擬內存,但這是一個悲劇)。固然若是你用於持久存儲的數據是有限的,那麼也是很是適合的。可是對於數據須要無限增加的業務,這個真不太適合。網站登陸首頁的feed,收件箱列表所有放在Redis中。收件箱列表隨着註冊用戶的增加而增加,須要消耗的內存也是逐步增加。爲了緩解這個內存壓力,客戶端作了sharding,實現內存分片。這雖然可以解決內存問題,但須要更多的機器來作。因此我也一直在想怎麼來緩解這塊內存的問題。扯遠了之後在把關於這方面的思考寫出來吧。如今仍是講一講Redis的應用。 安全

在緩存中的應用: 服務器

緩存中的應用在memcache中就是對key-value的緩存。它之因此能快,是由於它是存放在內存中的hash數組。內存,hash。這兩個關鍵詞就足夠解釋爲何讀寫快了。如今對於速度和容量都比合適的存儲也就內存最合適吧。ssd的速度快,容量也大。但須要在價格和壽命上有更大的突破才能被普遍使用。何況,ssd的速度比起主存來講仍是有必定差距的。 網絡

下面對redis中string hash list set sortset分別說下使用場景。 數據結構

string:這是使用最簡單,也是很是頻繁的一種數據類型。它就是一種單純的key-value對。適用於那些經過鍵直接找值得業務場景。get string的時候須要用到mutil get,這是能夠減小網絡通訊的,也是很是高效的。

hash:hash類型是對於string類型的補充,主要是有些key有共同的特徵,不如一個用戶對應的多個值,那麼用戶id只須要出現一次就能夠了,對於string來講是須要出現屢次的。因此hash是比string更節約了存儲。一樣hash也能夠對多列mutil get,一次網絡傳輸就能夠獲得全部值。

list:適用於不排重,且有序的操做。這其實就是一個隊列,先進先出。放不排重的列表頗有用。曾經還用它來作過隊列。但隊列仍是用專門的消息隊列更好,由於list不支持消息應答的,也就是不能確保你的消息被處理成功。

set:至關於java中的HashSet,用於排重的列表,可是無序的,在無序的列表仍是很適合的。

sortset:這是有序,排重的列表,至關於java中的TreeSet。不過TreeSet是用的紅黑樹算法,sortset是用的skip table。sortset是頗有用的一種數據結構,以前提到的收集箱列表就是用的sortset存放。不過這個數據結構很耗存儲。特別是否是用ziplist時。後面談優化的時候具體說。


Redis中的一些常見優化:

Redis中最好不要開啓VM,在2.6版本也是默認關閉的。即便把vm指定到ssd中,redis做者也是反對的。

對於cache的redis,必定要設置最大內存。這樣後臺線程的LRU數據淘汰策略才能被觸發。因此在未設置最大內存的狀況下,對數據設置過時時間每每是無效的。這是一個坑,曾經跳到這個大坑裏面,通宵解決性能問題。切記!

ziplist頗有必要設置大一點。好比以前提到的收件箱長度是500條,但有些時候長度可能達到700條,因此把ziplist設置成1024會是一個好的策略。下面具體介紹下這幾個參數:

hash-max-ziplist-entries:針對hash類型,只要hash的filed在這個範圍內使用緊湊存儲,這能夠節約存儲,也是爲何要儘可能使用hash少使用string的理由,特別是在原子計數的時候,對一個用戶可能要記錄不少數量,必定要採用hash的策略,這是很是有用的。固然這個值毫不是越大越好,一般不要超過1024
hash-max-ziplist-value:hash的value的大小,在這個大小內會使用緊湊存儲。這個值也不是越大越好,最好不要超過512。

list-max-ziplist-entries
list-max-ziplist-value
這兩個參數和hash用法同樣。不過這是針對list類型的。
 
zset-max-ziplist-entries
這個參數是針對sortset。在這個範圍內,將不使用紅黑樹,是線性查找,因此這個值也不能太大,通常不要超過1024。
 
Redis的擴容問題:
 Redis擴容目前來仍是比較麻煩一點的。由於服務端是不支持數據的sharding的。須要在客戶端作sharding。一般有兩種sharding策略。一致性hash和非一致性hash。
對於非一致性hash:
 能夠實現把Redis實例事先切分紅多個數據庫,好比32或64個庫。或者在同一個機器上啓多臺Redis實例,這樣還能夠利用多核系統多進程的優點提升併發。當單臺機器上的Redis難以支撐時。把這些實例遷移到其餘機器上,這樣就變相的減輕了單臺機器的壓力。還能夠把數據庫切分開,分別放到多臺機器上啓動。這種作法最大的缺點是,當單臺Redis實例的每一個數據庫都沒法支撐數據時,麻煩就來了。這是須要程序把數據遷移出來,這個遷移每每是很耗時的,也是很容易出錯的。對於線上的產品這每每是不能夠接受的。那麼就一致性hash登場吧。
對於一致性hash:
 對於一致性hash的介紹能夠搜索一下。主要的優點就是,在擴容的時候只須要移動受影響的節點。對於整個集羣來講,這些受影響節點的數據是很是少的。若是對於cache來講,加一臺機器,幾乎不須要移動數據。讓cache miss重建緩存也是能夠的。固然這個得針對具體應用評估。不要所以而熱點故障,掛網站了,呵呵。一致性hash的優點是能夠任意加機器解決容量問題。理論上只要機器夠,容量就不是問題。

嗯,暫時就寫到這裏吧,redis中的注意事項還不少,應用也毫不限於此,具體的官方文檔已經介紹的很詳細。用到的同窗,能夠發表和補充下Redis的其餘用途。若有任何紕漏還多多指出,謝謝!

相關文章
相關標籤/搜索