日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > 数据库 >内容正文

数据库

多线程、Redis、rabbitmq面试题

發(fā)布時(shí)間:2023/12/20 数据库 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 多线程、Redis、rabbitmq面试题 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

設(shè)計(jì)模式:
1.簡(jiǎn)單工廠模式:定義一個(gè)實(shí)例化對(duì)象的類,根據(jù)輸入?yún)?shù)的不同,來(lái)實(shí)例化對(duì)象
2.單例模式:確保一個(gè)類只有一個(gè)實(shí)例,提供一個(gè)公共的方法來(lái)獲取對(duì)象的實(shí)例
3.原型模式:復(fù)制一個(gè)現(xiàn)有的實(shí)例創(chuàng)建一個(gè)新的對(duì)象
4.外觀模式:隱藏系統(tǒng)的復(fù)雜性,并向客戶端提供了一個(gè)可以訪問系統(tǒng)的接口。
5.命令模式:將一個(gè)請(qǐng)求封裝為一個(gè)對(duì)象,使發(fā)出請(qǐng)求的責(zé)任和執(zhí)行請(qǐng)求的責(zé)任分割開。這樣兩者之間通過(guò)命令對(duì)象進(jìn)行溝通,這樣方便將命令對(duì)象進(jìn)行儲(chǔ)存、傳遞、調(diào)用、增加與管理。

多線程
1.緩存一致性,解決緩存不一致的辦法;
當(dāng)程序在運(yùn)行過(guò)程中,會(huì)將運(yùn)算需要的數(shù)據(jù)從主存復(fù)制一份到 CPU 的高速
緩存當(dāng)中,那么 CPU 進(jìn)行計(jì)算時(shí)就可以直接從它的高速緩存讀取數(shù)據(jù)和向其中
寫入數(shù)據(jù),當(dāng)運(yùn)算結(jié)束之后,再將高速緩存中的數(shù)據(jù)刷新到主存當(dāng)中。

這種在單線程中是沒有什么問題的,但是在多線程中就會(huì)遇到緩存一致性的問題,因?yàn)槊總€(gè)CPU都有一個(gè)自己的高度緩存,比如主存中有一個(gè)x值,然后每個(gè)高速緩存中的值都是x,如果這時(shí)y個(gè)線程要對(duì)x進(jìn)行+1,
那么這時(shí)運(yùn)行完是2,并不是結(jié)果x+y。

解決緩存不一致的方法就是緩存一致性協(xié)議,緩存一致性協(xié)議要求當(dāng)線程對(duì)高度緩存中的共享變量進(jìn)行改變時(shí),其他cpu緩存中也有這個(gè)副本,那么就會(huì)把其他CPU中的該變量置為無(wú)效狀態(tài),因此其他CPU需要讀取這個(gè)變量的時(shí)候會(huì)發(fā)現(xiàn)該變量是無(wú)效的,那么它就會(huì)從主存重新讀取。

2.volatile關(guān)鍵字的作用是什么;
使用 volatile 關(guān)鍵字會(huì)強(qiáng)制將修改的值立即寫入主存;如果一個(gè)共享變量被volatile修飾了,那么對(duì)不同的線程來(lái)說(shuō),這個(gè)新值是立即可見的。
同時(shí)volatile 關(guān)鍵字禁止指令重新排序。

3.內(nèi)存的內(nèi)存模型;
每個(gè)線程都有自己的工作內(nèi)存(類似于前面的高速緩存)。線程對(duì)變量的所有操作都必須在工作內(nèi)存中進(jìn)行,而不能直接對(duì)主存進(jìn)行操作,并且每個(gè)線程不能訪問其他線程的工作內(nèi)存。
4.Thread和task的區(qū)別;
Task和Thread都能創(chuàng)建用多線程的方式執(zhí)行代碼,Task不止能創(chuàng)建新線程,還能使用線程池(默認(rèn)),Thread是創(chuàng)建新的線程。
5.死鎖、活鎖、饑餓;
死鎖,是多線程中最差的一種情況,多個(gè)線程相互占用對(duì)方的資源的鎖,而又相互等對(duì)方釋放鎖,此時(shí)若無(wú)外力干預(yù),這些線程則一直處理阻塞的假死狀態(tài),形成死鎖。
活鎖,與死鎖相反,死鎖是線程都拿不到資源,且都占用著對(duì)方的資源,活鎖是拿到資源卻又相互釋放不執(zhí)行。當(dāng)多線程中出現(xiàn)了相互謙讓,都主動(dòng)將資源釋放給別的線程使用,缺點(diǎn):這個(gè)資源在多個(gè)線程之間跳動(dòng)卻有得不到執(zhí)行,這就是活鎖。
多線程執(zhí)行中存在線程優(yōu)先級(jí),優(yōu)先級(jí)高的線程能夠插隊(duì)并優(yōu)先執(zhí)行,如此若高優(yōu)先級(jí)的線程一直搶占低優(yōu)先級(jí)線程的資源,導(dǎo)致低優(yōu)先級(jí)線程無(wú)法得到執(zhí)行,這就是饑餓。當(dāng)然還有一種饑餓的情況,某一個(gè)線程一直占著一個(gè)資源不放而導(dǎo)致其他線程得不到執(zhí)行,與死鎖不同的是饑餓在以后一段時(shí)間內(nèi)還是能夠得到執(zhí)行的,比如那個(gè)占用資源的線程執(zhí)行結(jié)束并釋放了資源。
6.線程和進(jìn)程
線程是程序運(yùn)行的基本單位,進(jìn)程是資源分配的最小單位,同一個(gè)進(jìn)程中的多個(gè)線程共享進(jìn)程的堆和方法區(qū)資源,每個(gè)線程擁有自己的程序計(jì)數(shù)器、虛擬棧,棧內(nèi)存,

