Redis高級特性介紹及實例分析

作者:張豐哲鏈接:http://www.lxweimin.com/p/af7043e6c8f9來源:簡書


Redis基礎類型回顧

? ??????String:string是最簡單的類型,一個key對應一個value。string類型是二進制安全的,意思是redis的string可以包含任何數(shù)據(jù),比如jpg圖片或者序列化的對象。從內(nèi)部實現(xiàn)來看其實string可以看作byte數(shù)組最大上限是1G字節(jié)。Redis中最基本,也是最簡單的數(shù)據(jù)類型。注意,VALUE既可以是簡單的String,也可以是復雜的String,如JSON,在實際中常常利用fastjson將對象序列化后存儲到Redis中。另外注意mget批量獲取可以提高效率。


?string類型的定義:struct sdshdr { long len; long free; char buf[]; };

? ? ? ? 1. len是buf數(shù)組的長度。

? ? ? ? 2. free是數(shù)組中剩余可用字節(jié)數(shù),由此可以理解為什么string類型是二進制安全的了,因為它本質(zhì)上就是個byte數(shù)組,當然可以包含任何數(shù)據(jù)了

? ? ? ? 3. buf是個char數(shù)組用于存貯實際的字符串內(nèi)容,其實char和c#中的byte是等價的,都是一個字節(jié)。

????????另外string類型可以被部分命令按int處理。比如incr等命令,如果只用string類型,redis就可以被看作加上持久化特性的memcached。



? ??????setnx:設置key對應的值為string類型的value。如果key已經(jīng)存在,返回0,nx是not exist的意思。 例如我們添加一個name= HongWan_new的鍵值對。

???? ???setex:設置key對應的值為string類型的value,并指定此鍵值對應的有效期。 例如我們添加一個haircolor= red的鍵值對,并指定它的有效期是10秒,可以這樣做:setex haircolor 10 red

????setrange:設置指定key的value值的子字符串。 例如我們希望將HongWan的126郵箱替換為gmail郵箱,那么我們可以這樣做:

????????redis 127.0.0.1:6379> get name "HongWan@126.com"?

????????redis 127.0.0.1:6379> setrange name 8 gmail.com (integer) 17?

????????redis 127.0.0.1:6379> get name "HongWan@gmail.com" redis 127.0.0.1:6379>

????????其中的8是指從下標為8(包含8)的字符開始替換

????msetnx:?一次設置多個key的值,成功返回ok表示所有的值都設置了,失敗返回0表示沒有任何值被設置,但是不會覆蓋已經(jīng)存在的key

????getset:設置key的值,并返回key的舊值

????getrange:獲取指定key的value值的子字符串



? ??????Hash:Redis hash是一個string類型的field和value的映射表。它的添加、刪除操作都是O(1)(平均)。hash特別適合用于存儲對象。相較于將對象的每個字段存成單個string類型。將一個對象存儲在hash類型中會占用更少的內(nèi)存,并且可以更方便的存取整個對象。省內(nèi)存的原因是新建一個hash對象時開始是用zipmap(又稱為small hash)來存儲的。這個zipmap其實并不是hash table,但是zipmap相比正常的hash實現(xiàn)可以節(jié)省不少hash本身需要的一些元數(shù)據(jù)存儲開銷。盡管zipmap的添加,刪除,查找都是O(n),但是由于一般對象的field數(shù)量都不太多。所以使用zipmap也是很快的,也就是說添加刪除平均還是O(1)。

????????如果field或者value的大小超出一定限制后,Redis會在內(nèi)部自動將zipmap替換成正常的hash實現(xiàn)。這個限制可以在配置文件中指定。

????????hash-max-zipmap-entries 64 #配置字段最多64個

????????hash-max-zipmap-value 512 #配置value最大為512字節(jié)

? ? ? ?Hash結(jié)構(gòu)可以使你像在數(shù)據(jù)庫中Update一個屬性一樣只修改某一項屬性值,而且還可以快速定位數(shù)據(jù)。比如,如果我們把表User中的數(shù)據(jù)可以這樣放置到Redis中:Hash存儲,KEY:User,F(xiàn)ield:USERID,VALUE:user序列化后的string。

????????實際上,可以利用List的先進先出或者先進后出的特性維護一段列表,比如排行榜、實時列表等,甚至還可以簡單的當做消息隊列來使用。


????hset:設置hash field為指定值,如果key不存在,則先創(chuàng)建。



? ??List:Redis的list類型其實就是一個每個子元素都是string類型的雙向鏈表。鏈表的最大長度是(2的32次方)。我們可以通過push,pop操作從鏈表的頭部或者尾部添加刪除元素。這使得list既可以用作棧,也可以用作隊列。

