原創(chuàng)文章首發(fā)于公眾號(hào):「碼農(nóng)富哥」饭入,致力于分享后端技術(shù) (高并發(fā)架構(gòu), 中間件, Linux, TCP/IP, HTTP, MySQL, Redis), 高性能俭尖,分布式衬浑,微服務(wù)等原創(chuàng)干貨 和面試指南换怖!
@TOC
概述
什么是Redis
Redis 是一個(gè)使用 C 語(yǔ)言寫(xiě)成的桦沉,開(kāi)源的 key-value 數(shù)據(jù)庫(kù)役纹。和Memcached類似偶摔,它支持存儲(chǔ)的value類型相對(duì)更多,包括string(字符串)促脉、list(鏈表)辰斋、set(集合)、zset(sorted set --有序集合)和hash(哈希類型)瘸味。這些數(shù)據(jù)類型都支持push/pop宫仗、add/remove及取交集并集和差集及更豐富的操作,而且這些操作都是原子性的旁仿。在此基礎(chǔ)上藕夫,redis支持各種不同方式的排序。與memcached一樣枯冈,為了保證效率毅贮,數(shù)據(jù)都是緩存在內(nèi)存中。區(qū)別的是redis會(huì)周期性的把更新的數(shù)據(jù)寫(xiě)入磁盤(pán)或者把修改操作寫(xiě)入追加的記錄文件尘奏,并且在此基礎(chǔ)上實(shí)現(xiàn)了master-slave(主從)同步滩褥。目前,Vmware在資助著redis項(xiàng)目的開(kāi)發(fā)和維護(hù)炫加。
Redis為什么這么快
1瑰煎、完全基于內(nèi)存,絕大部分請(qǐng)求是純粹的內(nèi)存操作俗孝,非尘频椋快速。數(shù)據(jù)存在內(nèi)存中赋铝,類似于 HashMap插勤,HashMap 的優(yōu)勢(shì)就是查找和操作的時(shí)間復(fù)雜度都是O(1);
2、數(shù)據(jù)結(jié)構(gòu)簡(jiǎn)單饮六,對(duì)數(shù)據(jù)操作也簡(jiǎn)單,Redis 中的數(shù)據(jù)結(jié)構(gòu)是專門(mén)進(jìn)行設(shè)計(jì)的苛蒲;
3卤橄、采用單線程,避免了不必要的上下文切換和競(jìng)爭(zhēng)條件臂外,也不存在多進(jìn)程或者多線程導(dǎo)致的切換而消耗 CPU窟扑,不用去考慮各種鎖的問(wèn)題,不存在加鎖釋放鎖操作漏健,沒(méi)有因?yàn)榭赡艹霈F(xiàn)死鎖而導(dǎo)致的性能消耗嚎货;
4、使用多路 I/O 復(fù)用模型蔫浆,非阻塞 IO殖属;
5、使用底層模型不同瓦盛,它們之間底層實(shí)現(xiàn)方式以及與客戶端之間通信的應(yīng)用協(xié)議不一樣洗显,Redis 直接自己構(gòu)建了 VM 機(jī)制 ,因?yàn)橐话愕南到y(tǒng)調(diào)用系統(tǒng)函數(shù)的話原环,會(huì)浪費(fèi)一定的時(shí)間去移動(dòng)和請(qǐng)求挠唆;
Redis 數(shù)據(jù)類型
Redis有哪些數(shù)據(jù)類型?
Redis目前支持5種數(shù)據(jù)類型嘱吗,分別是:
String(字符串):
- 說(shuō)明:String是簡(jiǎn)單的 key-value 鍵值對(duì)玄组,value 不僅可以是 String,也可以是數(shù)字谒麦。
- 使用場(chǎng)景:String是最常用的一種數(shù)據(jù)類型俄讹,普通的key/value存儲(chǔ)都可以歸為此類
List(列表):
- 說(shuō)明:Redis列表是簡(jiǎn)單的字符串列表,簡(jiǎn)單的說(shuō)就是一個(gè)鏈表或者說(shuō)是一個(gè)隊(duì)列弄匕÷ぃ可以從頭部或尾部向Redis列表添加元素。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)。
- 使用場(chǎng)景:比如twitter的關(guān)注列表亡哄、粉絲列表等都可以用Redis的list結(jié)構(gòu)來(lái)實(shí)現(xiàn)枝缔,再比如有的應(yīng)用使用Redis的list類型實(shí)現(xiàn)一個(gè)簡(jiǎn)單的輕量級(jí)消息隊(duì)列,生產(chǎn)者push,消費(fèi)者pop/bpop愿卸。
Hash(字典):
- 說(shuō)明:類似C#中的dict類型或者C++中的hash_map類型灵临。
- 使用場(chǎng)景:假設(shè)有多個(gè)用戶及對(duì)應(yīng)的用戶信息,可以用來(lái)存儲(chǔ)以用戶ID為key趴荸,將用戶信息序列化為比如json格式做為value進(jìn)行保存儒溉。
Set(集合):
- 說(shuō)明:可以理解為一堆值不重復(fù)的列表,類似數(shù)學(xué)領(lǐng)域中的集合概念发钝,且Redis也提供了針對(duì)集合的求交集顿涣、并集、差集等操作酝豪。
set 的內(nèi)部實(shí)現(xiàn)是一個(gè) value永遠(yuǎn)為null的HashMap涛碑,實(shí)際就是通過(guò)計(jì)算hash的方式來(lái)快速排重的,這也是set能提供判斷一個(gè)成員是否在集合內(nèi)的原因孵淘。 - 使用場(chǎng)景:Redis set對(duì)外提供的功能與list類似是一個(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所不能提供的余黎。
又或者在微博應(yīng)用中,每個(gè)用戶關(guān)注的人存在一個(gè)集合中载萌,就很容易實(shí)現(xiàn)求兩個(gè)人的共同好友功能惧财。
Sorted Set(有序集合):
- 說(shuō)明:Redis有序集合類似Redis集合,不同的是增加了一個(gè)功能扭仁,即集合是有序的垮衷。一個(gè)有序集合的每個(gè)成員帶有分?jǐn)?shù),用于進(jìn)行排序乖坠。
- 使用場(chǎng)景:Redis sorted set的使用場(chǎng)景與set類似搀突,區(qū)別是set不是自動(dòng)有序的,而sorted set可以通過(guò)用戶額外提供一個(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í)間排好序的卒蘸。
又比如用戶的積分排行榜需求就可以通過(guò)有序集合實(shí)現(xiàn)雌隅。還有上面介紹的使用List實(shí)現(xiàn)輕量級(jí)的消息隊(duì)列,其實(shí)也可以通過(guò)Sorted Set實(shí)現(xiàn)有優(yōu)先級(jí)或按權(quán)重的隊(duì)列。
Redis的zset實(shí)現(xiàn)原理及時(shí)間復(fù)雜度
數(shù)據(jù)量少的時(shí)候使用壓縮鏈表ziplist實(shí)現(xiàn)恰起,有序集合使用緊挨在一起的壓縮列表節(jié)點(diǎn)來(lái)保存修械,第一個(gè)節(jié)點(diǎn)保存member,第二個(gè)保存score检盼。ziplist內(nèi)的集合元素按score從小到大排序祠肥,score較小的排在表頭位置。
數(shù)據(jù)量大的時(shí)候使用跳躍列表skiplist和哈希表hash_map結(jié)合實(shí)現(xiàn)梯皿,查找刪除插入的時(shí)間復(fù)雜度都是O(longN)
持久化原理
什么是持久化
Redis 是一種內(nèi)存數(shù)據(jù)庫(kù),將數(shù)據(jù)保存在內(nèi)存中县恕,一旦進(jìn)程退出东羹,Redis 的數(shù)據(jù)就會(huì)丟失。
為了解決這個(gè)問(wèn)題忠烛,Redis 提供了 RDB 和 AOF 兩種持久化方案属提,將內(nèi)存中的數(shù)據(jù)保存到磁盤(pán)中,避免數(shù)據(jù)丟失美尸。
持久化方式RDB和AOF底層原理
Redis 提供了不同級(jí)別的持久化方式:RDB(默認(rèn)方式)和AOF
RDB 持久化方式能夠在指定的時(shí)間間隔能對(duì)你的數(shù)據(jù)進(jìn)行快照(snapshotting)存儲(chǔ)冤议,將內(nèi)存中的數(shù)據(jù)不斷寫(xiě)入二進(jìn)制文件中,默認(rèn)文件dump.rdb师坎,可配置Redis在n秒內(nèi)如果超過(guò)m個(gè)key被修改就自動(dòng)保存快照恕酸。(性能高,但是可能會(huì)出現(xiàn)數(shù)據(jù)丟失)
例
save 900 1 #900秒內(nèi)如果超過(guò)1個(gè)key被修改胯陋,則發(fā)起快照保存蕊温。
save 300 10 #300秒內(nèi)如果超過(guò)10個(gè)key被修改,則快照保存遏乔。
RDB持久化只會(huì)周期性的保存數(shù)據(jù)义矛,在未觸發(fā)下一次存儲(chǔ)時(shí)服務(wù)宕機(jī),就會(huì)丟失增量數(shù)據(jù)盟萨。當(dāng)數(shù)據(jù)量較大的情況下凉翻,fork子進(jìn)程這個(gè)操作很消耗cpu,可能會(huì)發(fā)生長(zhǎng)達(dá)秒級(jí)別的阻塞情況捻激。
SAVE是阻塞式持久化制轰,執(zhí)行命令時(shí)Redis主進(jìn)程把內(nèi)存數(shù)據(jù)寫(xiě)入到RDB文件中直到創(chuàng)建完畢,期間Redis不能處理任何命令铺罢。
BGSAVE屬于非阻塞式持久化艇挨,創(chuàng)建一個(gè)子進(jìn)程把內(nèi)存中數(shù)據(jù)寫(xiě)入RDB文件里同時(shí)主進(jìn)程處理命令請(qǐng)求。
如圖展示了RDB使用save 或者 bgsave 進(jìn)行fork子進(jìn)程進(jìn)行持久化的流程:
AOF(Append-only file) 持久化方式記錄每次對(duì)服務(wù)器寫(xiě)的操作,當(dāng)服務(wù)器重啟的時(shí)候會(huì)重新執(zhí)行這些命令來(lái)恢復(fù)原始的數(shù)據(jù),AOF命令以redis協(xié)議追加保存每次寫(xiě)的操作到文件末尾.Redis還能對(duì)AOF文件進(jìn)行后臺(tái)重寫(xiě),使得AOF文件的體積不至于過(guò)大韭赘。(類似于MySql的日志方式缩滨,記錄每次更新的日志)(性能低,但是數(shù)據(jù)完整)
當(dāng)開(kāi)啟AOF后,服務(wù)端每執(zhí)行一次寫(xiě)操作就會(huì)把該條命令追加到一個(gè)單獨(dú)的AOF緩沖區(qū)的末尾脉漏,然后把AOF緩沖區(qū)的內(nèi)容寫(xiě)入AOF文件里苞冯,由于磁盤(pán)緩沖區(qū)的存在寫(xiě)入AOF文件之后,并不代表數(shù)據(jù)已經(jīng)落盤(pán)了侧巨,而何時(shí)進(jìn)行文件同步則是根據(jù)配置的appendfsync
來(lái)進(jìn)行配置:
appendfsync選項(xiàng):always舅锄、everysec和no:
always
:服務(wù)器在每執(zhí)行一個(gè)事件就把AOF緩沖區(qū)的內(nèi)容強(qiáng)制性的寫(xiě)入硬盤(pán)上的AOF文件里,保證了數(shù)據(jù)持久化的完整性司忱,效率是最慢的但最安全的皇忿;
everysec
:服務(wù)端每隔一秒才會(huì)進(jìn)行一次文件同步把內(nèi)存緩沖區(qū)里的AOF緩存數(shù)據(jù)真正寫(xiě)入AOF文件里,兼顧了效率和完整性坦仍,極端情況服務(wù)器宕機(jī)只會(huì)丟失一秒內(nèi)對(duì)Redis數(shù)據(jù)庫(kù)的寫(xiě)操作鳍烁;
no
:表示默認(rèn)系統(tǒng)的緩存區(qū)寫(xiě)入磁盤(pán)的機(jī)制,不做程序強(qiáng)制繁扎,數(shù)據(jù)安全性和完整性差一些怒炸。
RDB和AOF的優(yōu)缺點(diǎn)和使用場(chǎng)景
RDB優(yōu)點(diǎn):
- RDB是一個(gè)非常緊湊的文件,它保存了某個(gè)時(shí)間點(diǎn)得數(shù)據(jù)集,非常適用于數(shù)據(jù)集的備份,比如你可以在每個(gè)小時(shí)報(bào)保存一下過(guò)去24小時(shí)內(nèi)的數(shù)據(jù),同時(shí)每天保存過(guò)去30天的數(shù)據(jù),這樣即使出了問(wèn)題你也可以根據(jù)需求恢復(fù)到不同版本的數(shù)據(jù)集.
- RDB是一個(gè)緊湊的單一文件,很方便傳送到另一個(gè)遠(yuǎn)端數(shù)據(jù)中心或者亞馬遜的S3(可能加密)羡宙,非常適用于災(zāi)難恢復(fù).
- RDB在保存RDB文件時(shí)父進(jìn)程唯一需要做的就是fork出一個(gè)子進(jìn)程,接下來(lái)的工作全部由子進(jìn)程來(lái)做,父進(jìn)程不需要再做其他IO操作,所以RDB持久化方式可以最大化redis的性能.
- 與AOF相比,在恢復(fù)大的數(shù)據(jù)集的時(shí)候轨淌,RDB方式會(huì)更快一些.
RDB缺點(diǎn):
- RDB的數(shù)據(jù)安全性是不如AOF的桑包,保存整個(gè)數(shù)據(jù)集的過(guò)程是比繁重的尾抑,根據(jù)配置可能要幾分鐘才快照一次侮邀,如果服務(wù)器宕機(jī),那么就可能丟失幾分鐘的數(shù)據(jù)
- Redis數(shù)據(jù)集較大時(shí)盼忌,fork的子進(jìn)程要完成快照會(huì)比較耗CPU莉炉、耗時(shí)
AOF 優(yōu)點(diǎn):
使用AOF 會(huì)讓你的Redis更加耐久: 你可以使用不同的fsync策略:無(wú)fsync,每秒fsync,每次寫(xiě)的時(shí)候fsync.使用默認(rèn)的每秒fsync策略,Redis的性能依然很好(fsync是由后臺(tái)線程進(jìn)行處理的,主線程會(huì)盡力處理客戶端請(qǐng)求),一旦出現(xiàn)故障,你最多丟失1秒的數(shù)據(jù).(由于os會(huì)在內(nèi)核中緩存write做的修改碴犬,所以可能不是立即寫(xiě)到磁盤(pán)上絮宁,這樣aof方式的持久化也還是有可能會(huì)丟失一部分?jǐn)?shù)據(jù)》可以通過(guò)配置文件告訴redis我們想要通過(guò)fsync函數(shù)強(qiáng)制os寫(xiě)入到磁盤(pán)的時(shí)機(jī))
AOF文件是一個(gè)只進(jìn)行追加的日志文件,所以不需要寫(xiě)入seek,即使由于某些原因(磁盤(pán)空間已滿绍昂,寫(xiě)的過(guò)程中宕機(jī)等等)未執(zhí)行完整的寫(xiě)入命令,你也也可使用redis-check-aof工具修復(fù)這些問(wèn)題.
-
Redis 可以在 AOF 文件體積變得過(guò)大時(shí),通過(guò)命令
bgrewriteaof
自動(dòng)地在后臺(tái)對(duì) AOF 進(jìn)行重寫(xiě): 重寫(xiě)后的新 AOF 文件包含了恢復(fù)當(dāng)前數(shù)據(jù)集所需的最小命令集合偿荷。 整個(gè)重寫(xiě)操作是絕對(duì)安全的窘游,因?yàn)?Redis 在創(chuàng)建新 AOF 文件的過(guò)程中,會(huì)繼續(xù)將命令追加到現(xiàn)有的 AOF 文件里面跳纳,即使重寫(xiě)過(guò)程中發(fā)生停機(jī)忍饰,現(xiàn)有的 AOF 文件也不會(huì)丟失。 而一旦新 AOF 文件創(chuàng)建完畢寺庄,Redis 就會(huì)從舊 AOF 文件切換到新 AOF 文件艾蓝,并開(kāi)始對(duì)新 AOF 文件進(jìn)行追加操作力崇。
redis AOF的日志重寫(xiě)流程:
AOF 文件有序地保存了對(duì)數(shù)據(jù)庫(kù)執(zhí)行的所有寫(xiě)入操作, 這些寫(xiě)入操作以 Redis 協(xié)議的格式保存赢织, 因此 AOF 文件的內(nèi)容非常容易被人讀懂亮靴, 對(duì)文件進(jìn)行分析(parse)也很輕松。 導(dǎo)出(export) AOF 文件也非常簡(jiǎn)單: 舉個(gè)例子于置, 如果你不小心執(zhí)行了 FLUSHALL 命令茧吊, 但只要 AOF 文件未被重寫(xiě), 那么只要停止服務(wù)器八毯, 移除 AOF 文件末尾的 FLUSHALL 命令搓侄, 并重啟 Redis , 就可以將數(shù)據(jù)集恢復(fù)到 FLUSHALL 執(zhí)行之前的狀態(tài)话速。
AOF 缺點(diǎn):
- 對(duì)于相同的數(shù)據(jù)集來(lái)說(shuō)休讳,AOF 文件的體積通常要大于 RDB 文件的體積。
- 根據(jù)所使用的 fsync 策略尿孔,AOF 的速度可能會(huì)慢于 RDB 。 在一般情況下筹麸, 每秒 fsync 的性能依然非常高活合, 而關(guān)閉 fsync 可以讓 AOF 的速度和 RDB 一樣快, 即使在高負(fù)荷之下也是如此物赶。 不過(guò)在處理巨大的寫(xiě)入載入時(shí)白指,RDB 可以提供更有保證的最大延遲時(shí)間(latency)。
如何選擇RDB和AOF
- 如果是數(shù)據(jù)不那么敏感酵紫,且可以從其他地方重新生成補(bǔ)回的告嘲,那么可以關(guān)閉持久化
- 如果是數(shù)據(jù)比較重要,不想再?gòu)钠渌胤将@取奖地,且可以承受數(shù)分鐘的數(shù)據(jù)丟失橄唬,比如緩存等,那么可以只使用RDB
- 如果是用做內(nèi)存數(shù)據(jù)庫(kù)参歹,要使用Redis的持久化仰楚,建議是RDB和AOF都開(kāi)啟,或者定期執(zhí)行bgsave做快照備份犬庇,RDB方式更適合做數(shù)據(jù)的備份僧界,AOF可以保證數(shù)據(jù)的不丟失
RDB 和 AOF在數(shù)據(jù)恢復(fù)時(shí)的優(yōu)先級(jí)?
數(shù)據(jù)恢復(fù)時(shí) AOF 優(yōu)先于 RDB, 因?yàn)锳OF的同步頻率相對(duì)高臭挽,可靠性高
事務(wù)
什么是事務(wù)捂襟?
事務(wù)是一個(gè)單獨(dú)的隔離操作:事務(wù)中的所有命令都會(huì)序列化、按順序地執(zhí)行欢峰。事務(wù)在執(zhí)行的過(guò)程中葬荷,不會(huì)被其他客戶端發(fā)送來(lái)的命令請(qǐng)求所打斷涨共。
事務(wù)是一個(gè)原子操作:事務(wù)中的命令要么全部被執(zhí)行,要么全部都不執(zhí)行闯狱。
Redis事務(wù)的概念
Redis 事務(wù)的本質(zhì)是通過(guò)MULTI煞赢、EXEC、WATCH等一組命令的集合哄孤。事務(wù)支持一次執(zhí)行多個(gè)命令照筑,一個(gè)事務(wù)中所有命令都會(huì)被序列化。在事務(wù)執(zhí)行過(guò)程瘦陈,會(huì)按照順序串行化執(zhí)行隊(duì)列中的命令,其他客戶端提交的命令請(qǐng)求不會(huì)插入到事務(wù)執(zhí)行命令序列中晨逝。
總結(jié)說(shuō):redis事務(wù)就是一次性、順序性支鸡、排他性的執(zhí)行一個(gè)隊(duì)列中的一系列命令牧挣。
Redis事務(wù)命令
命令 | 格式 | 作用 | 返回結(jié)果 |
---|---|---|---|
WATCH | WATCH key [key ...] | 將給出的Keys標(biāo)記為監(jiān)測(cè)態(tài)醒陆,作為事務(wù)執(zhí)行的條件 | always OK. |
UNWATCH | UNWATCH | 清除事務(wù)中Keys的 監(jiān)測(cè)態(tài),如果調(diào)用了EXEC or DISCARD寺晌,則沒(méi)有必要再手動(dòng)調(diào)用UNWATCH | always OK. |
MULTI | MULTI | 顯式開(kāi)啟redis事務(wù),后續(xù)commands將排隊(duì)澡刹,等候使用EXEC進(jìn)行原子執(zhí)行 | always OK. |
EXEC | EXEC | 執(zhí)行事務(wù)中的commands隊(duì)列呻征,恢復(fù)連接狀態(tài)。如果WATCH在之前被調(diào)用罢浇,只有監(jiān)測(cè)中的Keys沒(méi)有被修改怕犁,命令才會(huì)被執(zhí)行,否則停止執(zhí)行 | 成功: 返回?cái)?shù)組 —— 每個(gè)元素對(duì)應(yīng)著原子事務(wù)中一個(gè) command的返回結(jié)果己莺;失斪喔Α: 返回NULL; |
DISCARD | DISCARD | 清除事務(wù)中的commands隊(duì)列凌受,恢復(fù)連接狀態(tài)阵子。如果WATCH在之前被調(diào)用,釋放 監(jiān)測(cè)中的Keys | always OK. |
Redis事務(wù)使用方法
Redis事務(wù)功能是通過(guò)MULTI胜蛉、EXEC挠进、DISCARD和WATCH 四個(gè)原語(yǔ)實(shí)現(xiàn)的
Redis會(huì)將一個(gè)事務(wù)中的所有命令序列化色乾,然后按順序執(zhí)行。
- 使用MULTI命令進(jìn)入一個(gè)Redis事務(wù)领突,這個(gè)命令的返回值總是OK暖璧。
- 用戶可以發(fā)出多個(gè)Redis命令。Redis會(huì)將這些命令放入隊(duì)列君旦,而不是直接執(zhí)行這些命令
- 如果調(diào)用EXEC命令澎办,那么Redis就會(huì)按順序執(zhí)行事務(wù)中的所有命令。
- 如果調(diào)用DISCARD命令將會(huì)清除事務(wù)隊(duì)列局蚀,然后退出事務(wù)。
以下示例會(huì)原子化地遞增foo鍵和bar鍵的值:
Redis事務(wù)中出錯(cuò)會(huì)怎樣千扶?
事務(wù)期間澎羞,可能會(huì)遇到幾種命令錯(cuò)誤:
- 命令可能存在語(yǔ)法錯(cuò)誤,進(jìn)入隊(duì)列的命令有誤接谨,比如參數(shù)數(shù)量錯(cuò)誤脓豪,錯(cuò)誤的命令名稱
- 執(zhí)行EXEC運(yùn)行時(shí)候時(shí)出錯(cuò),比如給一個(gè)list類型的變量 執(zhí)行incr + 1笤闯,這樣的命令語(yǔ)法上沒(méi)問(wèn)題颗味,只有在運(yùn)行的時(shí)候才能發(fā)行
對(duì)于第一種錯(cuò)誤浦马,客戶端會(huì)在EXEC調(diào)用之前檢測(cè)谨娜, 通過(guò)檢查排隊(duì)命令的狀態(tài)回復(fù)趴梢,如果命令使用QUEUED進(jìn)行響應(yīng),則它已正確排隊(duì)滩愁;否則Redis將返回錯(cuò)誤硝枉。
對(duì)于第二種錯(cuò)誤妻味,服務(wù)端會(huì)記住在累積命令期間發(fā)生的錯(cuò)誤,當(dāng)EXEC命令調(diào)用時(shí)雏逾,將拒絕執(zhí)行事務(wù)栖博,并返回這些錯(cuò)誤仇让,同時(shí)自動(dòng)清除命令隊(duì)列。即使事務(wù)中的某些命令執(zhí)行失敗蠢正,其他命令仍會(huì)被正常執(zhí)行嚣崭。(包括出錯(cuò)命令之后的命令)
為什么Redis事務(wù)不支持回滾雹舀?
事實(shí)上Redis命令在事務(wù)執(zhí)行時(shí)可能會(huì)失敗虚吟,但仍會(huì)繼續(xù)執(zhí)行剩余命令而不是Rollback(事務(wù)回滾)串慰。如果你使用過(guò)關(guān)系數(shù)據(jù)庫(kù)邦鲫,這種情況可能會(huì)讓你感到很奇怪。然而針對(duì)這種情況具備很好的解釋:
- Redis命令可能會(huì)執(zhí)行失敗滔以,僅僅是由于錯(cuò)誤的語(yǔ)法被調(diào)用(命令排隊(duì)時(shí)檢測(cè)不出來(lái)的錯(cuò)誤)你画,或者使用錯(cuò)誤的數(shù)據(jù)類型操作某個(gè)Key: 這意味著,實(shí)際上失敗的命令都是編程錯(cuò)誤造成的呈队,都是開(kāi)發(fā)中能夠被檢測(cè)出來(lái)的宪摧,生產(chǎn)環(huán)境中不應(yīng)該存在。(這番話沿彭,徹底甩鍋喉刘,“都是你們自己編程錯(cuò)誤造锅,與我們無(wú)關(guān)”哥蔚。)
- 由于不必支持Rollback,Redis內(nèi)部簡(jiǎn)潔并且更加高效糙箍。
“如果錯(cuò)誤就是發(fā)生了呢?”這是一個(gè)反對(duì)Redis觀點(diǎn)的爭(zhēng)論塌西。然而應(yīng)該指出的是捡需,通常情況下站辉,回滾并不能挽救編程錯(cuò)誤。鑒于沒(méi)有人能夠挽救程序員的錯(cuò)誤汰蓉,并且Redis命令失敗所需的錯(cuò)誤類型不太可能進(jìn)入生產(chǎn)環(huán)境顾孽,所以我們選擇了不支持錯(cuò)誤回滾(Rollback)這種更簡(jiǎn)單快捷的方法若厚。
Redis事務(wù)支持隔離性嗎
Redis 是單進(jìn)程程序,并且它保證在執(zhí)行事務(wù)時(shí)霎冯,不會(huì)對(duì)事務(wù)進(jìn)行中斷锚贱,事務(wù)可以運(yùn)行直到執(zhí)行完所有事務(wù)隊(duì)列中的命令為止拧廊。因此吧碾,Redis 的事務(wù)是總是帶有隔離性的。
Redis事務(wù)其他實(shí)現(xiàn)
基于Lua腳本睁本,Redis可以保證腳本內(nèi)的命令一次性呢堰、按順序地執(zhí)行,
其同時(shí)也不提供事務(wù)運(yùn)行錯(cuò)誤的回滾骂维,執(zhí)行過(guò)程中如果部分命令運(yùn)行錯(cuò)誤航闺,剩下的命令還是會(huì)繼續(xù)運(yùn)行完
基于中間標(biāo)記變量,通過(guò)另外的標(biāo)記變量來(lái)標(biāo)識(shí)事務(wù)是否執(zhí)行完成福铅,讀取數(shù)據(jù)時(shí)先讀取該標(biāo)記變量判斷是否事務(wù)執(zhí)行完成。但這樣會(huì)需要額外寫(xiě)代碼實(shí)現(xiàn)环揽,比較繁瑣
樂(lè)觀鎖與悲觀鎖的區(qū)別汛兜?
Redis中的管道有什么用
一次請(qǐng)求/響應(yīng)服務(wù)器能實(shí)現(xiàn)處理新的請(qǐng)求即使舊的請(qǐng)求還未被響應(yīng)粥谬。這樣就可以將多個(gè)命令發(fā)送到服務(wù)器漏策,而不用等待回復(fù)掺喻,最后在一個(gè)步驟中讀取該答復(fù)。這就是管道(pipelining)即硼,是一種幾十年來(lái)廣泛使用的技術(shù)谦絮。例如許多POP3協(xié)議已經(jīng)實(shí)現(xiàn)支持這個(gè)功能,大大加快了從服務(wù)器下載新郵件的過(guò)程叫胖。
緩存雪崩瓮增,緩存擊穿,緩存穿透現(xiàn)象及解決方案
緩存雪崩:
- 現(xiàn)象:影響輕則砸捏,查詢變慢梆暖,重則當(dāng)請(qǐng)求并發(fā)更高時(shí)轰驳,出來(lái)大面積服務(wù)不可用。
- 原因: 同一時(shí)間緩存大面積失效蠕趁,就像沒(méi)有緩存一樣俺陋,所有的請(qǐng)求直接打到數(shù)據(jù)庫(kù)上來(lái),DB扛不住掛了缴挖,如果是重要的庫(kù)映屋,例如用戶庫(kù),那牽聯(lián)就一大片了瘫析,瞬間倒一片贬循。
- 案例:電商首頁(yè)緩存,如果首頁(yè)的key全部都在某一時(shí)刻失效奇适,剛好在那一時(shí)刻有秒殺活動(dòng)滤愕,那這樣的話就所有的請(qǐng)求都被打到了DB。并發(fā)大的情況下DB必然扛不住魂贬,導(dǎo)致服務(wù)不可用。
- 解決方案:批量往redis存數(shù)據(jù)的時(shí)候键科,把每個(gè)key的失效時(shí)間加上個(gè)隨機(jī)數(shù)勋颖,這樣的話就能保證數(shù)據(jù)不會(huì)在同一個(gè)時(shí)間大面積失效。
緩存穿透:
現(xiàn)象與原因: 指用戶不斷發(fā)起請(qǐng)求的數(shù)據(jù)茄厘,在緩存和DB中都沒(méi)有次哈,比如DB中的用戶ID是自增的,但是用戶請(qǐng)求傳了-1葛假,或者是一個(gè)特別大的數(shù)字聊训,這個(gè)時(shí)候用戶很有可能就是一個(gè)攻擊者鼓寺,這樣的功擊會(huì)導(dǎo)致DB的壓力過(guò)大,嚴(yán)重的話就是把DB搞掛了苦银。因?yàn)槊看味祭@開(kāi)了緩存直接查詢DB
-
解決方案:
- 方法一:在接口層增加校驗(yàn)幔虏,不合法的參數(shù)直接返回。不相信任務(wù)調(diào)用方瑟蜈,根據(jù)自己提供的API接口規(guī)范來(lái)踪栋,作為被調(diào)用方,要考慮可能任何的參數(shù)傳值囤官。
- 方法二:在緩存查不到党饮,DB中也沒(méi)有的情況,可以將對(duì)應(yīng)的key的value寫(xiě)為null蹲堂,或者其他特殊值寫(xiě)入緩存,同時(shí)將過(guò)期失效時(shí)間設(shè)置短一點(diǎn)朽基,以免影響正常情況稼虎。這樣是可以防止反復(fù)用同一個(gè)ID來(lái)暴力攻擊哀军。
- 方法三:正常用戶是不會(huì)這樣暴力功擊排苍,只有是惡意者才會(huì)這樣做,可以在網(wǎng)關(guān)NG作一個(gè)配置項(xiàng)腻暮,為每一個(gè)IP設(shè)置訪問(wèn)閥值。
- 方法四:高級(jí)用戶布隆過(guò)濾器(Bloom Filter),這個(gè)也能很好地防止緩存穿透试幽。原理就是利用高效的數(shù)據(jù)結(jié)構(gòu)和算法快速判斷出你這個(gè)Key是否在DB中存在,不存在你return就好了济榨,存在你就去查了DB刷新KV再return擒滑。
緩存擊穿:
- 現(xiàn)象與原因:跟緩存雪崩類似,但是又有點(diǎn)不一樣淹冰。雪崩是因?yàn)榇竺娣e緩存失效凝颇,請(qǐng)求全打到DB;而緩存擊穿是指一個(gè)key是熱點(diǎn)芦岂,不停地扛住大并發(fā)請(qǐng)求禽最,全都集中訪問(wèn)此key,而當(dāng)此key過(guò)期瞬間川无,持續(xù)的大并發(fā)就擊穿緩存,全都打在DB上仅叫。就又引發(fā)雪崩的問(wèn)題诫咱。
- 解決方案:
- 方法一:設(shè)置熱點(diǎn)數(shù)據(jù)永遠(yuǎn)不過(guò)期。
- 方法二:加互斥鎖掏呼,互斥鎖
緩存高可用方案
這就是三者的區(qū)別,差不多岭接,但又有一些區(qū)別鸣戴。因?yàn)榫彺嫜┍馈⒋┩负蛽舸┤胪担蔷彺孀畲蟮膯?wèn)題疏之,要么不出現(xiàn),一旦出現(xiàn)就是致命性的問(wèn)題
一般避免以上情況發(fā)生我們從三個(gè)時(shí)間段去分析下:
- 事前:Redis 高可用丙曙,主從+哨兵,Redis cluster索抓,避免全盤(pán)崩潰纸兔。
- 事中:本地 ehcache 緩存 + Hystrix 限流+降級(jí),避免MySQL 被打死备禀。
- 事后:Redis 持久化 RDB+AOF,一旦重啟另患,自動(dòng)從磁盤(pán)上加載數(shù)據(jù)昆箕,快速恢復(fù)緩存數(shù)據(jù)。
Redis的過(guò)期策略和內(nèi)存淘汰策略
Redis key過(guò)期刪除策略
使用過(guò)Redis的同學(xué)應(yīng)該知道,我們?cè)谠O(shè)置一個(gè)key之后镜粤,可以指定這個(gè)key的過(guò)期時(shí)間公荧。那么這個(gè)key到了過(guò)期時(shí)間就會(huì)立即被刪除嗎庸诱?Redis是如何刪除這些過(guò)期key的呢?
Redis是使用定期刪除 + 惰性刪除 兩者配合的過(guò)期策略钠四。
-
定期刪除
定期刪除指的是Redis默認(rèn)每隔100ms就隨機(jī)抽取一些設(shè)置了過(guò)期時(shí)間的key,檢測(cè)這些key是否過(guò)期缕碎,如果過(guò)期了就將其刪掉。過(guò)期掃描不會(huì)遍歷過(guò)期字典中所有的 key赊抖,而是采用了一種簡(jiǎn)單的貪心策略寨典。- 從過(guò)期字典中隨機(jī) 20 個(gè) key氛雪;
- 刪除這 20 個(gè) key 中已經(jīng)過(guò)期的 key;
- 如果過(guò)期的 key 比率超過(guò) 1/4耸成,那就重復(fù)步驟 1报亩;
同時(shí),為了保證過(guò)期掃描不會(huì)出現(xiàn)循環(huán)過(guò)度墓猎,導(dǎo)致線程卡死現(xiàn)象捆昏,算法還增加了掃描時(shí)間的上限寇仓,默認(rèn)不會(huì)超過(guò) 25ms服猪。
因?yàn)槭请S機(jī)抽取一些key來(lái)刪除膳帕。這樣就有可能刪除不完娩缰,需要惰性刪除配合。
-
惰性刪除
惰性刪除不再是Redis去主動(dòng)刪除,而是在客戶端要獲取某個(gè)key的時(shí)候,Redis會(huì)先去檢測(cè)一下這個(gè)key是否已經(jīng)過(guò)期,如果沒(méi)有過(guò)期則返回給客戶端库正,如果已經(jīng)過(guò)期了喷楣,那么Redis會(huì)刪除這個(gè)key粗截,不會(huì)返回給客戶端灭美。所以惰性刪除可以解決一些過(guò)期了犁苏,但沒(méi)被定期刪除隨機(jī)抽取到的key。但有些過(guò)期的key既沒(méi)有被隨機(jī)抽取期丰,也沒(méi)有被客戶端訪問(wèn)几晤,就會(huì)一直保留在數(shù)據(jù)庫(kù)狸捕,占用內(nèi)存,長(zhǎng)期下去可能會(huì)導(dǎo)致內(nèi)存耗盡狠鸳。
所以Redis提供了內(nèi)存淘汰機(jī)制來(lái)解決這個(gè)問(wèn)題蛾派。
內(nèi)存淘汰策略
Redis在使用內(nèi)存達(dá)到某個(gè)閾值(通過(guò)maxmemory配置)的時(shí)候茫经,就會(huì)觸發(fā)內(nèi)存淘汰機(jī)制,選取一些key來(lái)刪除铃彰。內(nèi)存淘汰有許多策略霜浴,下面分別介紹這幾種不同的策略箭养。
noeviction:當(dāng)內(nèi)存不足以容納新寫(xiě)入數(shù)據(jù)時(shí)愿题,新寫(xiě)入操作會(huì)報(bào)錯(cuò)。默認(rèn)策略
allkeys-lru:當(dāng)內(nèi)存不足以容納新寫(xiě)入數(shù)據(jù)時(shí)掐暮,在鍵空間中灰羽,移除最近最少使用的key傍念。(這個(gè)是最常用的)
allkeys-random:當(dāng)內(nèi)存不足以容納新寫(xiě)入數(shù)據(jù)時(shí)驳概,在鍵空間中俯萌,隨機(jī)移除某個(gè)key爪飘。
volatile-lru:當(dāng)內(nèi)存不足以容納新寫(xiě)入數(shù)據(jù)時(shí)肴熏,在設(shè)置了過(guò)期時(shí)間的鍵空間中赊锚,移除最近最少使用的key域滥。
volatile-random:當(dāng)內(nèi)存不足以容納新寫(xiě)入數(shù)據(jù)時(shí)叽唱,在設(shè)置了過(guò)期時(shí)間的鍵空間中涝缝,隨機(jī)移除某個(gè)key。
volatile-ttl:當(dāng)內(nèi)存不足以容納新寫(xiě)入數(shù)據(jù)時(shí)萎胰,在設(shè)置了過(guò)期時(shí)間的鍵空間中锨推,有更早過(guò)期時(shí)間的key優(yōu)先移除。
如何選取合適的策略?比較推薦的是兩種lru策略今膊。根據(jù)自己的業(yè)務(wù)需求镣陕。如果你使用Redis只是作為緩存,不作為DB持久化勺择,那推薦選擇allkeys-lru;如果你使用Redis同時(shí)用于緩存和數(shù)據(jù)持久化,那推薦選擇volatile-lru绊起。
緩存淘汰算法: LRU 和 LFU的區(qū)別
LRU是最近最少使用頁(yè)面置換算法(Least Recently Used),也就是首先淘汰最長(zhǎng)時(shí)間未被使用的頁(yè)面!
LFU是最近最不常用頁(yè)面置換算法(Least Frequently Used),也就是淘汰一定時(shí)期內(nèi)被訪問(wèn)次數(shù)最少的頁(yè)!
線程模型
Redis集群方案
Redis主從復(fù)制是什么及作用铐尚?
redis的主從復(fù)制功能是支持多個(gè)數(shù)據(jù)庫(kù)之間的數(shù)據(jù)同步藤为。一類是主數(shù)據(jù)庫(kù)(master)一類是從數(shù)據(jù)庫(kù)(slave),主數(shù)據(jù)庫(kù)可以進(jìn)行讀寫(xiě)操作缅疟,當(dāng)發(fā)生寫(xiě)操作的時(shí)候自動(dòng)將數(shù)據(jù)同步到從數(shù)據(jù)庫(kù)分别,而從數(shù)據(jù)庫(kù)一般是只讀的,并接收主數(shù)據(jù)庫(kù)同步過(guò)來(lái)的數(shù)據(jù)存淫,一個(gè)主數(shù)據(jù)庫(kù)可以有多個(gè)從數(shù)據(jù)庫(kù)耘斩,而一個(gè)從數(shù)據(jù)庫(kù)只能有一個(gè)主數(shù)據(jù)庫(kù)。
通過(guò)redis的復(fù)制功能可以很好的實(shí)現(xiàn)數(shù)據(jù)庫(kù)的讀寫(xiě)分離纫雁,提高服務(wù)器的負(fù)載能力煌往。主數(shù)據(jù)庫(kù)主要進(jìn)行寫(xiě)操作,而從數(shù)據(jù)庫(kù)負(fù)責(zé)讀操作轧邪。
主從復(fù)制的架構(gòu)圖:
主從復(fù)制的作用:
- 數(shù)據(jù)冗余刽脖,實(shí)現(xiàn)數(shù)據(jù)的熱備份
- 故障恢復(fù),避免單點(diǎn)故障帶來(lái)的服務(wù)不可用
- 讀寫(xiě)分離忌愚,負(fù)載均衡曲管。主節(jié)點(diǎn)負(fù)載讀寫(xiě),從節(jié)點(diǎn)負(fù)責(zé)讀硕糊,提高服務(wù)器并發(fā)量
- 高可用基礎(chǔ)院水,是哨兵機(jī)制和集群實(shí)現(xiàn)的基礎(chǔ)
Redis主從復(fù)制過(guò)程及原理
主從復(fù)制過(guò)程:
- 1:當(dāng)一個(gè)從數(shù)據(jù)庫(kù)啟動(dòng)時(shí)腊徙,會(huì)向主數(shù)據(jù)庫(kù)發(fā)送sync命令,
- 2:主數(shù)據(jù)庫(kù)接收到sync命令后會(huì)開(kāi)始在后臺(tái)保存快照(執(zhí)行rdb操作)檬某,并將保存期間接收到的命令緩存起來(lái)
- 3:當(dāng)快照完成后撬腾,redis會(huì)將快照文件和所有緩存的命令發(fā)送給從數(shù)據(jù)庫(kù)。
- 4:從數(shù)據(jù)庫(kù)收到后恢恼,會(huì)載入快照文件并執(zhí)行收到的緩存的命令民傻。
主從復(fù)制過(guò)程如圖所示:
Redis Sentinel 哨兵機(jī)制
Redis Sentinel 是 Redis 高可用的實(shí)現(xiàn)方案,它是一個(gè)管理多個(gè) Redis 實(shí)例的工具场斑。
Redis Sentinel 的主要功能包括 主節(jié)點(diǎn)存活檢測(cè)漓踢、主從運(yùn)行情況檢測(cè)、自動(dòng)故障轉(zhuǎn)移 (failover)漏隐、主從切換喧半。Redis 的 Sentinel 最小配置是 一主一從。
Redis 的 Sentinel 系統(tǒng)可以用來(lái)管理多個(gè) Redis 服務(wù)器青责,該系統(tǒng)可以執(zhí)行以下四個(gè)任務(wù):
- 監(jiān)控: Sentinel 會(huì)不斷的檢查 主服務(wù)器 和 從服務(wù)器 是否正常運(yùn)行挺据。
- 通知:當(dāng)被監(jiān)控的某個(gè) Redis 服務(wù)器出現(xiàn)問(wèn)題,Sentinel 通過(guò) API 腳本 向 管理員 或者其他的 應(yīng)用程序 發(fā)送通知爽柒。
- 自動(dòng)故障轉(zhuǎn)移: 當(dāng)主節(jié)點(diǎn)不能正常工作時(shí)吴菠,Sentinel 會(huì)開(kāi)始一次 自動(dòng)的故障轉(zhuǎn)移操作,它會(huì)將與 失效主節(jié)點(diǎn) 是 主從關(guān)系 的其中一個(gè) 從節(jié)點(diǎn) 升級(jí)為新的 主節(jié)點(diǎn)浩村,并且將其他的 從節(jié)點(diǎn) 指向 新的主節(jié)點(diǎn)。
- 配置提供者:在 Redis Sentinel 模式下占哟,客戶端應(yīng)用 在初始化時(shí)連接的是 Sentinel 節(jié)點(diǎn)集合心墅,從中獲取 主節(jié)點(diǎn) 的信息。
如圖所示榨乎,Redis Sentinel 高可用架構(gòu) 的示意圖:
Redis 哨兵機(jī)制如何實(shí)現(xiàn)故障自動(dòng)轉(zhuǎn)移怎燥?
sentinel 集群通過(guò)主觀下線和客觀下線判斷redis節(jié)點(diǎn)是否失效
默認(rèn)情況下,每個(gè) Sentinel 節(jié)點(diǎn)會(huì)以每秒一次的頻率對(duì)Redis 節(jié)點(diǎn)和其它的Sentinel 節(jié)點(diǎn)發(fā)送 PING 命令蜜暑,并通過(guò)節(jié)點(diǎn)的 回復(fù) 來(lái)判斷節(jié)點(diǎn)是否在線铐姚。
-
主觀下線
主觀下線 適用于所有 主節(jié)點(diǎn) 和 從節(jié)點(diǎn)。如果在 down-after-milliseconds 毫秒內(nèi)肛捍,Sentinel 沒(méi)有收到 目標(biāo)節(jié)點(diǎn) 的有效回復(fù)隐绵,則會(huì)判定 該節(jié)點(diǎn) 為 主觀下線。 -
客觀下線
客觀下線 只適用于 主節(jié)點(diǎn)拙毫。如果 主節(jié)點(diǎn) 出現(xiàn)故障依许,Sentinel 節(jié)點(diǎn)會(huì)通過(guò) sentinel is-master-down-by-addr 命令,向其它 Sentinel 節(jié)點(diǎn)詢問(wèn)對(duì)該節(jié)點(diǎn)的 狀態(tài)判斷缀蹄。如果超過(guò) <quorum> 個(gè)數(shù)的節(jié)點(diǎn)判定 主節(jié)點(diǎn) 不可達(dá)峭跳,則該 Sentinel 節(jié)點(diǎn)會(huì)判斷 主節(jié)點(diǎn)為客觀下線膘婶。
當(dāng)判斷某個(gè)Redis節(jié)點(diǎn)是客觀下線后,Sentinel會(huì)把master轉(zhuǎn)移到另外的slave節(jié)點(diǎn)蛀醉,讓它充當(dāng)新的master接受請(qǐng)求悬襟,從而保證高可用性。
Redis集群的開(kāi)源方案有哪些拯刁?
Twemproxy
Twemproxy 是 twitter 開(kāi)源的一個(gè) redis 和 memcache 的 中間代理服務(wù)器 程序脊岳。Twemproxy 作為 代理,可接受來(lái)自多個(gè)程序的訪問(wèn)筛璧,按照 路由規(guī)則逸绎,轉(zhuǎn)發(fā)給后臺(tái)的各個(gè) Redis 服務(wù)器,再原路返回夭谤。Twemproxy 存在 單點(diǎn)故障 問(wèn)題棺牧,需要結(jié)合 Lvs 和 Keepalived 做 高可用方案。
- 優(yōu)點(diǎn):應(yīng)用范圍廣朗儒,穩(wěn)定性較高颊乘,中間代理層 高可用。
- 缺點(diǎn):無(wú)法平滑地 水平擴(kuò)容/縮容醉锄,無(wú) 可視化管理界面乏悄,運(yùn)維不友好,出現(xiàn)故障恳不,不能 自動(dòng)轉(zhuǎn)移檩小。
Codis
Codis 是一個(gè) 分布式 Redis 解決方案,對(duì)于上層應(yīng)用來(lái)說(shuō)烟勋,連接 Codis-Proxy 和直接連接 原生的 Redis-Server 沒(méi)有的區(qū)別规求。Codis 底層會(huì) 處理請(qǐng)求的轉(zhuǎn)發(fā),不停機(jī)的進(jìn)行 數(shù)據(jù)遷移 等工作卵惦。Codis 采用了無(wú)狀態(tài)的 代理層阻肿,對(duì)于 客戶端 來(lái)說(shuō),一切都是透明的沮尿。
- 優(yōu)點(diǎn):實(shí)現(xiàn)了上層 Proxy 和底層 Redis 的 高可用丛塌,數(shù)據(jù)分片 和 自動(dòng)平衡,提供 命令行接口 和 RESTful API畜疾,提供 監(jiān)控 和 管理 界面赴邻,可以動(dòng)態(tài) 添加 和 刪除 Redis 節(jié)點(diǎn)。
- 缺點(diǎn): 部署架構(gòu) 和 配置 復(fù)雜庸疾,不支持 跨機(jī)房 和 多租戶乍楚,不支持 鑒權(quán)管理。
Redis Cluster 集群架構(gòu)
Redis Cluster 實(shí)現(xiàn)了一種 混合形式 的 查詢路由届慈,但并不是 直接 將請(qǐng)求從一個(gè) Redis 節(jié)點(diǎn) 轉(zhuǎn)發(fā) 到另一個(gè) Redis 節(jié)點(diǎn)徒溪,而是在 客戶端 的幫助下直接 重定向( redirected)到正確的 Redis 節(jié)點(diǎn)忿偷。
優(yōu)點(diǎn):無(wú)中心節(jié)點(diǎn),數(shù)據(jù)按照 槽 存儲(chǔ)分布在多個(gè) Redis 實(shí)例上臊泌,可以平滑的進(jìn)行節(jié)點(diǎn) 擴(kuò)容/縮容鲤桥,支持 高可用 和 自動(dòng)故障轉(zhuǎn)移,運(yùn)維成本低渠概。
缺點(diǎn): 嚴(yán)重依賴 Redis-trib 工具茶凳,缺乏 監(jiān)控管理,需要依賴 Smart Client (維護(hù)連接播揪,緩存路由表贮喧,MultiOp 和 Pipeline 支持)。Failover 節(jié)點(diǎn)的 檢測(cè)過(guò)慢猪狈,不如 中心節(jié)點(diǎn) ZooKeeper 及時(shí)箱沦。Gossip 消息具有一定開(kāi)銷(xiāo)。無(wú)法根據(jù)統(tǒng)計(jì)區(qū)分 冷熱數(shù)據(jù)雇庙。
數(shù)據(jù)分區(qū)有哪些算法谓形?
分布式數(shù)據(jù)庫(kù) 首先要解決把 整個(gè)數(shù)據(jù)集 按照 分區(qū)規(guī)則 映射到 多個(gè)節(jié)點(diǎn) 的問(wèn)題,即把 數(shù)據(jù)集 劃分到 多個(gè)節(jié)點(diǎn) 上疆前,每個(gè)節(jié)點(diǎn)負(fù)責(zé) 整體數(shù)據(jù) 的一個(gè) 子集寒跳。
數(shù)據(jù)分布通常有 哈希分區(qū) 和 順序分區(qū) 兩種方式,對(duì)比如下:
分區(qū)方式 | 特點(diǎn) | 相關(guān)產(chǎn)品 |
---|---|---|
哈希分區(qū) | 離散程度好竹椒,數(shù)據(jù)分布與業(yè)務(wù)無(wú)關(guān)童太,無(wú)法順序訪問(wèn) | Redis Cluster,Cassandra胸完,Dynamo |
順序分區(qū) | 離散程度易傾斜康愤,數(shù)據(jù)分布與業(yè)務(wù)相關(guān),可以順序訪問(wèn) | BigTable舶吗,HBase,Hypertable |
節(jié)點(diǎn)取余分區(qū)
使用特定的數(shù)據(jù)择膝,如 Redis 的 鍵 或 用戶 ID誓琼,再根據(jù) 節(jié)點(diǎn)數(shù)量 N 使用公式:hash(key)% N 計(jì)算出 哈希值,用來(lái)決定數(shù)據(jù) 映射 到哪一個(gè)節(jié)點(diǎn)上肴捉。
優(yōu)點(diǎn): 這種方式的突出優(yōu)點(diǎn)是 簡(jiǎn)單性腹侣,常用于 數(shù)據(jù)庫(kù) 的 分庫(kù)分表規(guī)則。一般采用 預(yù)分區(qū) 的方式齿穗,提前根據(jù) 數(shù)據(jù)量 規(guī)劃好 分區(qū)數(shù)傲隶,比如劃分為 512 或 1024 張表,保證可支撐未來(lái)一段時(shí)間的 數(shù)據(jù)容量窃页,再根據(jù) 負(fù)載情況 將 表 遷移到其他 數(shù)據(jù)庫(kù) 中跺株。擴(kuò)容時(shí)通常采用 翻倍擴(kuò)容复濒,避免 數(shù)據(jù)映射 全部被 打亂,導(dǎo)致 全量遷移 的情況乒省。
缺點(diǎn): 當(dāng)節(jié)點(diǎn)數(shù)量變化時(shí)巧颈,如 擴(kuò)容 或 收縮 節(jié)點(diǎn),數(shù)據(jù)節(jié)點(diǎn) 映射關(guān)系 需要重新計(jì)算袖扛,會(huì)導(dǎo)致數(shù)據(jù)的 重新遷移砸泛。
一致性哈希分區(qū)
一致性哈希 可以很好的解決 穩(wěn)定性問(wèn)題,可以將所有的 存儲(chǔ)節(jié)點(diǎn) 排列在 收尾相接 的 Hash 環(huán)上蛆封,每個(gè) key 在計(jì)算 Hash 后會(huì) 順時(shí)針 找到 臨接 的 存儲(chǔ)節(jié)點(diǎn) 存放唇礁。而當(dāng)有節(jié)點(diǎn) 加入 或 退出 時(shí),僅影響該節(jié)點(diǎn)在 Hash 環(huán)上 順時(shí)針相鄰 的 后續(xù)節(jié)點(diǎn)惨篱。
優(yōu)點(diǎn):加入和刪除 節(jié)點(diǎn)只影響 哈希環(huán) 中 順時(shí)針?lè)较?的 相鄰的節(jié)點(diǎn)盏筐,對(duì)其他節(jié)點(diǎn)無(wú)影響。
缺點(diǎn): 加減節(jié)點(diǎn) 會(huì)造成 哈希環(huán) 中部分?jǐn)?shù)據(jù) 無(wú)法命中妒蛇。當(dāng)使用 少量節(jié)點(diǎn) 時(shí)机断,節(jié)點(diǎn)變化 將大范圍影響 哈希環(huán) 中 數(shù)據(jù)映射老充,不適合 少量數(shù)據(jù)節(jié)點(diǎn) 的分布式方案州藕。普通 的 一致性哈希分區(qū) 在增減節(jié)點(diǎn)時(shí)需要 增加一倍 或 減去一半 節(jié)點(diǎn)才能保證 數(shù)據(jù) 和 負(fù)載的均衡。
虛擬槽分區(qū)
虛擬槽分區(qū) 巧妙地使用了 哈吓欤空間陶耍,使用 分散度良好 的 哈希函數(shù) 把所有數(shù)據(jù) 映射 到一個(gè) 固定范圍 的 整數(shù)集合 中奋蔚,整數(shù)定義為 槽(slot)。這個(gè)范圍一般 遠(yuǎn)遠(yuǎn)大于 節(jié)點(diǎn)數(shù)烈钞,比如 Redis Cluster 槽范圍是 0 ~ 16383泊碑。槽 是集群內(nèi) 數(shù)據(jù)管理 和 遷移 的 基本單位。采用 大范圍槽 的主要目的是為了方便 數(shù)據(jù)拆分 和 集群擴(kuò)展毯欣。每個(gè)節(jié)點(diǎn)會(huì)負(fù)責(zé) 一定數(shù)量的槽馒过,如圖所示:
當(dāng)前集群有 5 個(gè)節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)平均大約負(fù)責(zé) 3276 個(gè) 槽酗钞。由于采用 高質(zhì)量 的 哈希算法腹忽,每個(gè)槽所映射的數(shù)據(jù)通常比較 均勻,將數(shù)據(jù)平均劃分到 5 個(gè)節(jié)點(diǎn)進(jìn)行 數(shù)據(jù)分區(qū)砚作。Redis Cluster 就是采用 虛擬槽分區(qū)窘奏。
節(jié)點(diǎn)1: 包含 0 到 3276 號(hào)哈希槽。
節(jié)點(diǎn)2:包含 3277 到 6553 號(hào)哈希槽葫录。
節(jié)點(diǎn)3:包含 6554 到 9830 號(hào)哈希槽着裹。
節(jié)點(diǎn)4:包含 9831 到 13107 號(hào)哈希槽。
節(jié)點(diǎn)5:包含 13108 到 16383 號(hào)哈希槽米同。
這種結(jié)構(gòu)很容易 添加 或者 刪除 節(jié)點(diǎn)骇扇。如果 增加 一個(gè)節(jié)點(diǎn) 6摔竿,就需要從節(jié)點(diǎn) 1 ~ 5 獲得部分 槽 分配到節(jié)點(diǎn) 6 上。如果想 移除 節(jié)點(diǎn) 1匠题,需要將節(jié)點(diǎn) 1 中的 槽 移到節(jié)點(diǎn) 2 ~ 5 上拯坟,然后將 沒(méi)有任何槽 的節(jié)點(diǎn) 1 從集群中 移除 即可。
由于從一個(gè)節(jié)點(diǎn)將 哈希槽 移動(dòng)到另一個(gè)節(jié)點(diǎn)并不會(huì) 停止服務(wù)韭山,所以無(wú)論 添加刪除 或者 改變 某個(gè)節(jié)點(diǎn)的 哈希槽的數(shù)量 都不會(huì)造成 集群不可用 的狀態(tài).
說(shuō)說(shuō) Redis Cluster 虛擬槽分區(qū)郁季?
Redis Cluster 采用 虛擬槽分區(qū),所有的 鍵 根據(jù) 哈希函數(shù) 映射到 0~16383 整數(shù)槽內(nèi)钱磅,計(jì)算公式:slot = CRC16(key)& 16383梦裂。每個(gè)節(jié)點(diǎn)負(fù)責(zé)維護(hù)一部分槽以及槽所映射的 鍵值數(shù)據(jù),如圖所示:
Redis虛擬槽分區(qū)的特點(diǎn):
- 解耦 數(shù)據(jù) 和 節(jié)點(diǎn) 之間的關(guān)系盖淡,簡(jiǎn)化了節(jié)點(diǎn) 擴(kuò)容 和 收縮 難度年柠。
- 節(jié)點(diǎn)自身 維護(hù)槽的 映射關(guān)系,不需要 客戶端 或者 代理服務(wù) 維護(hù) 槽分區(qū)元數(shù)據(jù)褪迟。
- 支持 節(jié)點(diǎn)冗恨、槽、鍵 之間的 映射查詢味赃,用于 數(shù)據(jù)路由掀抹、在線伸縮 等場(chǎng)景。
三主三從的集群使用多少臺(tái)機(jī)器部署比較好心俗?
土豪型: 使用6臺(tái)機(jī)器傲武,每臺(tái)部署一個(gè)Redis節(jié)點(diǎn)
經(jīng)濟(jì)型:使用3臺(tái)機(jī)器,每臺(tái)機(jī)器部署2個(gè)Redis節(jié)點(diǎn)城榛,主從混布揪利,即同一組主從節(jié)點(diǎn),分布在不同節(jié)點(diǎn)狠持,從而保證高可用疟位!
Redis性能優(yōu)化
Redis常見(jiàn)性能問(wèn)題和解決方案:
- Master最好不要做任何持久化工作,如RDB內(nèi)存快照和AOF日志文件
- 如果數(shù)據(jù)比較重要喘垂,某個(gè)Slave開(kāi)啟AOF備份數(shù)據(jù)献汗,策略設(shè)置為每秒同步一次
- 為了主從復(fù)制的速度和連接的穩(wěn)定性,Master和Slave最好在同一個(gè)局域網(wǎng)內(nèi)
盡量避免在壓力很大的主庫(kù)上增加從庫(kù)
總結(jié)
原創(chuàng)文章首發(fā)于公眾號(hào):「碼農(nóng)富哥」王污,致力于分享后端技術(shù) (高并發(fā)架構(gòu), 中間件, Linux, TCP/IP, HTTP, MySQL, Redis), 高性能,分布式楚午,微服務(wù)等原創(chuàng)干貨 和面試指南昭齐!