redis進階

redis高級用法包括:
排序
事務
管道
發(fā)布/訂閱
持久化
主從復制
虛擬內(nèi)存

排序
可以對list和set和zset進行排序傲诵,以list為例子來講:
既可以用list本身的按數(shù)字或者字母的升序律杠、降序來排序,例如:

127.0.0.1:6379> rpush l 3 4 1 5 2
(integer) 5
127.0.0.1:6379> lrange l 0 5
1) "3"
2) "4"
3) "1"
4) "5"
5) "2"
127.0.0.1:6379> sort l
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
127.0.0.1:6379>
---------------------------------------------------------------------------
127.0.0.1:6379> lpush l xiaoh huo xingming beijing changping
(integer) 5
127.0.0.1:6379> lrange l 0 5
1) "changping"
2) "beijing"
3) "xingming"
4) "huo"
5) "xiaoh"
127.0.0.1:6379> sort l alpha
1) "beijing"
2) "changping"
3) "huo"
4) "xiaoh"
5) "xingming"
127.0.0.1:6379> sort l alpha desc
1) "xingming"
2) "xiaoh"
3) "huo"
4) "changping"
5) "beijing"
127.0.0.1:6379>

又可以自己自定義規(guī)則瞬哼,根據(jù)自定義規(guī)則進行排序。但是redis里面的這個自定義規(guī)則看起來很奇怪奏属,這個規(guī)則和list里面的元素是怎么建立聯(lián)系的,好像并不是那么直觀:

127.0.0.1:6379> lpush l e s g f a
(integer) 5
127.0.0.1:6379> lrange l 0 9
1) "a"
2) "f"
3) "g"
4) "s"
5) "e"
127.0.0.1:6379> sort l alpha
1) "a"
2) "e"
3) "f"
4) "g"
5) "s"
127.0.0.1:6379> set namea 4
OK
127.0.0.1:6379> set namee 5
OK
127.0.0.1:6379> set namef 3
OK
127.0.0.1:6379> set nameg 1
OK
127.0.0.1:6379> set names 2
OK
127.0.0.1:6379> sort l by name*
1) "g"
2) "s"
3) "f"
4) "a"
5) "e"
-----------------------------------------------------------
127.0.0.1:6379> set namea xiaoh
OK
127.0.0.1:6379> set namee me
OK
127.0.0.1:6379> set namef huo
OK
127.0.0.1:6379> set nameg blog
OK
127.0.0.1:6379> set names xingming
OK
127.0.0.1:6379> sort l by name* alpha
1) "g"
2) "f"
3) "e"
4) "a"
5) "s"

上面的方法返回的是原始的數(shù)據(jù)內(nèi)容掏秩,如果想獲得新的KEY對應的值(也就是進行排序的KEY)可以使用 GET PATTERN

127.0.0.1:6379> sort l by name* alpha get name*
1) "blog"
2) "huo"
3) "me"
4) "xiaoh"
5) "xingming"
127.0.0.1:6379> sort l by name* alpha get name* get #
 1) "blog"
 2) "g"
 3) "huo"
 4) "f"
 5) "me"
 6) "e"
 7) "xiaoh"
 8) "a"
 9) "xingming"
10) "s"
127.0.0.1:6379>

對redis里面排序的思考:如果我們有多個redis server的話,不同的key可能存在于不同的server中荆姆,比如name1哗讥,name2,name3可能就分別存儲在不同的server中胞枕。這種情況對排序性能就會造成很大的影響。

redis作者在他的blog上提到了這個問題的解決辦法魏宽,就是通過key tag將需要排序的key都放到同一個server上 腐泻。

再回來,具體決定哪個key存儲在哪個server上队询,是由客戶端的hash算法決定的派桩。我們可以通過對key的一部分內(nèi)容進行hash來確保要排序的key在同一臺server上,比如name1蚌斩,name2铆惑,name3這三個key,如果只對相同的部分"name"進行hash送膳,則name1员魏,name2和name3這三個key肯定就hash到同一臺server上了,這樣就可以在一臺server上排序了叠聋。

