天天看點

Memcached與Redis(二)

2.2 Redis的常用資料類型

與Memcached僅支援簡單的key-value結構的資料記錄不同,Redis支援的資料類型要豐富得多。最為常用的資料類型主要由五種:String、Hash、List、Set和Sorted Set。在具體描述這幾種資料類型之前,我們先通過一張圖來了解下Redis内部記憶體管理中是如何描述這些不同資料類型的。

Memcached與Redis(二)

                        圖1 Redis對象

Redis内部使用一個redisObject對象來表示所有的key和value。redisObject最主要的資訊如圖1所示:type代表一個value對象具體是何種資料類型,encoding是不同資料類型在redis内部的存儲方式,比如:type=string代表value存儲的是一個普通字元串,那麼對應的encoding可以是raw或者是int,如果是int則代表實際redis内部是按數值型類存儲和表示這個字元串的,當然前提是這個字元串本身可以用數值表示,比如:"123" "456"這樣的字元串。這裡需要特殊說明一下vm字段,隻有打開了Redis的虛拟記憶體功能,此字段才會真正的配置設定記憶體,該功能預設是關閉狀态的。通過Figure1我們可以發現Redis使用redisObject來表示所有的key/value資料是比較浪費記憶體的,當然這些記憶體管理成本的付出主要也是為了給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。

Memcached與Redis(二)

圖2 Redis的Hash資料類型

3)List

常用指令:lpush/rpush/lpop/rpop/lrange等;

應用場景:Redis list的應用場景非常多,也是Redis最重要的資料結構之一,比如twitter的關注清單,粉絲清單等都可以用Redis的list結構來實作;

實作方式:Redis list的實作為一個雙向連結清單,即可以支援反向查找和周遊,更友善操作,不過帶來了部分額外的記憶體開銷,Redis内部的很多實作,包括發送緩沖隊列等也都是用的這個資料結構。

4)Set

常用指令:sadd/spop/smembers/sunion等;

應用場景:Redis set對外提供的功能與list類似是一個清單的功能,特殊之處在于set是可以自動排重的,當你需要存儲一個清單資料,又不希望出現重複資料時,set是一個很好的選擇,并且set提供了判斷某個成員是否在一個set集合内的重要接口,這個也是list所不能提供的;

實作方式:set 的内部實作是一個 value永遠為null的HashMap,實際就是通過計算hash的方式來快速排重的,這也是set能提供判斷一個成員是否在集合内的原因。

5)Sorted Set

常用指令:zadd/zrange/zrem/zcard等;

應用場景:Redis sorted set的使用場景與set類似,差別是set不是自動有序的,而sorted set可以通過使用者額外提供一個優先級(score)的參數來為成員排序,并且是插入有序的,即自動排序。當你需要一個有序的并且不重複的集合清單,那麼可以選擇sorted set資料結構,比如twitter 的public timeline可以以發表時間作為score來存儲,這樣擷取時就是自動按時間排好序的。

實作方式:Redis sorted set的内部使用HashMap和跳躍表(SkipList)來保證資料的存儲和有序,HashMap裡放的是成員到score的映射,而跳躍表裡存放的是所有的成員,排序依據是HashMap裡存的score,使用跳躍表的結構可以獲得比較高的查找效率,并且在實作上比較簡單。

2.3 Redis的持久化

Redis雖然是基于記憶體的存儲系統,但是它本身是支援記憶體資料的持久化的,而且提供兩種主要的持久化政策:RDB快照和AOF日志。我們會在下文分别介紹這兩種不同的持久化政策。

2.3.1 Redis的RDB快照

Redis支援将目前資料的快照存成一個資料檔案的持久化機制,即RDB快照。這種方法是非常好了解的,但是一個持續寫入的資料庫如何生成快照呢?Redis借助了fork指令的copy on write機制。在生成快照時,将目前程序fork出一個子程序,然後在子程序中循環所有的資料,将資料寫成為RDB檔案。

我們可以通過Redis的save指令來配置RDB快照生成的時機,比如你可以配置當10分鐘以内有100次寫入就生成快照,也可以配置當1小時内有1000次寫入就生成快照,也可以多個規則一起實施。這些規則的定義就在Redis的配置檔案中,你也可以通過Redis的CONFIG SET指令在Redis運作時設定規則,不需要重新開機Redis。

