分布式面试题(含解答)
分布式事務(wù)
指事務(wù)的每個(gè)操作步驟都位于不同的節(jié)點(diǎn)上,需要保證事務(wù)的 AICD 特性。
1. 產(chǎn)生原因
數(shù)據(jù)庫(kù)分庫(kù)分表;
SOA 架構(gòu),比如一個(gè)電商網(wǎng)站將訂單業(yè)務(wù)和庫(kù)存業(yè)務(wù)分離出來(lái)放到不同的節(jié)點(diǎn)上。
2. 應(yīng)用場(chǎng)景
下單:減少庫(kù)存同時(shí)更新訂單狀態(tài)。庫(kù)存和訂單不在不同一個(gè)數(shù)據(jù)庫(kù),因此涉及分布式事務(wù)。
支付:買家賬戶扣款同時(shí)賣家賬戶入賬。買家和賣家賬戶信息不在同一個(gè)數(shù)據(jù)庫(kù),因此涉及分布式事務(wù)。
3. 解決方案
3.1 兩階段提交協(xié)議
兩階段提交協(xié)議可以很好得解決分布式事務(wù)問(wèn)題,它可以使用 XA 來(lái)實(shí)現(xiàn),XA 它包含兩個(gè)部分:事務(wù)管理器和本地資源管理器。其中本地資源管理器往往由數(shù)據(jù)庫(kù)實(shí)現(xiàn),比如 Oracle、DB2 這些商業(yè)數(shù)據(jù)庫(kù)都實(shí)現(xiàn)了 XA 接口,而事務(wù)管理器作為全局的協(xié)調(diào)者,負(fù)責(zé)各個(gè)本地資源的提交和回滾。
3.2 消息中間件
消息中間件也可稱作消息系統(tǒng) (MQ),它本質(zhì)上是一個(gè)暫存轉(zhuǎn)發(fā)消息的一個(gè)中間件。在分布式應(yīng)用當(dāng)中,我們可以把一個(gè)業(yè)務(wù)操作轉(zhuǎn)換成一個(gè)消息,比如支付寶的余額轉(zhuǎn)如余額寶操作,支付寶系統(tǒng)執(zhí)行減少余額操作之后向消息系統(tǒng)發(fā)一個(gè)消息,余額寶系統(tǒng)訂閱這條消息然后進(jìn)行增加賬戶金額操作。
3.2.1 消息處理模型
點(diǎn)對(duì)點(diǎn)
.
發(fā)布/訂閱
.
3.2.2 消息的可靠性
消息的發(fā)送端的可靠性:發(fā)送端完成操作后一定能將消息成功發(fā)送到消息系統(tǒng)。
消息的接收端的可靠性:接收端僅且能夠從消息中間件成功消費(fèi)一次消息。
發(fā)送端的可靠性
在本地?cái)?shù)據(jù)建一張消息表,將消息數(shù)據(jù)與業(yè)務(wù)數(shù)據(jù)保存在同一數(shù)據(jù)庫(kù)實(shí)例里,這樣就可以利用本地?cái)?shù)據(jù)庫(kù)的事務(wù)機(jī)制。事務(wù)提交成功后,將消息表中的消息轉(zhuǎn)移到消息中間件,若轉(zhuǎn)移消息成功則刪除消息表中的數(shù)據(jù),否則繼續(xù)重傳。
接收端的可靠性
保證接收端處理消息的業(yè)務(wù)邏輯具有冪等性:只要具有冪等性,那么消費(fèi)多少次消息,最后處理的結(jié)果都是一樣的。
保證消息具有唯一編號(hào),并使用一張日志表來(lái)記錄已經(jīng)消費(fèi)的消息編號(hào)。
負(fù)載均衡的算法與實(shí)現(xiàn)
1. 算法
1.1 輪詢(Round Robin)
輪詢算法把每個(gè)請(qǐng)求輪流發(fā)送到每個(gè)服務(wù)器上。下圖中,一共有 6 個(gè)客戶端產(chǎn)生了 6 個(gè)請(qǐng)求,這 6 個(gè)請(qǐng)求按 (1, 2, 3, 4, 5, 6) 的順序發(fā)送。最后,(1, 3, 5) 的請(qǐng)求會(huì)被發(fā)送到服務(wù)器 1,(2, 4, 6) 的請(qǐng)求會(huì)被發(fā)送到服務(wù)器 2。
.
該算法比較適合每個(gè)服務(wù)器的性能差不多的場(chǎng)景,如果有性能存在差異的情況下,那么性能較差的服務(wù)器可能無(wú)法承擔(dān)多大的負(fù)載。下圖中,服務(wù)器 2 的性能比服務(wù)器 1 差,那么服務(wù)器 2 可能無(wú)法承擔(dān)多大的負(fù)載。
.
1.2 加權(quán)輪詢(Weighted Round Robbin)
加權(quán)輪詢是在輪詢的基礎(chǔ)上,根據(jù)服務(wù)器的性能差異,為服務(wù)器賦予一定的權(quán)值。例如下圖中,服務(wù)器 1 被賦予的權(quán)值為 5,服務(wù)器 2 被賦予的權(quán)值為 1,那么 (1, 2, 3, 4, 5) 請(qǐng)求會(huì)被發(fā)送到服務(wù)器 1,(6) 請(qǐng)求會(huì)被發(fā)送到服務(wù)器 2。
.
1.3 最少連接(least Connections)
由于每個(gè)請(qǐng)求的連接時(shí)間不一樣,使用輪詢或者加權(quán)輪詢算法的話,可能會(huì)讓一臺(tái)服務(wù)器當(dāng)前連接數(shù)多大,而另一臺(tái)服務(wù)器的連接多小,造成負(fù)載不均衡。例如下圖中,(1, 3, 5) 請(qǐng)求會(huì)被發(fā)送到服務(wù)器 1,但是 (1, 3) 很快就斷開(kāi)連接,此時(shí)只有 (5) 請(qǐng)求連接服務(wù)器 1;(2, 4, 6) 請(qǐng)求被發(fā)送到服務(wù)器 2,它們的連接都還沒(méi)有斷開(kāi),繼續(xù)運(yùn)行時(shí),服務(wù)器 2 會(huì)承擔(dān)多大的負(fù)載。
.
最少連接算法就是將請(qǐng)求發(fā)送給當(dāng)前最少連接數(shù)的服務(wù)器上。例如下圖中,服務(wù)器 1 當(dāng)前連接數(shù)最小,那么請(qǐng)求 6 就會(huì)被發(fā)送到服務(wù)器 1 上。
.
1.4 加權(quán)最小連接(Weighted Least Connection)
在最小連接的基礎(chǔ)上,根據(jù)服務(wù)器的性能為每臺(tái)服務(wù)器分配權(quán)重,然后根據(jù)權(quán)重計(jì)算出每臺(tái)服務(wù)器能處理的連接數(shù)。
.
1.5 隨機(jī)算法(Random)
把請(qǐng)求隨機(jī)發(fā)送到服務(wù)器上。和輪詢算法類似,該算法比較適合服務(wù)器性能差不多的場(chǎng)景。
.
2. 實(shí)現(xiàn)
2.1 DNS 解析
使用 DNS 作為負(fù)載均衡器,會(huì)根據(jù)負(fù)載情況返回不同服務(wù)器的 IP 地址。大型網(wǎng)站基本使用了這種方式最為第一級(jí)負(fù)載均衡手段,然后在內(nèi)部在第二級(jí)負(fù)載均衡。
.
2.2 修改 MAC 地址
使用 LVS(Linux Virtual Server)這種鏈路層負(fù)載均衡器,根據(jù)負(fù)載情況修改請(qǐng)求的 MAC 地址。
.
2.3 修改 IP 地址
在網(wǎng)絡(luò)層修改請(qǐng)求的目的 IP 地址。
.
2.4 HTTP 重定向
HTTP 重定向負(fù)載均衡服務(wù)器收到 HTTP 請(qǐng)求之后會(huì)返回服務(wù)器的地址,并將該地址寫(xiě)入 HTTP 重定向響應(yīng)中返回給瀏覽器,瀏覽器收到后再次發(fā)送請(qǐng)求。
.
2.5 反向代理
正向代理:發(fā)生在客戶端,是由用戶主動(dòng)發(fā)起的。比如FQ,客戶端通過(guò)主動(dòng)訪問(wèn)代理服務(wù)器,讓代理服務(wù)器獲得需要的外網(wǎng)數(shù)據(jù),然后轉(zhuǎn)發(fā)回客戶端。
反向代理:發(fā)生在服務(wù)器端,用戶不知道發(fā)生了代理。
.
分布式鎖
Java 提供了兩種內(nèi)置的鎖的實(shí)現(xiàn),一種是由 JVM 實(shí)現(xiàn)的 synchronized 和 JDK 提供的 Lock,當(dāng)你的應(yīng)用是單機(jī)或者說(shuō)單進(jìn)程應(yīng)用時(shí),可以使用 synchronized 或 Lock 來(lái)實(shí)現(xiàn)鎖。當(dāng)應(yīng)用涉及到多機(jī)、多進(jìn)程共同完成時(shí),那么這時(shí)候就需要一個(gè)全局鎖來(lái)實(shí)現(xiàn)多個(gè)進(jìn)程之間的同步。
1. 使用場(chǎng)景
例如一個(gè)應(yīng)用有手機(jī) APP 端和 Web 端,如果在兩個(gè)客戶端同時(shí)進(jìn)行一項(xiàng)操作時(shí),那么就會(huì)導(dǎo)致這項(xiàng)操作重復(fù)進(jìn)行。
2. 實(shí)現(xiàn)方式
2.1 數(shù)據(jù)庫(kù)分布式鎖
基于 MySQL 鎖表
該實(shí)現(xiàn)方式完全依靠數(shù)據(jù)庫(kù)唯一索引來(lái)實(shí)現(xiàn)。當(dāng)想要獲得鎖時(shí),就向數(shù)據(jù)庫(kù)中插入一條記錄,釋放鎖時(shí)就刪除這條記錄。如果記錄具有唯一索引,就不會(huì)同時(shí)插入同一條記錄。這種方式存在以下幾個(gè)問(wèn)題:
鎖沒(méi)有失效時(shí)間,解鎖失敗會(huì)導(dǎo)致死鎖,其他線程無(wú)法再獲得鎖。
只能是非阻塞鎖,插入失敗直接就報(bào)錯(cuò)了,無(wú)法重試。
不可重入,同一線程在沒(méi)有釋放鎖之前無(wú)法再獲得鎖。
采用樂(lè)觀鎖增加版本號(hào)
根據(jù)版本號(hào)來(lái)判斷更新之前有沒(méi)有其他線程更新過(guò),如果被更新過(guò),則獲取鎖失敗。
2.2 Redis 分布式鎖
基于 SETNX、EXPIRE
使用 SETNX(set if not exist)命令插入一個(gè)鍵值對(duì)時(shí),如果 Key 已經(jīng)存在,那么會(huì)返回 False,否則插入成功并返回 True。因此客戶端在嘗試獲得鎖時(shí),先使用 SETNX 向 Redis 中插入一個(gè)記錄,如果返回 True 表示獲得鎖,返回 False 表示已經(jīng)有客戶端占用鎖。
EXPIRE 可以為一個(gè)鍵值對(duì)設(shè)置一個(gè)過(guò)期時(shí)間,從而避免了死鎖的發(fā)生。
RedLock 算法
ReadLock 算法使用了多個(gè) Redis 實(shí)例來(lái)實(shí)現(xiàn)分布式鎖,這是為了保證在發(fā)生單點(diǎn)故障時(shí)還可用。
嘗試從 N 個(gè)相互獨(dú)立 Redis 實(shí)例獲取鎖,如果一個(gè)實(shí)例不可用,應(yīng)該盡快嘗試下一個(gè)。
計(jì)算獲取鎖消耗的時(shí)間,只有當(dāng)這個(gè)時(shí)間小于鎖的過(guò)期時(shí)間,并且從大多數(shù)(N/2+1)實(shí)例上獲取了鎖,那么就認(rèn)為鎖獲取成功了。
如果鎖獲取失敗,會(huì)到每個(gè)實(shí)例上釋放鎖。
2.3 Zookeeper 分布式鎖
Zookeeper 是一個(gè)為分布式應(yīng)用提供一致性服務(wù)的軟件,例如配置管理、分布式協(xié)同以及命名的中心化等,這些都是分布式系統(tǒng)中非常底層而且是必不可少的基本功能,但是如果自己實(shí)現(xiàn)這些功能而且要達(dá)到高吞吐、低延遲同時(shí)還要保持一致性和可用性,實(shí)際上非常困難。
抽象模型
Zookeeper 提供了一種樹(shù)形結(jié)構(gòu)級(jí)的命名空間,/app1/p_1 節(jié)點(diǎn)表示它的父節(jié)點(diǎn)為 /app1。
.
節(jié)點(diǎn)類型
永久節(jié)點(diǎn):不會(huì)因?yàn)闀?huì)話結(jié)束或者超時(shí)而消失;
臨時(shí)節(jié)點(diǎn):如果會(huì)話結(jié)束或者超時(shí)就會(huì)消失;
有序節(jié)點(diǎn):會(huì)在節(jié)點(diǎn)名的后面加一個(gè)數(shù)字后綴,并且是有序的,例如生成的有序節(jié)點(diǎn)為 /lock/node-0000000000,它的下一個(gè)有序節(jié)點(diǎn)則為 /lock/node-0000000001,依次類推。
監(jiān)聽(tīng)器
為一個(gè)節(jié)點(diǎn)注冊(cè)監(jiān)聽(tīng)器,在節(jié)點(diǎn)狀態(tài)發(fā)生改變時(shí),會(huì)給客戶端發(fā)送消息。
分布式鎖實(shí)現(xiàn)
創(chuàng)建一個(gè)鎖目錄 /lock。
在 /lock 下創(chuàng)建臨時(shí)的且有序的子節(jié)點(diǎn),第一個(gè)客戶端對(duì)應(yīng)的子節(jié)點(diǎn)為 /lock/lock-0000000000,第二個(gè)為 /lock/lock-0000000001,以此類推。
客戶端獲取 /lock 下的子節(jié)點(diǎn)列表,判斷自己創(chuàng)建的子節(jié)點(diǎn)是否為當(dāng)前子節(jié)點(diǎn)列表中序號(hào)最小的子節(jié)點(diǎn),如果是則認(rèn)為獲得鎖,否則監(jiān)聽(tīng)自己的前一個(gè)子節(jié)點(diǎn),獲得子節(jié)點(diǎn)的變更通知后重復(fù)此步驟直至獲得鎖;
執(zhí)行業(yè)務(wù)代碼,完成后,刪除對(duì)應(yīng)的子節(jié)點(diǎn)。
會(huì)話超時(shí)
如果一個(gè)已經(jīng)獲得鎖的會(huì)話超時(shí)了,因?yàn)閯?chuàng)建的是臨時(shí)節(jié)點(diǎn),因此該會(huì)話對(duì)應(yīng)的臨時(shí)節(jié)點(diǎn)會(huì)被刪除,其它會(huì)話就可以獲得鎖了??梢钥吹?#xff0c;Zookeeper 分布式鎖不會(huì)出現(xiàn)數(shù)據(jù)庫(kù)分布式鎖的死鎖問(wèn)題。
羊群效應(yīng)
在步驟二,一個(gè)節(jié)點(diǎn)未獲得鎖,需要監(jiān)聽(tīng)監(jiān)聽(tīng)自己的前一個(gè)子節(jié)點(diǎn),這是因?yàn)槿绻O(jiān)聽(tīng)所有的子節(jié)點(diǎn),那么任意一個(gè)子節(jié)點(diǎn)狀態(tài)改變,其它所有子節(jié)點(diǎn)都會(huì)收到通知,而我們只希望它的下一個(gè)子節(jié)點(diǎn)收到通知。
分布式 Session
如果不做任何處理的話,用戶將出現(xiàn)頻繁登錄的現(xiàn)象,比如集群中存在 A、B 兩臺(tái)服務(wù)器,用戶在第一次訪問(wèn)網(wǎng)站時(shí),Nginx 通過(guò)其負(fù)載均衡機(jī)制將用戶請(qǐng)求轉(zhuǎn)發(fā)到 A 服務(wù)器,這時(shí) A 服務(wù)器就會(huì)給用戶創(chuàng)建一個(gè) Session。當(dāng)用戶第二次發(fā)送請(qǐng)求時(shí),Nginx 將其負(fù)載均衡到 B 服務(wù)器,而這時(shí)候 B 服務(wù)器并不存在 Session,所以就會(huì)將用戶踢到登錄頁(yè)面。這將大大降低用戶體驗(yàn)度,導(dǎo)致用戶的流失,這種情況是項(xiàng)目絕不應(yīng)該出現(xiàn)的。
1. 粘性 Session
原理
粘性 Session 是指將用戶鎖定到某一個(gè)服務(wù)器上,比如上面說(shuō)的例子,用戶第一次請(qǐng)求時(shí),負(fù)載均衡器將用戶的請(qǐng)求轉(zhuǎn)發(fā)到了 A 服務(wù)器上,如果負(fù)載均衡器設(shè)置了粘性 Session 的話,那么用戶以后的每次請(qǐng)求都會(huì)轉(zhuǎn)發(fā)到 A 服務(wù)器上,相當(dāng)于把用戶和 A 服務(wù)器粘到了一塊,這就是粘性 Session 機(jī)制。
優(yōu)點(diǎn)
簡(jiǎn)單,不需要對(duì) Session 做任何處理。
缺點(diǎn)
缺乏容錯(cuò)性,如果當(dāng)前訪問(wèn)的服務(wù)器發(fā)生故障,用戶被轉(zhuǎn)移到第二個(gè)服務(wù)器上時(shí),他的 Session 信息都將失效。
適用場(chǎng)景
發(fā)生故障對(duì)客戶產(chǎn)生的影響較小;
服務(wù)器發(fā)生故障是低概率事件。
2. 服務(wù)器 Session 復(fù)制
原理
任何一個(gè)服務(wù)器上的 Session 發(fā)生改變,該節(jié)點(diǎn)會(huì)把這個(gè) Session 的所有內(nèi)容序列化,然后廣播給所有其它節(jié)點(diǎn),不管其他服務(wù)器需不需要 Session,以此來(lái)保證 Session 同步。
優(yōu)點(diǎn)
可容錯(cuò),各個(gè)服務(wù)器間 Session 能夠?qū)崟r(shí)響應(yīng)。
缺點(diǎn)
會(huì)對(duì)網(wǎng)絡(luò)負(fù)荷造成一定壓力,如果 Session 量大的話可能會(huì)造成網(wǎng)絡(luò)堵塞,拖慢服務(wù)器性能。
實(shí)現(xiàn)方式
設(shè)置 Tomcat 的 server.xml 開(kāi)啟 tomcat 集群功能。
在應(yīng)用里增加信息:通知應(yīng)用當(dāng)前處于集群環(huán)境中,支持分布式,即在 web.xml 中添加 選項(xiàng)。
3. Session 共享機(jī)制
使用分布式緩存方案比如 Memcached、Redis,但是要求 Memcached 或 Redis 必須是集群。
使用 Session 共享也分兩種機(jī)制,兩種情況如下:
3.1 粘性 Session 共享機(jī)制
和粘性 Session 一樣,一個(gè)用戶的 Session 會(huì)綁定到一個(gè) Tomcat 上。Memcached 只是起到備份作用。
.
3.2 非粘性 Session 共享機(jī)制
原理
Tomcat 本身不存儲(chǔ) Session,而是存入 Memcached 中。Memcached 集群構(gòu)建主從復(fù)制架構(gòu)。
.
優(yōu)點(diǎn)
可容錯(cuò),Session 實(shí)時(shí)響應(yīng)。
實(shí)現(xiàn)方式
用開(kāi)源的 msm 插件解決 Tomcat 之間的 Session 共享:Memcached_Session_Manager(MSM)
4. Session 持久化到數(shù)據(jù)庫(kù)
原理
拿出一個(gè)數(shù)據(jù)庫(kù),專門用來(lái)存儲(chǔ) Session 信息。保證 Session 的持久化。
優(yōu)點(diǎn)
服務(wù)器出現(xiàn)問(wèn)題,Session 不會(huì)丟失
缺點(diǎn)
如果網(wǎng)站的訪問(wèn)量很大,把 Session 存儲(chǔ)到數(shù)據(jù)庫(kù)中,會(huì)對(duì)數(shù)據(jù)庫(kù)造成很大壓力,還需要增加額外的開(kāi)銷維護(hù)數(shù)據(jù)庫(kù)。
5. Terracotta 實(shí)現(xiàn) Session 復(fù)制
原理
Terracotta 的基本原理是對(duì)于集群間共享的數(shù)據(jù),當(dāng)在一個(gè)節(jié)點(diǎn)發(fā)生變化的時(shí)候,Terracotta 只把變化的部分發(fā)送給 Terracotta 服務(wù)器,然后由服務(wù)器把它轉(zhuǎn)發(fā)給真正需要這個(gè)數(shù)據(jù)的節(jié)點(diǎn)。它是服務(wù)器 Session 復(fù)制的優(yōu)化。
.
優(yōu)點(diǎn)
這樣對(duì)網(wǎng)絡(luò)的壓力就非常小,各個(gè)節(jié)點(diǎn)也不必浪費(fèi) CPU 時(shí)間和內(nèi)存進(jìn)行大量的序列化操作。把這種集群間數(shù)據(jù)共享的機(jī)制應(yīng)用在 Session 同步上,既避免了對(duì)數(shù)據(jù)庫(kù)的依賴,又能達(dá)到負(fù)載均衡和災(zāi)難恢復(fù)的效果。
分庫(kù)與分表帶來(lái)的分布式困境與應(yīng)對(duì)之策
.
1. 事務(wù)問(wèn)題
使用分布式事務(wù)。
2. 查詢問(wèn)題
使用匯總表。
3. ID 唯一性
使用全局唯一 ID:GUID;
為每個(gè)分片指定一個(gè) ID 范圍。
轉(zhuǎn)載于:https://www.cnblogs.com/workstation-nigoudongma/p/9546801.html
總結(jié)
以上是生活随笔為你收集整理的分布式面试题(含解答)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: bbs-admin
- 下一篇: JDK8中ConcurrentHashM