1、 概況:
Redis是一款開源的、網絡化的、基於內存的、可進行數據持久化的Key-Value存儲系統。它的數據模型創建在外層,相似於其它結構化存儲系統,是經過Key映射Value的方式來創建字典以保存數據,有別於其它結構化存儲系統的是,它支持多類型存儲,包括String、List、Set、Sortset和Hash等,你能夠在這些數據類型上作不少原子性操做。redis
在操做方面,Redis基於TCP協議的特性使得它能夠經過管道的方式進行數據操做,Redis自己提供了一個可鏈接Server的客戶端,經過客戶端,可方便地進行數據存取操做。數據庫
Redis具備如下優勢:
a。單線程,利用redis隊列技術並將訪問變爲串行訪問,消除了傳統數據庫串行控制的開銷數組
b。redis具備快速和持久化的特徵,速度快,由於數據存在內存中。緩存
c。分佈式讀寫分離模式安全
d。支持豐富數據類型服務器
e。支持事務,操做都是原子性,所謂原子性就是對數據的更改要麼所有執行,要不所有不執行。網絡
f。可用於緩存,消息,按key設置過時時間,過時後自動刪除。數據結構
h。支持多種主流語言交互。app
Redis最適合全部數據in-momory的場景,雖然Redis也提供持久化功能,但實際更多的是一個disk-backed的功能,跟傳統意義上的持久化有比較大的差異。分佈式
2、 原理簡介:
redis核心對象
redisObject
Redis內部使用一個redisObject對象來表示全部的key和value。
redisObject最主要的信息如上圖所示:
type表明一個value對象具體是何種數據類型
encoding是不一樣數據類型在redis內部的存儲方式,好比:type=string表明value存儲的是一個普通字符串,那麼對應的encoding能夠是raw或者是int,若是是int則表明實際redis內部是按數值型類存儲和表示這個字符串的,固然前提是這個字符串自己能夠用數值表示,好比:"123""456"這樣的字符串。
vm字段,只有打開了Redis的虛擬內存功能,此字段纔會真正的分配內存,該功能默認是關閉狀態的。
五種數據類型的使用和內部實現方式:
1)String
經常使用命令:set/get/decr/incr/mget等;
應用場景:String是最經常使用的一種數據類型,普通的key/value存儲均可以歸爲此類;
實現方式:String在redis內部存儲默認就是一個字符串,被redisObject所引用,當遇到incr、decr等操做時會轉成數值型進行計算,此時redisObject的encoding字段爲int。
2)Hash
經常使用命令:hget/hset/hgetall等
應用場景:咱們要存儲一個用戶信息對象數據,其中包括用戶ID、用戶姓名、年齡和生日,經過用戶ID咱們但願獲取該用戶的姓名或者年齡或者生日;
實現方式:Redis的Hash實際是內部存儲的Value爲一個HashMap,並提供了直接存取這個Map成員的接口。如圖2所示,Key是用戶ID,value是一個Map。這個Map的key是成員的屬性名,value是屬性值。這樣對數據的修改和存取均可以直接經過其內部Map的Key(Redis裏稱內部Map的key爲field),也就是經過key(用戶ID)+field(屬性標籤)就能夠操做對應屬性數據。當前HashMap的實現有兩種方式:當HashMap的成員比較少時Redis爲了節省內存會採用相似一維數組的方式來緊湊存儲,而不會採用真正的HashMap結構,這時對應的value的redisObject的encoding爲zipmap,當成員數量增大時會自動轉成真正的HashMap,此時encoding爲ht。
3)List
經常使用命令:lpush/rpush/lpop/rpop/lrange等;
應用場景:Redislist的應用場景很是多,也是Redis最重要的數據結構之一,好比twitter的關注列表,粉絲列表等均可以用Redis的list結構來實現;
實現方式:Redislist的實現爲一個雙向鏈表,便可以支持反向查找和遍歷,更方便操做,不過帶來了部分額外的內存開銷,Redis內部的不少實現,包括髮送緩衝隊列等也都是用的這個數據結構。
4)Set
經常使用命令:sadd/spop/smembers/sunion等;
應用場景:Redisset對外提供的功能與list相似是一個列表的功能,特殊之處在於set是能夠自動排重的,當你須要存儲一個列表數據,又不但願出現重複數據時,set是一個很好的選擇,而且set提供了判斷某個成員是否在一個set集合內的重要接口,這個也是list所不能提供的;
實現方式:set的內部實現是一個value永遠爲null的HashMap,實際就是經過計算hash的方式來快速排重的,這也是set能提供判斷一個成員是否在集合內的緣由。
5)SortedSet
經常使用命令:zadd/zrange/zrem/zcard等;
應用場景:Redissortedset的使用場景與set相似,區別是set不是自動有序的,而sortedset能夠經過用戶額外提供一個優先級(score)的參數來爲成員排序,而且是插入有序的,即自動排序。當你須要一個有序的而且不重複的集合列表,那麼能夠選擇sortedset數據結構,好比twitter的publictimeline能夠以發表時間做爲score來存儲,這樣獲取時就是自動按時間排好序的。
實現方式:Redissortedset的內部使用HashMap和跳躍表(SkipList)來保證數據的存儲和有序,HashMap裏放的是成員到score的映射,而跳躍表裏存放的是全部的成員,排序依據是HashMap裏存的score,使用跳躍表的結構能夠得到比較高的查找效率,而且在實現上比較簡單。
Redis的持久化
Redis雖然是基於內存的存儲系統,可是它自己是支持內存數據持久化的,並且提供兩種主要的持久化策略:RDB快照和AOF日誌。
RDB快照:Redis支持將當前數據的快照存成一個數據文件的持久化機制。
可是一個持續寫入的數據庫如何生成快照呢?Redis藉助了fork命令的copyonwrite機制。在生成快照時,將當前進程fork出一個子進程,而後在子進程中循環全部的數據,將數據寫成爲RDB文件。
咱們能夠經過Redis的save指令來配置RDB快照生成的時機,好比你能夠配置當10分鐘之內有100次寫入就生成快照,也能夠配置當1小時內有1000次寫入就生成快照,也能夠多個規則一塊兒實施。這些規則的定義就在Redis的配置文件中,你也能夠經過Redis的CONFIGSET命令在Redis運行時設置規則,不須要重啓Redis。
Redis的RDB文件不會壞掉,由於其寫操做是在一個新進程中進行的,當生成一個新的RDB文件時,Redis生成的子進程會先將數據寫到一個臨時文件中,而後經過原子性rename系統調用將臨時文件重命名爲RDB文件,這樣在任什麼時候候出現故障,Redis的RDB文件都老是可用的。同時,Redis的RDB文件也是Redis主從同步內部實現中的一環。
可是,咱們能夠很明顯的看到,RDB有他的不足,就是一旦數據庫出現問題,那麼咱們的RDB文件中保存的數據並非全新的,從上次RDB文件生成到Redis停機這段時間的數據所有丟掉了。在某些業務下,這是能夠忍受的,咱們也推薦這些業務使用RDB的方式進行持久化,由於開啓RDB的代價並不高。可是對於另一些對數據安全性要求極高的應用,沒法容忍數據丟失的應用,RDB就無能爲力了,因此Redis引入了另外一個重要的持久化機制:AOF日誌。
AOF日誌:AOF日誌的全稱是appendonlyfile,從名字上咱們就能看出來,它是一個追加寫入的日誌文件。
通常數據庫的binlog不一樣的是,AOF文件是可識別的純文本,它的內容就是一個個的Redis標準命令。固然,並非發送發Redis的全部命令都要記錄到AOF日誌裏面,只有那些會致使數據發生修改的命令纔會追加到AOF文件。
那麼每一條修改數據的命令都生成一條日誌,那麼AOF文件是否是會很大?答案是確定的,AOF文件會愈來愈大,因此Redis又提供了一個功能,叫作AOFrewrite。其功能就是從新生成一份AOF文件,新的AOF文件中一條記錄的操做只會有一次,而不像一份老文件那樣,可能記錄了對同一個值的屢次操做。其生成過程和RDB相似,也是fork一個進程,直接遍歷數據,寫入新的AOF臨時文件。在寫入新文件的過程當中,全部的寫操做日誌仍是會寫到原來老的AOF文件中,同時還會記錄在內存緩衝區中。當重完操做完成後,會將全部緩衝區中的日誌一次性寫入到臨時文件中。而後調用原子性的rename命令用新的AOF文件取代老的AOF文件。
AOF是一個寫文件操做,其目的是將操做日誌寫到磁盤上,因此它也一樣會遇到咱們上面說的寫操做的5個流程。那麼寫AOF的操做安全性又有多高呢。實際上這是能夠設置的,在Redis中對AOF調用write(2)寫入後,什麼時候再調用fsync將其寫到磁盤上,經過appendfsync選項來控制,下面appendfsync的三個設置項,安全強度逐漸變強。
1)appendfsyncno
當設置appendfsync爲no的時候,Redis不會主動調用fsync去將AOF日誌內容同步到磁盤,因此這一切就徹底依賴於操做系統的調試了。對大多數Linux操做系統,是每30秒進行一次fsync,將緩衝區中的數據寫到磁盤上。
2)appendfsynceverysec
當設置appendfsync爲everysec的時候,Redis會默認每隔一秒進行一次fsync調用,將緩衝區中的數據寫到磁盤。可是當這一次的fsync調用時長超過1秒時。Redis會採起延遲fsync的策略,再等一秒鐘。也就是在兩秒後再進行fsync,這一次的fsync就無論會執行多長時間都會進行。這時候因爲在fsync時文件描述符會被阻塞,因此當前的寫操做就會阻塞。因此結論就是,在絕大多數狀況下,Redis會每隔一秒進行一次fsync。在最壞的狀況下,兩秒鐘會進行一次fsync操做。這一操做在大多數數據庫系統中被稱爲groupcommit,就是組合屢次寫操做的數據,一次性將日誌寫到磁盤。
3)appednfsyncalways
當設置appendfsync爲always時,每一次寫操做都會調用一次fsync,這時數據是最安全的,固然,因爲每次都會執行fsync,因此其性能也會受到影響。
Redis的內存管理機制
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鍵值數據庫中五種主要的數據存儲結構以及它們支持的各類應用場景,使得Redis具備了強大的性能和普遍的功能,很大程度上補償了memcached等鍵值數據庫的不足,在不少場合對關係型數據庫也起到了很好的補充做用。它支持主從結構且徹底實現了發佈/訂閱機制,在主從服務器搭建、處理冗餘數據和讀寫操做的可拓展性方面都具備至關的優點。
(參考http://chentian114.iteye.com/blog/2270254)