redis是一個(gè)高性能的key-value數(shù)據(jù)庫(kù),它是完全開(kāi)源免費(fèi)的,而且redis是一個(gè)NOSQL類(lèi)型數(shù)據(jù)庫(kù)途乃,是為了解決高并發(fā)狈醉、高擴(kuò)展,大數(shù)據(jù)存儲(chǔ)等一系列的問(wèn)題而產(chǎn)生的數(shù)據(jù)庫(kù)解決方案势誊,是一個(gè)非關(guān)系型的數(shù)據(jù)庫(kù)
Redis本質(zhì)上是一個(gè)Key-Value類(lèi)型的內(nèi)存數(shù)據(jù)庫(kù)谣蠢,很像memcached粟耻,整個(gè)數(shù)據(jù)庫(kù)統(tǒng)統(tǒng)加載在內(nèi)存當(dāng)中進(jìn)行操作,定期通過(guò)異步操作把數(shù)據(jù)庫(kù)數(shù)據(jù)flush到硬盤(pán)上進(jìn)行保存眉踱。因?yàn)槭羌儍?nèi)存操作挤忙,Redis的性能非常出色,每秒可以處理超過(guò) 10萬(wàn)次讀寫(xiě)操作谈喳,是已知性能最快的Key-Value DB册烈。
Redis的出色之處不僅僅是性能,Redis最大的魅力是支持保存多種數(shù)據(jù)結(jié)構(gòu)婿禽,此外單個(gè)value的最大限制是1GB赏僧,不像 memcached只能保存1MB的數(shù)據(jù),因此Redis可以用來(lái)實(shí)現(xiàn)很多有用的功能扭倾,比方說(shuō)用他的List來(lái)做FIFO雙向鏈表淀零,實(shí)現(xiàn)一個(gè)輕量級(jí)的高性 能消息隊(duì)列服務(wù),用他的Set可以做高性能的tag系統(tǒng)等等膛壹。另外Redis也可以對(duì)存入的Key-Value設(shè)置expire時(shí)間驾中,因此也可以被當(dāng)作一 個(gè)功能加強(qiáng)版的memcached來(lái)用。
Redis的主要缺點(diǎn)是數(shù)據(jù)庫(kù)容量受到物理內(nèi)存的限制恢筝,不能用作海量數(shù)據(jù)的高性能讀寫(xiě)哀卫,因此Redis適合的場(chǎng)景主要局限在較小數(shù)據(jù)量的高性能操作和運(yùn)算上。
3.1?速度快,因?yàn)閿?shù)據(jù)存在內(nèi)存中侄柔,類(lèi)似于HashMap共啃,HashMap的優(yōu)勢(shì)就是查找和操作的時(shí)間復(fù)雜度都是O(1)
3.2?支持豐富數(shù)據(jù)類(lèi)型占调,支持string,list移剪,set究珊,sorted set,hash
常用命令?:set/get/decr/incr/mget等纵苛;
應(yīng)用場(chǎng)景?:String是最常用的一種數(shù)據(jù)類(lèi)型剿涮,普通的key/value存儲(chǔ)都可以歸為此類(lèi);
實(shí)現(xiàn)方式:String在redis內(nèi)部存儲(chǔ)默認(rèn)就是一個(gè)字符串攻人,被redisObject所引用取试,當(dāng)遇到incr、decr等操作時(shí)會(huì)轉(zhuǎn)成數(shù)值型進(jìn)行計(jì)算怀吻,此時(shí)redisObject的encoding字段為int瞬浓。
常用命令?:hget/hset/hgetall等
應(yīng)用場(chǎng)景?:我們要存儲(chǔ)一個(gè)用戶(hù)信息對(duì)象數(shù)據(jù),其中包括用戶(hù)ID蓬坡、用戶(hù)姓名猿棉、年齡和生日,通過(guò)用戶(hù)ID我們希望獲取該用戶(hù)的姓名或者年齡或者生日屑咳;
實(shí)現(xiàn)方式:Redis的Hash實(shí)際是內(nèi)部存儲(chǔ)的Value為一個(gè)HashMap萨赁,并提供了直接存取這個(gè)Map成員的接口。如圖所示乔宿,Key是用戶(hù)ID, value是一個(gè)Map位迂。這個(gè)Map的key是成員的屬性名,value是屬性值详瑞。這樣對(duì)數(shù)據(jù)的修改和存取都可以直接通過(guò)其內(nèi)部Map的Key(Redis里稱(chēng)內(nèi)部Map的key為field)掂林,也就是通過(guò) key(用戶(hù)ID) + field(屬性標(biāo)簽) 就可以操作對(duì)應(yīng)屬性數(shù)據(jù)。當(dāng)前HashMap的實(shí)現(xiàn)有兩種方式:當(dāng)HashMap的成員比較少時(shí)Redis為了節(jié)省內(nèi)存會(huì)采用類(lèi)似一維數(shù)組的方式來(lái)緊湊存儲(chǔ)坝橡,而不會(huì)采用真正的HashMap結(jié)構(gòu)泻帮,這時(shí)對(duì)應(yīng)的value的redisObject的encoding為zipmap,當(dāng)成員數(shù)量增大時(shí)會(huì)自動(dòng)轉(zhuǎn)成真正的HashMap,此時(shí)redisObject的encoding字段為int计寇。
常用命令?:lpush/rpush/lpop/rpop/lrange等锣杂;
應(yīng)用場(chǎng)景?:Redis list的應(yīng)用場(chǎng)景?非常多,也是Redis最重要的數(shù)據(jù)結(jié)構(gòu)之一番宁,比如twitter的關(guān)注列表元莫,粉絲列表等都可以用Redis的list結(jié)構(gòu)來(lái)實(shí)現(xiàn);
實(shí)現(xiàn)方式:Redis list的實(shí)現(xiàn)為一個(gè)雙向鏈表蝶押,即可以支持反向查找和遍歷踱蠢,更方便操作,不過(guò)帶來(lái)了部分額外的內(nèi)存開(kāi)銷(xiāo)棋电,Redis內(nèi)部的很多實(shí)現(xiàn)茎截,包括發(fā)送緩沖隊(duì)列等也都是用的這個(gè)數(shù)據(jù)結(jié)構(gòu)苇侵。
常用命令?:sadd/spop/smembers/sunion等;
應(yīng)用場(chǎng)景?:Redis set對(duì)外提供的功能與list類(lèi)似是一個(gè)列表的功能企锌,特殊之處在于set是可以自動(dòng)排重的榆浓,當(dāng)你需要存儲(chǔ)一個(gè)列表數(shù)據(jù),又不希望出現(xiàn)重復(fù)數(shù)據(jù)時(shí)撕攒,set是一個(gè)很好的選擇陡鹃,并且set提供了判斷某個(gè)成員是否在一個(gè)set集合內(nèi)的重要接口,這個(gè)也是list所不能提供的打却;
實(shí)現(xiàn)方式:set 的內(nèi)部實(shí)現(xiàn)是一個(gè) value永遠(yuǎn)為null的HashMap杉适,實(shí)際就是通過(guò)計(jì)算hash的方式來(lái)快速排重的,這也是set能提供判斷一個(gè)成員是否在集合內(nèi)的原因柳击。
常用命令?:zadd/zrange/zrem/zcard等;
應(yīng)用場(chǎng)景?:Redis sorted set的使用場(chǎng)景與set類(lèi)似片习,區(qū)別是set不是自動(dòng)有序的捌肴,而sorted set可以通過(guò)用戶(hù)額外提供一個(gè)優(yōu)先級(jí)(score)的參數(shù)來(lái)為成員排序,并且是插入有序的藕咏,即自動(dòng)排序状知。當(dāng)你需要一個(gè)有序的并且不重復(fù)的集合列表,那么可以選擇sorted set數(shù)據(jù)結(jié)構(gòu)孽查,比如twitter 的public timeline可以以發(fā)表時(shí)間作為score來(lái)存儲(chǔ)饥悴,這樣獲取時(shí)就是自動(dòng)按時(shí)間排好序的。
實(shí)現(xiàn)方式:Redis sorted set的內(nèi)部使用HashMap和跳躍表(SkipList)來(lái)保證數(shù)據(jù)的存儲(chǔ)和有序盲再,HashMap里放的是成員到score的映射西设,而跳躍表里存放的是所有的成員,排序依據(jù)是HashMap里存的score,使用跳躍表的結(jié)構(gòu)可以獲得比較高的查找效率答朋,并且在實(shí)現(xiàn)上比較簡(jiǎn)單贷揽。
4、redis相比memcached有哪些優(yōu)勢(shì)梦碗?
4.1?memcached所有的值均是簡(jiǎn)單的字符串禽绪,redis作為其替代者,支持更為豐富的數(shù)據(jù)類(lèi)型4.2?redis的速度比memcached快很多 (3) redis可以持久化其數(shù)據(jù)
5洪规、Memcache與Redis的區(qū)別都有哪些印屁?
5.1?存儲(chǔ)方式 Memecache把數(shù)據(jù)全部存在內(nèi)存之中,斷電后會(huì)掛掉斩例,數(shù)據(jù)不能超過(guò)內(nèi)存大小雄人。Redis有部份存在硬盤(pán)上,這樣能保證數(shù)據(jù)的持久性樱拴。
5.2?數(shù)據(jù)支持類(lèi)型 Memcache對(duì)數(shù)據(jù)類(lèi)型支持相對(duì)簡(jiǎn)單柠衍。Redis有復(fù)雜的數(shù)據(jù)類(lèi)型洋满。
5.3?使用底層模型不同 它們之間底層實(shí)現(xiàn)方式 以及與客戶(hù)端之間通信的應(yīng)用協(xié)議不一樣。Redis直接自己構(gòu)建了VM 機(jī)制 珍坊,因?yàn)橐话愕南到y(tǒng)調(diào)用系統(tǒng)函數(shù)的話(huà)牺勾,會(huì)浪費(fèi)一定的時(shí)間去移動(dòng)和請(qǐng)求。
Redis最適合所有數(shù)據(jù)in-momory的場(chǎng)景驻民,如:
6.1 會(huì)話(huà)緩存(Session Cache)
最常用的一種使用Redis的情景是會(huì)話(huà)緩存(session cache)。用Redis緩存會(huì)話(huà)比其他存儲(chǔ)(如Memcached)的優(yōu)勢(shì)在于:Redis提供持久化履怯。
6.2 全頁(yè)緩存(FPC)
除基本的會(huì)話(huà)token之外回还,Redis還提供很簡(jiǎn)便的FPC平臺(tái)√局蓿回到一致性問(wèn)題柠硕,即使重啟了Redis實(shí)例,因?yàn)橛写疟P(pán)的持久化运提,用戶(hù)也不會(huì)看到頁(yè)面加載速度的下降蝗柔,這是一個(gè)極大改進(jìn),類(lèi)似PHP本地FPC民泵。
6.3 隊(duì)列
Reids在內(nèi)存存儲(chǔ)引擎領(lǐng)域的一大優(yōu)點(diǎn)是提供 list 和 set 操作癣丧,這使得Redis能作為一個(gè)很好的消息隊(duì)列平臺(tái)來(lái)使用。Redis作為隊(duì)列使用的操作栈妆,就類(lèi)似于本地程序語(yǔ)言(如Python)對(duì) list 的 push/pop 操作胁编。
如果你快速的在Google中搜索“Redis queues”,你馬上就能找到大量的開(kāi)源項(xiàng)目鳞尔,這些項(xiàng)目的目的就是利用Redis創(chuàng)建非常好的后端工具嬉橙,以滿(mǎn)足各種隊(duì)列需求。例如铅檩,Celery有一個(gè)后臺(tái)就是使用Redis作為broker憎夷,你可以從這里去查看。
6.4 排行榜/計(jì)數(shù)器
Redis在內(nèi)存中對(duì)數(shù)字進(jìn)行遞增或遞減的操作實(shí)現(xiàn)的非常好昧旨。集合(Set)和有序集合(Sorted Set)也使得我們?cè)趫?zhí)行這些操作的時(shí)候變的非常簡(jiǎn)單拾给,Redis只是正好提供了這兩種數(shù)據(jù)結(jié)構(gòu)。所以兔沃,我們要從排序集合中獲取到排名最靠前的10個(gè)用戶(hù)–我們稱(chēng)之為“user_scores”蒋得,我們只需要像下面一樣執(zhí)行即可:
當(dāng)然,這是假定你是根據(jù)你用戶(hù)的分?jǐn)?shù)做遞增的排序乒疏。如果你想返回用戶(hù)及用戶(hù)的分?jǐn)?shù)额衙,你需要這樣執(zhí)行:
ZRANGE user_scores 0 10 WITHSCORES
Agora Games就是一個(gè)很好的例子,用Ruby實(shí)現(xiàn)的,它的排行榜就是使用Redis來(lái)存儲(chǔ)數(shù)據(jù)的窍侧,你可以在這里看到县踢。
6.5 發(fā)布/訂閱
最后(但肯定不是最不重要的)是Redis的發(fā)布/訂閱功能。發(fā)布/訂閱的使用場(chǎng)景確實(shí)非常多伟件。
作為緩存系統(tǒng)都要定期清理無(wú)效數(shù)據(jù),就需要一個(gè)主鍵失效和淘汰策略.
在Redis當(dāng)中斧账,有生存期的key被稱(chēng)為volatile谴返。在創(chuàng)建緩存時(shí),要為給定的key設(shè)置生存期咧织,當(dāng)key過(guò)期的時(shí)候(生存期為0)嗓袱,它可能會(huì)被刪除。
1习绢、影響生存時(shí)間的一些操作
生存時(shí)間可以通過(guò)使用 DEL 命令來(lái)刪除整個(gè) key 來(lái)移除渠抹,或者被 SET 和 GETSET 命令覆蓋原來(lái)的數(shù)據(jù),也就是說(shuō)毯炮,修改key對(duì)應(yīng)的value和使用另外相同的key和value來(lái)覆蓋以后逼肯,當(dāng)前數(shù)據(jù)的生存時(shí)間不同。
比如說(shuō)桃煎,對(duì)一個(gè) key 執(zhí)行INCR命令,對(duì)一個(gè)列表進(jìn)行LPUSH命令大刊,或者對(duì)一個(gè)哈希表執(zhí)行HSET命令为迈,這類(lèi)操作都不會(huì)修改 key 本身的生存時(shí)間。另一方面缺菌,如果使用RENAME對(duì)一個(gè) key 進(jìn)行改名葫辐,那么改名后的 key的生存時(shí)間和改名前一樣。
RENAME命令的另一種可能是伴郁,嘗試將一個(gè)帶生存時(shí)間的 key 改名成另一個(gè)帶生存時(shí)間的 another_key 耿战,這時(shí)舊的 another_key (以及它的生存時(shí)間)會(huì)被刪除,然后舊的 key 會(huì)改名為 another_key 焊傅,因此剂陡,新的 another_key 的生存時(shí)間也和原本的 key 一樣。使用PERSIST命令可以在不刪除 key 的情況下狐胎,移除 key 的生存時(shí)間鸭栖,讓 key 重新成為一個(gè)persistent key 。
2握巢、如何更新生存時(shí)間
可以對(duì)一個(gè)已經(jīng)帶有生存時(shí)間的 key 執(zhí)行EXPIRE命令晕鹊,新指定的生存時(shí)間會(huì)取代舊的生存時(shí)間。過(guò)期時(shí)間的精度已經(jīng)被控制在1ms之內(nèi),主鍵失效的時(shí)間復(fù)雜度是O(1)溅话,
EXPIRE和TTL命令搭配使用晓锻,TTL可以查看key的當(dāng)前生存時(shí)間。設(shè)置成功返回 1飞几;當(dāng) key 不存在或者不能為 key 設(shè)置生存時(shí)間時(shí)砚哆,返回 0 。
最大緩存配置 在 redis 中循狰,允許用戶(hù)設(shè)置最大使用內(nèi)存大小 server.maxmemory 默認(rèn)為0窟社,沒(méi)有指定最大緩存,如果有新的數(shù)據(jù)添加绪钥,超過(guò)最大內(nèi)存灿里,則會(huì)使redis崩潰,所以一定要設(shè)置程腹。redis 內(nèi)存數(shù)據(jù)集大小上升到一定大小的時(shí)候匣吊,就會(huì)實(shí)行數(shù)據(jù)淘汰策略。redis 提供 6種數(shù)據(jù)淘汰策略:
volatile-lru:?從已設(shè)置過(guò)期時(shí)間的數(shù)據(jù)集(?server.db\[i\].expires)中挑選最近最少使用的數(shù)據(jù)淘汰
volatile-ttl:?從已設(shè)置過(guò)期時(shí)間的數(shù)據(jù)集(?server.db\[i\].expires)中挑選將要過(guò)期的數(shù)據(jù)淘汰
volatile-random:?從已設(shè)置過(guò)期時(shí)間的數(shù)據(jù)集(?server.db\[i\].expires)中任意選擇數(shù)據(jù)淘汰
allkeys-lru:?從數(shù)據(jù)集(?server.db\[i\].dict)中挑選最近最少使用的數(shù)據(jù)淘汰
allkeys-random:?從數(shù)據(jù)集(?server.db\[i\].dict)中任意選擇數(shù)據(jù)淘汰
no-enviction(驅(qū)逐):?禁止驅(qū)逐數(shù)據(jù)
注意這里的6種機(jī)制寸潦,volatile和allkeys規(guī)定了是對(duì)已設(shè)置過(guò)期時(shí)間的數(shù)據(jù)集淘汰數(shù)據(jù)還是從全部數(shù)據(jù)集淘汰數(shù)據(jù)色鸳,后面的lru、ttl以及random是三種不同的淘汰策略见转,再加上一種no-enviction永不回收的策略命雀。
使用策略規(guī)則:
1、?如果數(shù)據(jù)呈現(xiàn)冪律分布斩箫,也就是一部分?jǐn)?shù)據(jù)訪(fǎng)問(wèn)頻率高吏砂,一部分?jǐn)?shù)據(jù)訪(fǎng)問(wèn)頻率低,則使用allkeys-lru2乘客、?如果數(shù)據(jù)呈現(xiàn)平等分布狐血,也就是所有的數(shù)據(jù)訪(fǎng)問(wèn)頻率都相同,則使用allkeys-random
三種數(shù)據(jù)淘汰策略:
ttl和random比較容易理解易核,實(shí)現(xiàn)也會(huì)比較簡(jiǎn)單匈织。主要是Lru最近最少使用淘汰策略,設(shè)計(jì)上會(huì)對(duì)key 按失效時(shí)間排序牡直,然后取最先失效的key進(jìn)行淘汰
8缀匕、為什么redis需要把所有數(shù)據(jù)放到內(nèi)存中?
Redis為了達(dá)到最快的讀寫(xiě)速度將數(shù)據(jù)都讀到內(nèi)存中,并通過(guò)異步的方式將數(shù)據(jù)寫(xiě)入磁盤(pán)井氢。所以redis具有快速和數(shù)據(jù)持久化的特征弦追。如果不將數(shù)據(jù)放在內(nèi)存中,磁盤(pán)I/O速度為嚴(yán)重影響redis的性能花竞。在內(nèi)存越來(lái)越便宜的今天劲件,redis將會(huì)越來(lái)越受歡迎掸哑。
如果設(shè)置了最大使用的內(nèi)存,則數(shù)據(jù)已有記錄數(shù)達(dá)到內(nèi)存限值后不能繼續(xù)插入新值零远。
redis利用隊(duì)列技術(shù)將并發(fā)訪(fǎng)問(wèn)變?yōu)榇性L(fǎng)問(wèn),消除了傳統(tǒng)數(shù)據(jù)庫(kù)串行控制的開(kāi)銷(xiāo)
10牵辣、redis的并發(fā)競(jìng)爭(zhēng)問(wèn)題如何解決?
Redis為單進(jìn)程單線(xiàn)程模式摔癣,采用隊(duì)列模式將并發(fā)訪(fǎng)問(wèn)變?yōu)榇性L(fǎng)問(wèn)。Redis本身沒(méi)有鎖的概念纬向,Redis對(duì)于多個(gè)客戶(hù)端連接并不存在競(jìng)爭(zhēng)择浊,但是在Jedis客戶(hù)端對(duì)Redis進(jìn)行并發(fā)訪(fǎng)問(wèn)時(shí)會(huì)發(fā)生連接超時(shí)、數(shù)據(jù)轉(zhuǎn)換錯(cuò)誤逾条、阻塞琢岩、客戶(hù)端關(guān)閉連接等問(wèn)題,這些問(wèn)題均是
由于客戶(hù)端連接混亂造成师脂。對(duì)此有2種解決方法:
10.1?客戶(hù)端角度担孔,為保證每個(gè)客戶(hù)端間正常有序與Redis進(jìn)行通信,對(duì)連接進(jìn)行池化吃警,同時(shí)對(duì)客戶(hù)端讀寫(xiě)Redis操作采用內(nèi)部鎖synchronized糕篇。
10.2?服務(wù)器角度,利用setnx實(shí)現(xiàn)鎖酌心。注:對(duì)于第一種拌消,需要應(yīng)用程序自己處理資源的同步,可以使用的方法比較通俗安券,可以使用synchronized也可以使用lock拼坎;第二種需要用到Redis的setnx命令,但是需要注意一些問(wèn)題完疫。
11、redis常見(jiàn)性能問(wèn)題和解決方案
11.1?Master寫(xiě)內(nèi)存快照债蓝,save命令調(diào)度rdbSave函數(shù)壳鹤,會(huì)阻塞主線(xiàn)程的工作,當(dāng)快照比較大時(shí)對(duì)性能影響是非常大的饰迹,會(huì)間斷性暫停服務(wù)芳誓,所以Master最好不要寫(xiě)內(nèi)存快照。
11.2?Master AOF持久化啊鸭,如果不重寫(xiě)AOF文件锹淌,這個(gè)持久化方式對(duì)性能的影響是最小的,但是AOF文件會(huì)不斷增大赠制,AOF文件過(guò)大會(huì)影響Master重啟的恢復(fù)速度赂摆。Master最好不要做任何持久化工作,包括內(nèi)存快照和AOF日志文件,特別是不要啟用內(nèi)存快照做持久
化,如果數(shù)據(jù)比較關(guān)鍵烟号,某個(gè)Slave開(kāi)啟AOF備份數(shù)據(jù)绊谭,策略為每秒同步一次。
11.3?Master調(diào)用BGREWRITEAOF重寫(xiě)AOF文件汪拥,AOF在重寫(xiě)的時(shí)候會(huì)占大量的CPU和內(nèi)存資源达传,導(dǎo)致服務(wù)load過(guò)高,出現(xiàn)短暫服務(wù)暫推戎現(xiàn)象宪赶。
11.4?Redis主從復(fù)制的性能問(wèn)題,為了主從復(fù)制的速度和連接的穩(wěn)定性脯燃,Slave和Master最好在同一個(gè)局域網(wǎng)內(nèi)搂妻。
12、redis事物的了解CAS(check-and-set 操作實(shí)現(xiàn)樂(lè)觀鎖 )?
和眾多其它數(shù)據(jù)庫(kù)一樣曲伊,Redis作為NoSQL數(shù)據(jù)庫(kù)也同樣提供了事務(wù)機(jī)制叽讳。在Redis中,MULTI/EXEC/DISCARD/WATCH這四個(gè)命令是我們實(shí)現(xiàn)事務(wù)的基石坟募。相信對(duì)有關(guān)系型數(shù)據(jù)庫(kù)開(kāi)發(fā)經(jīng)驗(yàn)的開(kāi)發(fā)者而言這一概念并不陌生岛蚤,即便如此,我們還是會(huì)簡(jiǎn)要的列出
Redis中
事務(wù)的實(shí)現(xiàn)特征:
12.1?在事務(wù)中的所有命令都將會(huì)被串行化的順序執(zhí)行懈糯,事務(wù)執(zhí)行期間涤妒,Redis不會(huì)再為其它客戶(hù)端的請(qǐng)求提供任何服務(wù),從而保證了事物中的所有命令被原子的執(zhí)行赚哗。
12.2?和關(guān)系型數(shù)據(jù)庫(kù)中的事務(wù)相比她紫,在Redis事務(wù)中如果有某一條命令執(zhí)行失敗,其后的命令仍然會(huì)被繼續(xù)執(zhí)行屿储。
12.3 我們可以通過(guò)MULTI命令開(kāi)啟一個(gè)事務(wù)贿讹,有關(guān)系型數(shù)據(jù)庫(kù)開(kāi)發(fā)經(jīng)驗(yàn)的人可以將其理解為"BEGIN TRANSACTION"語(yǔ)句。在該語(yǔ)句之后執(zhí)行的命令都將被視為事務(wù)之內(nèi)的操作够掠,最后我們可以通過(guò)執(zhí)行EXEC/DISCARD命令來(lái)提交/回滾該事務(wù)內(nèi)的所有操作民褂。這兩個(gè)Redis命令可被視為等同于關(guān)系型數(shù)據(jù)庫(kù)中的COMMIT/ROLLBACK語(yǔ)句。
12.4?在事務(wù)開(kāi)啟之前疯潭,如果客戶(hù)端與服務(wù)器之間出現(xiàn)通訊故障并導(dǎo)致網(wǎng)絡(luò)斷開(kāi)赊堪,其后所有待執(zhí)行的語(yǔ)句都將不會(huì)被服務(wù)器執(zhí)行。然而如果網(wǎng)絡(luò)中斷事件是發(fā)生在客戶(hù)端執(zhí)行EXEC命令之后竖哩,那么該事務(wù)中的所有命令都會(huì)被服務(wù)器執(zhí)行哭廉。
12.5?當(dāng)使用Append-Only模式時(shí)大渤,Redis會(huì)通過(guò)調(diào)用系統(tǒng)函數(shù)write將該事務(wù)內(nèi)的所有寫(xiě)操作在本次調(diào)用中全部寫(xiě)入磁盤(pán)桐愉。然而如果在寫(xiě)入的過(guò)程中出現(xiàn)系統(tǒng)崩潰,如電源故障導(dǎo)致的宕機(jī),那么此時(shí)也許只有部分?jǐn)?shù)據(jù)被寫(xiě)入到磁盤(pán)磷斧,而另外一部分?jǐn)?shù)據(jù)卻已經(jīng)丟失愉适。Redis服務(wù)器會(huì)在重新啟動(dòng)時(shí)執(zhí)行一系列必要的一致性檢測(cè)啸盏,一旦發(fā)現(xiàn)類(lèi)似問(wèn)題什湘,就會(huì)立即退出并給出相應(yīng)的錯(cuò)誤提示。此時(shí)赎离,我們就要充分利用Redis工具包中提供的redis-check-aof工具逛犹,該工具可以幫助我們定位到數(shù)據(jù)不一致的錯(cuò)誤,并將已經(jīng)寫(xiě)入的部分?jǐn)?shù)據(jù)進(jìn)行回滾梁剔。修復(fù)之后我們就可以再次重新啟動(dòng)Redis服務(wù)器了虽画。
在Redis的事務(wù)中荣病,WATCH命令可用于提供CAS(check-and-set)功能码撰。假設(shè)我們通過(guò)WATCH命令在事務(wù)執(zhí)行之前監(jiān)控了多個(gè)Keys,倘若在WATCH之后有任何Key的值發(fā)生了變化个盆,EXEC命令執(zhí)行的事務(wù)都將被放棄脖岛,同時(shí)返回Null multi-bulk應(yīng)答以通知調(diào)用者事務(wù)
執(zhí)行失敗。例如颊亮,我們?cè)俅渭僭O(shè)Redis中并未提供incr命令來(lái)完成鍵值的原子性遞增柴梆,如果要實(shí)現(xiàn)該功能,我們只能自行編寫(xiě)相應(yīng)的代碼终惑。其偽碼如下:
val = GET mykey val = val + 1 SET mykey $val
以上代碼只有在單連接的情況下才可以保證執(zhí)行結(jié)果是正確的绍在,因?yàn)槿绻谕粫r(shí)刻有多個(gè)客戶(hù)端在同時(shí)執(zhí)行該段代碼,那么就會(huì)出現(xiàn)多線(xiàn)程程序中經(jīng)常出現(xiàn)的一種錯(cuò)誤場(chǎng)景--競(jìng)態(tài)爭(zhēng)用(race condition)雹有。比如偿渡,客戶(hù)端A和B都在同一時(shí)刻讀取了mykey的原有值,假設(shè)該值為10霸奕,此后兩個(gè)客戶(hù)端又均將該值加一后set回Redis服務(wù)器溜宽,這樣就會(huì)導(dǎo)致mykey的結(jié)果為11,而不是我們認(rèn)為的12质帅。為了解決類(lèi)似的問(wèn)題坑质,我們需要借助WATCH命令的幫助,見(jiàn)如下代碼:
WATCH mykey val = GET mykey val = val + 1 MULTI SET mykey $val EXEC
和此前代碼不同的是临梗,新代碼在獲取mykey的值之前先通過(guò)WATCH命令監(jiān)控了該鍵,此后又將set命令包圍在事務(wù)中稼跳,這樣就可以有效的保證每個(gè)連接在執(zhí)行EXEC之前盟庞,如果當(dāng)前連接獲取的mykey的值被其它連接的客戶(hù)端修改,那么當(dāng)前連接的EXEC命令將執(zhí)行失敗汤善。這樣調(diào)用者在判斷返回值后就可以獲悉val是否被重新設(shè)置成功什猖。
14票彪、使用過(guò)Redis分布式鎖么,它是什么回事不狮?
先拿setnx來(lái)爭(zhēng)搶鎖降铸,搶到之后,再用expire給鎖加一個(gè)過(guò)期時(shí)間防止鎖忘記了釋放摇零。
這時(shí)候?qū)Ψ綍?huì)告訴你說(shuō)你回答得不錯(cuò)推掸,然后接著問(wèn)如果在setnx之后執(zhí)行expire之前進(jìn)程意外crash或者要重啟維護(hù)了,那會(huì)怎么樣驻仅?
這時(shí)候你要給予驚訝的反饋:唉谅畅,是喔,這個(gè)鎖就永遠(yuǎn)得不到釋放了噪服。緊接著你需要抓一抓自己得腦袋毡泻,故作思考片刻,好像接下來(lái)的結(jié)果是你主動(dòng)思考出來(lái)的粘优,然后回答:我記得set指令有非常復(fù)雜的參數(shù)仇味,這個(gè)應(yīng)該是可以同時(shí)把setnx和expire合成一條指令來(lái)用的!對(duì)方這時(shí)會(huì)顯露笑容雹顺,心里開(kāi)始默念:摁丹墨,這小子還不錯(cuò)。
15无拗、假如Redis里面有1億個(gè)key带到,其中有10w個(gè)key是以某個(gè)固定的已知的前綴開(kāi)頭的,如果將它們?nèi)空页鰜?lái)英染?
使用keys指令可以?huà)叱鲋付J降膋ey列表揽惹。
對(duì)方接著追問(wèn):如果這個(gè)redis正在給線(xiàn)上的業(yè)務(wù)提供服務(wù),那使用keys指令會(huì)有什么問(wèn)題四康?
這個(gè)時(shí)候你要回答redis關(guān)鍵的一個(gè)特性:redis的單線(xiàn)程的搪搏。keys指令會(huì)導(dǎo)致線(xiàn)程阻塞一段時(shí)間,線(xiàn)上服務(wù)會(huì)停頓闪金,直到指令執(zhí)行完畢疯溺,服務(wù)才能恢復(fù)。這個(gè)時(shí)候可以使用scan指令哎垦,scan指令可以無(wú)阻塞的提取出指定模式的key列表囱嫩,但是會(huì)有一定的重復(fù)概率,在客戶(hù)端做一次去重就可以了漏设,但是整體所花費(fèi)的時(shí)間會(huì)比直接用keys指令長(zhǎng)墨闲。
16、使用過(guò)Redis做異步隊(duì)列么郑口,你是怎么用的鸳碧?
一般使用list結(jié)構(gòu)作為隊(duì)列盾鳞,rpush生產(chǎn)消息,lpop消費(fèi)消息瞻离。當(dāng)lpop沒(méi)有消息的時(shí)候腾仅,要適當(dāng)sleep一會(huì)再重試。
如果對(duì)方追問(wèn)可不可以不用sleep呢套利?list還有個(gè)指令叫blpop推励,在沒(méi)有消息的時(shí)候,它會(huì)阻塞住直到消息到來(lái)日裙。
如果對(duì)方追問(wèn)能不能生產(chǎn)一次消費(fèi)多次呢吹艇?使用pub/sub主題訂閱者模式,可以實(shí)現(xiàn)1:N的消息隊(duì)列昂拂。
如果對(duì)方追問(wèn)pub/sub有什么缺點(diǎn)受神?在消費(fèi)者下線(xiàn)的情況下,生產(chǎn)的消息會(huì)丟失格侯,得使用專(zhuān)業(yè)的消息隊(duì)列如rabbitmq等鼻听。
如果對(duì)方追問(wèn)redis如何實(shí)現(xiàn)延時(shí)隊(duì)列?我估計(jì)現(xiàn)在你很想把面試官一棒打死如果你手上有一根棒球棍的話(huà)联四,怎么問(wèn)的這么詳細(xì)撑碴。但是你很克制,然后神態(tài)自若的回答道:使用sortedset朝墩,拿時(shí)間戳作為score醉拓,消息內(nèi)容作為key調(diào)用zadd來(lái)生產(chǎn)消息,消費(fèi)者用zrangebyscore指令獲取N秒之前的數(shù)據(jù)輪詢(xún)進(jìn)行處理收苏。
到這里亿卤,面試官暗地里已經(jīng)對(duì)你豎起了大拇指。但是他不知道的是此刻你卻豎起了中指鹿霸,在椅子背后排吴。
17、如果有大量的key需要設(shè)置同一時(shí)間過(guò)期懦鼠,一般需要注意什么钻哩?
如果大量的key過(guò)期時(shí)間設(shè)置的過(guò)于集中,到過(guò)期的那個(gè)時(shí)間點(diǎn)肛冶,redis可能會(huì)出現(xiàn)短暫的卡頓現(xiàn)象街氢。一般需要在時(shí)間上加一個(gè)隨機(jī)值,使得過(guò)期時(shí)間分散一些睦袖。
bgsave做鏡像全量持久化,aof做增量持久化近范。因?yàn)閎gsave會(huì)耗費(fèi)較長(zhǎng)時(shí)間,不夠?qū)崟r(shí)延蟹,在停機(jī)的時(shí)候會(huì)導(dǎo)致大量丟失數(shù)據(jù)评矩,所以需要aof來(lái)配合使用。在redis實(shí)例重啟時(shí)阱飘,會(huì)使用bgsave持久化文件重新構(gòu)建內(nèi)存斥杜,再使用aof重放近期的操作指令來(lái)實(shí)現(xiàn)完整恢復(fù)重啟之前的狀態(tài)。
對(duì)方追問(wèn)那如果突然機(jī)器掉電會(huì)怎樣沥匈?取決于aof日志sync屬性的配置蔗喂,如果不要求性能,在每條寫(xiě)指令時(shí)都sync一下磁盤(pán)高帖,就不會(huì)丟失數(shù)據(jù)缰儿。但是在高性能的要求下每次都sync是不現(xiàn)實(shí)的,一般都使用定時(shí)sync散址,比如1s1次乖阵,這個(gè)時(shí)候最多就會(huì)丟失1s的數(shù)據(jù)。
對(duì)方追問(wèn)bgsave的原理是什么预麸?你給出兩個(gè)詞匯就可以了瞪浸,fork和cow。fork是指redis通過(guò)創(chuàng)建子進(jìn)程來(lái)進(jìn)行bgsave操作吏祸,cow指的是copy on write对蒲,子進(jìn)程創(chuàng)建后,父子進(jìn)程共享數(shù)據(jù)段贡翘,父進(jìn)程繼續(xù)提供讀寫(xiě)服務(wù)蹈矮,寫(xiě)臟的頁(yè)面數(shù)據(jù)會(huì)逐漸和子進(jìn)程分離開(kāi)來(lái)。
19床估、Pipeline有什么好處含滴,為什么要用pipeline?
可以將多次IO往返的時(shí)間縮減為一次丐巫,前提是pipeline執(zhí)行的指令之間沒(méi)有因果相關(guān)性谈况。使用redis-benchmark進(jìn)行壓測(cè)的時(shí)候可以發(fā)現(xiàn)影響redis的QPS峰值的一個(gè)重要因素是pipeline批次指令的數(shù)目。
Redis可以使用主從同步,從從同步缎脾。第一次同步時(shí)祝闻,主節(jié)點(diǎn)做一次bgsave,并同時(shí)將后續(xù)修改操作記錄到內(nèi)存buffer,待完成后將rdb文件全量同步到復(fù)制節(jié)點(diǎn)联喘,復(fù)制節(jié)點(diǎn)接受完成后將rdb鏡像加載到內(nèi)存华蜒。加載完成后,再通知主節(jié)點(diǎn)將期間修改的操作記錄同步到復(fù)制節(jié)點(diǎn)進(jìn)行重放就完成了同步過(guò)程豁遭。
21叭喜、是否使用過(guò)Redis集群,集群的原理是什么蓖谢?
Redis Sentinal著眼于高可用捂蕴,在master宕機(jī)時(shí)會(huì)自動(dòng)將slave提升為master,繼續(xù)提供服務(wù)闪幽。
Redis Cluster著眼于擴(kuò)展性啥辨,在單個(gè)redis內(nèi)存不足時(shí),使用Cluster進(jìn)行分片存儲(chǔ)盯腌。