正常業(yè)務(wù)中數(shù)據(jù)庫(kù)承受大量的讀寫請(qǐng)求,造成數(shù)據(jù)庫(kù)壓力過(guò)大從而導(dǎo)致服務(wù)器宕機(jī)等情況時(shí)有發(fā)生,即使通過(guò)數(shù)據(jù)庫(kù)的橫向拓展,通過(guò)搭建主從復(fù)制實(shí)現(xiàn)讀寫分離或者垂直拓展實(shí)現(xiàn)分庫(kù)分表的策略,雖然都在一定程度上能夠緩解單節(jié)點(diǎn)數(shù)據(jù)庫(kù)服務(wù)器的壓力,但是隨著業(yè)務(wù)量的持續(xù)增大,通過(guò)持續(xù)投錢買服務(wù)器顯然并不是合理的方案
為了緩和CPU與內(nèi)存之間的速度差異,計(jì)算機(jī)的制造商為CPU增加了緩存,從而緩存的概念深入人心,而java程序員都不陌生的redis,在使用起來(lái)既滿足我們大部分業(yè)務(wù)需求但同時(shí)也帶了部分隱患
熱點(diǎn)數(shù)據(jù)的緩存redis作為熱點(diǎn)數(shù)據(jù)的緩存實(shí)現(xiàn),客戶端請(qǐng)求先去redis查詢,redis沒(méi)有再去數(shù)據(jù)庫(kù)查詢,再根據(jù)結(jié)果完成緩存重建,這是最常規(guī)的redis緩存使用方式,但是同時(shí)也引出了諸多問(wèn)題
緩存穿透:客戶端請(qǐng)求的id在redis緩存中沒(méi)有,在數(shù)據(jù)庫(kù)中也沒(méi)有,短時(shí)間有大量的請(qǐng)求都會(huì)到達(dá)數(shù)據(jù)庫(kù),數(shù)據(jù)庫(kù)會(huì)承受巨大的壓力甚至可能導(dǎo)致服務(wù)器宕機(jī)而redis緩存也失去了意義
解決方案:
緩存擊穿:某個(gè)高并發(fā)訪問(wèn)并且緩存重建邏輯復(fù)雜的key突然過(guò)期,在緩存重建期間大量的請(qǐng)求達(dá)到數(shù)據(jù)庫(kù)導(dǎo)致服務(wù)器存在處理緩慢的情況甚至可能導(dǎo)致服務(wù)器宕機(jī)
解決方案:
通過(guò)對(duì)比互斥鎖與邏輯過(guò)期的解決方案,可以發(fā)現(xiàn)其實(shí)是在數(shù)據(jù)的一致性跟服務(wù)的可用性之間做抉擇,如選擇互斥鎖,則是犧牲了響應(yīng)時(shí)間,保證了數(shù)據(jù)的一致性;若選擇的事邏輯過(guò)期的方案,則是保證了服務(wù)的可用性,犧牲的數(shù)據(jù)的一致性,存在數(shù)據(jù)的短期不一致的問(wèn)題
計(jì)數(shù)器在當(dāng)個(gè)jvm進(jìn)程中通常使用JUC(java.util.concurrent)包下的AtomicInteger類使用的cas,避免了使用synchronized帶了的性能損耗,但是在分布式集群部署下,多進(jìn)程的jvm則不能夠滿足要求。Redis6.0之前只對(duì)外提供了單線程服務(wù),即redis的所有單條命令都是原子性的,并且通過(guò)redis的單線程,可以將并行的請(qǐng)求轉(zhuǎn)換為串行執(zhí)行,依托這個(gè)特性,可以通過(guò)redis incr命令進(jìn)行一些計(jì)數(shù)的操作,并且在多進(jìn)程jvm下可見(jiàn)
常見(jiàn)的業(yè)務(wù)使用:秒殺業(yè)務(wù),比如說(shuō)某件商品的秒殺,肯定要判斷商品的庫(kù)存,依托每次都去數(shù)據(jù)庫(kù)中查詢,如果使用悲觀鎖(synchonized,數(shù)據(jù)庫(kù)中查詢庫(kù)存,如果大于0,則執(zhí)行更新操作)效率一定很低,通過(guò)改進(jìn)樂(lè)觀鎖的sql,在update時(shí)set stockNum=stockNum-1 where stockNum>0;這個(gè)策略效率肯定比悲觀鎖要好,但是如果某件商品的庫(kù)存是1w,同時(shí)有100w用戶在搶購(gòu),豈不是如此大的并發(fā)量同時(shí)落到數(shù)據(jù)庫(kù)中了嗎? 所以我們?cè)趽Q種策略,在秒殺活動(dòng)開(kāi)始前,提前把商品的庫(kù)存放入redis緩存中,通過(guò)incr -1的命令完成庫(kù)存的自減,要知道官方提供的數(shù)據(jù),redis每秒讀11w次,寫8.1w次,在庫(kù)存為0時(shí),對(duì)其他請(qǐng)求可以立刻返回失敗,其次結(jié)合lua腳本,可以同時(shí)可以判斷用戶是否已下單(滿足一人一單的需求)
緩存過(guò)期時(shí)間使用redis命令時(shí)set nx px,可以設(shè)置緩存過(guò)期時(shí)間,通過(guò)這種緩存自動(dòng)過(guò)期的策略,可以實(shí)現(xiàn)的業(yè)務(wù)場(chǎng)景有:優(yōu)惠卷自動(dòng)過(guò)期、訂單超時(shí)未支付過(guò)期、手機(jī)驗(yàn)證碼失效的場(chǎng)景
但是如果緩存的數(shù)據(jù)沒(méi)有設(shè)置過(guò)期時(shí)間,當(dāng)達(dá)到內(nèi)存閾值瓶頸時(shí),要依托內(nèi)存淘汰策略去刪除key
分布式鎖單節(jié)點(diǎn)的redis,獨(dú)立于多節(jié)點(diǎn)部署的jvm進(jìn)程之外,面對(duì)synchronized和ReentrantLock只在同一進(jìn)程內(nèi)有效的鎖而分布式集群下部署的jvm則失去了對(duì)共享資源爭(zhēng)奪的互斥性,多節(jié)點(diǎn)都能同時(shí)獲取鎖成功,此時(shí)需要存在多進(jìn)程jvm都可見(jiàn)的鎖,也就是分布式鎖,分布式鎖的實(shí)現(xiàn)方案業(yè)內(nèi)常見(jiàn)的redis和zookeeper;
redis分布式鎖常用的方式,依托string結(jié)構(gòu),set key value,nx:not exist 不存在則創(chuàng)建,px:設(shè)置鎖的過(guò)期時(shí)間避免死鎖
存在的問(wèn)題:
死鎖:如果某一線程加鎖后,沒(méi)來(lái)得及刪除鎖,服務(wù)器就宕機(jī)了,那其他jvm進(jìn)程中的線程則再也獲取不到鎖,也就是set nx key value失敗,則要設(shè)置鎖的過(guò)期時(shí)間 ,避免死鎖
鎖誤刪:
鎖不可重入:jvm實(shí)現(xiàn)的synchronized鎖和java api實(shí)現(xiàn)的ReentrantLock底層都維護(hù)了一個(gè)整型字段,用于鎖重入的實(shí)現(xiàn),而redis獲取鎖并沒(méi)有鎖重入的實(shí)現(xiàn),導(dǎo)致同一線程多次獲取鎖的業(yè)務(wù)無(wú)需求法實(shí)現(xiàn)
獲取鎖失敗不可重試:使用set nx px 單次獲取鎖失敗立即返回失敗,不會(huì)再有重試的機(jī)會(huì),造成不能靈活的控制業(yè)務(wù),充分利用cpu
主從復(fù)制引起的鎖重復(fù)添加成功的情況:為了保存redis的高可用性,搭建了主從集群,實(shí)現(xiàn)主從復(fù)制,現(xiàn)有ab線程,假設(shè)a現(xiàn)在在master 1 加鎖成功,在master1 跟slave1 數(shù)據(jù)同步前,master出現(xiàn)腦裂的情況,master突然網(wǎng)絡(luò)異常,但是仍舊是正常運(yùn)行的,sentinel集群判定該master為客觀下線后,開(kāi)始自動(dòng)故障轉(zhuǎn)移,選舉slave1為新的主節(jié)點(diǎn)master2,此時(shí)b線程在新的主節(jié)點(diǎn)又加鎖成功,此時(shí)master1和master2都存在同一個(gè)key
針對(duì)上面這些問(wèn)題,可以用redisson客戶端解決這些問(wèn)題
redisson 利用hash結(jié)構(gòu)記錄線程id和重入次數(shù)
獲取鎖失敗的線程,通過(guò)訂閱釋放鎖的信號(hào),靈活控制鎖的重試等待,cpu利用率比較高,不會(huì)無(wú)限等待
超時(shí)續(xù)約:利用watchDog,每隔一段時(shí)間(releaseTime /3)重置超時(shí)時(shí)間,但是只有tryLock方法中參數(shù)leaseTime釋放鎖時(shí)間為-1時(shí)才能夠啟用超時(shí)續(xù)約
利用multiLock:通過(guò)搭建多個(gè)獨(dú)立的Redis節(jié)點(diǎn),必須在所有節(jié)點(diǎn)獲取到鎖才算真正的獲取鎖成功,避免主從復(fù)制帶來(lái)的重復(fù)加鎖成功的情況
如果部署redis服務(wù)器發(fā)生故障,如果只使用RDB的持久化策略,可能會(huì)丟失最后一次RDB后的數(shù)據(jù),并且重啟服務(wù)器的這段期間,服務(wù)都是不可用的狀態(tài),況且不清楚服務(wù)器宕機(jī)的具體原因,單節(jié)點(diǎn)下的redis并沒(méi)有故障自動(dòng)恢復(fù)的能力,導(dǎo)致長(zhǎng)時(shí)間的服務(wù)不可用,此時(shí)就需要備份數(shù)據(jù),通過(guò)冗余數(shù)據(jù)來(lái)保證服務(wù)的可用性;
2、單節(jié)點(diǎn)redis自身資源有限,無(wú)法承載更多資源分配redis的緩存是基于內(nèi)存實(shí)現(xiàn)的,單節(jié)點(diǎn)內(nèi)存是有限的,如果內(nèi)存占滿了會(huì)啟用淘汰策略,而這個(gè)期間客戶端的連接請(qǐng)求都會(huì)超時(shí),造成服務(wù)的短暫不可用,刪除一部分緩存,刪除緩存的操作并不是我們通過(guò)業(yè)務(wù)主觀的操作,可能導(dǎo)致部分查詢緩存的業(yè)務(wù)失效,從而使大量請(qǐng)求達(dá)到數(shù)據(jù)庫(kù);隨著業(yè)務(wù)的發(fā)展,數(shù)據(jù)量的增大,當(dāng)存在單臺(tái)服務(wù)器的存儲(chǔ)上限和算力上限影響業(yè)務(wù)的正常使用,此時(shí)就需要通過(guò)一些策略去橫向拓展存儲(chǔ)和算力
3、并發(fā)訪問(wèn),給服務(wù)器主機(jī)帶來(lái)壓力,性能瓶頸客戶端的每一個(gè)tcp連接都會(huì)消耗redis服務(wù)器的資源,雖然redis官方號(hào)稱每秒讀11w次,寫8.1w次,但是這僅僅是統(tǒng)計(jì)了理想狀態(tài)下的讀寫請(qǐng)求,并沒(méi)有其他外力因素,比如說(shuō)此時(shí)主進(jìn)程需要fork出子進(jìn)程完成RDB,或者執(zhí)行rebgwriteaof,對(duì)aof文件進(jìn)行重寫等影響redis服務(wù)器性能的操作,此時(shí)需要通過(guò)部署主從節(jié)點(diǎn),對(duì)讀寫請(qǐng)求分開(kāi)處理,從而提高redis服務(wù)器集群的響應(yīng)能力,提高整體算力,在數(shù)據(jù)量非常大的情況下,還需要通過(guò)搭建分片集群,提高redis服務(wù)集群的存儲(chǔ)上限
2、主從復(fù)制為了提高redis的并發(fā)量,通過(guò)搭建redis的主從集群,利用讀寫分離來(lái)提高并發(fā)量;通過(guò)redis來(lái)緩存數(shù)據(jù),客戶端對(duì)redis的操作肯定是讀多寫少的情況,讀操作主從庫(kù)都可以接收,寫操作,首先到主庫(kù)上執(zhí)行,在通過(guò)主從復(fù)制同步給從庫(kù)
主從復(fù)制的作用每臺(tái)redis服務(wù)節(jié)點(diǎn)默認(rèn)的角色都是master,可以登錄redis服務(wù)端,通過(guò)info replication命令查看當(dāng)前服務(wù)節(jié)點(diǎn)的主從關(guān)系,也可以通過(guò)slaveof ip port 主master 設(shè)置從節(jié)點(diǎn)綁定的主節(jié)點(diǎn)
主從復(fù)制是通過(guò)RBD實(shí)現(xiàn)的,分為全量復(fù)制和增量復(fù)制
全量復(fù)制:
增量復(fù)制:
從節(jié)點(diǎn)向master發(fā)送psync命令,請(qǐng)求的runId是第一次全量同步時(shí)接收master的runId,以及當(dāng)前同步的offset,master接收到psync命令后,確認(rèn)runId是自己,就從repl_backlog_buffer(環(huán)形緩沖區(qū)),該緩沖區(qū)中存放了master 的offset,以及諸多slave 的salve_repl_offset,用于保存master與slave之間的數(shù)據(jù)的差異,master就將這部分差異的數(shù)據(jù)repl_backlog發(fā)送給slave
什么時(shí)候進(jìn)行全量同步1、搭建主從集群后,Slave節(jié)點(diǎn)宕機(jī)恢復(fù)后可以找master節(jié)點(diǎn)同步數(shù)據(jù),那master節(jié)點(diǎn)宕機(jī)怎么辦?
2、Master宕機(jī)期間,重啟數(shù)據(jù)恢復(fù)期間,都不能接收客戶端的寫請(qǐng)求該怎么辦?
3、腦裂以及redis的數(shù)據(jù)丟失
異步復(fù)制導(dǎo)致的數(shù)據(jù)丟失
? 因?yàn)橹鲝膹?fù)制是通過(guò)bgsave進(jìn)行的復(fù)制是異步的,所以可能有部分?jǐn)?shù)據(jù)還沒(méi)復(fù)制到slave,master就宕機(jī)了,此時(shí)這些部分?jǐn)?shù)據(jù)就丟失了
腦裂導(dǎo)致的數(shù)據(jù)丟失
腦裂,主從集群中,master如果網(wǎng)絡(luò)異常,被哨兵集群判定為客觀下線,但是實(shí)際上master還運(yùn)行著,開(kāi)啟選舉,將最接近master的slave節(jié)點(diǎn)通過(guò)slave no one 將slave切換成主節(jié)點(diǎn),其他slave執(zhí)行slaveof ip port完成主從切換,此時(shí)整個(gè)集群就會(huì)有兩個(gè)master;
此時(shí)雖然某個(gè)slave被切換成了master,但是可能client還沒(méi)來(lái)得及切換到新的master,還繼續(xù)寫向舊master的數(shù)據(jù)可能也丟失了,因此舊master再次恢復(fù)的時(shí)候,會(huì)被作為一個(gè)slave掛到新的master上去,自己的數(shù)據(jù)會(huì)清空,重新從新的master復(fù)制數(shù)據(jù)
使用redis主從集群架構(gòu)后,實(shí)現(xiàn)讀寫分離,但是不能夠保證主節(jié)點(diǎn)宕機(jī)后依舊能夠響應(yīng)客戶端請(qǐng)求,當(dāng)然我們可以通過(guò)人工的方式手動(dòng)執(zhí)行 slaveof no one去完成slave切換為master,然后通過(guò)slaveof ip port命令去告知其他從節(jié)點(diǎn)更換了新的master,但是我們更希望的是提供故障的自動(dòng)解決,如果由人工完成,則需要增加人力成本,且容易產(chǎn)生人工錯(cuò)誤,還會(huì)造成一段時(shí)間的程序不可用;當(dāng)master節(jié)點(diǎn)異常時(shí)自動(dòng)從多個(gè)slave中選舉出最接近master節(jié)點(diǎn)的新master,redis為我們提供了哨兵集群,保證Redis的高可用,使得系統(tǒng)更加健壯
哨兵的作用會(huì)從slave集群中選擇與master數(shù)據(jù)最接近的slave作為新的master節(jié)點(diǎn),一旦發(fā)現(xiàn)master故障,sentinel需要在salve中選擇一個(gè)作為新的master,選擇依據(jù)是這樣的:
當(dāng)選出一個(gè)新的master后,該如何實(shí)現(xiàn)切換呢?
流程如下:
主從模式或哨兵模式每個(gè)節(jié)點(diǎn)存儲(chǔ)的數(shù)據(jù)都是全量的數(shù)據(jù),數(shù)據(jù)量過(guò)大時(shí),就需要對(duì)存儲(chǔ)的數(shù)據(jù)進(jìn)行分片后存儲(chǔ)到多個(gè)redis實(shí)例上。此時(shí)就要用到Redis Sharding技術(shù)。
4、分片集群 為什么要引入分片集群 哨兵集群留下的問(wèn)題Redis的master宕機(jī)后,在主從切換的過(guò)程中,Redis開(kāi)啟了保護(hù)機(jī)制,禁止一切的寫操作,直到選舉出新的Redis主節(jié)點(diǎn)
海量數(shù)據(jù)的問(wèn)題為了提供主從同步的性能,我們通過(guò)不會(huì)將的redis的master內(nèi)存設(shè)置得太高,如果內(nèi)存設(shè)置得太高,在一定頻率下進(jìn)行RDB持久化或者多從節(jié)點(diǎn)進(jìn)行全量同步時(shí)會(huì)有很多進(jìn)程爭(zhēng)奪的磁盤帶寬,并且redis的master主節(jié)點(diǎn)內(nèi)存過(guò)大還會(huì)導(dǎo)致fork出子進(jìn)程時(shí)阻塞的時(shí)間過(guò)長(zhǎng),此時(shí)無(wú)法接受客戶端的寫請(qǐng)求。
但是如果降低master節(jié)點(diǎn)的內(nèi)存上限,此時(shí)還有海量數(shù)據(jù)該如何存儲(chǔ)?
高并發(fā)寫的問(wèn)題我們通過(guò)搭建主從集群、哨兵集群來(lái)保證服務(wù)的高可用,并且為了適應(yīng)讀多寫少的情況,通過(guò)讀寫分離分擔(dān)master服務(wù)器壓力,來(lái)解決高并發(fā)讀的問(wèn)題,并且從節(jié)點(diǎn)故障恢復(fù)后可以通過(guò)主從復(fù)制中的全量同步或者增量同步來(lái)保證數(shù)據(jù)的一致性,主節(jié)點(diǎn)宕機(jī)后通過(guò)哨兵集群完成服務(wù)的自動(dòng)故障轉(zhuǎn)移,保證讀的可靠性,但是高并發(fā)寫的問(wèn)題依舊沒(méi)有解決
分片集群其他中間件也是通過(guò)主從復(fù)制來(lái)解決高并發(fā)讀的問(wèn)題,通過(guò)多主多從來(lái)解決高并發(fā)寫的問(wèn)題,在redis中提供了分片集群也是以多主多從的形式來(lái)解決高并發(fā)寫的問(wèn)題
使用分片集群與之前的主從集群、哨兵集群的區(qū)別:
主從集群:數(shù)據(jù)量不大,業(yè)務(wù)中只需要滿足讀寫分離,并且對(duì)服務(wù)的可用性不高,允許短暫的服務(wù)不可用帶來(lái)的風(fēng)險(xiǎn),并且需要手動(dòng)完成主從切換,則單使用主從集群完全夠用
哨兵集群:數(shù)據(jù)量不大,并且對(duì)服務(wù)的可用性要求比較高,可以使用主從集群搭配哨兵集群,完成故障的自動(dòng)轉(zhuǎn)移,主節(jié)點(diǎn)不可用時(shí)自動(dòng)完成主從切換
分片集群:主要針對(duì)海量數(shù)據(jù)、高并發(fā)、高可用的場(chǎng)景
以上便是Redis在多種業(yè)務(wù)場(chǎng)景下的使用方案,如有誤解,請(qǐng)?jiān)谠u(píng)論區(qū)指出,謝謝
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級(jí)服務(wù)器適合批量采購(gòu),新人活動(dòng)首月15元起,快前往官網(wǎng)查看詳情吧