Redis的RDB檔案不會壞掉,因為其寫操作是在一個新程序中進行的,當生成一個新的RDB檔案時,Redis生成的子程序會先将資料寫到一個臨時檔案中,然後通過原子性rename系統調用将臨時檔案重命名為RDB檔案,這樣在任何時候出現故障,Redis的RDB檔案都總是可用的。同時,Redis的RDB檔案也是Redis主從同步内部實作中的一環。

但是,我們可以很明顯的看到,RDB有他的不足,就是一旦資料庫出現問題,那麼我們的RDB檔案中儲存的資料并不是全新的,從上次RDB檔案生成到Redis停機這段時間的資料全部丢掉了。在某些業務下,這是可以忍受的,我們也推薦這些業務使用RDB的方式進行持久化,因為開啟RDB的代價并不高。但是對于另外一些對資料安全性要求極高的應用,無法容忍資料丢失的應用,RDB就無能為力了,是以Redis引入了另一個重要的持久化機制:AOF日志。

2.3.2 Redis的AOF日志

AOF日志的全稱是append only file,從名字上我們就能看出來,它是一個追加寫入的日志檔案。與一般資料庫的binlog不同的是,AOF檔案是可識别的純文字,它的内容就是一個個的Redis标準指令。當然,并不是發送發Redis的所有指令都要記錄到AOF日志裡面,隻有那些會導緻資料發生修改的指令才會追加到AOF檔案。那麼每一條修改資料的指令都生成一條日志,那麼AOF檔案是不是會很大?答案是肯定的,AOF檔案會越來越大,是以Redis又提供了一個功能,叫做AOF rewrite。其功能就是重新生成一份AOF檔案,新的AOF檔案中一條記錄的操作隻會有一次,而不像一份老檔案那樣,可能記錄了對同一個值的多次操作。其生成過程和RDB類似,也是fork一個程序,直接周遊資料,寫入新的AOF臨時檔案。在寫入新檔案的過程中,所有的寫記錄檔還是會寫到原來老的AOF檔案中,同時還會記錄在記憶體緩沖區中。當重完操作完成後,會将所有緩沖區中的日志一次性寫入到臨時檔案中。然後調用原子性的rename指令用新的AOF檔案取代老的AOF檔案。

AOF是一個寫檔案操作,其目的是将記錄檔寫到磁盤上,是以它也同樣會遇到我們上面說的寫操作的5個流程。那麼寫AOF的操作安全性又有多高呢。實際上這是可以設定的,在Redis中對AOF調用write(2)寫入後,何時再調用fsync将其寫到磁盤上,通過appendfsync選項來控制,下面appendfsync的三個設定項,安全強度逐漸變強。

1)appendfsync no

當設定appendfsync為no的時候,Redis不會主動調用fsync去将AOF日志内容同步到磁盤,是以這一切就完全依賴于作業系統的調試了。對大多數Linux作業系統,是每30秒進行一次fsync,将緩沖區中的資料寫到磁盤上。

2)appendfsync everysec

當設定appendfsync為everysec的時候,Redis會預設每隔一秒進行一次fsync調用,将緩沖區中的資料寫到磁盤。但是當這一次的fsync調用時長超過1秒時。Redis會采取延遲fsync的政策,再等一秒鐘。也就是在兩秒後再進行fsync,這一次的fsync就不管會執行多長時間都會進行。這時候由于在fsync時檔案描述符會被阻塞,是以目前的寫操作就會阻塞。是以結論就是,在絕大多數情況下,Redis會每隔一秒進行一次fsync。在最壞的情況下,兩秒鐘會進行一次fsync操作。這一操作在大多數資料庫系統中被稱為group commit,就是組合多次寫操作的資料,一次性将日志寫到磁盤。

3)appednfsync always

當設定appendfsync為always時,每一次寫操作都會調用一次fsync,這時資料是最安全的,當然,由于每次都會執行fsync,是以其性能也會受到影響。

本文轉自liujing0751CTO部落格,原文連結:http://blog.51cto.com/13281352/1981036 ,如需轉載請自行聯系原作者

上一篇: linux sed
下一篇: js 滾動條