舉個例子假如我們的client如果發(fā)現(xiàn)key中包含[]撕阎。那么只對key中[]包含的內(nèi)容進行hash。我們將四個name相關(guān)的key碌补,都這樣命名[name]1 [name]2 [name]3 [name]4 [name]5虏束,于是client程序就會把他們都放到同一server上。

還有一個問題厦章,如果要排序的set或者list很大镇匀,排序就需要消耗很長時間。由于redis是單線程的袜啃,所以長時間的排序操作會阻塞其他client的請求汗侵。解決辦法是通過主從復制,把數(shù)據(jù)從主server復制到slave server上群发,只在slave server上做排序晃择,主server就可以無阻的接收其他client的請求了。slave server上做的排序操作還可以緩存起來也物。另一個辦法就是用sorted set來對需要按某個順序排序的集合建立索引宫屠。

事務
redis對事務的支持比較簡單,redis只能保證一個client發(fā)起的事務執(zhí)行過程中滑蚯,不會插入其他client的命令浪蹂。
因為是單線程的抵栈,所以實現(xiàn)事務是很容易的。但是redis里面的事務有缺點坤次。
事務是從multi開始古劲,以exec結(jié)束。一般client提交請求缰猴,server會馬上執(zhí)行并返回結(jié)果給client产艾。但是當client在一個連接中發(fā)出multi命令,這個連接會進入一個事務上下文環(huán)境滑绒,該連接后續(xù)的命令并不是立刻執(zhí)行闷堡,而是放到一個隊列中,當此連接接收到一個exec命令后疑故,redis會順序的執(zhí)行隊列中的那些命令杠览,并將執(zhí)行結(jié)果一起返回給client,然后此連接結(jié)束事務上下文纵势。

127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr a
QUEUED
127.0.0.1:6379> incr a
QUEUED
127.0.0.1:6379> incr b
QUEUED
127.0.0.1:6379> exec
1) (integer) 1
2) (integer) 2
3) (integer) 1
127.0.0.1:6379>

redis事務的缺點:
第一個問題是redis只能保證事務的每個命令連續(xù)執(zhí)行踱阿,但是如果事務中的一個命令失敗了,并不回滾其他命令钦铁,比如使用的命令類型不匹配软舌。

127.0.0.1:6379> set b 1
OK
127.0.0.1:6379> set a e
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr a
QUEUED
127.0.0.1:6379> incr b
QUEUED
127.0.0.1:6379> exec
1) (error) ERR value is not an integer or out of range
2) (integer) 2
127.0.0.1:6379>

可以發(fā)現(xiàn),雖然執(zhí)行中間有問題牛曹,但并沒有回滾葫隙,其他命令還是執(zhí)行了,而且第一條命令執(zhí)行失敗,并不影響后面命令的執(zhí)行躏仇。
最后一個十分罕見的問題是恋脚,當事務的執(zhí)行過程中,如果redis意外的掛了焰手。很遺憾只有部分命令執(zhí)行了糟描,后面的也就被丟棄了。當然如果我們使用的append-only file方式持久化书妻,redis會用單個write操作寫入整個事務內(nèi)容船响。即是是這種方式還是有可能只部分寫入了事務到磁盤。發(fā)生部分寫入事務的情況下躲履,redis重啟時會檢測到這種情況见间,然后失敗退出」げ拢可以使用redis-check-aof工具進行修復米诉,修復會刪除部分寫入的事務內(nèi)容。修復完后就能夠重新啟動了篷帅。

管道pipeline
redis是一個cs模式的tcp server史侣,使用和http類似的請求響應協(xié)議拴泌。

一個client可以通過一個socket連接發(fā)起多個請求命令。每個請求命令發(fā)出后client通常會阻塞并等待redis服務處理惊橱,redis處理完后請求命令后會將結(jié)果通過響應報文返回給client蚪腐。基本的通信過程如下

Client: INCR X
Server: 1
Client: INCR X
Server: 2
Client: INCR X
Server: 3
Client: INCR X
Server: 4