7.什么是線程池,線程池的工作原理,使用線程池有什么好處;
一個(gè)線程池管理了一組工作線程,默認(rèn)情況下,在創(chuàng)建了線程池后,線程池中的線程數(shù)為 0。當(dāng)任務(wù)提交給
線程池之后的處理策略如下:
1)如果此時(shí)線程池中的數(shù)量小于 corePoolSize(核心池的大小),即
使線程池中的線程都處于空閑狀態(tài),也要?jiǎng)?chuàng)建新的線程來(lái)處理被添加的任務(wù)(也
就是每來(lái)一個(gè)任務(wù),就要?jiǎng)?chuàng)建一個(gè)線程來(lái)執(zhí)行任務(wù))。
2).如果此時(shí)線程池中的數(shù)量大于等于 corePoolSize,但是緩沖隊(duì)列
workQueue 未滿,那么任務(wù)被放入緩沖隊(duì)列,則該任務(wù)會(huì)等待空閑線程將其
取出去執(zhí)行。
3) 如 果 此 時(shí) 線 程 池 中 的 數(shù) 量 大 于 等 于 corePoolSize , 緩 沖 隊(duì) 列
workQueue 滿,并且線程池中的數(shù)量小于 maximumPoolSize(線程池
最大線程數(shù)),建新的線程來(lái)處理被添加的任務(wù)。
4) 如果 此時(shí) 線程 池中 的數(shù)量 大 于 等 于 corePoolSize, 緩 沖 隊(duì)列
workQueue 滿,并且線程池中的數(shù)量等于 maximumPoolSize,那么通
過(guò) RejectedExecutionHandler 所指定的策略(任務(wù)拒絕策略)來(lái)處理此任務(wù)。
也就是處理任務(wù)的優(yōu)先級(jí)為:核心線程 corePoolSize、任務(wù)隊(duì)列
workQueue、最大線程 maximumPoolSize,如果三者都滿了,使用
handler

8.網(wǎng)站高并發(fā)怎么解決;
.HTML 頁(yè)面靜態(tài)化
.圖片服務(wù)器與應(yīng)用服務(wù)器分離
.緩存(用戶緩存,cdn緩存,反向代理)
.負(fù)載均衡(f服務(wù)器集群)
.消息隊(duì)列

網(wǎng)絡(luò)協(xié)議
1.三次握手
1.第一次握手:建立連接。客戶端發(fā)送連接請(qǐng)求報(bào)文段,將SYN位置為1,Sequence Number為x;然后,客戶端進(jìn)入SYN_SEND狀態(tài),等待服務(wù)器的確認(rèn);

2.第二次握手:服務(wù)器收到SYN報(bào)文段。服務(wù)器收到客戶端的SYN報(bào)文段,需要對(duì)這個(gè)SYN報(bào)文段進(jìn)行確認(rèn),設(shè)置Acknowledgment Number為x+1;同時(shí),自己還要發(fā)送SYN請(qǐng)求信息,將SYN位置為1,Sequence Number為y;服務(wù)器端將上述所有信息放到一個(gè)報(bào)文段(即SYN+ACK報(bào)文段)中,一并發(fā)送給客戶端,此時(shí)服務(wù)器進(jìn)入SYN_RECV狀態(tài);

3.第三次握手:客戶端收到服務(wù)器的SYN+ACK報(bào)文段。然后將Acknowledgment Number設(shè)置為y+1,向服務(wù)器發(fā)送ACK報(bào)文段,這個(gè)報(bào)文段發(fā)送完畢以后,客戶端和服務(wù)器端都進(jìn)入ESTABLISHED狀態(tài),完成TCP三次握手。

2.四次分手
1.第一次分手:主機(jī)1(可以使客戶端,也可以是服務(wù)器端),設(shè)置Sequence Number和Acknowledgment Number,向主機(jī)2發(fā)送一個(gè)FIN報(bào)文段;此時(shí),主機(jī)1進(jìn)入FIN_WAIT_1狀態(tài);這表示主機(jī)1沒有數(shù)據(jù)要發(fā)送給主機(jī)2了;

2.第二次分手:主機(jī)2收到了主機(jī)1發(fā)送的FIN報(bào)文段,向主機(jī)1回一個(gè)ACK報(bào)文段,Acknowledgment Number為Sequence Number加1;主機(jī)1進(jìn)入FIN_WAIT_2狀態(tài);主機(jī)2告訴主機(jī)1,我知道了;

3.第三次分手:主機(jī)2向主機(jī)1發(fā)送FIN報(bào)文段,請(qǐng)求關(guān)閉連接,同時(shí)主機(jī)2進(jìn)入CLOSE_WAIT狀態(tài);

4.第四次分手:主機(jī)1收到主機(jī)2發(fā)送的FIN報(bào)文段,向主機(jī)2發(fā)送ACK報(bào)文段,然后主機(jī)1進(jìn)入TIME_WAIT狀態(tài);主機(jī)2收到主機(jī)1的ACK報(bào)文段以后,就關(guān)閉連接;此時(shí),主機(jī)1等待2MSL后依然沒有收到回復(fù),則證明Server端已正常關(guān)閉,那好,主機(jī)1也可以關(guān)閉連接了。

http 和 socket 的區(qū)別,兩個(gè)協(xié)議哪個(gè)更高效一點(diǎn)。
創(chuàng)建 Socket 連接時(shí),可以指定使用的傳輸層協(xié)議,Socket 可以支持不同的傳輸層協(xié)
議(TCP 或 UDP),當(dāng)使用 TCP 協(xié)議進(jìn)行連接時(shí),該 Socket 連接就是一個(gè) TCP 連接。
Socket 連接一旦建立,通信雙方即可開始相互發(fā)送數(shù)據(jù)內(nèi)容,直到雙方連接斷開。注意,
同 HTTP 不同的是 http 只能基于 tcp,socket 不僅能走 tcp,而且還能走 udp,這個(gè)是 socket
的第一個(gè)特點(diǎn)

