分佈式分爲分佈式緩存(Redis)、分佈式鎖(Redis 或 Zookeeper)、分佈式服務(Dubbo 或 SpringCloud)、分佈式服務協調(Zookeeper)、分佈式消息隊列(Kafka 、RabbitMq)、分佈式 Session 、分佈式事務、分佈式搜索(Elasticsearch)等。不可能全部分佈式內容都熟悉,必定要在某個領域有所專長。node
分佈式理論
問:分佈式有哪些理論?
CAP 、BASE。分佈式 CAP 理論,任何一個分佈式系統都沒法同時知足 Consistency(一致性)、Availability(可用性)、Partition tolerance(分區容錯性) 這三個基本需求。最多隻能知足其中兩項。而 Partition tolerance(分區容錯性) 是必須的,所以通常是 CP ,或者 AP。redis
問:你怎麼理解分佈式一致性?
數據一致性一般指關聯數據之間的邏輯關係是否正確和完整。在分佈式系統中,數據一致性每每指的是因爲數據的複製,不一樣數據節點中的數據內容是否完整而且相同。算法
一致性還分爲強一致性,弱一致性,還有最終一致性。強一致性就是立刻就保持一致。 最終一致性是指通過一段時間後,能夠保持一致。spring
分佈式事務
問:你怎麼理解分佈式事務?分佈式事務的協議有哪些?
分佈式事務是指會涉及到操做多個數據庫的事務。目的是爲了保證分佈式系統中的數據一致性。分佈式事務類型:二階段提交 2PC ,三階段提交 3PC。數據庫
-
2PC :第一階段:準備階段(投票階段)和第二階段:提交階段(執行階段)。json
-
3PC :三個階段:CanCommit 、PreCommit 、DoCommit。數組
問:分佈式事務的解決方案有哪些?
分佈式事務解決方案:補償機制 TCC 、XA 、消息隊列 MQ。瀏覽器
問:講一下 TCC。
T(Try)鎖資源:鎖定某個資源,設置一個預備類的狀態,凍結部分數據。緩存
-
好比,訂單的支付狀態,先把狀態修改成"支付中(PAYING)"。安全
-
好比,原本庫存數量是 100 ,如今賣出了 2 個,不要直接扣減這個庫存。在一個單獨的凍結庫存的字段,好比 prepare _ remove _ stock 字段,設置一個 2。也就是說,有 2 個庫存是給凍結了。
-
積分服務的也是同理,別直接給用戶增長會員積分。你能夠先在積分表裏的一個預增長積分字段加入積分。
-
好比:用戶積分本來是 1190 ,如今要增長 10 個積分,別直接 1190 + 10 = 1200 個積分啊!你能夠保持積分爲 1190 不變,在一個預增長字段裏,好比說 prepare _ add _ credit 字段,設置一個 10 ,表示有 10 個積分準備增長。
C(Confirm):在各個服務裏引入了一個 TCC 分佈式事務的框架,事務管理器能夠感知到各個服務的 Try 操做是否都成功了。假如都成功了, TCC 分佈式事務框架會控制進入 TCC 下一個階段,第一個 C 階段,也就是 Confirm 階段。此時,須要把 Try 階段鎖住的資源進行處理。
-
好比,把訂單的狀態設置爲「已支付(Payed)」。
-
好比,扣除掉相應的庫存。
-
好比,增長用戶積分。
C(Cancel):在 Try 階段,假如某個服務執行出錯,好比積分服務執行出錯了,那麼服務內的 TCC 事務框架是能夠感知到的,而後它會決定對整個 TCC 分佈式事務進行回滾。
TCC 分佈式事務框架只要感知到了任何一個服務的 Try 邏輯失敗了,就會跟各個服務內的 TCC 分佈式事務框架進行通訊,而後調用各個服務的 Cancel 邏輯。也就是說,會執行各個服務的第二個 C 階段, Cancel 階段。
-
好比,訂單的支付狀態,先把狀態修改成" closed "狀態。
-
好比,凍結庫存的字段, prepare _ remove _ stock 字段,將凍結的庫存 2 清零。
-
好比,預增長積分的字段, prepare _ add _ credit 字段,將準備增長的積分 10 清零。
問:事務管理器宕掉了,怎麼辦?
作冗餘,設置多個事務管理器,一個宕掉了,其餘的還能夠用。
問:怎麼保證分佈式系統的冪等性?
狀態機制。版本號機制。
Redis
問:Redis 有哪些優點?
-
速度快,由於數據存在內存中。
-
支持豐富數據類型,支持 string、list、set 、sorted set、hash。
-
支持事務,操做都是原子性,所謂的原子性就是對數據的更改要麼所有執行,要麼所有不執行。
-
豐富的特性:可用於緩存,消息,按 key 設置過時時間,過時後將會自動刪除。
-
單線程,單進程,採用 IO 多路複用技術。
問:Redis 的存儲結構是怎樣的?
key-value 鍵值對。
問:Redis 支持哪些數據結構?
string(字符串), hash(哈希), list(隊列), set(集合)及 zset(sorted set 有序集合)。
問:Redis 的數據結構,有哪些應用場景?
-
string:簡單地 get / set 緩存。
-
hash:能夠緩存用戶資料。好比命令:hmset user1 name "lin" sex "male" age "25" ,緩存用戶 user1 的資料,姓名爲 lin ,性別爲男,年齡 25。
-
list:能夠作隊列。往 list 隊列裏面 push 數據,而後再 pop 出來。
-
zset:能夠用來作排行榜。
問:Redis 的數據結構,底層分別是由什麼實現的?
-
Redis 字符串,卻不是 C 語言中的字符串(即以空字符 ’\0’ 結尾的字符數組),它是本身構建了一種名爲 簡單動態字符串(simple dynamic string , SDS)的抽象類型,並將 SDS 做爲 Redis 的默認字符串表示。
-
Redi List ,底層是 ZipList ,不知足 ZipList 就使用雙向鏈表。ZipList 是爲了節約內存而開發的。和各類語言的數組相似,它是由連續的內存塊組成的,這樣一來,因爲內存是連續的,就減小了不少內存碎片和指針的內存佔用,進而節約了內存。
問:Redis 怎麼保證可靠性?Redis 的持久化方式有哪些?有哪些優缺點?
一個可靠安全的系統,確定要考慮數據的可靠性,尤爲對於內存爲主的 Redis ,就要考慮一旦服務器掛掉,啓動以後,如何恢復數據的問題,也就是說數據如何持久化的問題。 AOF 就是備份操做記錄。AOF 因爲是備份操做命令,備份快、恢復慢。 AOF 的優勢:AOF 更好保證數據不會被丟失,最多隻丟失一秒內的數據。另外重寫操做保證了數據的有效性,即便日誌文件過大也會進行重寫。AOF 的日誌文件的記錄可讀性很是的高。 AOF 的缺點:對於相同數量的數據集而言, AOF 文件一般要大於 RDB 文件。 RDB 就是備份全部數據,使用了快照。RDB 恢復數據比較快。
問:AOF 文件過大,怎麼處理?
會進行 AOF 文件重寫。
-
隨着 AOF 文件愈來愈大,裏面會有大部分是重複命令或者能夠合併的命令。
-
重寫的好處:減小 AOF 日誌尺寸,減小內存佔用,加快數據庫恢復時間。
執行一個 AOF 文件重寫操做,重寫會建立一個當前 AOF 文件的體積優化版本。
問:講一下 Redis 的事務。
先以 MULTI 開始一個事務, 而後將多個命令入隊到事務中, 最後由 EXEC 命令觸發事務, 一併執行事務中的全部命令。若是想放棄這個事務,可使用 DISCARD 命令。
問:Redis 事務沒法回滾,那怎麼處理? 問:怎麼設置 Redis 的 key 過時時間?
key 的的過時時間經過 EXPIRE key seconds 命令來設置數據的過時時間。返回 1 代表設置成功,返回 0 代表 key 不存在或者不能成功設置過時時間。
問:Redis 的過時策略有哪些?
惰性刪除:當讀/寫一個已通過期的 key 時,會觸發惰性刪除策略,直接刪除掉這個過時 key ,並按照 key 不存在去處理。惰性刪除,對內存不太好,已通過期的 key 會佔用太多的內存。 按期刪除:每隔一段時間,就會對 Redis 進行檢查,主動刪除一批已過時的 key。
問:爲何 Redis 不使用定時刪除?
定時刪除,就是在設置 key 的過時時間的同時,建立一個定時器,讓定時器在過時時間來臨時,當即執行對 key 的刪除操做。 定時刪會佔用 CPU ,影響服務器的響應時間和性能。
問:Redis 的內存回收機制都有哪些?
當前已用內存超過 maxmemory 限定時,會觸發主動清理策略,也就是 Redis 的內存回收策略。 LRU 、TTL。 noeviction :默認策略,不會刪除任何數據,拒絕全部寫入操做並返回客戶端錯誤信息,此時 Redis 只響應讀操做。
-
volatitle - lru :根據 LRU 算法刪除設置了超時屬性的鍵,知道騰出足夠空間爲止。若是沒有可刪除的鍵對象,回退到 noeviction 策略。
-
allkeys - lru :根據 LRU 算法刪除鍵,無論數據有沒有設置超時屬性,直到騰出足夠空間爲止。
-
allkeys - random :隨機刪除全部鍵,知道騰出足夠空間爲止。
-
volatitle - random :隨機刪除過時鍵,知道騰出足夠空間爲止。
-
volatitle - ttl :根據鍵值對象的 ttl 屬性,刪除最近將要過時數據。若是沒有,回退到 noeviction 策略。
問:手寫一下 LRU 算法。 問:Redis 的搭建有哪些模式?
主從模式、哨兵模式、Cluster(集羣)模式。最好是用集羣模式。
問:你用過的 Redis 是多主多從的,仍是一主多從的?集羣用到了多少節點?用到了多少個哨兵?
集羣模式。三主三從。
問:Redis 採用多主多從的集羣模式,各個主節點的數據是否一致?
問:Redis 集羣有哪些特性
master 和 slaver。主從複製。讀寫分離。哨兵模式。
問:Redis 是怎麼進行水平擴容的? 問:Redis 集羣數據分片的原理是什麼?
Redis 數據分片原理是哈希槽(hash slot)。
Redis 集羣有 16384 個哈希槽。每個 Redis 集羣中的節點都承擔一個哈希槽的子集。
哈希槽讓在集羣中添加和移除節點很是容易。例如,若是我想添加一個新節點 D ,我須要從節點 A 、B、C 移動一些哈希槽到節點 D。一樣地,若是我想從集羣中移除節點 A ,我只須要移動 A 的哈希槽到 B 和 C。當節點 A 變成空的之後,我就能夠從集羣中完全刪除它。由於從一個節點向另外一個節點移動哈希槽並不須要中止操做,因此添加和移除節點,或者改變節點持有的哈希槽百分比,都不須要任何停機時間(downtime)。
問:講一下一致性 Hash 算法。
一致性 Hash 算法將整個哈希值空間組織成一個虛擬的圓環, 咱們對 key 進行哈希計算,使用哈希後的結果對 2 ^ 32 取模,hash 環上一定有一個點與這個整數對應。依此肯定此數據在環上的位置,今後位置沿環順時針「行走」,第一臺遇到的服務器就是其應該定位到的服務器。 一致性 Hash 算法對於節點的增減都只需重定位環空間中的一小部分數據,具備較好的容錯性和可擴展性。 好比,集羣有四個節點 Node A 、B 、C 、D ,增長一臺節點 Node X。Node X 的位置在 Node B 到 Node C 直接,那麼受到影響的僅僅是 Node B 到 Node X 間的數據,它們要從新落到 Node X 上。 因此一致性哈希算法對於容錯性和擴展性有很是好的支持。
問:爲何 Redis Cluster 分片不使用 Redis 一致性 Hash 算法?
一致性哈希算法也有一個嚴重的問題,就是數據傾斜。 若是在分片的集羣中,節點太少,而且分佈不均,一致性哈希算法就會出現部分節點數據太多,部分節點數據太少。也就是說沒法控制節點存儲數據的分配。
問:集羣的拓撲結構有沒有了解過?集羣是怎麼鏈接的?
無中心結構。Redis-Cluster 採用無中心結構,每一個節點保存數據和整個集羣狀態,每一個節點都和其餘全部節點鏈接。
問:講一下 Redis 主從複製的過程。
從機發送 SYNC(同步)命令,主機接收後會執行 BGSAVE(異步保存)命令備份數據。
主機備份後,就會向從機發送備份文件。主機以後還會發送緩衝區內的寫命令給從機。 當緩衝區命令發送完成後,主機執行一條寫命令,就會往從機發送同步寫入命令。
問:講一下 Redis 哨兵機制。
下面是 Redis 官方文檔對於哨兵功能的描述:
-
監控(Monitoring):哨兵會不斷地檢查主節點和從節點是否運做正常。
-
自動故障轉移(Automatic Failover):當主節點不能正常工做時,哨兵會開始自動故障轉移操做,它會將失效主節點的其中一個從節點升級爲新的主節點,並讓其餘從節點改成複製新的主節點。
-
配置提供者(Configuration Provider):客戶端在初始化時,經過鏈接哨兵來得到當前 Redis 服務的主節點地址。
-
通知(Notification):哨兵能夠將故障轉移的結果發送給客戶端。
問:講一下布隆過濾器。
布隆過濾器的主要是由一個很長的二進制向量和若干個(k 個)散列映射函數組成。由於每一個元數據的存儲信息值固定,並且總的二進制向量固定。因此在內存佔用和查詢時間上都遠遠超過通常的算法。固然存在必定的不許確率(能夠控制)和不容易刪除樣本數據。 布隆過濾器的優勢:大批量數據去重,特別的佔用內存。可是用布隆過濾器(Bloom Filter)會很是的省內存。 布隆過濾器的特色:當布隆過濾器說某個值存在時,那可能就不存在,若是說某個值不存在時,那確定就是不存在了。 布隆過濾器的應用場景:新聞推送(不重複推送)。解決緩存穿透的問題。
緩存
問:緩存雪崩是什麼?
若是緩存數據設置的過時時間是相同的,而且 Redis 剛好將這部分數據所有刪光了。這就會致使在這段時間內,這些緩存同時失效,所有請求到數據庫中。這就是緩存雪崩。
問:怎麼解決緩存雪崩?
解決方法:在緩存的時候給過時時間加上一個隨機值,這樣就會大幅度的減小緩存在同一時間過時。
問:緩存穿透是什麼?
緩存穿透是指查詢一個必定不存在的數據。因爲緩存不命中,而且出於容錯考慮,若是從數據庫查不到數據則不寫入緩存,這將致使這個不存在的數據每次請求都要到數據庫去查詢,失去了緩存的意義。
問:怎麼解決緩存穿透?
問:什麼是緩存與數據庫雙寫一致問題?
問:如何保證緩存與數據庫的一致性?
讀的時候,先讀緩存,緩存沒有的話,就讀數據庫,而後取出數據後放入緩存,同時返回響應。
先刪除緩存,再更新數據庫。
問:爲何是先刪除緩存,而不是先更新緩存?
問:先更新數據庫,再刪除緩存,會有什麼問題?
先更新數據庫,再刪除緩存。可能出現如下狀況:
-
若是更新完數據庫, Java 服務提交了事務,而後掛掉了,那 Redis 仍是會執行,這樣也會不一致。
-
若是更新數據庫成功,刪除緩存失敗了,那麼會致使數據庫中是新數據,緩存中是舊數據,數據就出現了不一致。
先刪除緩存,再更新數據庫。
-
若是刪除緩存失敗,那就不更新數據庫,緩存和數據庫的數據都是舊數據,數據是一致的。
-
若是刪除緩存成功,而數據庫更新失敗了,那麼數據庫中是舊數據,緩存中是空的,數據不會不一致。由於讀的時候緩存沒有,因此去讀了數據庫中的舊數據,而後更新到緩存中。
問:先刪除緩存,在寫數據庫成功以前,若是有讀請求發生,可能致使舊數據入緩存,引起數據不一致,怎麼處理?
分佈式鎖
問:Redis 如何實現分佈式鎖?
使用 set key value ex nx 命令。
-
當 key 不存在時,將 key 的值設爲 value ,返回 1。若給定的 key 已經存在,則 setnx 不作任何動做,返回 0。
-
當 setnx 返回 1 時,表示獲取鎖,作完操做之後 del key ,表示釋放鎖,若是 setnx 返回 0 表示獲取鎖失敗。
詳細的命令以下:
set key value [EX seconds] [PX milliseconds] [NX|XX]EX seconds:設置失效時長,單位秒PX milliseconds:設置失效時長,單位毫秒NX:key不存在時設置value,成功返回OK,失敗返回(nil)XX:key存在時設置value,成功返回OK,失敗返回(nil)。
複製代碼
示例以下:
set name fenglin ex 100 nx
複製代碼
問:爲何不先 set nx ,而後再使用 expire 設置超時時間?
咱們須要保證 setnx 命令和 expire 命令以原子的方式執行,不然若是客戶端執行 setnx 得到鎖後,這時客戶端宕機了,那麼這把鎖沒有設置過時時間,致使其餘客戶端永遠沒法得到鎖了。
問:使用 Redis 分佈式鎖, key 和 value 分別設置成什麼?
value 可使用 json 格式的字符串,示例:
{ "count":1, "expireAt":147506817232, "jvmPid":22224, "mac":"28-D2-44-0E-0D-9A", "threadId":14}
複製代碼
問:Redis 實現的分佈式鎖,若是某個系統獲取鎖後,宕機了怎麼辦?
系統模塊宕機的話,能夠經過設置過時時間(就是設置緩存失效時間)解決。系統宕機時鎖阻塞,過時後鎖釋放。 問:設置緩存失效時間,那若是前一個線程把這個鎖給刪除了呢?
問:若是加鎖和解鎖之間的業務邏輯執行的時間比較長,超過了鎖過時的時間,執行完了,又刪除了鎖,就會把別人的鎖給刪了。怎麼辦?
這兩個屬於鎖超時的問題。
能夠將鎖的 value 設置爲 Json 字符串,在其中加入線程的 id 或者請求的 id ,在刪除以前, get 一下這個 key ,判斷 key 對應的 value 是否是當前線程的。只有是當前線程獲取的鎖,當前線程才能夠刪除。
問:Redis 分佈式鎖,怎麼保證可重入性?
能夠將鎖的 value 設置爲 Json 字符串,在其中加入線程的 id 和 count 變量。
-
當 count 變量的值爲 0 時,表示當前分佈式鎖沒有被線程佔用。
-
若是 count 變量的值大於 0 ,線程 id 不是當前線程,表示當前分佈式鎖已經被其餘線程佔用。
-
若是 count 變量的值大於 0 ,線程 id 是當前線程的 id ,表示當前線程已經拿到了鎖,沒必要阻塞,能夠直接重入,並將 count 變量的值加一便可。
這種思路,其實就是參考了 ReentrantLock 可重入鎖的機制。
問:Redis 作分佈式鎖, Redis 作了主從,若是設置鎖以後,主機在傳輸到從機的時候掛掉了,從機尚未加鎖信息,如何處理?
可使用開源框架 Redisson ,採用了 redLock。
問:講一下 Redis 的 redLock。 問:Zookeeper 是怎麼實現分佈式鎖的?
分佈式鎖:基於 Zookeeper 一致性文件系統,實現鎖服務。鎖服務分爲保存獨佔及時序控制兩類。
-
保存獨佔:將 Zookeeper 上的一個 znode 看做是一把鎖,經過 createznode 的方式來實現。全部客戶端都去建立 / distribute _ lock 節點,最終成功建立的那個客戶端也即擁有了這把鎖。用完刪除本身建立的 distribute _ lock 節點就釋放鎖。
-
時序控制:基於/ distribute _ lock 鎖,全部客戶端在它下面建立臨時順序編號目錄節點,和選 master 同樣,編號最小的得到鎖,用完刪除,依次方便。
更詳細的回答以下:
其實基於 Zookeeper ,就是使用它的臨時有序節點來實現的分佈式鎖。
原理就是:當某客戶端要進行邏輯的加鎖時,就在 Zookeeper 上的某個指定節點的目錄下,去生成一個惟一的臨時有序節點, 而後判斷本身是不是這些有序節點中序號最小的一個,若是是,則算是獲取了鎖。若是不是,則說明沒有獲取到鎖,那麼就須要在序列中找到比本身小的那個節點,並對其調用 exist() 方法,對其註冊事件監聽,當監聽到這個節點被刪除了,那就再去判斷一次本身當初建立的節點是否變成了序列中最小的。若是是,則獲取鎖,若是不是,則重複上述步驟。
當釋放鎖的時候,只需將這個臨時節點刪除便可。
Zookeeper
問:Zookeeper 的原理是什麼?
問:Zookeeper 是怎麼保證一致性的?
zab 協議。
zab 協議有兩種模式,它們分別是恢復模式(選主)和廣播模式(同步)。當服務啓動或者在領導者崩潰後, zab 就進入了恢復模式,當領導者被選舉出來,且大多數 server 完成了和 leader 的狀態同步之後,恢復模式就結束了。狀態同步保證了 leader 和 server 具備相同的系統狀態。
問:Zookeeper 有哪些應用場景?
Zookeeper 能夠做爲服務協調的註冊中心。還能夠作分佈式鎖(若是沒有用過度布式鎖就不要說)。
問:Zookeeper 爲何能作註冊中心?
Zookeeper 的數據模型是樹型結構,由不少數據節點組成, zk 將全量數據存儲在內存中,可謂是高性能,並且支持集羣,可謂高可用。另外支持事件監聽(watch 命令)。
Zookeeper 能夠做爲一個數據發佈/訂閱系統。
問:Zookeeper 的節點有哪些類型?有什麼區別?
臨時節點,永久節點。更加細分就是臨時有序節點、臨時無序節點、永久有序節點、永久無序節點。
臨時節點:當建立臨時節點的程序停掉以後,這個臨時節點就會消失,存儲的數據也沒有了。
問:Zookeeper 作爲註冊中心,主要存儲哪些數據?存儲在哪裏?
IP、端口、還有心跳機制。數據存儲在 Zookeeper 的節點上面。
問:心跳機制有什麼用? 問:Zookeeper 的廣播模式有什麼缺陷?
廣播風暴。
問:講一下 Zookeeper 的讀寫機制。
-
Leader 主機負責讀和寫。
-
Follower 負責讀,並將寫操做轉發給 Leader。Follower 還參與 Leader 選舉投票,參與事務請求 Proposal 投票。
-
Observer 充當觀察者的角色。Observer 和 Follower 的惟一區別在於:Observer 不參與任何投票。
問:講一下 Zookeeper 的選舉機制。
Leader 不可用時,會從新選舉 Leader。超過半數的 Follower 選舉投票便可,Observer 不參與投票。
問:大家的 Zookeeper 集羣配置了幾個節點?
3 個節點。注意:Zookeeper 集羣節點,最好是奇數個的。
集羣中的 Zookeeper 節點須要超過半數,整個集羣對外才可用。
這裏所謂的整個集羣對外才可用,是指整個集羣還能選出一個 Leader 來, Zookeeper 默認採用 quorums 來支持 Leader 的選舉。
若是有 2 個 Zookeeper,那麼只要有 1 個死了 Zookeeper 就不能用了,由於 1 沒有過半,因此 2 個 Zookeeper 的死亡容忍度爲 0 ;同理,要是有 3 個 Zookeeper,一個死了,還剩下 2 個正常的,過半了,因此 3 個 Zookeeper 的容忍度爲 1 ;同理你多列舉幾個:2 -> 0 ; 3 -> 1 ; 4 -> 1 ; 5 -> 2 ; 6 -> 2 會發現一個規律, 2n 和 2n - 1 的容忍度是同樣的,都是 n - 1 ,因此爲了更加高效,何須增長那一個沒必要要的 Zookeeper 呢。
問:Zookeeper 的集羣節點,若是不是奇數可能會出現什麼問題?
可能會出現腦裂。
-
假死:因爲心跳超時(網絡緣由致使的)認爲 master 死了,但其實 master 還存活着。
-
腦裂:因爲假死會發起新的 master 選舉,選舉出一個新的 master ,但舊的 master 網絡又通了,致使出現了兩個 master ,有的客戶端鏈接到老的 master 有的客戶端連接到新的 master。
消息隊列
問:爲何使用消息隊列?消息隊列有什麼優勢和缺點?Kafka 、ActiveMQ 、RabbitMq 、RocketMQ 都有什麼優勢和缺點?
消息隊列解耦,削峯,限流。
問:如何保證消息隊列的高可用?(多副本)
問:如何保證消息不被重複消費?(如何保證消息消費的冪等性)
問:如何保證消息的可靠性傳輸?(如何處理消息丟失的問題)
問:如何保證消息的順序性?
問:如何解決消息隊列的延時以及過時失效問題?消息隊列滿了之後該怎麼處理?有幾百萬消息持續積壓幾小時,說說怎麼解決?
問:若是讓你寫一個消息隊列,該如何進行架構設計啊?說一下你的思路。
Kafka
問:講一下 Kafka。
Kafka 的簡單理解
問:Kafka 相對其餘消息隊列,有什麼特色?
-
持久化:Kafka 的持久化能力比較好,經過磁盤持久化。而 RabbitMQ 是經過內存持久化的。
-
吞吐量:Rocket 的併發量很是高。
-
消息處理:RabbitMQ 的消息不支持批量處理,而 RocketMQ 和 Kafka 支持批量處理。
-
高可用:RabbitMQ 採用主從模式。Kafka 也是主從模式,經過 Zookeeper 管理,選舉 Leader ,還有 Replication 副本。
-
事務:RocketMQ 支持事務,而 Kafka 和 RabbitMQ 不支持。
問:Kafka 有哪些模式?
若是一個生產者或者多個生產者產生的消息可以被多個消費者同時消費的狀況,這樣的消息隊列稱爲"發佈訂閱模式"的消息隊列。
問:Kafka 做爲消息隊列,有哪些優點?
分佈式的消息系統。
高吞吐量。即便存儲了許多 TB 的消息,它也保持穩定的性能。 數據保留在磁盤上,所以它是持久的。
問:Kafka 爲何處理速度會很快?kafka 的吞吐量爲何高?
-
零拷貝:Kafka 實現了"零拷貝"原理來快速移動數據,避免了內核之間的切換。
-
消息壓縮、分批發送:Kafka 能夠將數據記錄分批發送,從生產者到文件系統(Kafka 主題日誌)到消費者,能夠端到端的查看這些批次的數據。
-
批處理可以進行更有效的數據壓縮並減小 I / O 延遲。
-
順序讀寫:Kafka 採起順序寫入磁盤的方式,避免了隨機磁盤尋址的浪費。
問:講一下 Kafka 中的零拷貝。
數據的拷貝從內存拷貝到 kafka 服務進程那塊,又拷貝到 socket 緩存那塊,整個過程耗費的時間比較高, kafka 利用了 Linux 的 sendFile 技術(NIO),省去了進程切換和一次數據拷貝,讓性能變得更好。
問:Kafka 的偏移量是什麼?
消費者每次消費數據的時候,消費者都會記錄消費的物理偏移量(offset)的位置。等到下次消費時,他會接着上次位置繼續消費
問:Kafka 的生產者,是如何發送消息的?
-
生產者的消息是先被寫入分區中的緩衝區中,而後分批次發送給 Kafka Broker。
-
生產者的消息發送機制,有同步發送和異步發送。
-
同步發送消息都有個問題,那就是同一時間只能有一個消息在發送,這會形成許多消息。
-
沒法直接發送,形成消息滯後,沒法發揮效益最大化。
-
異步發送消息的同時可以對異常狀況進行處理,生產者提供了 Callback 回調。
問:Kafka 生產者發送消息,有哪些分區策略?
Kafka 的分區策略指的就是將生產者發送到哪一個分區的算法。有順序輪詢、隨機輪詢、key - ordering 策略。
key - ordering 策略:Kafka 中每條消息都會有本身的 key ,一旦消息被定義了 Key ,那麼你就能夠保證同一個 Key 的全部消息都進入到相同的分區裏面,因爲每一個分區下的消息處理都是有順序的,故這個策略被稱爲按消息鍵保序策略。
問:Kafka 爲何要分區?
實現負載均衡和水平擴展。Kafka 能夠將主題(Topic)劃分爲多個分區(Partition),會根據分區規則選擇把消息存儲到哪一個分區中,只要若是分區規則設置的合理,那麼全部的消息將會被均勻的分佈到不一樣的分區中,這樣就實現了負載均衡和水平擴展。另外,多個訂閱者能夠從一個或者多個分區中同時消費數據,以支撐海量數據處理能力。
問:Kafka 是如何在 Broker 間分配分區的?
在 broker 間平均分佈分區副本。
假設有 6 個 broker ,打算建立一個包含 10 個分區的 Topic ,複製係數爲 3 ,那麼 Kafka 就會有 30 個分區副本,它能夠被分配給這 6 個 broker ,這樣的話,每一個 broker 能夠有 5 個副本。
要確保每一個分區的每一個副本分佈在不一樣的 broker 上面:
假設 Leader 分區 0 會在 broker1 上面, Leader 分區 1 會在 broker2 上面, Leder 分區 2 會在 broker3 上面。
接下來會分配跟隨者副本。若是分區 0 的第一個 Follower 在 broker2 上面,第二個 Follower 在 broker3 上面。分區 1 的第一個 Follower 在 broker3 上面,第二個 Follower 在 broker4 上面。
問:Kafka 如何保證消息的順序性?
Kafka 能夠保證同一個分區裏的消息是有序的。也就是說消息發送到一個 Partition 是有順序的。
問:Kafka 的消費者羣組 Consumer Group 訂閱了某個 Topic ,假如這個 Topic 接收到消息並推送,那整個消費者羣組能收到消息嗎?
Kafka 官網中有這樣一句" Consumers label themselves with a consumer group name , and each record published to a topic is delivered to one consumer instance within each subscribing consumer group . "
表示推送到 topic 上的 record ,會被傳遞到已訂閱的消費者羣組裏面的一個消費者實例。
問:如何提升 Kafka 的消費速度?
問:Kafka 出現消息積壓,有哪些緣由?怎麼解決?
出現消息積壓,多是由於消費的速度太慢。
擴容消費者。之因此消費延遲大,就是消費者處理能力有限,能夠增長消費者的數量。 擴大分區。一個分區只能被消費者羣組中的一個消費者消費。消費者擴大,分區最好多隨之擴大。
問:Kafka 消息消費者宕機了,怎麼確認有沒有收到消息?
ACK 機制,若是接收方收到消息後,會返回一個確認字符。
問:講一下 Kafka 的 ACK 機制。
acks 參數指定了要有多少個分區副本接收消息,生產者才認爲消息是寫入成功的。此參數對消息丟失的影響較大。
若是 acks = 0 ,就表示生產者也不知道本身產生的消息是否被服務器接收了,它才知道它寫成功了。若是發送的途中產生了錯誤,生產者也不知道,它也比較懵逼,由於沒有返回任何消息。這就相似於 UDP 的運輸層協議,只管發,服務器接受不接受它也不關心。
若是 acks = 1 ,只要集羣的 Leader 接收到消息,就會給生產者返回一條消息,告訴它寫入成功。若是發送途中形成了網絡異常或者 Leader 還沒選舉出來等其餘狀況致使消息寫入失敗,生產者會受到錯誤消息,這時候生產者每每會再次重發數據。由於消息的發送也分爲 同步 和 異步, Kafka 爲了保證消息的高效傳輸會決定是同步發送仍是異步發送。若是讓客戶端等待服務器的響應(經過調用 Future 中的 get() 方法),顯然會增長延遲,若是客戶端使用回調,就會解決這個問題。
若是 acks = all ,這種狀況下是隻有當全部參與複製的節點都收到消息時,生產者纔會接收到一個來自服務器的消息。不過,它的延遲比 acks = 1 時更高,由於咱們要等待不僅一個服務器節點接收消息。
問:Kafka 如何避免消息丟失?
一、生產者丟失消息的狀況
生產者(Producer) 調用 send 方法發送消息以後,消息可能由於網絡問題並無發送過去。
因此,咱們不能默認在調用 send 方法發送消息以後消息消息發送成功了。爲了肯定消息是發送成功,咱們要判斷消息發送的結果。能夠採用爲其添加回調函數的形式,獲取回調結果。
若是消息發送失敗的話,咱們檢查失敗的緣由以後從新發送便可!能夠設置 Producer 的 retries(重試次數)爲一個比較合理的值,通常是 3 ,可是爲了保證消息不丟失的話通常會設置比較大一點。
設置完成以後,當出現網絡問題以後可以自動重試消息發送,避免消息丟失。
二、消費者丟失消息的狀況
當消費者拉取到了分區的某個消息以後,消費者會自動提交了 offset。自動提交的話會有一個問題,試想一下,當消費者剛拿到這個消息準備進行真正消費的時候,忽然掛掉了,消息實際上並無被消費,可是 offset 卻被自動提交了。手動關閉閉自動提交 offset ,每次在真正消費完消息以後以後再本身手動提交 offset 。
3 、Kafka 丟失消息
a、假如 leader 副本所在的 broker 忽然掛掉,那麼就要從 follower 副本從新選出一個 leader ,可是 leader 的數據還有一些沒有被 follower 副本的同步的話,就會形成消息丟失。所以能夠設置 ack = all。
b、設置 replication . factor >= 3 。爲了保證 leader 副本能有 follower 副本能同步消息,咱們通常會爲 topic 設置 replication . factor >= 3。這樣就能夠保證每一個 分區(partition) 至少有 3 個副本。雖然形成了數據冗餘,可是帶來了數據的安全性。
問:Kafka 怎麼保證可靠性?
多副本以及 ISR 機制。
在 Kafka 中主要經過 ISR 機制來保證消息的可靠性。
ISR(in sync replica):是 Kafka 動態維護的一組同步副本,在 ISR 中有成員存活時,只有這個組的成員才能夠成爲 leader ,內部保存的爲每次提交信息時必須同步的副本(acks = all 時),每當 leader 掛掉時,在 ISR 集合中選舉出一個 follower 做爲 leader 提供服務,當 ISR 中的副本被認爲壞掉的時候,會被踢出 ISR ,當從新跟上 leader 的消息數據時,從新進入 ISR。
問:什麼是 HW ?
HW(high watermark):副本的高水印值, replica 中 leader 副本和 follower 副本都會有這個值,經過它能夠得知副本中已提交或已備份消息的範圍, leader 副本中的 HW ,決定了消費者能消費的最新消息能到哪一個 offset。
問:什麼是 LEO ?
LEO(log end offset):日誌末端位移,表明日誌文件中下一條待寫入消息的 offset ,這個 offset 上實際是沒有消息的。無論是 leader 副本仍是 follower 副本,都有這個值。
問:Kafka 怎麼保證一致性?(存疑)
一致性定義:若某條消息對 client 可見,那麼即便 Leader 掛了,在新 Leader 上數據依然能夠被讀到。 HW - HighWaterMark : client 能夠從 Leader 讀到的最大 msg offset ,即對外可見的最大 offset , HW = max(replica . offset)
對於 Leader 新收到的 msg , client 不能馬上消費, Leader 會等待該消息被全部 ISR 中的 replica 同步後,更新 HW ,此時該消息才能被 client 消費,這樣就保證了若是 Leader fail ,該消息仍然能夠重新選舉的 Leader 中獲取。 對於來自內部 Broker 的讀取請求,沒有 HW 的限制。同時, Follower 也會維護一份本身的 HW , Folloer . HW = min(Leader . HW , Follower . offset).
問:Kafka 怎麼處理重複消息?怎麼避免重複消費?
偏移量 offset :消費者每次消費數據的時候,消費者都會記錄消費的物理偏移量(offset)的位置。等到下次消費時,他會接着上次位置繼續消費。 通常狀況下, Kafka 重複消費都是因爲未正常提交 offset 形成的,好比網絡異常,消費者宕機之類的。 使用的是 spring-Kafka ,因此把 Kafka 消費者的配置 enable.auto. commit 設爲 false ,禁止 Kafka 自動提交 offset ,從而使用 spring-Kafka 提供的 offset 提交策略。
sprin-Kafka 中的 offset 提交策略能夠保證一批消息數據沒有完成消費的狀況下,也能提交 offset ,從而避免了提交失敗而致使永遠重複消費的問題。
問:怎麼避免重複消費?
將消息的惟一標識保存起來,每次消費時判斷是否處理過便可。
問:如何保證消息不被重複消費?(如何保證消息消費的冪等性)
怎麼保證消息隊列消費的冪等性?其實仍是得結合業務來思考,有幾個思路: 好比你拿個數據要寫庫,你先根據主鍵查一下,若是這數據都有了,你就別插入了, update 一下好吧。 好比你是寫 Redis ,那沒問題了,反正每次都是 set ,自然冪等性。 若是是複雜一點的業務,那麼每條消息加一個全局惟一的 id ,相似訂單 id 之類的東西,而後消費到了以後,先根據這個 id 去好比 Redis 裏查一下,以前消費過嗎? 若是沒有消費過,你就處理,而後這個 id 寫 Redis。若是消費過了,那你就別處理了,保證別重複處理相同的消息便可。
問:Kafka 消息是採用 pull 模式,仍是 push 模式?
問:pull 模式和 push 模式,各有哪些特色?
pull 模式,準確性?能夠較大保證消費者能獲取到消息。
push 模式,即時性?能夠在 broker 獲取消息後立刻送達消費者。
問:Kafka 是如何存儲消息的?
Kafka 使用日誌文件的方式來保存生產者和發送者的消息,每條消息都有一個 offset 值來表示它在分區中的偏移量。
Kafka 中存儲的通常都是海量的消息數據,爲了不日誌文件過大, 一個分片並非直接對應在一個磁盤上的日誌文件,而是對應磁盤上的一個目錄。 數據存儲設計的特色在於如下幾點:
-
Kafka 把主題中一個分區劃分紅多個分段的小文件段,經過多個小文件段,就容易根據偏移量查找消息、按期清除和刪除已經消費完成的數據文件,減小磁盤容量的佔用;
-
採用稀疏索引存儲的方式構建日誌的偏移量索引文件,並將其映射至內存中,提升查找消息的效率,同時減小磁盤 IO 操做;
-
Kafka 將消息追加的操做邏輯變成爲日誌數據文件的順序寫入,極大的提升了磁盤 IO 的性能;
問:講一下 Kafka 集羣的 Leader 選舉機制。
Kafka 在 Zookeeper 上針對每一個 Topic 都維護了一個 ISR(in - sync replica ---已同步的副本)的集合,集合的增減 Kafka 都會更新該記錄。若是某分區的 Leader 不可用, Kafka 就從 ISR 集合中選擇一個副本做爲新的 Leader。
分庫分表
問:數據庫如何處理海量數據?
分庫分表,主從架構,讀寫分離。
問:數據庫分庫分表,什麼時候分?怎麼分?
水平分庫/分表,垂直分庫/分表。
-
水平分庫/表,各個庫和表的結構如出一轍。
-
垂直分庫/表,各個庫和表的結構不同。
問:讀寫分離怎麼作?
主機負責寫,從機負責讀。
系統設計
一、分佈式、高併發場景
遇到高併發場景,可使用 Redis 緩存、Redis 限流、MQ 異步、MQ 削峯等。
問:在實踐中,遇到過哪些併發的業務場景?
秒殺。好比搶商品,搶紅包。
二、秒殺
問:如何設計一個秒殺/搶券系統?
-
能夠經過隊列配合異步處理實現秒殺。
-
使用 redis 的 list ,將商品 push 進隊列, pop 出隊列。
-
異步操做不會阻塞,不會消耗太多時間。
問:如何提升搶券系統的性能?
-
使用多個 list。
-
使用多線程從隊列中拉取數據。
-
集羣提升可用性。
-
MQ 異步處理,削峯。
問:秒殺怎麼避免少賣或超賣?
redis 是單進程單線程的,操做具備原子性,不會致使少賣或者超賣。另外,也能夠設置一個版本號 version ,樂觀鎖機制。
問:考勤打卡,假如高峯期有幾萬人同時打卡,那麼怎麼應對這種高併發?
使用 Redis 緩存。員工點擊簽到,能夠在緩存中 set 狀態。將工號做爲 key ,打卡狀態做爲 value ,打卡成功爲 01 ,未打卡或者打卡失敗爲 00 ,而後再將數據異步地寫入到數據庫裏面就能夠了。
問:如何應對高峯期的超高併發量?
Redis 限流。Redis 能夠用計數器限流。使用 INCR 命令,每次都加一,處理完業務邏輯就減一。而後設置一個最大值,當達到最大值後就直接返回,不處理後續的邏輯。
Redis 還能夠用令牌桶限流。使用 Redis 隊列,每十個數據中 push 一個令牌桶,每一個請求進入後會先從隊列中 pop 數據,若是是令牌就能夠通行,不是令牌就直接返回。
三、短連接 問:如何將長連接轉換成短連接,併發送短信?
短 URL 從生成到使用分爲如下幾步:
-
有一個服務,將要發送給你的長 URL 對應到一個短 URL 上.例如 www.baidu.com -> www.t.cn/1。
-
把短 url 拼接到短信等的內容上發送。
-
用戶點擊短 URL ,瀏覽器用 301 / 302 進行重定向,訪問到對應的長 URL。
-
展現對應的內容。
問:長連接和短連接如何互相轉換?
思路是創建一個發號器。每次有一個新的長 URL 進來,咱們就增長一。而且將新的數值返回.第一個來的 url 返回"www.x.cn/0",第二個返回"www.x.cn/1". 問:長連接和短連接的對應關係如何存儲?
若是數據量小且 QPS 低,直接使用數據庫的自增主鍵就能夠實現。 還能夠將最近/最熱門的對應關係存儲在 K-V 數據庫中,這樣子能夠節省空間的同時,加快響應速度。
系統架構與設計
問:如何提升系統的併發能力?
-
使用分佈式系統。
-
部署多臺服務器,並作負載均衡。
-
使用緩存(Redis)集羣。
-
數據庫分庫分表 + 讀寫分離。
-
引入消息中間件集羣。
問:設計一個紅包系統,須要考慮哪些問題,如何解決?(本質上也是秒殺系統)
問:若是讓你設計一個消息隊列,你會怎麼設計?
項目經驗及數據量 問:這個項目的亮點、難點在哪裏?
問:若是這個模塊掛掉了怎麼辦? 問:大家的項目有多少臺機器?
問:大家的項目有多少個實例?
問:大家的系統 QPS(TPS)是多少?
QPS ,每秒查詢量。QPS 爲幾百/幾千,已經算是比較高的了。 TPS ,每秒處理事務數。TPS 即每秒處理事務數,包括:」用戶請求服務器」、」服務器本身的內部處理」、」服務器返回給用戶」,這三個過程,每秒可以完成 N 個這三個過程, TPS 也就是 3。
問:一個接口,多少秒響應才正常?
快的話幾毫秒。慢的話 1-2 秒。異常狀況可能會 10 幾秒;最好保證 99 %以上的請求是正常的。
問:這個接口的請求時間,大概多久?主要耗時在哪裏? 問:系統的數據量多少?有沒有分庫分表?
正常狀況下,幾百萬的數據量沒有必要分庫分表。只有超過幾千萬才須要分庫分表。
問:插入/更新一條數據要多久?更新十萬/百萬條數據要多久?
插入/更新一條數據通常要幾毫秒;更新十萬條數據最好在 10 秒之內; 百萬條數據最好在 50-100 秒之內。