本文摘抄至大鵬的redis教程
redis是一種kv存儲(chǔ)系統(tǒng)杆融,value支持五種數(shù)據(jù)類型:
- 字符串(strings)
- 字符串列表(lists)
- 字符串集合(sets)
- 有序字符串集合(sorted sets)
- 哈希(hashes)
key則是字符串類型累澡,其中key一般以如下方式命名吉嫩,用于保存盡可能多的信息微驶。例如user:10000:passwd。
strings
strings是redis的基礎(chǔ)類型熏版,例子如下:
set mystr "hello world!" //設(shè)置字符串類型
get mystr //讀取字符串類型
字符串類型的用法就是這么簡(jiǎn)單咖驮,因?yàn)槭?strong>二進(jìn)制安全的,所以你完全可以把一個(gè)圖片文件的內(nèi)容作為字符串來(lái)存儲(chǔ)启妹。
另外筛严,我們還可以通過字符串類型進(jìn)行數(shù)值操作:
127.0.0.1:6379> set mynum "2"
OK
127.0.0.1:6379> get mynum
"2"
127.0.0.1:6379> incr mynum
(integer) 3
127.0.0.1:6379> get mynum
"3"
在遇到數(shù)值操作時(shí),redis會(huì)將字符串類型轉(zhuǎn)換成數(shù)值饶米。
由于INCR等指令本身就具有原子操作的特性桨啃,所以我們完全可以利用redis的INCR、INCRBY檬输、DECR照瘾、DECRBY等指令來(lái)實(shí)現(xiàn)原子計(jì)數(shù)的效果,假如丧慈,在某種場(chǎng)景下有3個(gè)客戶端同時(shí)讀取了mynum的值(值為2)析命,然后對(duì)其同時(shí)進(jìn)行了加1的操作,那么逃默,最后mynum的值一定是5鹃愤。不少網(wǎng)站都利用redis的這個(gè)特性來(lái)實(shí)現(xiàn)業(yè)務(wù)上的統(tǒng)計(jì)計(jì)數(shù)需求。
lists
Lists(列表)是Redis另外一個(gè)重要的數(shù)據(jù)結(jié)構(gòu)完域。redis中的lists在底層實(shí)現(xiàn)上并不是數(shù)組软吐,而是鏈表,也就是說對(duì)于一個(gè)具有上百萬(wàn)個(gè)元素的lists來(lái)說吟税,在頭部和尾部插入一個(gè)新元素凹耙,其時(shí)間復(fù)雜度是常數(shù)級(jí)別的鸟蟹,比如用LPUSH在10個(gè)元素的lists頭部插入新元素,和在上千萬(wàn)元素的lists頭部插入新元素的速度應(yīng)該是相同的使兔。
雖然lists有這樣的優(yōu)勢(shì),但同樣有其弊端藤韵,那就是虐沥,鏈表型lists的元素定位會(huì)比較慢,而數(shù)組型lists的元素定位就會(huì)快得多泽艘。
lists的常用操作包括LPUSH欲险、RPUSH、LRANGE等匹涮。我們可以用LPUSH在lists的左側(cè)插入一個(gè)新元素天试,用RPUSH在lists的右側(cè)插入一個(gè)新元素,用LRANGE命令從lists中指定一個(gè)范圍來(lái)提取元素然低。我們來(lái)看幾個(gè)例子:
//新建一個(gè)list叫做mylist喜每,并在列表頭部插入元素"1"
127.0.0.1:6379> lpush mylist "1"
//返回當(dāng)前mylist中的元素個(gè)數(shù)
(integer) 1
//在mylist右側(cè)插入元素"2"
127.0.0.1:6379> rpush mylist "2"
(integer) 2
//在mylist左側(cè)插入元素"0"
127.0.0.1:6379> lpush mylist "0"
(integer) 3
//列出mylist中從編號(hào)0到編號(hào)1的元素
127.0.0.1:6379> lrange mylist 0 1
1) "0"
2) "1"
//列出mylist中從編號(hào)0到倒數(shù)第一個(gè)元素
127.0.0.1:6379> lrange mylist 0 -1
1) "0"
2) "1"
3) "2"
lists的應(yīng)用相當(dāng)廣泛,隨便舉幾個(gè)例子:
- 我們可以利用lists來(lái)實(shí)現(xiàn)一個(gè)消息隊(duì)列雳攘,而且可以確保先后順序带兜。
- 利用LRANGE還可以很方便的實(shí)現(xiàn)分頁(yè)的功能。
set
redis的集合吨灭,是一種無(wú)序的集合刚照,集合中的元素沒有先后順序。
集合相關(guān)的操作也很豐富喧兄,如添加新元素无畔、刪除已有元素、取交集吠冤、取并集浑彰、取差集等。我們來(lái)看例子:
//向集合myset中加入一個(gè)新元素"one"
127.0.0.1:6379> sadd myset "one"
(integer) 1
127.0.0.1:6379> sadd myset "two"
(integer) 1
//列出集合myset中的所有元素
127.0.0.1:6379> smembers myset
1) "one"
2) "two"
//判斷元素1是否在集合myset中咨演,返回1表示存在
127.0.0.1:6379> sismember myset "one"
(integer) 1
//判斷元素3是否在集合myset中闸昨,返回0表示不存在
127.0.0.1:6379> sismember myset "three"
(integer) 0
//新建一個(gè)新的集合yourset
127.0.0.1:6379> sadd yourset "1"
(integer) 1
127.0.0.1:6379> sadd yourset "2"
(integer) 1
127.0.0.1:6379> smembers yourset
1) "1"
2) "2"
//對(duì)兩個(gè)集合求并集
127.0.0.1:6379> sunion myset yourset
1) "1"
2) "one"
3) "2"
4) "two"
對(duì)于集合的使用,也有一些常見的方式薄风,比如饵较,QQ有一個(gè)社交功能叫做“好友標(biāo)簽”,大家可以給你的好友貼標(biāo)簽遭赂,比如“大美女”循诉、“土豪”、“歐巴”等等撇他,這時(shí)就可以使用redis的集合來(lái)實(shí)現(xiàn)茄猫,把每一個(gè)用戶的標(biāo)簽都存儲(chǔ)在一個(gè)集合之中狈蚤。
有序集合
redis不但提供了無(wú)需集合(sets),還很體貼的提供了有序集合(sorted sets)划纽。有序集合中的每個(gè)元素都關(guān)聯(lián)一個(gè)序號(hào)(score)脆侮,這便是排序的依據(jù)。
很多時(shí)候勇劣,我們都將redis中的有序集合叫做zsets靖避,這是因?yàn)樵趓edis中,有序集合相關(guān)的操作指令都是以z開頭的比默,比如zrange幻捏、zadd、zrevrange命咐、zrangebyscore等等
老規(guī)矩篡九,我們來(lái)看幾個(gè)生動(dòng)的例子:
//新增一個(gè)有序集合myzset,并加入一個(gè)元素baidu.com醋奠,給它賦予的序號(hào)是1
127.0.0.1:6379> zadd myzset 1 baidu.com
(integer) 1
//向myzset中新增一個(gè)元素360.com榛臼,賦予它的序號(hào)是3
127.0.0.1:6379> zadd myzset 3 360.com
(integer) 1
//向myzset中新增一個(gè)元素google.com,賦予它的序號(hào)是2
127.0.0.1:6379> zadd myzset 2 google.com
(integer) 1
//列出myzset的所有元素窜司,同時(shí)列出其序號(hào)讽坏,可以看出myzset已經(jīng)是有序的了。
127.0.0.1:6379> zrange myzset 0 -1 with scores
1) "baidu.com"
2) "1"
3) "google.com"
4) "2"
5) "360.com"
6) "3"
//只列出myzset的元素
127.0.0.1:6379> zrange myzset 0 -1
1) "baidu.com"
2) "google.com"
3) "360.com"
哈希
最后要給大家介紹的是hashes例证,即哈希路呜。hashes存的是字符串和字符串值之間的映射,比如一個(gè)用戶要存儲(chǔ)其全名织咧、姓氏胀葱、年齡等等,就很適合使用哈希笙蒙。
我們來(lái)看一個(gè)例子:
//建立哈希抵屿,并賦值
127.0.0.1:6379> HMSET user:001 username antirez password P1pp0 age 34 OK
//列出哈希的內(nèi)容
127.0.0.1:6379> HGETALL user:001
1) "username"
2) "antirez"
3) "password"
4) "P1pp0"
5) "age"
6) "34"
//更改哈希中的某一個(gè)值
127.0.0.1:6379> HSET user:001 password 12345
(integer) 0
//再次列出哈希的內(nèi)容
127.0.0.1:6379> HGETALL user:001
1) "username"
2) "antirez"
3) "password"
4) "12345"
5) "age"
6) "34"
Redis持久化
redis提供了兩種持久化的方式,分別是RDB和AOF捅位。
- RDB轧葛,簡(jiǎn)而言之,就是在不同的時(shí)間點(diǎn)艇搀,將redis存儲(chǔ)的數(shù)據(jù)生成快照并存儲(chǔ)到磁盤等介質(zhì)上尿扯;
- AOF,則是換了一個(gè)角度來(lái)實(shí)現(xiàn)持久化焰雕,那就是將redis執(zhí)行過的所有寫指令記錄下來(lái)衷笋,在下次redis重新啟動(dòng)時(shí),只要把這些寫指令從前到后再重復(fù)執(zhí)行一遍矩屁,就可以實(shí)現(xiàn)數(shù)據(jù)恢復(fù)了辟宗。
其實(shí)RDB和AOF兩種方式也可以同時(shí)使用爵赵,在這種情況下,如果redis重啟的話泊脐,則會(huì)優(yōu)先采用AOF方式來(lái)進(jìn)行數(shù)據(jù)恢復(fù)空幻,這是因?yàn)?strong>AOF方式的數(shù)據(jù)恢復(fù)完整度更高。
如果你沒有數(shù)據(jù)持久化的需求容客,也完全可以關(guān)閉RDB和AOF方式氛悬,這樣的話,redis將變成一個(gè)純內(nèi)存數(shù)據(jù)庫(kù)耘柱,就像memcache一樣。
RDB
RDB方式棍现,是將redis某一時(shí)刻的數(shù)據(jù)持久化到磁盤中调煎,是一種快照式的持久化方法。
redis在進(jìn)行數(shù)據(jù)持久化的過程中己肮,會(huì)先將數(shù)據(jù)寫入到一個(gè)臨時(shí)文件中士袄,待持久化過程都結(jié)束了,才會(huì)用這個(gè)臨時(shí)文件替換上次持久化好的文件谎僻。正是這種特性娄柳,讓我們可以隨時(shí)來(lái)進(jìn)行備份,因?yàn)榭煺瘴募偸峭暾捎玫摹?br>
對(duì)于RDB方式艘绍,redis會(huì)單獨(dú)創(chuàng)建(fork)一個(gè)子進(jìn)程來(lái)進(jìn)行持久化赤拒,而主進(jìn)程是不會(huì)進(jìn)行任何IO操作的,這樣就確保了redis極高的性能诱鞠。
如果需要進(jìn)行大規(guī)模數(shù)據(jù)的恢復(fù)挎挖,且對(duì)于數(shù)據(jù)恢復(fù)的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效航夺。
雖然RDB有不少優(yōu)點(diǎn)蕉朵,但它的缺點(diǎn)也是不容忽視的。如果你對(duì)數(shù)據(jù)的完整性非常敏感阳掐,那么RDB方式就不太適合你始衅,因?yàn)榧词鼓忝?分鐘都持久化一次,當(dāng)redis故障時(shí)缭保,仍然會(huì)有近5分鐘的數(shù)據(jù)丟失汛闸。所以,redis還提供了另一種持久化方式艺骂,那就是AOF蛉拙。
AOF
AOF,英文是Append Only File彻亲,即只允許追加不允許改寫的文件孕锄。
如前面介紹的吮廉,AOF方式是將執(zhí)行過的寫指令記錄下來(lái),在數(shù)據(jù)恢復(fù)時(shí)按照從前到后的順序再將指令都執(zhí)行一遍畸肆,就這么簡(jiǎn)單宦芦。
我們通過配置redis.conf中的appendonly yes就可以打開AOF功能。
如果有寫操作(如SET等)轴脐,redis就會(huì)被追加到AOF文件的末尾调卑。
默認(rèn)的AOF持久化策略是每秒鐘fsync一次(fsync是指把緩存中的寫指令記錄到磁盤中),因?yàn)樵谶@種情況下大咱,redis仍然可以保持很好的處理性能恬涧,即使redis故障,也只會(huì)丟失最近1秒鐘的數(shù)據(jù)碴巾。
如果在追加日志時(shí)溯捆,恰好遇到磁盤空間滿、inode滿或斷電等情況導(dǎo)致日志寫入不完整厦瓢,也沒有關(guān)系提揍,redis提供了redis-check-aof工具,可以用來(lái)進(jìn)行日志修復(fù)煮仇。
因?yàn)椴捎昧俗芳臃绞嚼驮荆绻蛔鋈魏翁幚淼脑挘珹OF文件會(huì)變得越來(lái)越大浙垫,為此刨仑,redis提供了AOF文件重寫(rewrite)機(jī)制,即當(dāng)AOF文件的大小超過所設(shè)定的閾值時(shí)夹姥,redis就會(huì)啟動(dòng)AOF文件的內(nèi)容壓縮贸人,只保留可以恢復(fù)數(shù)據(jù)的最小指令集。舉個(gè)例子或許更形象佃声,假如我們調(diào)用了100次INCR指令艺智,在AOF文件中就要存儲(chǔ)100條指令,但這明顯是很低效的圾亏,完全可以把這100條指令合并成一條SET指令十拣,這就是重寫機(jī)制的原理。
在進(jìn)行AOF重寫時(shí)志鹃,仍然是采用先寫臨時(shí)文件夭问,全部完成后再替換的流程,所以斷電曹铃、磁盤滿等問題都不會(huì)影響AOF文件的可用性缰趋,這點(diǎn)大家可以放心。
AOF方式的另一個(gè)好處,我們通過一個(gè)“場(chǎng)景再現(xiàn)”來(lái)說明秘血。某同學(xué)在操作redis時(shí)味抖,不小心執(zhí)行了FLUSHALL,導(dǎo)致redis內(nèi)存中的數(shù)據(jù)全部被清空了灰粮,這是很悲劇的事情仔涩。不過這也不是世界末日,只要redis配置了AOF持久化方式粘舟,且AOF文件還沒有被重寫(rewrite)熔脂,我們就可以用最快的速度暫停redis并編輯AOF文件,將最后一行的FLUSHALL命令刪除柑肴,然后重啟redis霞揉,就可以恢復(fù)redis的所有數(shù)據(jù)到FLUSHALL之前的狀態(tài)了。是不是很神奇晰骑,這就是AOF持久化方式的好處之一适秩。但是如果AOF文件已經(jīng)被重寫了,那就無(wú)法通過這種方法來(lái)恢復(fù)數(shù)據(jù)了些侍。
雖然優(yōu)點(diǎn)多多,但AOF方式也同樣存在缺陷政模,比如在同樣數(shù)據(jù)規(guī)模的情況下岗宣,AOF文件要比RDB文件的體積大。而且淋样,AOF方式的恢復(fù)速度也要慢于RDB方式耗式。
如果你直接執(zhí)行BGREWRITEAOF命令,那么redis會(huì)生成一個(gè)全新的AOF文件趁猴,其中便包括了可以恢復(fù)現(xiàn)有數(shù)據(jù)的最少的命令集刊咳。
如果運(yùn)氣比較差,AOF文件出現(xiàn)了被寫壞的情況儡司,也不必過分擔(dān)憂娱挨,redis并不會(huì)貿(mào)然加載這個(gè)有問題的AOF文件,而是報(bào)錯(cuò)退出捕犬。這時(shí)可以通過以下步驟來(lái)修復(fù)出錯(cuò)的文件:
1:備份被寫壞的AOF文件
2:運(yùn)行redis-check-aof –fix進(jìn)行修復(fù)
3:用diff -u來(lái)看下兩個(gè)文件的差異跷坝,確認(rèn)問題點(diǎn)
4:重啟redis,加載修復(fù)后的AOF文件
詳細(xì)介紹AOF重寫
AOF 重寫并不需要對(duì)原有的 AOF 文件進(jìn)行任何寫入和讀取碉碉, 它針對(duì)的是數(shù)據(jù)庫(kù)中鍵的當(dāng)前值柴钻。
考慮這樣一個(gè)情況, 如果服務(wù)器對(duì)鍵 list執(zhí)行了以下四條命令:
RPUSH list 1 2 3 4 // [1, 2, 3, 4]
RPOP list // [1, 2, 3]
LPOP list // [2, 3]
LPUSH list 1 // [1, 2, 3]
那么當(dāng)前列表鍵 list 在數(shù)據(jù)庫(kù)中的值就為 [1, 2, 3] 垢粮。
如果我們要保存這個(gè)列表的當(dāng)前狀態(tài)贴届, 并且盡量減少所使用的命令數(shù), 那么最簡(jiǎn)單的方式不是去 AOF 文件上分析前面執(zhí)行的四條命令, 而是直接讀取 list 鍵在數(shù)據(jù)庫(kù)的當(dāng)前值毫蚓, 然后用一條 RPUSH 1 2 3 命令來(lái)代替前面的四條命令占键。
再考慮這樣一個(gè)例子, 如果服務(wù)器對(duì)集合鍵 animal 執(zhí)行了以下命令:
SADD animal cat // {cat}
SADD animal dog panda tiger // {cat, dog, panda, tiger}
SREM animal cat // {dog, panda, tiger}
SADD animal cat lion // {cat, lion, dog, panda, tiger}
那么使用一條 SADD animal cat lion dog panda tiger命令绍些, 就可以還原 animal
集合的狀態(tài)捞慌, 這比之前的四條命令調(diào)用要大大減少筷狼。
除了列表和集合之外涯竟, 字符串、有序集慰毅、哈希表等鍵也可以用類似的方法來(lái)保存狀態(tài)氮帐, 并且保存這些狀態(tài)所使用的命令數(shù)量嗅虏, 比起之前建立這些鍵的狀態(tài)所使用命令的數(shù)量要大大減少。
根據(jù)鍵的類型上沐, 使用適當(dāng)?shù)膶懭朊顏?lái)重現(xiàn)鍵的當(dāng)前值皮服, 這就是 AOF 重寫的實(shí)現(xiàn)原理。
AOF 后臺(tái)重寫
上一節(jié)展示的 AOF 重寫程序可以很好地完成創(chuàng)建一個(gè)新 AOF 文件的任務(wù)参咙, 但是龄广, 在執(zhí)行這個(gè)程序的時(shí)候, 調(diào)用者線程會(huì)被阻塞蕴侧。
很明顯择同, 作為一種輔佐性的維護(hù)手段, Redis 不希望 AOF 重寫造成服務(wù)器無(wú)法處理請(qǐng)求净宵, 所以 Redis 決定將 AOF 重寫程序放到(后臺(tái))子進(jìn)程里執(zhí)行 敲才。不過, 使用子進(jìn)程也有一個(gè)問題需要解決: 因?yàn)?strong>子進(jìn)程在進(jìn)行 AOF 重寫期間择葡, 主進(jìn)程還需要繼續(xù)處理命令紧武, 而新的命令可能對(duì)現(xiàn)有的數(shù)據(jù)進(jìn)行修改, 這會(huì)讓當(dāng)前數(shù)據(jù)庫(kù)的數(shù)據(jù)和重寫后的 AOF 文件中的數(shù)據(jù)不一致敏储。
為了解決這個(gè)問題阻星, Redis 增加了一個(gè)AOF 重寫緩存, 這個(gè)緩存在 fork 出子進(jìn)程之后開始啟用已添, Redis 主進(jìn)程在接到新的寫命令之后迫横, 除了會(huì)將這個(gè)寫命令的協(xié)議內(nèi)容追加到現(xiàn)有的 AOF 文件之外, 還會(huì)追加到這個(gè)緩存中.
換言之酝碳, 當(dāng)子進(jìn)程在執(zhí)行 AOF 重寫時(shí)矾踱, 主進(jìn)程需要執(zhí)行以下三個(gè)工作:
1:處理命令請(qǐng)求。
2:將寫命令追加到現(xiàn)有的 AOF 文件中疏哗。
3:將寫命令追加到 AOF 重寫緩存中呛讲。
這樣一來(lái)可以保證:
現(xiàn)有的 AOF 功能會(huì)繼續(xù)執(zhí)行,即使在 AOF 重寫期間發(fā)生停機(jī),也不會(huì)有任何數(shù)據(jù)丟失贝搁。
所有對(duì)數(shù)據(jù)庫(kù)進(jìn)行修改的命令都會(huì)被記錄到 AOF 重寫緩存中吗氏。
當(dāng)子進(jìn)程完成 AOF 重寫之后, 它會(huì)向父進(jìn)程發(fā)送一個(gè)完成信號(hào)雷逆, 父進(jìn)程在接到完成信號(hào)之后弦讽, 會(huì)調(diào)用一個(gè)信號(hào)處理函數(shù), 并完成以下工作:
- 將 AOF 重寫緩存中的內(nèi)容全部寫入到新 AOF 文件中膀哲。
- 對(duì)新的 AOF 文件進(jìn)行改名往产,覆蓋原有的 AOF 文件。
步驟 1 執(zhí)行完畢之后某宪, 現(xiàn)有 AOF 文件仿村、新 AOF 文件和數(shù)據(jù)庫(kù)三者的狀態(tài)就完全一致了。
當(dāng)步驟 2 執(zhí)行完畢之后兴喂, 程序就完成了新舊兩個(gè) AOF 文件的交替蔼囊。
這個(gè)信號(hào)處理函數(shù)執(zhí)行完畢之后, 主進(jìn)程就可以繼續(xù)像往常一樣接受命令請(qǐng)求了衣迷。 在整個(gè) AOF 后臺(tái)重寫過程中畏鼓, 只有最后的寫入緩存和改名操作會(huì)造成主進(jìn)程阻塞, 在其他時(shí)候壶谒, AOF 后臺(tái)重寫都不會(huì)對(duì)主進(jìn)程造成阻塞云矫, 這將 AOF 重寫對(duì)性能造成的影響降到了最低。
以上就是 AOF 后臺(tái)重寫佃迄。
對(duì)于我們應(yīng)該選擇RDB還是AOF泼差,官方的建議是兩個(gè)同時(shí)使用贵少。這樣可以提供更可靠的持久化方案呵俏。
Redis的主從機(jī)制
主從結(jié)構(gòu),一是為了純粹的冗余備份滔灶,二是為了提升讀性能普碎,比如很消耗性能的SORT就可以由從服務(wù)器來(lái)承擔(dān)。
redis的主從同步是異步進(jìn)行的录平,這意味著主從同步不會(huì)影響主邏輯麻车,也不會(huì)降低redis的處理性能。
主從架構(gòu)中斗这,可以考慮關(guān)閉主服務(wù)器的數(shù)據(jù)持久化功能动猬,只讓從服務(wù)器進(jìn)行持久化,這樣可以提高主服務(wù)器的處理性能表箭。
在主從架構(gòu)中赁咙,從服務(wù)器通常被設(shè)置為只讀模式,這樣可以避免從服務(wù)器的數(shù)據(jù)被誤修改。
主從同步原理
從服務(wù)器會(huì)向主服務(wù)器發(fā)出SYNC指令彼水,當(dāng)主服務(wù)器接到此命令后崔拥,就會(huì)調(diào)用BGSAVE指令來(lái)創(chuàng)建一個(gè)子進(jìn)程專門進(jìn)行數(shù)據(jù)持久化工作,也就是將主服務(wù)器的數(shù)據(jù)寫入RDB文件中凤覆。在數(shù)據(jù)持久化期間链瓦,主服務(wù)器將執(zhí)行的寫指令都緩存在內(nèi)存中。
在BGSAVE指令執(zhí)行完成后盯桦,主服務(wù)器會(huì)將持久化好的RDB文件發(fā)送給從服務(wù)器慈俯,從服務(wù)器接到此文件后會(huì)將其存儲(chǔ)到磁盤上,然后再將其讀取到內(nèi)存中俺附。這個(gè)動(dòng)作完成后肥卡,主服務(wù)器會(huì)將這段時(shí)間緩存的寫指令再以redis協(xié)議的格式發(fā)送給從服務(wù)器。
另外事镣,要說的一點(diǎn)是步鉴,即使有多個(gè)從服務(wù)器同時(shí)發(fā)來(lái)SYNC指令,主服務(wù)器也只會(huì)執(zhí)行一次BGSAVE璃哟,然后把持久化好的RDB文件發(fā)給多個(gè)下游氛琢。在redis2.8版本之前,如果從服務(wù)器與主服務(wù)器因某些原因斷開連接的話随闪,都會(huì)進(jìn)行一次主從之間的全量的數(shù)據(jù)同步阳似;而在2.8版本之后,redis支持了效率更高的增量同步策略铐伴,這大大降低了連接斷開的恢復(fù)成本撮奏。
主服務(wù)器會(huì)在內(nèi)存中維護(hù)一個(gè)緩沖區(qū),緩沖區(qū)中存儲(chǔ)著將要發(fā)給從服務(wù)器的內(nèi)容当宴。從服務(wù)器在與主服務(wù)器出現(xiàn)網(wǎng)絡(luò)瞬斷之后畜吊,從服務(wù)器會(huì)嘗試再次與主服務(wù)器連接,一旦連接成功户矢,從服務(wù)器就會(huì)把“希望同步的主服務(wù)器ID”和“希望請(qǐng)求的數(shù)據(jù)的偏移位置(replication offset)”發(fā)送出去玲献。主服務(wù)器接收到這樣的同步請(qǐng)求后,首先會(huì)驗(yàn)證主服務(wù)器ID是否和自己的ID匹配梯浪,其次會(huì)檢查“請(qǐng)求的偏移位置”是否存在于自己的緩沖區(qū)中捌年,如果兩者都滿足的話,主服務(wù)器就會(huì)向從服務(wù)器發(fā)送增量?jī)?nèi)容挂洛。
增量同步功能礼预,需要服務(wù)器端支持全新的PSYNC指令。這個(gè)指令虏劲,只有在redis-2.8之后才具有托酸。
Redis事務(wù)
眾所周知荠藤,事務(wù)是指“一個(gè)完整的動(dòng)作,要么全部執(zhí)行获高,要么什么也沒有做”哈肖。
在聊redis事務(wù)處理之前,要先和大家介紹四個(gè)redis指令念秧,即MULTI淤井、EXEC、DISCARD摊趾、WATCH币狠。這四個(gè)指令構(gòu)成了redis事務(wù)處理的基礎(chǔ)。
- MULTI用來(lái)組裝一個(gè)事務(wù)砾层;
- EXEC用來(lái)執(zhí)行一個(gè)事務(wù)漩绵;
- DISCARD用來(lái)取消一個(gè)事務(wù);
- WATCH用來(lái)監(jiān)視一些key肛炮,一旦這些key在事務(wù)執(zhí)行之前被改變止吐,則取消事務(wù)的執(zhí)行。
我們來(lái)看一個(gè)MULTI和EXEC的例子:
redis> MULTI //標(biāo)記事務(wù)開始
OK
redis> INCR user_id //多條命令按順序入隊(duì)
QUEUED
redis> INCR user_id
QUEUED
redis> INCR user_id
QUEUED
redis> PING
QUEUED
redis> EXEC //執(zhí)行
1) (integer) 1
2) (integer) 2
3) (integer) 3
4) PONG
在上面的例子中侨糟,我們看到了QUEUED的字樣碍扔,這表示我們?cè)谟肕ULTI組裝事務(wù)時(shí),每一個(gè)命令都會(huì)進(jìn)入到內(nèi)存隊(duì)列中緩存起來(lái)秕重,如果出現(xiàn)QUEUED則表示我們這個(gè)命令成功插入了緩存隊(duì)列不同,在將來(lái)執(zhí)行EXEC時(shí),這些被QUEUED的命令都會(huì)被組裝成一個(gè)事務(wù)來(lái)執(zhí)行溶耘。
對(duì)于事務(wù)的執(zhí)行來(lái)說二拐,如果redis開啟了AOF持久化的話,那么一旦事務(wù)被成功執(zhí)行凳兵,事務(wù)中的命令就會(huì)通過write命令一次性寫到磁盤中去百新,如果在向磁盤中寫的過程中恰好出現(xiàn)斷電、硬件故障等問題留荔,那么就可能出現(xiàn)只有部分命令進(jìn)行了AOF持久化吟孙,這時(shí)AOF文件就會(huì)出現(xiàn)不完整的情況澜倦,這時(shí)聚蝶,我們可以使用redis-check-aof工具來(lái)修復(fù)這一問題,這個(gè)工具會(huì)將AOF文件中不完整的信息移除藻治,確保AOF文件完整可用碘勉。
有關(guān)事務(wù),大家經(jīng)常會(huì)遇到的是兩類錯(cuò)誤:
- 調(diào)用EXEC之前的錯(cuò)誤
- 調(diào)用EXEC之后的錯(cuò)誤
調(diào)用EXEC之前的錯(cuò)誤桩卵,有可能是由于語(yǔ)法有誤導(dǎo)致的验靡,也可能時(shí)由于內(nèi)存不足導(dǎo)致的倍宾。只要出現(xiàn)某個(gè)命令無(wú)法成功寫入緩沖隊(duì)列的情況,redis都會(huì)進(jìn)行記錄胜嗓,在客戶端調(diào)用EXEC時(shí)高职,redis會(huì)拒絕執(zhí)行這一事務(wù)。我們來(lái)看一個(gè)這樣的例子:
127.0.0.1:6379> multi
OK
127.0.0.1:6379> haha //一個(gè)明顯錯(cuò)誤的指令
(error) ERR unknown command 'haha'
127.0.0.1:6379> ping
QUEUED
127.0.0.1:6379> exec//redis無(wú)情的拒絕了事務(wù)的執(zhí)行辞州,原因是“之前出現(xiàn)了錯(cuò)誤”
(error) EXECABORT Transaction discarded because of previous errors.
而對(duì)于“調(diào)用EXEC之后的錯(cuò)誤”怔锌,redis則采取了完全不同的策略,即redis不會(huì)理睬這些錯(cuò)誤变过,而是繼續(xù)向下執(zhí)行事務(wù)中的其他命令埃元。這是因?yàn)椋瑢?duì)于應(yīng)用層面的錯(cuò)誤媚狰,并不是redis自身需要考慮和處理的問題岛杀,所以一個(gè)事務(wù)中如果某一條命令執(zhí)行失敗,并不會(huì)影響接下來(lái)的其他命令的執(zhí)行崭孤。我們也來(lái)看一個(gè)例子:
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set age 23
QUEUED
//age不是集合类嗤,所以如下是一條明顯錯(cuò)誤的指令
127.0.0.1:6379> sadd age 15
QUEUED
127.0.0.1:6379> set age 29
QUEUED
127.0.0.1:6379> exec //執(zhí)行事務(wù)時(shí),redis不會(huì)理睬第2條指令執(zhí)行錯(cuò)誤
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
3) OK
127.0.0.1:6379> get age
"29" //可以看出第3條指令被成功執(zhí)行了
好了辨宠,我們來(lái)說說最后一個(gè)指令“WATCH”土浸,這是一個(gè)很好用的指令,它可以幫我們實(shí)現(xiàn)類似于“樂觀鎖”的效果彭羹,即CAS(check and set)黄伊。
WATCH本身的作用是“監(jiān)視key是否被改動(dòng)過”,而且支持同時(shí)監(jiān)視多個(gè)key派殷,只要還沒真正觸發(fā)事務(wù)还最,WATCH都會(huì)盡職盡責(zé)的監(jiān)視,一旦發(fā)現(xiàn)某個(gè)key被修改了毡惜,在執(zhí)行EXEC時(shí)就會(huì)返回nil拓轻,表示事務(wù)無(wú)法觸發(fā)。
127.0.0.1:6379> set age 23
OK
127.0.0.1:6379> watch age //開始監(jiān)視age
OK
127.0.0.1:6379> set age 24 //在EXEC之前经伙,age的值被修改了
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set age 25
QUEUED
127.0.0.1:6379> get age
QUEUED
127.0.0.1:6379> exec //觸發(fā)EXEC
(nil) //事務(wù)無(wú)法被執(zhí)行