Redis
Redis相比memcached有哪些優(yōu)勢(shì)?
(1) memcached所有的值均是簡(jiǎn)單的字符串,redis作為其替代者,支持更為豐富的數(shù)據(jù)類型
(2) redis的速度比memcached快很多
(3) redis可以持久化其數(shù)據(jù)

Redis集群方案應(yīng)該怎么做?都有哪些方案?
1.codis。
目前用的最多的集群方案,基本和twemproxy一致的效果,但它支持在 節(jié)點(diǎn)數(shù)量改變情況下,舊節(jié)點(diǎn)
數(shù)據(jù)可恢復(fù)到新hash節(jié)點(diǎn)。
2.redis cluster3.0自帶的集群,特點(diǎn)在于他的分布式算法不是一致性hash,而是hash槽的概念,以及
自身支持節(jié)點(diǎn)設(shè)置從節(jié)點(diǎn)。具體看官方文檔介紹。
3.在業(yè)務(wù)代碼層實(shí)現(xiàn),起幾個(gè)毫無(wú)關(guān)聯(lián)的redis實(shí)例,在代碼層,對(duì)key 進(jìn)行hash計(jì)算,然后去對(duì)應(yīng)的
redis實(shí)例操作數(shù)據(jù)。 這種方式對(duì)hash層代碼要求比較高,考慮部分包括,節(jié)點(diǎn)失效后的替代算法方
案,數(shù)據(jù)震蕩后的自動(dòng)腳本恢復(fù),實(shí)例的監(jiān)控,等等。

Redis集群方案什么情況下會(huì)導(dǎo)致整個(gè)集群不可用?
有A,B,C三個(gè)節(jié)點(diǎn)的集群,在沒有復(fù)制模型的情況下,如果節(jié)點(diǎn)B失敗了,那么整個(gè)集群就會(huì)以為缺少
5501-11000這個(gè)范圍的槽而不可用。

Redis有哪些適合的場(chǎng)景?
(1)會(huì)話緩存(Session Cache)
最常用的一種使用Redis的情景是會(huì)話緩存(session cache)。用Redis緩存會(huì)話比其他存儲(chǔ)(如
Memcached)的優(yōu)勢(shì)在于:Redis提供持久化。當(dāng)維護(hù)一個(gè)不是嚴(yán)格要求一致性的緩存時(shí),如果用戶的
購(gòu)物車信息全部丟失,大部分人都會(huì)不高興的,現(xiàn)在,他們還會(huì)這樣嗎?
幸運(yùn)的是,隨著 Redis 這些年的改進(jìn),很容易找到怎么恰當(dāng)?shù)氖褂肦edis來(lái)緩存會(huì)話的文檔。甚至廣為
人知的商業(yè)平臺(tái)Magento也提供Redis的插件。
(2)全頁(yè)緩存(FPC)
除基本的會(huì)話token之外,Redis還提供很簡(jiǎn)便的FPC平臺(tái)。回到一致性問題,即使重啟了Redis實(shí)例,
因?yàn)橛写疟P的持久化,用戶也不會(huì)看到頁(yè)面加載速度的下降,這是一個(gè)極大改進(jìn),類似PHP本地FPC。
再次以Magento為例,Magento提供一個(gè)插件來(lái)使用Redis作為全頁(yè)緩存后端。
此外,對(duì)WordPress的用戶來(lái)說(shuō),Pantheon有一個(gè)非常好的插件 wp-redis,這個(gè)插件能幫助你以最快
速度加載你曾瀏覽過(guò)的頁(yè)面。
(3)隊(duì)列
Reids在內(nèi)存存儲(chǔ)引擎領(lǐng)域的一大優(yōu)點(diǎn)是提供 list 和 set 操作,這使得Redis能作為一個(gè)很好的消息隊(duì)列
平臺(tái)來(lái)使用。Redis作為隊(duì)列使用的操作,就類似于本地程序語(yǔ)言(如Python)對(duì) list 的 push/pop 操
作。
如果你快速的在Google中搜索“Redis queues”,你馬上就能找到大量的開源項(xiàng)目,這些項(xiàng)目的目的就是
利用Redis創(chuàng)建非常好的后端工具,以滿足各種隊(duì)列需求。例如,Celery有一個(gè)后臺(tái)就是使用Redis作為
broker,你可以從這里去查看。
(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è)用戶–我們稱之為“user_scores”,我們只需要像下
面一樣執(zhí)行即可:
當(dāng)然,這是假定你是根據(jù)你用戶的分?jǐn)?shù)做遞增的排序。如果你想返回用戶及用戶的分?jǐn)?shù),你需要這樣執(zhí)
行:
ZRANGE user_scores 0 10 WITHSCORES
Agora Games就是一個(gè)很好的例子,用Ruby實(shí)現(xiàn)的,它的排行榜就是使用Redis來(lái)存儲(chǔ)數(shù)據(jù)的,你可以
在這里看到。
(5)發(fā)布/訂閱
最后(但肯定不是最不重要的)是Redis的發(fā)布/訂閱功能。發(fā)布/訂閱的使用場(chǎng)景確實(shí)非常多。我已看見
人們?cè)谏缃痪W(wǎng)絡(luò)連接中使用,還可作為基于發(fā)布/訂閱的腳本觸發(fā)器,甚至用Redis的發(fā)布/訂閱功能來(lái)建
立聊天系統(tǒng)!

