1,Redis支持服務器端數據操作:與Memcached相比,Redis的數據結構更多,支持更豐富的數據操作。通常,在Memcached中,您需要將數據帶到客戶端進行類似的修改,然後將其設置回來。這大大增加了網絡IO的數量和數據量。在Redis中,這些復雜的操作通常與壹般的GET/SET壹樣高效。因此,如果您需要壹個可以支持更復雜結構和操作的緩存,那麽Redis將是壹個不錯的選擇。
2.內存利用效率比較:如果使用簡單的鍵值存儲,Memcached的內存利用率更高,而如果Redis使用哈希結構進行鍵值存儲,由於其組合壓縮,其內存利用率將高於Memcached。
3.性能比較:由於Redis僅使用單核,而Memcached可以使用多核,因此平均而言,Redis在每個核上存儲小數據的性能高於Memcached。在100k以上的數據中,Memcached的性能高於Redis。盡管Redis最近優化了存儲大數據的性能,但與Memcached相比仍略遜壹籌。
為什麽會出現上述結論?以下是收集的數據:
1,支持不同的數據類型。
與Memcached只支持簡單的鍵值數據記錄不同,Redis支持更豐富的數據類型。最常用的數據類型主要由五種類型組成:字符串、哈希、列表、集合和排序集合。Redis在內部使用壹個redisObject對象來表示所有的鍵和值。redisObject的主要信息如圖所示:
Type表示值對象的具體數據類型,編碼是redis中不同數據類型的存儲方式。例如,type=string表示value存儲壹個普通字符串,對應的編碼可以是raw或int。如果是int,則意味著字符串實際上是根據數值類型在redis中存儲和表示的,前提是字符串本身可以用數值表示,例如“123”和“456”。只有打開Redis的虛擬內存功能時,vm字段才會真正分配內存,默認情況下是關閉的。
1)字符串
常用命令:set/get/decr/incr/mget等。
應用場景:字符串是最常用的數據類型,普通的鍵/值存儲都可以歸入這壹類;
實現:redis中存儲的String默認是壹個字符串,它被redisObject引用。當遇到incr、decr等操作時,會轉換為數值型進行計算,redisObject的編碼字段為int。
雜湊
常用命令:hget/hset/hgetall等。
應用場景:我們希望存儲壹個用戶信息對象數據,包括用戶ID、用戶名、年齡和生日,我們希望通過用戶ID獲取用戶的姓名或年齡或生日;
實現:Redis的Hash實際上是內部存儲的值是壹個HashMap,它提供了壹個直接訪問這個Map成員的接口。如圖所示,鍵是用戶ID,值是映射。這個映射的鍵是成員的屬性名,值是屬性值。這樣就可以直接通過其內部映射的key(Redis中內部映射的key稱為field)來修改和訪問數據,即通過key(用戶ID)+field(屬性標簽)來操作相應的屬性數據。目前HashMap的實現方式有兩種:當HashMap的成員數量較少時,Redis為了節省內存,會以類似壹維數組的方式將其緊湊存儲,而不是使用真正的HashMap結構。此時對應值redisObject的編碼為zipmap,當成員數量增加時,會自動變成壹個真正的HashMap,此時的編碼為ht。
三、列表
常用命令:lpush/rpush/lpop/rpop/lrange等。
應用場景:Redis列表的應用場景很多,也是Redis最重要的數據結構之壹,比如twitter的關註列表、粉絲列表等,都可以通過Redis的列表結構來實現;
實現:Redis list的實現是雙向鏈表,可以支持反向搜索和遍歷,操作起來更方便,但帶來了壹些額外的內存開銷。Redis內部的許多實現(包括發送緩沖隊列)也使用這種數據結構。
設定
常用命令:sadd/spop/semembers/union等。
應用場景:Redis set提供的功能與list提供的功能類似,但特殊之處在於set可以自動復制。當您需要存儲壹個列表數據並且不想要重復數據時,set是壹個很好的選擇,並且set提供了壹個重要的接口來判斷某個成員是否在set集合中,這是list無法提供的。
實現:set的內部實現是壹個HashMap,其值始終為null。實際上,它是通過計算hash來快速排列重復項,這就是為什麽set可以提供壹種判斷成員是否在集合中的方法。
5)
常用命令:zadd/zrange/zrem/zcard等。
應用場景:Redis排序集的使用場景與set類似,但不同的是set不是自動排序的,而排序集可以通過提供壹個額外的參數score對成員進行排序,並且是有序插入的,即自動排序。當您需要有序且不重復的集合列表時,可以選擇有序集合數據結構。例如,twitter的公共時間線可以以發布時間為分數進行存儲,以便在獲取時自動按時間排序。
實現方式:Redis排序集中使用HashMap和SkipList來保證數據的存儲和順序。HashMap是成員到分數的映射,而skip list存儲所有成員,排序基於HashMap中存儲的分數。使用跳表結構可以實現較高的搜索效率,並且實現相對簡單。
2.內存管理機制是不同的。
在Redis中,並非所有數據都總是存儲在內存中。這是與Memcached相比最大的區別。當物理內存耗盡時,Redis可以將壹些很久沒有使用的值交換到磁盤上。Redis只會緩存所有鍵的信息。如果Redis發現內存使用量超過某個閾值,它將觸發交換操作。Redis將根據“交換能力=年齡* log(size _ in _ memory)”計算哪些鍵的相應值需要交換到磁盤。則對應於這些鍵的值被保存到磁盤並在內存中被清除。這個特性允許Redis保存比自己機器的內存大小更多的數據。當然,機器本身的內存必須能夠容納所有的鍵。畢竟,這些數據不會被交換。同時,當Redis將內存中的數據交換到磁盤時,提供服務的主線程和執行交換操作的子線程將* * *享有這部分內存,因此如果需要交換的數據被更新,Redis將阻塞此操作,直到子線程完成交換操作。從Redis讀取數據時,如果讀取鍵對應的值不在內存中,Redis需要從交換文件中加載相應的數據,然後將其返回給請求者。這裏有壹個I/O線程池問題。默認情況下,Redis將阻塞,也就是說,在加載所有交換文件之前,它不會響應。當客戶端數量較少且進行批量操作時,此策略更適合。但是,如果將Redis應用於大型網站應用,顯然無法滿足大並發的情況。因此Redis運行我們來設置I/O線程池的大小,並對需要從交換文件中加載相應數據的讀取請求執行並發操作,從而減少阻塞時間。
對於基於內存的數據庫系統,如Redis和Memcached,內存管理效率是影響系統性能的關鍵因素。傳統C語言中的Malloc/free函數是分配和釋放內存最常用的方法,但這種方法存在很大缺陷:首先,Malloc和free之間的不匹配容易給開發人員造成內存泄漏;其次,頻繁調用會導致大量內存碎片無法恢復和重用,降低內存利用率;最後,作為系統調用,其系統開銷遠遠大於壹般函數調用。因此,為了提高內存管理的效率,高效的內存管理方案不會直接使用malloc/free調用。Redis和Memcached都使用自己的內存管理機制,但在實現方法上有很大差異。下面將分別介紹它們的內存管理機制。
Memcached默認使用Slab分配機制來管理內存,其主要思想是將分配的內存按照預先指定的大小劃分為特定長度的塊,以存儲相應長度的鍵值數據記錄,從而徹底解決內存碎片問題。Slab分配機制只為存儲外部數據而設計,也就是說所有的鍵值數據都存儲在Slab分配系統中,而Memcached的其他內存請求則通過普通的malloc/free來申請,因為這些請求的數量和頻率決定了它們不會影響整個系統的性能。平板分配的原理非常簡單。如圖所示,它首先向操作系統申請壹大塊內存,並將其劃分為各種大小的塊,並將大小相同的塊劃分為組Slab Class。其中,Chunk是用於存儲鍵值數據的最小單元。啟動Memcached時,可以通過設置增長因子來控制每個Slab類的大小。假設圖中增長因子的值為1.25,如果第壹個塊的大小為88字節,則第二個塊的大小為112字節,依此類推。
當Memcached接收到客戶端發送的數據時,它將首先根據接收到的數據的大小選擇最合適的Slab類,然後通過查詢Memcached保存的Slab類Uchikoga Chunks的列表來找到可用於存儲數據的Chunk。當數據庫過期或被丟棄時,記錄占用的塊可以被回收並再次添加到空閑列表中。從上面的過程中,我們可以看到Memcached的內存管理系統是高效的,不會造成內存碎片,但其最大的缺點是會導致空間浪費。因為每個塊都分配了特定長度的內存空間,所以可變長度數據無法充分利用這些空間。如圖所示,100字節的數據緩存在128字節的Chunk中,剩余的28字節被浪費。
Redis的內存管理主要通過源代碼中的兩個文件zmalloc.h和zmalloc.c來實現,為了方便管理內存,Redis在分配壹個內存後會將這個內存的大小存儲在內存塊的頭中。如圖所示,real_ptr是redis調用malloc後返回的指針。Redis在頭中存儲內存塊的大小,大小占用的內存大小是已知的,也就是size_t類型的長度,然後返回ret_ptr。當需要釋放內存時,ret_ptr被傳遞給內存管理器。通過ret_ptr,程序可以輕松地計算real_ptr的值,然後將real_ptr傳遞給free以釋放內存。
Redis通過定義壹個長度為ZMALLOC_MAX_ALLOC_STAT的數組來記錄所有內存分配情況。數組的每個元素代表當前程序分配的內存塊的數量,內存塊的大小是元素的下標。在源代碼中,這個數組是zmalloc_allocations。zmalloc _ allocations【16】表示長度為16字節的已分配內存塊的數量。zmalloc.c中有壹個靜態變量used_memory,用於記錄總的分配內存大小。所以,總的來說,Redis使用的是packed mallc/free,這比Memcached的內存管理方法簡單得多。
3.數據持久性支持
盡管Redis是基於內存的存儲系統,但它支持內存數據的持久化,並提供兩種主要的持久化策略:RDB快照和AOF日誌。Memcached不支持數據持久性。
1)RDB快照
Redis支持將當前數據的快照保存為數據文件的持久化機制,即RDB快照。但是壹個不斷寫入的數據庫如何生成快照呢?Redis使用fork命令的寫入時復制機制。生成快照時,將當前進程從子進程中分出,然後循環子進程中的所有數據並將數據寫入RDB文件。我們可以通過Redis的save命令配置RDB快照生成的時間,例如配置10分鐘生成快照,配置1000次寫入生成快照,或者同時實施多個規則。這些規則的定義在Redis的配置文件中。您還可以通過Redis的CONFIG SET命令在Redis運行時設置規則,而無需重新啟動Redis。
Redis的RDB文件不會被破壞,因為它的寫入操作是在壹個新的進程中進行的。當生成壹個新的RDB文件時,Redis生成的子進程首先將數據寫入壹個臨時文件,然後通過原子重命名系統調用將該臨時文件重命名為RDB文件,這樣無論何時發生故障,Redis的RDB文件始終可用。同時,Redis的RDB文件也是Redis內部實現主從同步的壹部分。RDB有它的缺點,那就是壹旦數據庫出現問題,存儲在我們RDB文件中的數據就不是全新的了,從上壹代RDB文件到Redis停機的所有數據都丟失了。在壹些企業中,這是可以忍受的。
2)AOF日誌
AOF日誌的全名是僅附加文件,這是壹個附加日誌文件。與壹般數據庫的binlog不同,AOF文件是壹個可識別的純文本,其內容是壹個個Redis標準命令。只有那些會導致數據修改的命令才會被附加到AOF文件中。每個修改數據的命令都會生成壹個日誌,AOF文件會變得越來越大,因此Redis提供了另壹個名為AOF重寫的功能。它的功能是重新生成壹個AOF文件。新AOF文件中的壹條記錄將只被操作壹次,這與舊文件不同,舊文件可能會記錄對同壹值的多次操作。它的生成過程類似於RDB,也是fork的過程,直接遍歷數據並寫入新的AOF臨時文件。在寫入新文件的過程中,所有寫入操作日誌仍將寫入舊的AOF文件並記錄在內存緩沖區中。當重放操作完成時,緩沖區中的所有日誌將壹次性寫入臨時文件。然後調用原子重命名命令用新的AOF文件替換舊的AOF文件。
AOF是壹種文件寫入操作,其目的是將操作日誌寫入磁盤,因此也會遇到我們上面提到的寫入操作過程。在Redis中調用write to AOF後,調用fsync將其寫入磁盤的時間由appendfsync選項控制。appendfsync的以下三個設置越來越強。
Appendfsync no當Appendfsync設置為no時,Redis不會主動調用fsync將AOF日誌內容同步到磁盤,因此這壹切完全取決於操作系統的調試。對於大多數Linux操作系統,每30秒執行壹次fsync,將緩沖區中的數據寫入磁盤。
Appendfsync everysec當Appendfsync設置為everysec時,Redis將默認每秒進行壹次fsync調用,以將緩沖區中的數據寫入磁盤。但是,當這個fsync調用的時間超過1秒時。Redis會采取延遲fsync的策略,再等壹秒。也就是說,fsync會在兩秒後執行,而這壹次fsync無論執行多久都會執行。此時,因為文件描述符將在fsync期間被阻止,所以當前的寫操作將被阻止。所以結論是,在大多數情況下,Redis會每隔壹秒進行壹次fsync。在最壞的情況下,fsync操作將每兩秒執行壹次。這種操作在大多數數據庫系統中稱為組提交,它是將多次寫操作的數據組合在壹起,並將日誌壹次性寫入磁盤。
Appednfsync always當Appednfsync設置為always時,每次寫操作都會調用壹次fsync,此時的數據是最安全的。當然,它的性能也會受到影響,因為它每次都會被執行。
對於壹般業務需求,建議使用RDB進行持久化,因為RDB的開銷比AOF日誌低得多。對於那些不能忍受數據丟失的應用程序,建議使用AOF日誌。
4.不同的集群管理。
Memcached是壹個全內存數據緩沖系統。雖然Redis支持數據持久化,但全內存是其高性能的本質。作為基於內存的存儲系統,機器物理內存的大小是系統可以容納的最大數據量。如果要處理的數據量超過了單臺機器的物理內存大小,則有必要構建壹個分布式集群來擴展存儲容量。
Memcached本身不支持分布式,因此Memcached的分布式存儲只能在客戶端通過壹致性哈希等分布式算法來實現。下圖顯示了Memcached的分布式存儲實現架構。客戶端在向Memcached集群發送數據之前,會先通過內置的分布式算法計算出數據的目標節點,然後數據會直接發送到該節點進行存儲。但是客戶端查詢數據時,還需要計算查詢數據所在的節點,然後直接向節點發送查詢請求來獲取數據。
與Memcached只能在客戶端實現分布式存儲相比,Redis更傾向於在服務器端構建分布式存儲。最新版本的Redis已經支持分布式存儲功能。Redis集群是Redis的高級版本,它實現了分布式並允許單點故障。它沒有中心節點,具有線性可擴展性。下圖顯示了Rediscoluster的分布式存儲架構,其中節點之間通過二進制協議進行通信,節點與客戶端之間通過ascii協議進行通信。在數據放置策略中,Redis Cluster將整個關鍵數值域劃分為4096個哈希槽,每個節點可以存儲壹個或多個哈希槽,這意味著Redis Cluster支持的最大節點數為4096個。Redis集群使用的分布式算法也很簡單:CRC 16(key)% hash _ slots _ number。
為了保證單點故障下的數據可用性,Redis集群引入了主節點和從節點。在Redis集群中,每個主節點將有兩個對應的從節點用於冗余。這樣,整個集群中任意兩個節點的宕機都不會導致數據不可用。當主節點退出時,群集將自動選擇壹個從節點成為新的主節點。