????????有意思的是list的pop操作還有阻塞版本的,當我們[lr]pop一個list對象時,如果list是空,或者不存在,會立即返回nil。但是阻塞版本的b[lr]pop則可以阻塞,當然可以加超時時間,超時后也會返回nil。為什么要阻塞版本的pop呢,主要是為了避免輪詢。舉個簡單的例子如果我們用list來實現(xiàn)一個工作隊列。執(zhí)行任務的thread可以調(diào)用阻塞版本的pop去獲取任務這樣就可以避免輪詢?nèi)z查是否有任務存在。當任務來時候工作線程可以立即返回,也可以避免輪詢帶來的延遲。


????????實際操作的方法吧:

? ?????linsert:在key對應list的特定位置之前或之后添加字符串元素

? ? ? ? ? ? ? ?redis 127.0.0.1:6379> linsert mylist3 before "world" "there"

???????lset:設置list中指定下標的元素值(下標從0開始)

? ? ? ?lrem:從key對應list中刪除count個和value相同的元素。 count>0時,按從頭到尾的順序刪除,count<0時,按從尾到頭的順序刪除。count=0時,刪除全部。

? ? ? ltrim:保留指定key 的值范圍內(nèi)的數(shù)據(jù)

? ? ??rpoplpush:從第一個list的尾部移除元素并添加到第二個list的頭部,最后返回被移除的元素值,整個操作是原子的。如果第一個list是空或者不存在返回nil。


????Set:set的是通過hash table實現(xiàn)的,所以添加、刪除和查找的復雜度都是O(1)。hash table會隨著添加或者刪除自動的調(diào)整大小。需要注意的是調(diào)整hash table大小時候需要同步(獲取寫鎖)會阻塞其他讀寫操作,可能不久后就會改用跳表(skip list)來實現(xiàn),跳表已經(jīng)在sorted set中使用了。關(guān)于set集合類型除了基本的添加刪除操作,其他有用的操作還包含集合的取并集(union),交集(intersection),差集(difference)。通過這些操作可以很容易的實現(xiàn)sns中的好友推薦和blog的tag功能。

?????????Set是String類型的不重復無序集合。Set的特點在于,它提供了集合的一些運算,比如交集、并集、差集等。這些運算特性,非常方便的解決實際場景中的一些問題,如共同關(guān)注、共同粉絲等。????



? ??sadd:向名稱為key的set中添加元素

? ??spop:隨機返回并刪除名稱為key的set中一個元素

????sdiffstore:返回所有給定key與第一個key的差集,并將結(jié)果存為另一個key

? ??smove:從第一個key對應的set中移除member并添加到第二個對應set中

????????????redis 127.0.0.1:6379> smove myset2 myset7 three

????scard:返回名稱為key的set的元素個數(shù)

? ??srandmember:隨機返回名稱為key的set的一個元素,但是不刪除元素


????ZSet:是set的一個升級版本,它在set的基礎上增加了一個順序?qū)傩?/b>,這一屬性在添加修改元素的時候可以指定,每次指定后,zset會自動重新按新的值調(diào)整順序。可以理解為有兩列的mysql表,一列存value,一列存順序。操作中key理解為zset的名字。和set一樣sorted set也是string類型元素的集合,不同的是每個元素都會關(guān)聯(lián)一個double類型的score。sorted set的實現(xiàn)是skip list和hash table的混合體。

????????當元素被添加到集合中時,一個元素到score的映射被添加到hash table中,所以給定一個元素獲取score的開銷是O(1),另一個score到元素的映射被添加到skip list,并按照score排序,所以就可以有序的獲取集合中的元素。添加,刪除操作開銷都是O(log(N))和skip list的開銷一致,redis的skip list實現(xiàn)用的是雙向鏈表,這樣就可以逆序從尾部取元素。sorted set最經(jīng)常的使用方式應該是作為索引來使用.我們可以把要排序的字段作為score存儲,對象的id當元素存儲。



? ??zadd:向名稱為key的zset中添加元素member,score用于排序。如果該元素已經(jīng)存在,則根據(jù)score更新該元素的順序

????????????redis 127.0.0.1:6379> zadd myzset 1 "one"

????zincrby:如果在名稱為key的zset中已經(jīng)存在元素member,則該元素的score增加increment;否則向集合中添加該元素,其score的值為increment。

作者:OzanShareing

鏈接:http://www.lxweimin.com/p/d6b176370efd

來源:簡書

著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。