基本上四個命令需要8個tcp報文才能完成税朴。由于通信會有網(wǎng)絡延遲,假如從client和server之間的包傳輸時間需要0.125秒回季。那么上面的四個命令8個報文至少會需要1秒才能完成。這樣即使redis每秒能處理100個命令正林,而我們的client也只能一秒鐘發(fā)出四個命令泡一。這顯示沒有充分利用redis的處理能力。除了可以利用mget,mset之類的單條命令處理多個key的命令外我們還可以利用pipeline的方式從client打包多條命令一起發(fā)出卓囚,不需要等待單條命令的響應返回,而redis服務端會處理完多條命令后會將多條命令的處理結(jié)果打包到一起返回給客戶端诅病。通信過程如下

Client: INCR X
Client: INCR X
Client: INCR X
Client: INCR X
Server: 1
Server: 2
Server: 3
Server: 4

假設(shè)不會因為tcp報文過長而被拆分哪亿。可能兩個tcp報文就能完成四條命令,client可以將四個incr命令放到一個tcp報文一起發(fā)送,server則可以將四條命令的處理結(jié)果放到一個tcp報文返回。

通過pipeline方式當有大批量的操作時候聘裁。我們可以節(jié)省很多原來浪費在網(wǎng)絡延遲的時間艳汽。需要注意到是用pipeline方式打包命令發(fā)送,redis必須在處理完所有命令前先緩存起所有命令的處理結(jié)果嫩絮。打包的命令越多,緩存消耗內(nèi)存也越多。所以并是不是打包的命令越多越好板辽。具體多少合適需要根據(jù)具體情況測試。

下面是我測試是否使用pipeline的代碼:

from redis import Redis
import time

r = Redis()

def without_pipeline(times=100000):
    r.set('a', 0)
    for i in range(times):
        r.incr('a')

def use_pipeline(times=100000):
    r.set('a', 0)
    r.set('a', 0)
    pip = r.pipeline()
    for i in range(times):
        pip.incr('a')
    pip.execute()

start = time.time()
without_pipeline()
end = time.time()
print 'without pipeline spendtime:%f' % (end-start)

start = time.time()
use_pipeline()
end = time.time()
print 'use pipeline spendtime:%f' % (end-start)

結(jié)果為:

without pipeline spendtime:4.221240
use pipeline spendtime:1.572646

看起來對效果提升還是很明顯的棘催。

持久化
有快照和appendof兩種方式劲弦,
快照是將數(shù)據(jù)全部寫入到rdb文件中,appendof是將修改命令寫入到aof文件中醇坝,所以aof文件會越來越大邑跪,redis提供了bgrewriteaof命令來整理aof文件。

主從復制(自己沒用過主從復制呼猪,都是別人的經(jīng)驗)
Redis主從復制配置和使用都非常簡單画畅。通過主從復制可以允許多個slave server擁有和master server相同的數(shù)據(jù)庫副本。

關(guān)于redis主從復制的一些特點

master可以有多個slave
除了多個slave連到相同的master外宋距,slave也可以連接其他slave形成圖狀結(jié)構(gòu)
主從復制不會阻塞master轴踱。也就是說當一個或多個slave與master進行初次同步數(shù)據(jù)時,master可以繼續(xù)處理client發(fā)來的請求谚赎。相反slave在初次同步數(shù)據(jù)時則會阻塞不能處理client的請求寇僧。
主從復制可以用來提高系統(tǒng)的可伸縮性,我們可以用多個slave專門用于client的讀請求摊腋,比如sort操作可以使用slave來處理。也可以用來做簡單的數(shù)據(jù)冗余
可以在master禁用數(shù)據(jù)持久化嘁傀,只需要注釋掉master配置文件中的所有save配置兴蒸,然后只在slave上配置數(shù)據(jù)持久化。

下面介紹下主從復制的過程

當設(shè)置好slave服務器后细办,slave會建立和master的連接橙凳,然后發(fā)送sync命令。

無論是第一次同步建立的連接還是連接斷開后的重新連接笑撞,master都會啟動一個后臺進程岛啸,將數(shù)據(jù)庫快照保存到文件中,同時master主進程會開始收集新的寫命令并緩存起來茴肥。后臺進程完成寫文件后坚踩,master就發(fā)送文件給slave,slave將文件保存到磁盤上瓤狐,然后加載到內(nèi)存恢復數(shù)據(jù)庫快照到slave上瞬铸。

