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