????????ZSet就是SortedSet。實際中,很多排序場景都可以考慮ZSet來做。


Redis發(fā)展過程中的三種模式:主從、哨兵、集群

????????Redis的發(fā)展可以從版本的變化看出來,從1.X的主從模式,到2.X的哨兵模式,再到今天3.X的集群模式,可以說這些都是Redis保證數(shù)據(jù)可靠性、高可用的思路。下面我們來簡單實踐下。環(huán)境說明:這里準備了4臺Centos Linux,裝有redis的3.0版本。

主從模式

????????Redis早期用于保證數(shù)據(jù)可靠性的一種簡單方式。具體來說,Master(主)可用于寫、讀,而Slave(從)一般只用于讀。通過主從復制可以允許多個slave server 擁有和master server 相同的數(shù)據(jù)庫副本

redis 主從復制特點:

????????master 可以擁有多個slave

????????多個slave 可以連接同一個master 外,還可以連接到其他slave

????????主從復制不會阻塞master,在同步數(shù)據(jù)時,master 可以繼續(xù)處理client 請求

????????提高系統(tǒng)的伸縮性

redis 主從復制過程:

????????當配置好slave 后,slave 與master 建立連接,然后發(fā)送sync 命令。無論是第一次連接還是重新連接,master 都會啟動一個后臺進程,將數(shù)據(jù)庫快照保存到文件中,同時master主進程會開始收集新的寫命令并緩存。后臺進程完成寫文件后,master 就發(fā)送文件給slave,slave將文件保存到硬盤上,再加載到內(nèi)存中,接著master 就會把緩存的命令轉(zhuǎn)發(fā)給slave,后續(xù)master 將收到的寫命令發(fā)送給slave。如果master 同時收到多個slave 發(fā)來的同步連接命令,master 只會啟動一個進程來寫數(shù)據(jù)庫鏡像,然后發(fā)送給所有的slave。

????????其實在配置上相當簡單,只需要在Slave節(jié)點配置下Master的IP、PORT、密碼即可

Master info

Slave info

????????一個Master可以擁有多個Slave,主從復制不會阻塞住Master,在同步數(shù)據(jù)時Master可以繼續(xù)處理client端請求。


哨兵模式

????????對于主從復制模式而言,有個明顯的缺點:一旦主節(jié)點掛了,那么redis服務將不可用。在2.X中,為了確保可高用,所以發(fā)展出來哨兵模式。顧名思義,就是哨兵站崗,去監(jiān)聽master心跳,如果master掛了,那么將從slave中選舉出一個master來,從而實現(xiàn)了故障自動切換

????????實質(zhì)上,在Master-Slave模式基礎上,只需要在啟動一個哨兵服務進行監(jiān)聽就可以,這個哨兵服務可以部署在Master/Slave上,也可以部署到其他機器上。當然,在實際中為了避免哨兵節(jié)點的單點性,也會配置多個哨兵服務。

????????哨兵節(jié)點192.168.99.124? sentinel.conf:

????????????????sentinel monitor mymaster 192.168.99.121 6379 1

????????????????sentinel?down-after-milliseconds?mymaster?5000

????????????????sentinel?parallel-syncs?mymaster?2

????????我們需要告訴哨兵服務:

? ??????????????監(jiān)控的主節(jié)點的IP,PORT

? ??????????????如果master掛了,那么選舉的時候,slave達到多少票就可以成為主節(jié)點

? ??????????????監(jiān)控主節(jié)點的心跳頻率

? ??????????????主節(jié)點下有多少slave


集群模式

????????Redis集群模式是目前應用非常廣泛的,Redis集群模式的出現(xiàn),也使得以前的一些Redis技術(shù),比如分片、都不在適用了,同時數(shù)據(jù)的高可靠、數(shù)據(jù)分布性、服務的高可用性進一步加強。關(guān)于Redis集群將在下一篇博客中詳細介紹。????


Redis的簡單事務

????????目前來看,Redis對事務的支持是比較簡單的,在實際應用中,我們基本上是不會使用的。看一個實例,你就會明白。通過multi開啟事務,通過exec來提交事務。可以看到,redis的事務目前是不支持一起成功,一起失敗這種基本要求的,即便在事務中有錯誤,亦不會回退,和MySQL的事務功能相距甚遠吧。