接著master就會把緩存的命令轉(zhuǎn)發(fā)給slave。而且后續(xù)master收到的寫命令都會通過開始建立的連接發(fā)送給slave础锐。

從master到slave的同步數(shù)據(jù)的命令和從client發(fā)送的命令使用相同的協(xié)議格式嗓节。當master和slave的連接斷開時slave可以自動重新建立連接。

如果master同時收到多個slave發(fā)來的同步連接命令皆警,只會使用啟動一個進程來寫數(shù)據(jù)庫鏡像拦宣,然后發(fā)送給所有slave。

配置slave服務器很簡單信姓,只需要在配置文件中加入如下配置

slaveof 192.168.1.1 6379  #指定master的ip和端口

虛擬內(nèi)存
首先說明下redis
的虛擬內(nèi)存與os
的虛擬內(nèi)存不是一碼事鸵隧,但是思路和目的都是相同的。就是暫時把不經(jīng)常訪問的數(shù)據(jù)從內(nèi)存交換到磁盤中意推,從而騰出寶貴的內(nèi)存空間用于其他需要訪問的數(shù)據(jù)掰派。尤其是對于redis
這樣的內(nèi)存數(shù)據(jù)庫,內(nèi)存總是不夠用的左痢。除了可以將數(shù)據(jù)分割到多個redis server
外靡羡。另外的能夠提高數(shù)據(jù)庫容量的辦法就是使用vm
把那些不經(jīng)常訪問的數(shù)據(jù)交換的磁盤上。

如果我們的存儲的數(shù)據(jù)總是有少部分數(shù)據(jù)被經(jīng)常訪問俊性,大部分數(shù)據(jù)很少被訪問略步,對于網(wǎng)站來說確實總是只有少量用戶經(jīng)常活躍定页。當少量數(shù)據(jù)被經(jīng)常訪問時趟薄,使用vm不但能提高單臺redis server
數(shù)據(jù)庫的容量,而且也不會對性能造成太多影響典徊。

redis
沒有使用os
提供的虛擬內(nèi)存機制而是自己在用戶態(tài)實現(xiàn)了自己的虛擬內(nèi)存機制,作者在自己的blog
專門解釋了其中原因杭煎。http://antirez.com/post/redis-virtual-memory-story.html

主要的理由有兩點

os
的虛擬內(nèi)存是已4k頁面為最小單位進行交換的恩够。而redis
的大多數(shù)對象都遠小于4k,所以一個os頁面上可能有多個redis
對象羡铲。另外redis
的集合對象類型如list
,set
可能存在與多個os頁面上蜂桶。最終可能造成只有10%key被經(jīng)常訪問,但是所有os
頁面都會被os
認為是活躍的也切,這樣只有內(nèi)存真正耗盡時os才會交換頁面扑媚。

相比于os的交換方式。redis
可以將被交換到磁盤的對象進行壓縮,保存到磁盤的對象可以去除指針和對象元數(shù)據(jù)信息雷恃。一般壓縮后的對象會比內(nèi)存中的對象小10倍疆股。這樣redis
的vm會比os vm能少做很多io操作。

下面是vm相關(guān)配置

vm-enabled yes #開啟vm功能

vm-swap-file /tmp/redis.swap #交換出來的value保存的文件路徑/tmp/redis.swap

vm-max-memory 1000000 #redis使用的最大內(nèi)存上限倒槐,超過上限后redis開始交換value到磁盤文件中旬痹。

vm-page-size 32 #每個頁面的大小32個字節(jié)

vm-pages 134217728 #最多使用在文件中使用多少頁面,交換文件的大小 = vm-page-size * vm-pages

vm-max-threads 4 #用于執(zhí)行value對象換入換出的工作線程數(shù)量。0表示不使用工作線程(后面介紹)

