分布式分為分布式快取(Redis)、分布式鎖(Redis 或 Zookeeper)、分布式服務(Dubbo 或 SpringCloud)、分布式服務協調(Zookeeper)、分布式訊息佇列(Kafka 、RabbitMq)、分布式 Session 、分布式事務、分布式搜索(Elasticsearch)等,不可能所有分布式內容都熟悉,一定要在某個領域有所專長,
分布式理論
問:分布式有哪些理論?
CAP 、BASE,分布式 CAP 理論,任何一個分布式系統都無法同時滿足 Consistency(一致性)、Availability(可用性)、Partition tolerance(磁區容錯性) 這三個基本需求,最多只能滿足其中兩項,而 Partition tolerance(磁區容錯性) 是必須的,因此一般是 CP ,或者 AP,
問:你怎么理解分布式一致性?
資料一致性通常指關聯資料之間的邏輯關系是否正確和完整,在分布式系統中,資料一致性往往指的是由于資料的復制,不同資料節點中的資料內容是否完整并且相同,
一致性還分為強一致性,弱一致性,還有最終一致性,強一致性就是馬上就保持一致, 最終一致性是指經過一段時間后,可以保持一致,
分布式事務
問:你怎么理解分布式事務?分布式事務的協議有哪些?
分布式事務是指會涉及到操作多個資料庫的事務,目的是為了保證分布式系統中的資料一致性,分布式事務型別:二階段提交 2PC ,三階段提交 3PC,
-
2PC :第一階段:準備階段(投票階段)和第二階段:提交階段(執行階段),
-
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 如何避免訊息丟失?
1、生產者丟失訊息的情況
生產者(Producer) 呼叫 send 方法發送訊息之后,訊息可能因為網路問題并沒有發送過去,
所以,我們不能默認在呼叫 send 方法發送訊息之后訊息訊息發送成功了,為了確定訊息是發送成功,我們要判斷訊息發送的結果,可以采用為其添加回呼函式的形式,獲取回呼結果,
如果訊息發送失敗的話,我們檢查失敗的原因之后重新發送即可!可以設定 Producer 的 retries(重試次數)為一個比較合理的值,一般是 3 ,但是為了保證訊息不丟失的話一般會設定比較大一點,
設定完成之后,當出現網路問題之后能夠自動重試訊息發送,避免訊息丟失,
2、消費者丟失訊息的情況
當消費者拉取到了磁區的某個訊息之后,消費者會自動提交了 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 模式,
問: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,
分庫分表
問:資料庫如何處理海量資料?
分庫分表,主從架構,讀寫分離,
問:資料庫分庫分表,何時分?怎么分?
水平分庫/分表,垂直分庫/分表,
-
水平分庫/表,各個庫和表的結構一模一樣,
-
垂直分庫/表,各個庫和表的結構不一樣,
問:讀寫分離怎么做?
主機負責寫,從機負責讀,
系統設計
1、分布式、高并發場景
遇到高并發場景,可以使用 Redis 快取、Redis 限流、MQ 異步、MQ 削峰等,
問:在實踐中,遇到過哪些并發的業務場景?
秒殺,比如搶商品,搶紅包,
2、秒殺
問:如何設計一個秒殺/搶券系統?
-
可以通過佇列配合異步處理實作秒殺,
-
使用 redis 的 list ,將商品 push 進佇列, pop 出佇列,
-
異步操作不會阻塞,不會消耗太多時間,
問:如何提高搶券系統的性能?
-
使用多個 list,
-
使用多執行緒從佇列中拉取資料,
-
集群提高可用性,
-
MQ 異步處理,削峰,
問:秒殺怎么避免少賣或超賣?
redis 是單行程單執行緒的,操作具有原子性,不會導致少賣或者超賣,另外,也可以設定一個版本號 version ,樂觀鎖機制,
問:考勤打卡,假如高峰期有幾萬人同時打卡,那么怎么應對這種高并發?
使用 Redis 快取,員工點擊簽到,可以在快取中 set 狀態,將工號作為 key ,打卡狀態作為 value ,打卡成功為 01 ,未打卡或者打卡失敗為 00 ,然后再將資料異步地寫入到資料庫里面就可以了,
問:如何應對高峰期的超高并發量?
Redis 限流,Redis 可以用計數器限流,使用 INCR 命令,每次都加一,處理完業務邏輯就減一,然后設定一個最大值,當達到最大值后就直接回傳,不處理后續的邏輯,
Redis 還可以用令牌桶限流,使用 Redis 佇列,每十個資料中 push 一個令牌桶,每個請求進入后會先從佇列中 pop 資料,如果是令牌就可以通行,不是令牌就直接回傳,
3、短鏈接 問:如何將長鏈接轉換成短鏈接,并發送短信?
短 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)集群,
-
資料庫分庫分表 + 讀寫分離,
-
引入訊息中間件集群,
問:設計一個紅包系統,需要考慮哪些問題,如何解決?(本質上也是秒殺系統)
問:如果讓你設計一個訊息佇列,你會怎么設計?
專案經驗及資料量 問:這個專案的亮點、難點在哪里?
問:如果這個模塊掛掉了怎么辦? 問:你們的專案有多少臺機器?
問:你們的專案有多少個實體?
4 個實體,
問:你們的系統 QPS(TPS)是多少?
QPS ,每秒查詢量,QPS 為幾百/幾千,已經算是比較高的了, TPS ,每秒處理事務數,TPS 即每秒處理事務數,包括:”用戶請求服務器”、”服務器自己的內部處理”、”服務器回傳給用戶”,這三個程序,每秒能夠完成 N 個這三個程序, TPS 也就是 3,
問:一個介面,多少秒回應才正常?
快的話幾毫秒,慢的話 1-2 秒,例外情況可能會 10 幾秒;最好保證 99 %以上的請求是正常的,
問:這個介面的請求時間,大概多久?主要耗時在哪里? 問:系統的資料量多少?有沒有分庫分表?
正常情況下,幾百萬的資料量沒有必要分庫分表,只有超過幾千萬才需要分庫分表,
問:插入/更新一條資料要多久?更新十萬/百萬條資料要多久?
插入/更新一條資料一般要幾毫秒;更新十萬條資料最好在 10 秒以內; 百萬條資料最好在 50-100 秒以內,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/242707.html
標籤:其他
上一篇:Java 定時調配 Timer 類和定任務 TimerTask 類
下一篇:harbor安裝實操筆記