????????redis 只能保證一個client 發(fā)起的事務中的命令可以連續(xù)的執(zhí)行,而中間不會插入其他client 的命令。由于redis 是單線程來處理所有client 的請求的所以做到這點是很容易的。一般情況下redis 在接受到一個client 發(fā)來的命令后會立即處理并返回處理結(jié)果,但是當一個client 在一個連接中發(fā)出multi 命令,這個連接會進入一個事務上下文,該連接后續(xù)的命令并不是立即執(zhí)行,而是先放到一個隊列中。當從此連接受到exec 命令后,redis 會順序的執(zhí)行隊列中的所有命令。并將所有命令的運行結(jié)果打包到一起返回給client。然后此連接就結(jié)束事務上下文。


multi

????????一般情況下redis在接受到一個client發(fā)來的命令后會立即處理并返回處理結(jié)果,但是當一個client在一個連接中發(fā)出multi命令,這個連接會進入一個事務上下文,該連接后續(xù)的命令并不是立即執(zhí)行,而是先放到一個隊列中。當從此連接受到exec命令后,redis會順序的執(zhí)行隊列中的所有命令。并將所有命令的運行結(jié)果打包到一起返回給client。然后此連接就結(jié)束事務上下文。

watch

????????Watch監(jiān)視一個(或多個) key ,如果在事務執(zhí)行之前這個(或這些) key 被其他命令所改動,那么事務將被打斷

缺點

? ? ? ? 1. redis的事務實現(xiàn)是如此簡單,當然會存在一些問題。第一個問題是redis只能保證事務的每個命令連續(xù)執(zhí)行,但是如果事務中的一個命令失敗了,并不回滾其他命令,比如使用的命令類型不匹配。

? ? ? ? 2. 當事務的執(zhí)行過程中,如果redis意外的掛了。很遺憾只有部分命令執(zhí)行了后面的也就被丟棄了。當然如果我們使用的append-only file方式持久化,redis會用單個write操作寫入整個事務內(nèi)容。即是是這種方式還是有可能只部分寫入了事務到磁盤。發(fā)生部分寫入事務的情況下,redis重啟時會檢測到這種情況,然后失敗退出。可以使用redis-check-aof工具進行修復,修復會刪除部分寫入的事務內(nèi)容。修復完后就能夠重新啟動了。


Redis持久化機制

????????Redis是一個支持持久化的內(nèi)存數(shù)據(jù)庫,也就是說Redis需要經(jīng)常將內(nèi)存中的數(shù)據(jù)同步到硬盤來保證持久化,有2種方式實現(xiàn)。

RDB

????????RDB方式,也稱作快照snapshotting,將內(nèi)存中的數(shù)據(jù)以快照的方式寫入到二進制文件dump.rdb中,這種方式也是redis的默認方式。可以在redis.conf中設置保存的策略。一句話:redis在N秒內(nèi)如果超過M個KEY發(fā)生修改則自動做快照保存


AOF

????????AOF,即Append-Only File。要知道RDB的方式,是在一定的時間間隔做一次,如果redis意外down掉,這將意味著會丟失最后一次快照后的所有修改數(shù)據(jù),這在生產(chǎn)環(huán)境將不太可能接受。AOF比RDB有著更好的持久化方式,通過AOF,redis會將每一個收到的寫命令都通過write函數(shù)追加到命令中,當redis重新啟動時,會重新執(zhí)行文件中保存的寫命令來重建數(shù)據(jù)內(nèi)容

redis.conf:

????????在實際應用中,為了確保數(shù)據(jù)高可靠性,應該使用always策略。


Redis案例設計分析

????????假設一個類似的場景,有幾百萬,甚至幾千萬的商品數(shù)據(jù),考慮下如何快速實現(xiàn)搜索查詢呢?當然,我們不可能直接查詢MySQL,應該需要在MySQL上加一層,可以考慮加一層Redis。

????????將MySQL中的數(shù)據(jù)加載至Redis中,給定條件,直接遍歷Hash數(shù)據(jù)進行查詢。如果就這樣簡單的設計的話,對于京東這樣的大流量平臺,每天有非常多的人進行商品搜索,而且每個人搜索的條件還不一樣,根本無法快速響應。

如上圖所示,我們可以這樣設計:

? ? ? ? (1) 我們事先建立好一系列的SET,實際上這些Set都是各種分類的ProductID集合

? ? ? ? (2) 用戶的搜索條件,實際上就是各種SET進行交、并、補的運算而已

? ? ? ? (3) 要知道SET進行運算后的結(jié)果,就是ProductID集合,此時范圍已經(jīng)有所縮小,比起直接遍歷全部商品數(shù)據(jù)要小不少。從這里也可以看出,Redis雖然用起來簡單,但是要綜合運用,并根據(jù)業(yè)務場景進行設計,還是挺有意思的。


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容