前言
最近在看redis,有一些心得,為了不忘,現(xiàn)在記錄下來。
我嘗試從問題出發(fā),步步遞進(jìn),給大家較好的閱讀體驗(yàn),具體如下
- 什么是緩存,為啥要有它
- 緩存的實(shí)現(xiàn)方式-進(jìn)程內(nèi)的緩存
- 緩存的實(shí)現(xiàn)方式-進(jìn)程級(jí)別的緩存
- redis作為一個(gè)進(jìn)程級(jí)別的數(shù)據(jù)庫,優(yōu)點(diǎn)
- redis的高可用實(shí)現(xiàn)
什么是緩存,為啥要有它
一般我們將數(shù)據(jù)存儲(chǔ)在數(shù)據(jù)庫中,對(duì)于一些經(jīng)常查詢的業(yè)務(wù),比如用戶訪問某一資源,通常我們會(huì)通過查詢數(shù)據(jù)庫查看用戶是否有這個(gè)訪問權(quán)限,這樣業(yè)務(wù)的特點(diǎn)是查詢頻率高,查詢數(shù)據(jù)庫是比較重的操作,主要的耗時(shí)有數(shù)據(jù)庫本身的查詢耗時(shí)和網(wǎng)絡(luò)耗時(shí)。如果將一些常使用的數(shù)據(jù)放在程序運(yùn)行時(shí)內(nèi)存里,隨著程序的啟動(dòng),加載數(shù)據(jù)庫中信息到內(nèi)存中,這樣就減少了網(wǎng)絡(luò)交互和數(shù)據(jù)庫本身的查詢耗時(shí)。
緩存的實(shí)現(xiàn)方式-進(jìn)程內(nèi)的緩存
這種就是在寫程序時(shí)開辟一塊內(nèi)存,專門當(dāng)做緩存供程序使用
優(yōu)點(diǎn)
- 不需要網(wǎng)絡(luò)傳輸。
- 不需要線程間切換。
- 緩存更新方便。因?yàn)槭浅绦騼?nèi)的內(nèi)存區(qū)域,我需要更新哪個(gè)區(qū)域,我可以直接定位到內(nèi)存位置,并更新它。
缺點(diǎn)
- 拓展性差。還是一個(gè)商城的例子,用戶想看自己的購(gòu)物車,假設(shè)我們使用token進(jìn)行狀態(tài)保持,我解析token,抽取用戶id,查看是否具有這個(gè)業(yè)務(wù)操作的權(quán)限。如果部署的程序只有一個(gè),完全沒問題。但是為了滿足未來用戶量高速增長(zhǎng),通過nginx橫向拓展程序就可以有問題。假如我們平行的部署兩個(gè)程序,用戶來登錄,程序a處理了這個(gè)請(qǐng)求,并將該用戶的權(quán)限信息,token存在自己的內(nèi)存區(qū)域,當(dāng)該用戶發(fā)起另外一個(gè)業(yè)務(wù)請(qǐng)求時(shí),程序b接受這個(gè)請(qǐng)求,程序b拿到用戶的token,抽取用戶信息,查詢自己的內(nèi)存區(qū)域,發(fā)現(xiàn)沒有這個(gè)用戶信息,就判斷這個(gè)用戶沒有訪問權(quán)限。
- 如果是橫向拓展的話,很有可能浪費(fèi)內(nèi)存。多個(gè)程序都有自己的內(nèi)存區(qū)域,但是內(nèi)存區(qū)域存的東西也是基本一樣的。
緩存的實(shí)現(xiàn)方式-進(jìn)程級(jí)別的緩存
優(yōu)點(diǎn)
- 節(jié)約內(nèi)存資源。
- 職責(zé)分離更明確
缺點(diǎn)
- 增加網(wǎng)絡(luò)傳輸
- 增加線程間切換
- 單點(diǎn),有風(fēng)險(xiǎn)
redis作為一個(gè)進(jìn)程級(jí)別的數(shù)據(jù)庫,優(yōu)點(diǎn)
- 支持高可用部署
- 為了降低總體網(wǎng)絡(luò)傳輸?shù)难舆t,提出了一個(gè)新思想:
假設(shè)我是新街口東方餃子王老板,提供送餐服務(wù),我有2個(gè)快遞員,假設(shè)我的飯是提前做好的
- 12:00,京師科技大廈小強(qiáng)定了一份餃子,快遞員1去送餐,十分鐘可以送到,再有十分鐘回到飯店
- 12:02,京師科技大廈小紅定了一份餃子,快遞員2去送餐,十分鐘可以送到,再有十分鐘回到飯店
- 12:03,京師科技大廈小花定了一份餃子,沒有快遞員送餐:快遞員1要到12:20才能回來,快遞員2要到12:22才能回來。等待
- 12:04,京師科技大廈特朗普定了5份餃子,沒有快遞員送餐:快遞員1要到12:20才能回來,快遞員2要到12:22才能回來。等待
- 12:05,京師科技大廈克林頓定了5份餃子,沒有快遞員送餐:快遞員1要到12:20才能回來,快遞員2要到12:22才能回來。等待
- 12:20,快遞員1接到小花訂單,去送餐
- 12:22,快遞員2接到特朗普訂單,去送餐
假如特朗普和小強(qiáng)小紅是一個(gè)公司的,特朗普心里就不爽了,都是同一時(shí)間定的,為啥他們到了,我的還沒到?
那有什么解決方案嗎 ?
- 減少每趟耗時(shí)。比如原來是快遞員騎自行車,現(xiàn)在換成摩托車,原來是摩托車,現(xiàn)在換成汽車,原來汽車,現(xiàn)在換成火箭..
- 增加快遞員的數(shù)量。 原來是2個(gè),我就再招十來個(gè) 。
- 快遞員每趟運(yùn)送的貨物爭(zhēng)取多一點(diǎn)。比如上述例子,僅僅五分鐘內(nèi),連續(xù)有京師科技大廈5單訂單,一共13份餃子,假設(shè)一個(gè)快遞員的后備箱可以裝10分餃子,那在12:05的時(shí)候,快遞員1 運(yùn)送前四個(gè)訂單,一共8份,快遞員2運(yùn)送第五個(gè)訂單,一個(gè)5分。這樣特朗普就和小強(qiáng)一起吃上飯了。
在計(jì)算機(jī)中 ,這三種解決方案分別對(duì)應(yīng)的是
- 減少每趟耗時(shí)。往返時(shí)間:從發(fā)送方發(fā)送數(shù)據(jù)開始,直到發(fā)送方收到接收方回復(fù)的確認(rèn)為止。數(shù)據(jù)傳輸?shù)姆绞胶?jiǎn)單來說是這樣的,先將數(shù)據(jù)轉(zhuǎn)換為字節(jié)流,然后通過一個(gè)裝置(調(diào)制解頻器)轉(zhuǎn)換為電磁波 ,通過網(wǎng)線或者無線電波傳輸,接收方接受到無線電波,將其轉(zhuǎn)換成字節(jié)流,然后在轉(zhuǎn)換成具體的可供程序使用的數(shù)據(jù)。是不是腦袋有點(diǎn)大,這個(gè)過程的優(yōu)化可能不是軟件工程師能做的。
- 吞吐量:?jiǎn)挝粫r(shí)間內(nèi)通過某個(gè)網(wǎng)絡(luò)的數(shù)據(jù)量。這個(gè)優(yōu)化可能不是軟件工程師能做的。
- 對(duì)傳輸?shù)臄?shù)據(jù)進(jìn)行緩沖,降低總體網(wǎng)絡(luò)延遲。
第三種思想在很多地方都采用了,比如郵件服務(wù)器使用這種思想可以更快的下載新郵件,
redis 也是采用了第三種解決方案
感興趣的同學(xué)可以參考官方文檔
https://redis.io/topics/pipelining
redis的高可用實(shí)現(xiàn)
高可用的業(yè)務(wù)需求,當(dāng)緩存服務(wù)因?yàn)槟承┰虿豢捎脮r(shí),可以切換到另外一臺(tái)機(jī)器的的緩存服務(wù)上。
可能涉及到的難點(diǎn)
- 我怎么判斷緩存服務(wù)不可用
- 當(dāng)不可用的時(shí)候,我怎么通知到所有使用緩存服務(wù)的客戶端,現(xiàn)在你訪問的這個(gè)有問題,你應(yīng)該訪問另外一個(gè)好的
- 備份的緩存服務(wù)存儲(chǔ)的數(shù)據(jù)怎么和正在使用的緩存服務(wù)存儲(chǔ)的數(shù)據(jù)保持一致
- 當(dāng)我down掉的節(jié)點(diǎn)可以重新提供使用,怎么能加入到緩存,當(dāng)做備胎使用
我嘗試著對(duì)這幾個(gè)問題提出自己的思考
1 我怎么判斷緩存服務(wù)不可用
我需要知道每一個(gè)客戶端使用緩存服務(wù)的情況,假如大家都說緩存有問題,那沒問題,就換一個(gè)好的,如果至少有一半的客戶都認(rèn)為有問題,那這個(gè)緩存就有問題。像這樣的事情肯定是誰用誰知道。哈哈
2 當(dāng)緩存不可用的時(shí)候,我怎么通知到所有使用緩存服務(wù)的客戶端,現(xiàn)在你訪問的這個(gè)有問題,你應(yīng)該訪問另外一個(gè)好的
這個(gè)就要在程序部署的時(shí)候記錄一下需要使用緩存服務(wù)的client,當(dāng)要切換緩存時(shí),我就逐一通知client就行
3 備份的緩存服務(wù)存儲(chǔ)的數(shù)據(jù)怎么和正在使用的緩存服務(wù)存儲(chǔ)的數(shù)據(jù)保持一致
這個(gè)其實(shí)很簡(jiǎn)單,把訪問緩存的請(qǐng)求copy一份給備份的緩存就行。當(dāng)然這個(gè)操作應(yīng)該誰來做。如果是我的話,我就在client里來做,我做完一個(gè)操作,就把這個(gè)操作加到一個(gè)緩存隊(duì)列中,讓后臺(tái)線程處理這個(gè)copy操作。
4 當(dāng)我down掉的節(jié)點(diǎn)可以重新提供使用,怎么能加入到緩存,當(dāng)做備胎使用
這個(gè)有兩種方式,一種是down掉的緩存直接通知我,我活了,我想當(dāng)備胎,你看怎么辦。一種是 我每隔一段的時(shí)間對(duì)down掉的節(jié)點(diǎn)看一下,如果好了,我就將他允許他當(dāng)備胎。
現(xiàn)在看看redis怎么做的
它提出了一種類型的人:哨兵,他的的職責(zé)如下
- 監(jiān)控。他負(fù)責(zé)檢查緩存和備胎的健康情況
- 通知。他監(jiān)控出現(xiàn)異常的時(shí)候,可以通知管理員,或者其他程序。
- 自動(dòng)故障恢復(fù)。當(dāng)緩存不可以時(shí),在備胎請(qǐng)求緩存的時(shí)候啟動(dòng)一個(gè)故障恢復(fù)的進(jìn)程,當(dāng)client使用緩存的時(shí)候就會(huì)被通知,你要換個(gè)備胎來用了。
- 配置提供者。client不能直接的訪問緩存,需要先問一下這哥們我應(yīng)該訪問那個(gè)緩存。
針對(duì)上面的四個(gè)問題,看看redis是怎么做的
1 我怎么判斷緩存服務(wù)不可用
他提出了一個(gè)閾值的概念 quorum ,當(dāng)且僅當(dāng)這兩個(gè)條件滿足時(shí),才能確定一個(gè)緩存不可用
- 有幾個(gè)哨兵同意當(dāng)前緩存不可用
- 為了找到一個(gè)哨兵啟動(dòng)故障恢復(fù)的進(jìn)程,哨兵們需要選舉一個(gè)領(lǐng)導(dǎo),由他來執(zhí)行這個(gè)操作。
這個(gè)就有點(diǎn)坑爹了,如果是這樣部署的緩存服務(wù)
- server001 master soilder01
- server002 replica1 soilder02
閾值設(shè)置的是1 ,當(dāng)soilder02說 master不能使了,ok滿足第一個(gè)條件,那第二個(gè)條件是找到一個(gè)哨兵來啟動(dòng)故障恢復(fù)程序,就剩一個(gè)soilder,還怎么選舉,還需要選舉嗎?
當(dāng)然這種情況可以認(rèn)為的避免,比如可以將哨兵也可以安排在client里
- server001 master soilder01
- server002 replica1 soilder02
- server003 client1 soilder03
- server004 client2 soilder04
我把閾值設(shè)置為 1,就可以解決這個(gè)問題。
2 當(dāng)緩存不可用的時(shí)候,我怎么通知到所有使用緩存服務(wù)的客戶端,現(xiàn)在你訪問的這個(gè)有問題,你應(yīng)該訪問另外一個(gè)好的
客戶端通過哨兵來間接的訪問緩存服務(wù)。這樣做的弊端就是增加了一層網(wǎng)絡(luò)消耗。本來我直接訪問緩存拿到數(shù)據(jù)了,現(xiàn)在我需要問一下哨兵,現(xiàn)在的緩存地址是啥,我再根據(jù)這個(gè)地址訪問緩存服務(wù)。
3 備份的緩存服務(wù)存儲(chǔ)的數(shù)據(jù)怎么和正在使用的緩存服務(wù)存儲(chǔ)的數(shù)據(jù)保持一致
當(dāng)備胎和緩存master連上時(shí),master負(fù)責(zé)備胎和自己保持一致性。
4 當(dāng)我down掉的節(jié)點(diǎn)可以重新提供使用,怎么能加入到緩存,當(dāng)做備胎使用
當(dāng)備胎和緩存master連上時(shí),master負(fù)責(zé)備胎和自己保持一致性。