簡(jiǎn)單說(shuō)說(shuō)緩存雪崩及解決方法
緩存雪崩我們可以簡(jiǎn)單的理解為:由于原有緩存失效,新緩存未到期間
(例如:我們?cè)O(shè)置緩存時(shí)采用了相同的過(guò)期時(shí)間,在同一時(shí)刻出現(xiàn)大面積的緩存過(guò)期),所有原本應(yīng)該訪
問緩存的請(qǐng)求都去查詢數(shù)據(jù)庫(kù)了,而對(duì)數(shù)據(jù)庫(kù)CPU和內(nèi)存造成巨大壓力,嚴(yán)重的會(huì)造成數(shù)據(jù)庫(kù)宕機(jī)。從
而形成一系列連鎖反應(yīng),造成整個(gè)系統(tǒng)崩潰。)
解決辦法:
大多數(shù)系統(tǒng)設(shè)計(jì)者考慮用加鎖( 最多的解決方案)或者隊(duì)列的方式保證來(lái)保證不會(huì)有大量的線程對(duì)數(shù)據(jù)
庫(kù)一次性進(jìn)行讀寫,從而避免失效時(shí)大量的并發(fā)請(qǐng)求落到底層存儲(chǔ)系統(tǒng)上。還有一個(gè)簡(jiǎn)單方案就時(shí)講緩
存失效時(shí)間分散開。

