Redis PK Memcached,哪个更牛叉
轉載自?Redis PK Memcached,哪個更牛叉
說到 redis 就會聯想到 memcached,反之亦然。了解過兩者的同學有那么個大致的印象:
-
redis 與 memcached 相比,不僅支持簡單的 key-value 數據類型,同時還提供 list,set,zset,hash 等數據結構的存儲;
-
redis 支持數據的備份,即 master-slave 模式的數據備份;
-
redis 支持數據的持久化,可以將內存中的數據保持在磁盤中,重啟的時候可以再次加載進行使用等等。
這似乎看起來 redis 比 memcached 更加牛X一些,那么事實上是不是這樣的呢?
存在即合理,我們來根據幾個不同點來一一比較一下。
網絡IO模型
memcached 是多線程,非阻塞 IO 復用的網絡模型,分為監聽主線程和 worker子線程,監聽線程監聽網絡連接,接受請求后,將連接描述字 pipe 傳遞給 worker 線程,進行讀寫 IO。
網絡層使用 libevent 封裝的事件庫,多線程模型可以發揮多核作用,但是引入了 cache coherency 和鎖的問題,比如:memcached 最常用的 stats 命令,實際 memcached 所有操作都要對這個全局變量加鎖,進行技術等工作,帶來了性能損耗。
redis 使用單線程的 IO 復用模型,自己封裝了一個簡單的 AeEvent 事件處理框架,主要實現了 epoll, kqueue 和 select,對于單存只有 IO 操作來說,單線程可以將速度優勢發揮到最大。
但是 redis 也提供了一些簡單的計算功能,比如排序、聚合等,對于這些操作,單線程模型施加會嚴重影響整體吞吐量,CPU 計算過程中,整個 IO 調度都是被阻塞的。
數據支持類型
memcached 使用 key-value 形式存儲和訪問數據,在內存中維護一張巨大的 HashTable,使得對數據查詢的時間復雜度降低到O(1),保證了對數據的高性能訪問。
正如開篇所說:redis 與 memcached 相比,比僅支持簡單的 key-value 數據類型,同時還提供 list,set,zset,hash 等數據結構的存儲;詳細可以翻閱《Redis 內存使用優化與存儲》
http://blog.csdn.net/u013256816/article/details/51133134
內存管理機制
對于像 Redis 和 Memcached 這種基于內存的數據庫系統來說,內存管理的效率高低是影響系統性能的關鍵因素。
傳統C語言中的 malloc/free 函數是最常用的分配和釋放內存的方法,但是這種方法存在著很大的缺陷:
-
首先,對于開發人員來說不匹配的 malloc 和 free 容易造成內存泄露;
-
其次頻繁調用會造成大量內存碎片無法回收重新利用,降低內存利用率;
-
最后作為系統調用,其系統開銷遠遠大于一般函數調用。
所以,為了提高內存的管理效率,高效的內存管理方案都不會直接使用 malloc/free 調用。Redis 和 Memcached 均使用了自身設計的內存管理機制,但是實現方法存在很大的差異,下面將會對兩者的內存管理機制分別進行介紹。
Memcached 默認使用 Slab Allocation 機制管理內存,其主要思想是按照預先規定的大小,將分配的內存分割成特定長度的塊以存儲相應長度的 key-value 數據記錄,以完全解決內存碎片問題。
Slab Allocation 機制只為存儲外部數據而設計,也就是說所有的 key-value 數據都存儲在 Slab Allocation 系統里,而 Memcached 的其它內存請求則通過普通的 malloc/free 來申請,因為這些請求的數量和頻率決定了它們不會對整個系統的性能造成影響 Slab Allocation 的原理相當簡單。
如圖所示,它首先從操作系統申請一大塊內存,并將其分割成各種尺寸的塊 Chunk,并把尺寸相同的塊分成組 Slab Class。其中,Chunk 就是用來存儲 key-value 數據的最小單位。
每個 Slab Class 的大小,可以在 Memcached 啟動的時候通過制定 Growth Factor 來控制。假定圖中 Growth Factor 的取值為1.25,如果第一組 Chunk 的大小為88個字節,第二組 Chunk 的大小就為112個字節,依此類推。
?
當 Memcached 接收到客戶端發送過來的數據時首先會根據收到數據的大小選擇一個最合適的 Slab Class,然后通過查詢 Memcached 保存著的該 Slab Class 內空閑 Chunk 的列表就可以找到一個可用于存儲數據的 Chunk。
當一條數據庫過期或者丟棄時,該記錄所占用的 Chunk 就可以回收,重新添加到空閑列表中。
從以上過程我們可以看出 Memcached 的內存管理制效率高,而且不會造成內存碎片,但是它最大的缺點就是會導致空間浪費。因為每個 Chunk 都分配了特定長度的內存空間,所以變長數據無法充分利用這些空間。
如圖 所示,將100個字節的數據緩存到128個字節的 Chunk 中,剩余的28個字節就浪費掉了。
?
Redis 的內存管理主要通過源碼中 zmalloc.h 和 zmalloc.c 兩個文件來實現的。
Redis 為了方便內存的管理,在分配一塊內存之后,會將這塊內存的大小存入內存塊的頭部。
如圖所示,real_ptr 是 redis 調用 malloc 后返回的指針。redis 將內存塊的大小size 存入頭部,size 所占據的內存大小是已知的,為 size_t 類型的長度,然后返回 ret_ptr。當需要釋放內存的時候,ret_ptr 被傳給內存管理程序。
通過 ret_ptr,程序可以很容易的算出 real_ptr 的值,然后將 real_ptr 傳給 free 釋放內存。
?
Redis 通過定義一個數組來記錄所有的內存分配情況,這個數組的長度為ZMALLOC_MAX_ALLOC_STAT。數組的每一個元素代表當前程序所分配的內存塊的個數,且內存塊的大小為該元素的下標。在源碼中,這個數組為 zmalloc_allocations。
zmalloc_allocations[16]代表已經分配的長度為16bytes的內存塊的個數。zmalloc.c 中有一個靜態變量 used_memory 用來記錄當前分配的內存總大小。
所以,總的來看,Redis 采用的是包裝的 mallc/free,相較于 Memcached 的內存管理方法來說,要簡單很多。
在 Redis 中,并不是所有的數據都一直存儲在內存中的。這是和 Memcached 相比一個最大的區別。當物理內存用完時,Redis 可以將一些很久沒用到的 value 交換到磁盤。
Redis 只會緩存所有的 key 的信息,如果 Redis 發現內存的使用量超過了某一個閥值,將觸發 swap 的操作,Redis 根據“swappability = age*log(size_in_memory)”計算出哪些 key 對應的 value 需要 swap 到磁盤。
然后再將這些 key 對應的 value 持久化到磁盤中,同時在內存中清除。
這種特性使得 Redis 可以保持超過其機器本身內存大小的數據。
當然,機器本身的內存必須要能夠保持所有的 key,畢竟這些數據是不會進行 swap 操作的。
同時由于 Redis 將內存中的數據 swap 到磁盤中的時候,提供服務的主線程和進行 swap 操作的子線程會共享這部分內存,所以如果更新需要 swap 的數據,Redis 將阻塞這個操作,直到子線程完成 swap 操作后才可以進行修改。
當從 Redis 中讀取數據的時候,如果讀取的 key 對應的 value 不在內存中,那么 Redis 就需要從 swap 文件中加載相應數據,然后再返回給請求方。這里就存在一個 I/O 線程池的問題。
在默認的情況下,Redis 會出現阻塞,即完成所有的 swap 文件加載后才會相應。這種策略在客戶端的數量較小,進行批量操作的時候比較合適。
但是如果將 Redis 應用在一個大型的網站應用程序中,這顯然是無法滿足大并發的情況的。所以 Redis 運行我們設置 I/O 線程池的大小,對需要從 swap 文件中加載相應數據的讀取請求進行并發操作,減少阻塞的時間。
Memcached 使用預分配的內存池的方式,使用 slab 和大小不同的 chunk 來管理內存,Item 根據大小選擇合適的 chunk 存儲,內存池的方式可以省去申請/釋放內存的開銷,并且能減小內存碎片產生,但這種方式也會帶來一定程度上的空間浪費,并且在內存仍然有很大空間時,新的數據也可能會被剔除,原因可以參考 Timyang 的文章:http://timyang.net/data/Memcached-lru-evictions/
Redis 使用現場申請內存的方式來存儲數據,并且很少使用 free-list 等方式來優化內存分配,會在一定程度上存在內存碎片,Redis 跟據存儲命令參數,會把帶過期時間的數據單獨存放在一起,并把它們稱為臨時數據,非臨時數據是永遠不會被剔除的,即便物理內存不夠,導致 swap 也不會剔除任何非臨時數據(但會嘗試剔除部分臨時數據),這點上 Redis 更適合作為存儲而不是 cache。
數據存儲及持久化
memcached 不支持內存數據的持久化操作,所有的數據都以 in-memory 的形式存儲。
redis 支持持久化操作。redis 提供了兩種不同的持久化方法來講數據存儲到硬盤里面,一種是快照(snapshotting),它可以將存在于某一時刻的所有數據都寫入硬盤里面。另一種方法叫只追加文件(append-only file, AOF),它會在執行寫命令時,將被執行的寫命令復制到硬盤里面。
數據一致性問題
Memcached 提供了 cas 命令,可以保證多個并發訪問操作同一份數據的一致性問題。
Redis 沒有提供 cas 命令,并不能保證這點,不過 Redis 提供了事務的功能,可以保證一串 命令的原子性,中間不會被任何操作打斷。
集群管理不同
Memcached 是全內存的數據緩沖系統,Redis 雖然支持數據的持久化,但是全內存畢竟才是其高性能的本質。作為基于內存的存儲系統來說,機器物理內存的大小就是系統能夠容納的最大數據量。如果需要處理的數據量超過了單臺機器的物理內存大小,就需要構建分布式集群來擴展存儲能力。
Memcached 本身并不支持分布式,因此只能在客戶端通過像一致性哈希這樣的分布式算法來實現 Memcached 的分布式存儲。下圖給出了 Memcached 的分布式存儲實現架構。
當客戶端向 Memcached 集群發送數據之前,首先會通過內置的分布式算法計算出該條數據的目標節點,然后數據會直接發送到該節點上存儲。但客戶端查詢數據時,同樣要計算出查詢數據所在的節點,然后直接向該節點發送查詢請求以獲取數據。
相較于 Memcached 只能采用客戶端實現分布式存儲,Redis 更偏向于在服務器端構建分布式存儲。最新版本的 Redis 已經支持了分布式存儲功能。
Redis Cluster 是一個實現了分布式且允許單點故障的 Redis 高級版本,它沒有中心節點,具有線性可伸縮的功能。
Redis Cluster 的分布式存儲架構,節點與節點之間通過二進制協議進行通信,節點與客戶端之間通過 ascii 協議進行通信。
在數據的放置策略上,Redis Cluster 將整個 key 的數值域分成4096個哈希槽,每個節點上可以存儲一個或多個哈希槽,也就是說當前 Redis Cluster 支持的最大節點數就是4096。
Redis Cluster 使用的分布式算法也很簡單:crc16( key ) % HASH_SLOTS_NUMBER。
為了保證單點故障下的數據可用性,Redis Cluster 引入了 Master 節點和 Slave 節點。
在 Redis Cluster 中,每個 Master 節點都會有對應的兩個用于冗余的 Slave 節點。這樣在整個集群中,任意兩個節點的宕機都不會導致數據的不可用。當 Master 節點退出后,集群會自動選擇一個 Slave 節點成為新的 Master 節點。
總結
以上是生活随笔為你收集整理的Redis PK Memcached,哪个更牛叉的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 仅 1579.05 元:追觅 H12 无
- 下一篇: MySQL 5.7 聚合(GROUP