redis
的vm
在設(shè)計上為了保證key
的查找速度讨越,只會將value
交換到swap
文件中两残。所以如果是內(nèi)存問題是由于太多value
很小的key
造成的,那么vm
并不能解決谎痢。

和os一樣redis
也是按頁面來交換對象的磕昼。redis
規(guī)定同一個頁面只能保存一個對象卷雕。但是一個對象可以保存在多個頁面中节猿。

在redis
使用的內(nèi)存沒超過vm-max-memory
之前是不會交換任何value
的。當超過最大內(nèi)存限制后漫雕,redis
會選擇較老的對象滨嘱。如果兩個對象一樣老會優(yōu)先交換比較大的對象,精確的公式swappability = age*log(size_in_memory)
浸间。

對于vm-page-size
的設(shè)置應該根據(jù)自己的應用將頁面的大小設(shè)置為可以容納大多數(shù)對象的大小太雨。太大了會浪費磁盤空間,太小了會造成交換文件出現(xiàn)碎片魁蒜。

對于交換文件中的每個頁面囊扳,redis
會在內(nèi)存中對應一個1bit
值來記錄頁面的空閑狀態(tài)。所以像上面配置中頁面數(shù)量(vm-pages 134217728
)會占用16M內(nèi)存用來記錄頁面空閑狀態(tài)兜看。`

vm-max-threads表示用做交換任務的線程數(shù)量锥咸。如果大于0推薦設(shè)為服務器的
cpu core`的數(shù)量。如果是0則交換過程在主線程進行细移。

總結(jié)一下Redis的高級用法

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末搏予,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子弧轧,更是在濱河造成了極大的恐慌雪侥,老刑警劉巖碗殷,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異速缨,居然都是意外死亡锌妻,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門鸟廓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來从祝,“玉大人,你說我怎么就攤上這事引谜‰鼓埃” “怎么了?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵员咽,是天一觀的道長毒涧。 經(jīng)常有香客問我,道長贝室,這世上最難降的妖魔是什么契讲? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮滑频,結(jié)果婚禮上捡偏,老公的妹妹穿的比我還像新娘。我一直安慰自己峡迷,他們只是感情好银伟,可當我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著绘搞,像睡著了一般彤避。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上夯辖,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天琉预,我揣著相機與錄音,去河邊找鬼蒿褂。 笑死圆米,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的啄栓。 我是一名探鬼主播娄帖,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼谴供!你這毒婦竟也來了块茁?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎数焊,沒想到半個月后永淌,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡佩耳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年遂蛀,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片干厚。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡李滴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蛮瞄,到底是詐尸還是另有隱情所坯,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布挂捅,位于F島的核電站芹助,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏闲先。R本人自食惡果不足惜状土,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望伺糠。 院中可真熱鬧蒙谓,春花似錦、人聲如沸训桶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽渊迁。三九已至慰照,卻和暖如春灶挟,著一層夾襖步出監(jiān)牢的瞬間琉朽,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工稚铣, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留箱叁,地道東北人。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓惕医,卻偏偏與公主長得像耕漱,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子抬伺,可洞房花燭夜當晚...
    茶點故事閱讀 45,044評論 2 355

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

  • 1.1 資料 螟够,最好的入門小冊子,可以先于一切文檔之前看,免費妓笙。 作者Antirez的博客若河,Antirez維護的R...
    JefferyLcm閱讀 17,059評論 1 51
  • NOSQL類型簡介鍵值對:會使用到一個哈希表,表中有一個特定的鍵和一個指針指向特定的數(shù)據(jù)寞宫,如redis萧福,volde...
    MicoCube閱讀 3,984評論 2 27
  • 摘自http://xiaoh.me/2016/06/30/redis-advanced/ 排序 redis支持對l...
    鴕鳥要抬頭閱讀 66,456評論 1 3
  • 安全性 設(shè)置客戶端連接后進行任何其他指令前需要使用的密碼。 警告:因為redis 速度相當快辈赋,所以在一臺比較好的服...
    OzanShareing閱讀 1,755評論 1 7
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理鲫忍,服務發(fā)現(xiàn),斷路器钥屈,智...
    卡卡羅2017閱讀 134,661評論 18 139