緩存穿透怎么導(dǎo)致的?
在高并發(fā)下查詢key不存在的數(shù)據(jù),會(huì)穿過(guò)緩去存查詢數(shù)據(jù)庫(kù)。導(dǎo)致數(shù)據(jù)庫(kù)壓力過(guò)大而宕機(jī)。
解決方法:

  • 對(duì)查詢結(jié)果為空的情況也進(jìn)行緩存,緩存時(shí)間(ttl)設(shè)置短一點(diǎn),或者該key對(duì)應(yīng)的數(shù)據(jù)insert了
    之后清理緩存。
    缺點(diǎn):緩存太多空值占用了更多的空間
  • 使用布隆過(guò)濾器。在緩存之前在加一層布隆過(guò)濾器,在查詢的時(shí)候先去布隆過(guò)濾器查詢 key 是否
    存在,如果不存在就直接返回,存在再查緩存和DB。
    布隆過(guò)濾器原理: 當(dāng)一個(gè)元素被加入集合時(shí),將這個(gè)元素通過(guò)n次Hash函數(shù)結(jié)果映射成一個(gè)數(shù)組中的n
    個(gè)點(diǎn),把它們置為1。檢索時(shí),我們只要看看這些點(diǎn)是不是都是1就(大約)知道集合中有沒有它了:如
    果這些點(diǎn)有任何一個(gè)0,則被檢元素一定不在;如果都是1,則被檢元素很可能在。總之布隆過(guò)濾器是一
    個(gè)很大二進(jìn)制的位數(shù)組,數(shù)組里面只存0和1。
  • Redis的內(nèi)存淘汰策略有哪些?
    Redis的內(nèi)存淘汰策略是指在Redis的用于緩存的內(nèi)存不足時(shí),怎么處理需要新寫入且需要申請(qǐng)額外空間
    的數(shù)據(jù)。
    1、全局的鍵空間選擇性移除
    noeviction:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),新寫入操作會(huì)報(bào)錯(cuò)。
    allkeys-lru:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),在鍵空間中,移除最近最少使用的key。(這個(gè)是
    最常用的)
    allkeys-random:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),在鍵空間中,隨機(jī)移除某個(gè)key。
    2、設(shè)置過(guò)期時(shí)間的鍵空間選擇性移除
    volatile-lru:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),在設(shè)置了過(guò)期時(shí)間的鍵空間中,移除最近最少使用
    的key。
    volatile-random:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),在設(shè)置了過(guò)期時(shí)間的鍵空間中,隨機(jī)移除某
    個(gè)key。
    volatile-ttl:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),在設(shè)置了過(guò)期時(shí)間的鍵空間中,有更早過(guò)期時(shí)間的
    key優(yōu)先移除。

    什么時(shí)候需要緩存降級(jí)?
    當(dāng)訪問量劇增、服務(wù)出現(xiàn)問題(如響應(yīng)時(shí)間慢或不響應(yīng))或非核心服務(wù)影響到核心流程的性能時(shí),仍然
    需要保證服務(wù)還是可用的,即使是有損服務(wù)。系統(tǒng)可以根據(jù)一些關(guān)鍵數(shù)據(jù)進(jìn)行自動(dòng)降級(jí),也可以配置開
    關(guān)實(shí)現(xiàn)人工降級(jí)。
    緩存降級(jí)的最終目的是保證核心服務(wù)可用,即使是有損的。而且有些服務(wù)是無(wú)法降級(jí)的(如加入購(gòu)物
    車、結(jié)算)。
    在進(jìn)行降級(jí)之前要對(duì)系統(tǒng)進(jìn)行梳理,看看系統(tǒng)是不是可以丟卒保帥;從而梳理出哪些必須誓死保護(hù),哪
    些可降級(jí);比如可以參考日志級(jí)別設(shè)置預(yù)案:

  • 一般:比如有些服務(wù)偶爾因?yàn)榫W(wǎng)絡(luò)抖動(dòng)或者服務(wù)正在上線而超時(shí),可以自動(dòng)降級(jí);
  • 警告:有些服務(wù)在一段時(shí)間內(nèi)成功率有波動(dòng)(如在95~100%之間),可以自動(dòng)降級(jí)或人工降級(jí),
    并發(fā)送告警;
  • 錯(cuò)誤:比如可用率低于90%,或者數(shù)據(jù)庫(kù)連接池被打爆了,或者訪問量突然猛增到系統(tǒng)能承受的最
    大閥值,此時(shí)可以根據(jù)情況自動(dòng)降級(jí)或者人工降級(jí);
  • 嚴(yán)重錯(cuò)誤:比如因?yàn)樘厥庠驍?shù)據(jù)錯(cuò)誤了,此時(shí)需要緊急人工降級(jí)。
    服務(wù)降級(jí)的目的,是為了防止Redis服務(wù)故障,導(dǎo)致數(shù)據(jù)庫(kù)跟著一起發(fā)生雪崩問題。因此,對(duì)于不重要
    的緩存數(shù)據(jù),可以采取服務(wù)降級(jí)策略,例如一個(gè)比較常見的做法就是,Redis出現(xiàn)問題,不去數(shù)據(jù)庫(kù)查
    詢,而是直接返回默認(rèn)值給用戶。
  • 如何保證緩存與數(shù)據(jù)庫(kù)雙寫時(shí)的數(shù)據(jù)一致性?
    你只要用緩存,就可能會(huì)涉及到緩存與數(shù)據(jù)庫(kù)雙存儲(chǔ)雙寫,你只要是雙寫,就一定會(huì)有數(shù)據(jù)一致性的問
    題,那么你如何解決一致性問題?
    一般來(lái)說(shuō),就是如果你的系統(tǒng)不是嚴(yán)格要求緩存+數(shù)據(jù)庫(kù)必須一致性的話,緩存可以稍微的跟數(shù)據(jù)庫(kù)偶
    爾有不一致的情況,最好不要做這個(gè)方案,讀請(qǐng)求和寫請(qǐng)求串行化,串到一個(gè)內(nèi)存隊(duì)列里去,這樣就可
    以保證一定不會(huì)出現(xiàn)不一致的情況
    串行化之后,就會(huì)導(dǎo)致系統(tǒng)的吞吐量會(huì)大幅度的降低,用比正常情況下多幾倍的機(jī)器去支撐線上的一個(gè)
    請(qǐng)求。
    還有一種方式就是可能會(huì)暫時(shí)產(chǎn)生不一致的情況,但是發(fā)生的幾率特別小,就是先更新數(shù)據(jù)庫(kù),然后再
    刪除緩存

    消息隊(duì)列有什么缺點(diǎn)
    4.1. 系統(tǒng)可用性降低
    本來(lái)系統(tǒng)運(yùn)行好好的,現(xiàn)在你非要加入個(gè)消息隊(duì)列進(jìn)去,那消息隊(duì)列掛了,你的系統(tǒng)不是呵呵了。因
    此,系統(tǒng)可用性會(huì)降低;
    4.2. 系統(tǒng)復(fù)雜度提高
    加入了消息隊(duì)列,要多考慮很多方面的問題,比如:一致性問題、如何保證消息不被重復(fù)消費(fèi)、如何
    保證消息可靠性傳輸?shù)取R虼?#xff0c;需要考慮的東西更多,復(fù)雜性增大。
    4.3. 一致性問題
    A 系統(tǒng)處理完了直接返回成功了,人都以為你這個(gè)請(qǐng)求就成功了;但是問題是,要是 BCD 三個(gè)系統(tǒng)那
    里,BD 兩個(gè)系統(tǒng)寫庫(kù)成功了,結(jié)果 C 系統(tǒng)寫庫(kù)失敗了,咋整?你這數(shù)據(jù)就不一致了。

    Rabbitmq一般用在什么場(chǎng)景
    (1)服務(wù)間異步通信
    (2)順序消費(fèi)
    (3)定時(shí)任務(wù)
    (4)請(qǐng)求削峰

    RabbitMQ有幾種工作模式
    simple模式(即最簡(jiǎn)單的收發(fā)模式)
    消息產(chǎn)生消息,將消息放入隊(duì)列
    消息的消費(fèi)者(consumer) 監(jiān)聽 消息隊(duì)列,如果隊(duì)列中有消息,就消費(fèi)掉,消息被拿走后,自動(dòng)從隊(duì)列中
    刪除(隱患 消息可能沒有被消費(fèi)者正確處理,已經(jīng)從隊(duì)列中消失了,造成消息的丟失,這里可以設(shè)置成
    手動(dòng)的ack,但如果設(shè)置成手動(dòng)ack,處理完后要及時(shí)發(fā)送ack消息給隊(duì)列,否則會(huì)造成內(nèi)存溢出)。

    7.2 work工作模式(資源的競(jìng)爭(zhēng))
    消息產(chǎn)生者將消息放入隊(duì)列消費(fèi)者可以有多個(gè),消費(fèi)者1,消費(fèi)者2同時(shí)監(jiān)聽同一個(gè)隊(duì)列,消息被消費(fèi)。
    C1 C2共同爭(zhēng)搶當(dāng)前的消息隊(duì)列內(nèi)容,誰(shuí)先拿到誰(shuí)負(fù)責(zé)消費(fèi)消息(隱患:高并發(fā)情況下,默認(rèn)會(huì)產(chǎn)生某
    一個(gè)消息被多個(gè)消費(fèi)者共同使用,可以設(shè)置一個(gè)開關(guān)(syncronize) 保證一條消息只能被一個(gè)消費(fèi)者使
    用)

    publish/subscribe發(fā)布訂閱(共享資源)
    每個(gè)消費(fèi)者監(jiān)聽自己的隊(duì)列;
    生產(chǎn)者將消息發(fā)給broker,由交換機(jī)將消息轉(zhuǎn)發(fā)到綁定此交換機(jī)的每個(gè)隊(duì)列,每個(gè)綁定交換機(jī)的隊(duì)
    列都將接收到消息。

    routing路由模式
    消息生產(chǎn)者將消息發(fā)送給交換機(jī)按照路由判斷,路由是字符串(info) 當(dāng)前產(chǎn)生的消息攜帶路由字符(對(duì)
    象的方法),交換機(jī)根據(jù)路由的key,只能匹配上路由key對(duì)應(yīng)的消息隊(duì)列,對(duì)應(yīng)的消費(fèi)者才能消費(fèi)消息;
    根據(jù)業(yè)務(wù)功能定義路由字符串
    從系統(tǒng)的代碼邏輯中獲取對(duì)應(yīng)的功能字符串,將消息任務(wù)扔到對(duì)應(yīng)的隊(duì)列中。
    業(yè)務(wù)場(chǎng)景:error 通知;EXCEPTION;錯(cuò)誤通知的功能;傳統(tǒng)意義的錯(cuò)誤通知;客戶通知;利用key路由,可
    以將程序中的錯(cuò)誤封裝成消息傳入到消息隊(duì)列中,開發(fā)者可以自定義消費(fèi)者,實(shí)時(shí)接收錯(cuò)誤;

    topic 主題模式(路由模式的一種)

    • , # 代表通配符
    • 代表多個(gè)單詞, # 代表一個(gè)單詞
      路由功能添加模糊匹配
      消息產(chǎn)生者產(chǎn)生消息,把消息交給交換機(jī)
      交換機(jī)根據(jù)key的規(guī)則模糊匹配到對(duì)應(yīng)的隊(duì)列,由隊(duì)列的監(jiān)聽消費(fèi)者接收消息消費(fèi)

    如何保證RabbitMQ消息的順序性

    將原來(lái)的一個(gè)queue拆分成多個(gè)queue,每個(gè)queue都有一個(gè)自己的consumer。該種方案的核心
    是生產(chǎn)者在投遞消息的時(shí)候根據(jù)業(yè)務(wù)數(shù)據(jù)關(guān)鍵值(例如訂單ID哈希值對(duì)訂單隊(duì)列數(shù)取模)來(lái)將需要
    保證先后順序的同一類數(shù)據(jù)(同一個(gè)訂單的數(shù)據(jù)) 發(fā)送到同一個(gè)queue當(dāng)中。
    一個(gè)queue就一個(gè)consumer,在consumer中維護(hù)多個(gè)內(nèi)存隊(duì)列,根據(jù)業(yè)務(wù)數(shù)據(jù)關(guān)鍵值(例如訂
    單ID哈希值對(duì)內(nèi)存隊(duì)列數(shù)取模)將消息加入到不同的內(nèi)存隊(duì)列中,然后多個(gè)真正負(fù)責(zé)處理消息的線
    程去各自對(duì)應(yīng)的內(nèi)存隊(duì)列當(dāng)中獲取消息進(jìn)行消費(fèi)。

    如何保證消息不被重復(fù)消費(fèi)

    分析重復(fù)消費(fèi)原因
    生產(chǎn)時(shí)消息重復(fù)
    由于生產(chǎn)者發(fā)送消息給MQ,在MQ確認(rèn)的時(shí)候出現(xiàn)了網(wǎng)絡(luò)波動(dòng),生產(chǎn)者沒有收到確認(rèn),實(shí)際上MQ
    已經(jīng)接收到了消息。這時(shí)候生產(chǎn)者就會(huì)重新發(fā)送一遍這條消息。生產(chǎn)者中如果消息未被確認(rèn),或確
    認(rèn)失敗,我們可以使用定時(shí)任務(wù)+(redis/db)來(lái)進(jìn)行消息重試。
    消費(fèi)時(shí)消息重復(fù)
    消費(fèi)者消費(fèi)成功后,再給MQ確認(rèn)的時(shí)候出現(xiàn)了網(wǎng)絡(luò)波動(dòng),MQ沒有接收到確認(rèn),為了保證消息被
    消費(fèi),MQ就會(huì)繼續(xù)給消費(fèi)者投遞之前的消息。這時(shí)候消費(fèi)者就接收到了兩條一樣的消息。
    解決方案
    讓每個(gè)消息攜帶一個(gè)全局的唯一ID,即可保證消息的冪等性
    消費(fèi)者獲取到消息后先根據(jù)id去查詢r(jià)edis/db是否存在該消息。
    如果不存在,則正常消費(fèi),消費(fèi)完畢后寫入redis/db。
    如果存在,則證明消息被消費(fèi)過(guò),直接丟棄。

    、如何確保消息接收方消費(fèi)了消息?
    發(fā)送方確認(rèn)模式
    將信道設(shè)置成 confirm 模式(發(fā)送方確認(rèn)模式),則所有在信道上發(fā)布的消息都會(huì)被指派一個(gè)唯一
    的 ID。
    一旦消息被投遞到目的隊(duì)列后,或者消息被寫入磁盤后(可持久化的消息),信道會(huì)發(fā)送一個(gè)確認(rèn)
    給生產(chǎn)者(包含消息唯一 ID)。
    如果 RabbitMQ 發(fā)生內(nèi)部錯(cuò)誤從而導(dǎo)致消息丟失,會(huì)發(fā)送一條 nack(notacknowledged,未確
    認(rèn))消息。
    發(fā)送方確認(rèn)模式是異步的,生產(chǎn)者應(yīng)用程序在等待確認(rèn)的同時(shí),可以繼續(xù)發(fā)送消息。當(dāng)確認(rèn)消息到
    達(dá)生產(chǎn)者應(yīng)用程序,生產(chǎn)者應(yīng)用程序的回調(diào)方法就會(huì)被觸發(fā)來(lái)處理確認(rèn)消息。
    接收方確認(rèn)機(jī)制
    消費(fèi)者接收每一條消息后都必須進(jìn)行確認(rèn)(消息接收和消息確認(rèn)是兩個(gè)不同操作)。只有消費(fèi)者確
    認(rèn)了消息,RabbitMQ 才能安全地把消息從隊(duì)列中刪除。
    這里并沒有用到超時(shí)機(jī)制,RabbitMQ 僅通過(guò) Consumer 的連接中斷來(lái)確認(rèn)是否需要重新發(fā)送消
    息。也就是說(shuō),只要連接不中斷,RabbitMQ 給了 Consumer 足夠長(zhǎng)的時(shí)間來(lái)處理消息。保證數(shù)據(jù)
    的最終一致性;
    下面羅列幾種特殊情況
    如果消費(fèi)者接收到消息,在確認(rèn)之前斷開了連接或取消訂閱,RabbitMQ 會(huì)認(rèn)為消息沒有被分發(fā),
    然后重新分發(fā)給下一個(gè)訂閱的消費(fèi)者。(可能存在消息重復(fù)消費(fèi)的隱患,需要去重)
    如果消費(fèi)者接收到消息卻沒有確認(rèn)消息,連接也未斷開,則 RabbitMQ 認(rèn)為該消費(fèi)者繁忙,將不會(huì)
    給該消費(fèi)者分發(fā)更多的消息。

    如何保證RabbitMQ消息的可靠傳輸?
    題型分析:
    消息不可靠的情況可能是消息丟失,劫持等原因;
    丟失又分為:生產(chǎn)者丟失消息、消息列表丟失消息、消費(fèi)者丟失消息;
    生產(chǎn)者丟失消息:
    從生產(chǎn)者弄丟數(shù)據(jù)這個(gè)角度來(lái)看,RabbitMQ提供transaction和confirm模式來(lái)確保生產(chǎn)者不丟消
    息;
    事務(wù)機(jī)制:發(fā)送消息前,開啟事務(wù)(channel.txSelect()),然后發(fā)送消息,如果發(fā)送過(guò)程中出現(xiàn)什
    么異常,事務(wù)就會(huì)回滾(channel.txRollback()),如果發(fā)送成功則提交事務(wù)
    (channel.txCommit())。然而,這種方式有個(gè)缺點(diǎn):吞吐量下降;
    confirm模式:一旦channel進(jìn)入confirm模式,所有在該信道上發(fā)布的消息都將會(huì)被指派一個(gè)唯一
    的ID(從1開始),一旦消息被投遞到所有匹配的隊(duì)列之后;rabbitMQ就會(huì)發(fā)送一個(gè)ACK給生產(chǎn)者
    (包含消息的唯一ID),這就使得生產(chǎn)者知道消息已經(jīng)正確到達(dá)目的隊(duì)列了;如果rabbitMQ沒能
    處理該消息,則會(huì)發(fā)送一個(gè)Nack消息給你,你可以進(jìn)行重試操作。
    消息隊(duì)列丟數(shù)據(jù):
    處理消息隊(duì)列丟數(shù)據(jù)的情況,一般是開啟持久化磁盤的配置。這個(gè)持久化配置可以和confirm機(jī)制
    配合使用,你可以在消息持久化磁盤后,再給生產(chǎn)者發(fā)送一個(gè)Ack信號(hào)。這樣,如果消息持久化磁
    盤之前,rabbitMQ陣亡了,那么生產(chǎn)者收不到Ack信號(hào),生產(chǎn)者會(huì)自動(dòng)重發(fā)。

    RabbitMQ 是比較有代表性的,因?yàn)槭腔谥鲝?#xff08;非分布式)做高可用性的,我們就以 RabbitMQ
    為例子講解第一種 MQ 的高可用性怎么實(shí)現(xiàn)。RabbitMQ 有三種模式:單機(jī)模式、普通集群模
    式、鏡像集群模式。
    單機(jī)模式,就是 Demo 級(jí)別的,一般就是學(xué)習(xí)時(shí)候用的,沒人在生產(chǎn)環(huán)境使用單機(jī)模式
    普通集群模式:
    意思就是在多臺(tái)機(jī)器上啟動(dòng)多個(gè) RabbitMQ 實(shí)例,每個(gè)機(jī)器啟動(dòng)一個(gè)。
    你創(chuàng)建的 queue,只會(huì)放在一個(gè) RabbitMQ 實(shí)例上,但是每個(gè)實(shí)例都同步 queue 的元數(shù)據(jù)
    (元數(shù)據(jù)可以認(rèn)為是 queue 的一些配置信息,通過(guò)元數(shù)據(jù),可以找到 queue 所在實(shí)例)。你
    消費(fèi)的時(shí)候,實(shí)際上如果連接到了另外一個(gè)實(shí)例,那么那個(gè)實(shí)例會(huì)從 queue 所在實(shí)例上拉取
    數(shù)據(jù)過(guò)來(lái)。這方案主要是提高吞吐量的,就是說(shuō)讓集群中多個(gè)節(jié)點(diǎn)來(lái)服務(wù)某個(gè) queue 的讀寫
    操作。
    鏡像集群模式:
    這種模式,才是所謂的 RabbitMQ 的高可用模式。跟普通集群模式不一樣的是,在鏡像集群
    模式下,你創(chuàng)建的 queue,無(wú)論元數(shù)據(jù)還是 queue 里的消息都會(huì)存在于多個(gè)實(shí)例上,就是
    說(shuō),每個(gè) RabbitMQ 節(jié)點(diǎn)都有這個(gè) queue 的一個(gè)完整鏡像,包含 queue 的全部數(shù)據(jù)的意
    思。然后每次你寫消息到 queue 的時(shí)候,都會(huì)自動(dòng)把消息同步到多個(gè)實(shí)例的 queue 上。
    RabbitMQ 有很好的管理控制臺(tái),就是在后臺(tái)新增一個(gè)策略,這個(gè)策略是鏡像集群模式的策
    略,指定的時(shí)候是可以要求數(shù)據(jù)同步到所有節(jié)點(diǎn)的,也可以要求同步到指定數(shù)量的節(jié)點(diǎn),再次
    創(chuàng)建 queue 的時(shí)候,應(yīng)用這個(gè)策略,就會(huì)自動(dòng)將數(shù)據(jù)同步到其他的節(jié)點(diǎn)上去了。
    這樣的好處在于,你任何一個(gè)機(jī)器宕機(jī)了,沒事兒,其它機(jī)器(節(jié)點(diǎn))還包含了這個(gè) queue
    的完整數(shù)據(jù),別的 consumer 都可以到其它節(jié)點(diǎn)上去消費(fèi)數(shù)據(jù)。壞處在于,第一,這個(gè)性能
    開銷也太大了吧,消息需要同步到所有機(jī)器上,導(dǎo)致網(wǎng)絡(luò)帶寬壓力和消耗很重!RabbitMQ 一
    個(gè) queue 的數(shù)據(jù)都是放在一個(gè)節(jié)點(diǎn)里的,鏡像集群下,也是每個(gè)節(jié)點(diǎn)都放這個(gè) queue 的完整
    數(shù)據(jù)。

    如何解決消息隊(duì)列的延時(shí)以及過(guò)期失效問題?消息隊(duì)
    列滿了以后該怎么處理?有幾百萬(wàn)消息持續(xù)積壓幾小時(shí),說(shuō)
    說(shuō)怎么解決?
    線上挺常見的,一般不出,一出就是大 case。一般常見于,舉個(gè)例子,消費(fèi)端每次消費(fèi)之后要寫
    mysql,結(jié)果 mysql 掛了,消費(fèi)端 hang 那兒了,不動(dòng)了;或者是消費(fèi)端出了個(gè)什么岔子,導(dǎo)致消
    費(fèi)速度極其慢。
    一般這個(gè)時(shí)候,只能臨時(shí)緊急擴(kuò)容了,具體操作步驟和思路如下:
    先修復(fù) consumer 的問題,確保其恢復(fù)消費(fèi)速度,然后將現(xiàn)有 consumer 都停掉。
    新建一個(gè) topic,partition 是原來(lái)的 10 倍,臨時(shí)建立好原先 10 倍的 queue 數(shù)量。
    然后寫一個(gè)臨時(shí)的分發(fā)數(shù)據(jù)的 consumer 程序,這個(gè)程序部署上去消費(fèi)積壓的數(shù)據(jù), 消費(fèi)之后不
    做耗時(shí)的處理,直接均勻輪詢寫入臨時(shí)建立好的 10 倍數(shù)量的 queue。
    接著臨時(shí)征用 10 倍的機(jī)器來(lái)部署 consumer,每一批 consumer 消費(fèi)一個(gè)臨時(shí) queue 的數(shù)據(jù)。這
    種做法相當(dāng)于是臨時(shí)將 queue 資源和 consumer 資源擴(kuò)大 10 倍,以正常的 10 倍速度來(lái)消費(fèi)數(shù)
    據(jù)。
    等快速消費(fèi)完積壓數(shù)據(jù)之后, 得恢復(fù)原先部署的架構(gòu), 重新用原先的 consumer 機(jī)器來(lái)消費(fèi)消
    息。

    消息過(guò)期失效了
    假設(shè)你用的是 RabbitMQ,RabbtiMQ 是可以設(shè)置過(guò)期時(shí)間的,也就是 TTL。如果消息在 queue 中積壓
    超過(guò)一定的時(shí)間就會(huì)被 RabbitMQ 給清理掉,這個(gè)數(shù)據(jù)就沒了。那這就是第二個(gè)坑了。這就不是說(shuō)數(shù)據(jù)
    會(huì)大量積壓在 mq 里,而是大量的數(shù)據(jù)會(huì)直接搞丟。
    這個(gè)情況下,就不是說(shuō)要增加 consumer 消費(fèi)積壓的消息,因?yàn)閷?shí)際上沒啥積壓,而是丟了大量的消
    息。我們可以采取一個(gè)方案,就是批量重導(dǎo),這個(gè)我們之前線上也有類似的場(chǎng)景干過(guò)。就是大量積壓的
    時(shí)候,我們當(dāng)時(shí)就直接丟棄數(shù)據(jù)了,然后等過(guò)了高峰期以后,比如大家一起喝咖啡熬夜到晚上12點(diǎn)以
    后,用戶都睡覺了。這個(gè)時(shí)候我們就開始寫程序,將丟失的那批數(shù)據(jù),寫個(gè)臨時(shí)程序,一點(diǎn)一點(diǎn)的查出
    來(lái),然后重新灌入 mq 里面去,把白天丟的數(shù)據(jù)給他補(bǔ)回來(lái)。也只能是這樣了。
    假設(shè) 1 萬(wàn)個(gè)訂單積壓在 mq 里面,沒有處理,其中 1000 個(gè)訂單都丟了,你只能手動(dòng)寫程序把那 1000
    個(gè)訂單給查出來(lái),手動(dòng)發(fā)到 mq 里去再補(bǔ)一次。
    都快寫滿了
    如果消息積壓在 mq 里,你很長(zhǎng)時(shí)間都沒有處理掉,此時(shí)導(dǎo)致 mq 都快寫滿了,咋辦?這個(gè)還有別的辦
    法嗎?沒有,誰(shuí)讓你第一個(gè)方案執(zhí)行的太慢了,你臨時(shí)寫程序,接入數(shù)據(jù)來(lái)消費(fèi),消費(fèi)一個(gè)丟棄一個(gè),
    都不要了,快速消費(fèi)掉所有的消息。然后走第二個(gè)方案,到了晚上再補(bǔ)數(shù)據(jù)吧。

    1.MongoDB 分片機(jī)制;
    2.MongoDB有哪些優(yōu)勢(shì);
    3.MongoDB的使用場(chǎng)景;

    總結(jié)

    以上是生活随笔為你收集整理的多线程、Redis、rabbitmq面试题的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。