分布式分為分布式緩存(Redis)、分布式鎖(Redis或Zookeeper)、分布式服務(Dubbo或SpringCloud)、分布式服務協調(Zookeeper)、分布式消息隊列(Kafka、RabbitMq)、分布式Session、分布式事務、分布式搜索(elastaticSearch)等。
不可能所有分布式內容都熟悉,一定要在某個領域有所專長。
分布式理論
Q:分布式有哪些理論?
CAP、BASE。
分布式CAP理論,任何一個分布式系統都無法同時滿足Consistency(一致性)、Availability(可用性)、Partition tolerance(分區容錯性) 這三個基本需求。最多只能滿足其中兩項。
而Partition tolerance(分區容錯性) 是必須的,因此一般是CP,或者AP。
Q:你怎么理解分布式一致性?
數據一致性通常指關聯數據之間的邏輯關系是否正確和完整。
在分布式系統中,數據一致性往往指的是由於數據的復制,不同數據節點中的數據內容是否完整並且相同。
一致性還分為強一致性,弱一致性,還有最終一致性。
強一致性就是馬上就保持一致。
最終一致性是指經過一段時間后,可以保持一致。
分布式事務
Q:你怎么理解分布式事務?分布式事務的協議有哪些?
分布式事務是指會涉及到操作多個數據庫的事務。目的是為了保證分布式系統中的數據一致性。
分布式事務類型:二階段提交2PC,三階段提交3PC。
2PC:第一階段:准備階段(投票階段)和第二階段:提交階段(執行階段)。
3PC:三個階段:CanCommit、PreCommit、DoCommit
Q:分布式事務的解決方案有哪些?
分布式事務解決方案:補償機制TCC、XA、消息隊列MQ。
詳情見:https://www.cnblogs.com/expiator/p/9841703.html
面試題詳細答案見:https://juejin.im/post/5d70b0535188251637727de8
Q:講一下TCC
T(try):鎖資源。鎖定某個資源,設置一個預備類的狀態,凍結部分數據。
比如,訂單的支付狀態,先把狀態修改為"支付中(PAYING)"。
比如,本來庫存數量是 100,現在賣出了2個,不要直接扣減這個庫存。
在一個單獨的凍結庫存的字段,比如prepare_remove_stock字段,設置一個 2。也就是說,有 2 個庫存是給凍結了。
積分服務的也是同理,別直接給用戶增加會員積分。你可以先在積分表里的一個預增加積分字段加入積分。
比如:用戶積分原本是 1190,現在要增加 10 個積分,別直接 1190 + 10 = 1200 個積分啊!
你可以保持積分為 1190 不變,在一個預增加字段里,比如說 prepare_add_credit 字段,設置一個 10,表示有 10 個積分准備增加。
C(comfirm):
在各個服務里引入了一個 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清零。
詳情見:https://www.cnblogs.com/jajian/p/10014145.html
Q:事務管理器宕掉了,怎么辦?
做冗余,設置多個事務管理器,一個宕掉了,其他的還可以用。
Q:怎么保證分布式系統的冪等性?
狀態機制。版本號機制。
Redis
Q:Redis有哪些優勢?
1.速度快,因為數據存在內存中
2.支持豐富數據類型,支持string,list,set,sorted set,hash
3.支持事務,操作都是原子性,所謂的原子性就是對數據的更改要么全部執行,要么全部不執行
4.豐富的特性:可用於緩存,消息,按key設置過期時間,過期后將會自動刪除
5.單線程,單進程,采用IO多路復用技術。
Q:Redis的存儲結構是怎樣的?
key-value鍵值對。
Q:Redis支持哪些數據結構?
string(字符串),hash(哈希),list(隊列),set(集合)及zset(sorted set:有序集合)。
Q:Redis的數據結構,有哪些應用場景?
string,簡單地get/set緩存。
hash,可以緩存用戶資料。比如命令: hmset user1 name "lin" sex "male" age "25" ,緩存用戶user1的資料,姓名為lin,性別為男,年齡25。
list,可以做隊列。往list隊列里面push數據,然后再pop出來。
zset,可以用來做排行榜。
詳情見:https://www.cnblogs.com/expiator/p/10274151.html
Q:Redis的數據結構,底層分別是由什么實現的?
Redis的字符串,卻不是 C 語言中的字符串(即以空字符’\0’結尾的字符數組),它是自己構建了一種名為 簡單動態字符串(simple dynamic string,SDS)的抽象類型,並將 SDS 作為 Redis的默認字符串表示。
Redis的List,底層是ZipList,不滿足ZipList就使用雙向鏈表。ZipList是為了節約內存而開發的。和各種語言的數組類似,它是由連續的內存塊組成的,這樣一來,由於內存是連續的,就減少了很多內存碎片和指針的內存占用,進而節約了內存。
參考資料:https://www.cnblogs.com/ysocean/p/9102811.html
詳情見:https://blog.csdn.net/Sqdmn/article/details/103634208?utm_medium=distribute.wap_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.nonecase&depth_1-utm_source=distribute.wap_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.nonecase
Q:Redis的持久化方式有哪些?有哪些優缺點?
aof,就是備份操作記錄。aof由於是備份操作命令,備份快,恢復慢。
AOF的優缺點
優點: AOF更好保證數據不會被丟失,最多只丟失一秒內的數據。
另外重寫操作保證了數據的有效性,即使日志文件過大也會進行重寫。
AOF的日志文件的記錄可讀性非常的高。
對於相同數量的數據集而言,AOF文件通常要大於RDB文件。
rdb,就是備份所有數據,使用了快照。rdb恢復數據比較快。
Q:aof文件過大,怎么處理?
會進行aof文件重寫。
1.隨着AOF文件越來越大,里面會有大部分是重復命令或者可以合並的命令
2.重寫的好處:減少AOF日志尺寸,減少內存占用,加快數據庫恢復時間。
執行一個 AOF文件重寫操作,重寫會創建一個當前 AOF 文件的體積優化版本。
詳情見: https://blog.csdn.net/stevendbaguo/article/details/82855726
Q:講一下Redis的事務
先以 MULTI 開始一個事務, 然后將多個命令入隊到事務中, 最后由 EXEC 命令觸發事務, 一並執行事務中的所有命令。如果想放棄這個事務,可以使用DISCARD命令。
Redis事務無法回滾,那怎么處理?
Q:怎么設置Redis的key的過期時間?
key的的過期時間通過EXPIRE key seconds命令來設置數據的過期時間。返回1表明設置成功,返回0表明key不存在或者不能成功設置過期時間。
Q:Redis的過期策略有哪些?
惰性刪除:當讀/寫一個已經過期的key時,會觸發惰性刪除策略,直接刪除掉這個過期key,並按照key不存在去處理。
惰性刪除,對內存不太好,已經過期的key會占用太多的內存。
定期刪除:每隔一段時間,就會對Redis進行檢查,主動刪除一批已過期的key。
Q:為什么Redis不使用定時刪除?
定時刪除,就是在設置key的過期時間的同時,創建一個定時器,讓定時器在過期時間來臨時,立即執行對key的刪除操作。
定時刪除,會占用CPU,影響服務器的響應時間和性能。
Q:Redis 的內存回收機制都有哪些?
當前已用內存超過maxmemory限定時,會觸發主動清理策略,也就是Redis的內存回收策略。
LRU、TTL。
noeviction:默認策略,不會刪除任何數據,拒絕所有寫入操作並返回客戶端錯誤信息,此時Redis只響應讀操作。
volatitle-lru:根據LRU算法刪除設置了超時屬性的鍵,知道騰出足夠空間為止。如果沒有可刪除的鍵對象,回退到noeviction策略。
allkeys-lru:根據LRU算法刪除鍵,不管數據有沒有設置超時屬性,直到騰出足夠空間為止。
allkeys-random:隨機刪除所有鍵,知道騰出足夠空間為止。
volatitle-random:隨機刪除過期鍵,知道騰出足夠空間為止。
volatitle-ttl:根據鍵值對象的ttl屬性,刪除最近將要過期數據。如果沒有,回退到noeviction策略
Q:手寫一下LRU算法 。
Q:Redis的搭建有哪些模式?
主從模式、哨兵模式、Cluster(集群)模式。
最好是用集群模式。
詳情見:https://new.qq.com/omn/20180126/20180126G00THE.html
Q:你用過的Redis是多主多從的,還是一主多從的?集群用到了多少節點?用到了多少個哨兵?
集群模式。三主三從。
Q:Redis采用多主多從的集群模式,各個主節點的數據是否一致?
Q:Redis集群有哪些特性?
master和slaver。主從復制。讀寫分離。哨兵模式。
Q:Redis集群數據分片的原理是什么?
Redis數據分片原理是哈希槽(hash slot)。
Redis 集群有 16384 個哈希槽。 每一個 Redis 集群中的節點都承擔一個哈希槽的子集。
哈希槽讓在集群中添加和移除節點非常容易。例如,如果我想添加一個新節點 D,我需要從節點 A,B, C 移動一些哈希槽到節點 D。同樣地,如果我想從集群中移除節點 A,我只需要移動 A 的哈希槽到 B 和 C。 當節點 A 變成空的以后,我就可以從集群中徹底刪除它。 因為從一個節點向另一個節點移動哈希槽並不需要停止操作,所以添加和移除節點,或者改變節點持有的哈希槽百分比,都不需要任何停機時間(downtime)。
Q:講一下Redis一致性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 上。
所以一致性哈希算法對於容錯性和擴展性有非常好的支持。
參考資料: https://blog.csdn.net/qq_21125183/article/details/90019034
Q:為什么Redis Cluster分片不使用Redis一致性Hash算法?
一致性哈希算法也有一個嚴重的問題,就是數據傾斜。
如果在分片的集群中,節點太少,並且分布不均,一致性哈希算法就會出現部分節點數據太多,部分節點數據太少。也就是說無法控制節點存儲數據的分配。
Q:集群的拓撲結構有沒有了解過?集群是怎么連接的?
無中心結構。Redis-Cluster采用無中心結構,每個節點保存數據和整個集群狀態,每個節點都和其他所有節點連接。
Q:講一下Redis主從復制的過程。
從機發送SYNC(同步)命令,主機接收后會執行BGSAVE(異步保存)命令備份數據。
主機備份后,就會向從機發送備份文件。主機之后還會發送緩沖區內的寫命令給從機。
當緩沖區命令發送完成后,主機執行一條寫命令,就會往從機發送同步寫入命令。
更詳細的步驟見: https://www.cnblogs.com/expiator/p/9881989.html
Q:講一下Redis哨兵機制。
下面是Redis官方文檔對於哨兵功能的描述:
監控(Monitoring):哨兵會不斷地檢查主節點和從節點是否運作正常。
自動故障轉移(Automatic Failover):當主節點不能正常工作時,哨兵會開始自動故障轉移操作,它會將失效主節點的其中一個從節點升級為新的主節點,並讓其他從節點改為復制新的主節點。
配置提供者(Configuration Provider):客戶端在初始化時,通過連接哨兵來獲得當前Redis服務的主節點地址。
通知(Notification):哨兵可以將故障轉移的結果發送給客戶端。
Q:講一下布隆過濾器。
布隆過濾器的主要是由一個很長的二進制向量和若干個(k個)散列映射函數組成。因為每個元數據的存儲信息值固定,而且總的二進制向量固定。所以在內存占用和查詢時間上都遠遠超過一般的算法。當然存在一定的不准確率(可以控制)和不容易刪除樣本數據。
布隆過濾器的優點: 大批量數據去重,特別的占用內存。但是用布隆過濾器(Bloom Filter)會非常的省內存。
布隆過濾器的特點:當布隆過濾器說某個值存在時,那可能就不存在,如果說某個值不存在時,那肯定就是不存在了。
布隆過濾器的應用場景:新聞推送(不重復推送)。解決緩存穿透的問題。
緩存
Q:緩存雪崩是什么?
如果緩存數據設置的過期時間是相同的,並且Redis恰好將這部分數據全部刪光了。這就會導致在這段時間內,這些緩存同時失效,全部請求到數據庫中。這就是緩存雪崩。
怎么解決緩存雪崩?
解決方法:在緩存的時候給過期時間加上一個隨機值,這樣就會大幅度的減少緩存在同一時間過期。
Q:緩存穿透是什么?
緩存穿透是指查詢一個一定不存在的數據。由於緩存不命中,並且出於容錯考慮,如果從數據庫查不到數據則不寫入緩存,這將導致這個不存在的數據每次請求都要到數據庫去查詢,失去了緩存的意義。
怎么解決緩存穿透?
Q:什么是緩存與數據庫雙寫一致問題?
Q:如何保證緩存與數據庫的一致性?
讀的時候,先讀緩存,緩存沒有的話,就讀數據庫,然后取出數據后放入緩存,同時返回響應。
先刪除緩存,再更新數據庫。
Q:為什么是先刪除緩存,而不是先更新緩存?
Q:先更新數據庫,再刪除緩存,會有什么問題?
先更新數據庫,再刪除緩存。可能出現以下情況:
-
如果更新完數據庫,java服務提交了事務,然后掛掉了,那Redis還是會執行,這樣也會不一致。
-
如果更新數據庫成功,刪除緩存失敗了,那么會導致數據庫中是新數據,緩存中是舊數據,數據就出現了不一致。
先刪除緩存,再更新數據庫。
如果刪除緩存失敗,那就不更新數據庫,緩存和數據庫的數據都是舊數據,數據是一致的。
如果刪除緩存成功,而數據庫更新失敗了,那么數據庫中是舊數據,緩存中是空的,數據不會不一致。因為讀的時候緩存沒有,所以去讀了數據庫中的舊數據,然后更新到緩存中。
Q:先刪除緩存,在寫數據庫成功之前,如果有讀請求發生,可能導致舊數據入緩存,引發數據不一致,怎么處理?
https://blog.csdn.net/striveb/article/details/95110502
https://www.w3cschool.cn/architectroad/architectroad-consistency-of-cache-with-database.html
分布式鎖
Q: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
詳情見:https://blog.csdn.net/qq_30038111/article/details/90696233
Q:為什么不先set nx,然后再使用expire設置超時時間?
我們需要保證setnx命令和expire命令以原子的方式執行,否則如果客戶端執行setnx獲得鎖后,這時客戶端宕機了,那么這把鎖沒有設置過期時間,導致其他客戶端永遠無法獲得鎖了。
Q:使用Redis分布式鎖,key和value分別設置成什么?
value可以使用json格式的字符串。
示例:
{
"count":1,
"expireAt":147506817232,
"jvmPid":22224,
"mac":"28-D2-44-0E-0D-9A",
"threadId":14
}
Q:Redis實現的分布式鎖,如果某個系統獲取鎖后,宕機了怎么辦?
系統模塊宕機的話,可以通過設置過期時間(就是設置緩存失效時間)解決。系統宕機時鎖阻塞,過期后鎖釋放。
Q:設置緩存失效時間,那如果前一個線程把這個鎖給刪除了呢?
Q:如果加鎖和解鎖之間的業務邏輯執行的時間比較長,超過了鎖過期的時間,執行完了,又刪除了鎖,就會把別人的鎖給刪了。怎么辦?
這兩個屬於鎖超時的問題。
可以將鎖的value設置為Json字符串,在其中加入線程的id或者請求的id,在刪除之前,get一下這個key,判斷key對應的value是不是當前線程的。只有是當前線程獲取的鎖,當前線程才可以刪除。
Q:Redis分布式鎖,怎么保證可重入性?
可以將鎖的value設置為Json字符串,在其中加入線程的id和count變量。
當count變量的值為0時,表示當前分布式鎖沒有被線程占用。
如果count變量的值大於0,線程id不是當前線程,表示當前分布式鎖已經被其他線程占用。
如果count變量的值大於0,線程id是當前線程的id,表示當前線程已經拿到了鎖,不必阻塞,可以直接重入,並將count變量的值加一即可。
這種思路,其實就是參考了ReentrantLock可重入鎖的機制。
Q:Redis做分布式鎖,Redis做了主從,如果設置鎖之后,主機在傳輸到從機的時候掛掉了,從機還沒有加鎖信息,如何處理?
可以使用開源框架Redisson,采用了redLock。(待補充)
Q:講一下Redis的redLock。
Q:Zookeeper是怎么實現分布式鎖的?
分布式鎖:基於Zookeeper一致性文件系統,實現鎖服務。鎖服務分為保存獨占及時序控制兩類。
保存獨占:將Zookeeper上的一個znode看作是一把鎖,通過createznode的方式來實現。所有客戶端都去創建 /distribute_lock 節點,最終成功創建的那個客戶端也即擁有了這把鎖。用完刪除自己創建的distribute_lock 節點就釋放鎖。
時序控制:基於/distribute_lock鎖,所有客戶端在它下面創建臨時順序編號目錄節點,和選master一樣,編號最小的獲得鎖,用完刪除,依次方便。
更詳細的回答如下:
其實基於Zookeeper,就是使用它的臨時有序節點來實現的分布式鎖。
原理就是:當某客戶端要進行邏輯的加鎖時,就在Zookeeper上的某個指定節點的目錄下,去生成一個唯一的臨時有序節點, 然后判斷自己是否是這些有序節點中序號最小的一個,如果是,則算是獲取了鎖。如果不是,則說明沒有獲取到鎖,那么就需要在序列中找到比自己小的那個節點,並對其調用exist()方法,對其注冊事件監聽,當監聽到這個節點被刪除了,那就再去判斷一次自己當初創建的節點是否變成了序列中最小的。如果是,則獲取鎖,如果不是,則重復上述步驟。
當釋放鎖的時候,只需將這個臨時節點刪除即可。
Zookeeper
Q:Zookeeper的原理是什么?
Q:Zookeeper是怎么保證一致性的?
zab協議。
zab協議有兩種模式,它們分別是恢復模式(選主)和廣播模式(同步)。當服務啟動或者在領導者崩潰后,zab就進入了恢復模式,當領導者被選舉出來,且大多數server完成了和 leader的狀態同步以后,恢復模式就結束了。狀態同步保證了leader和server具有相同的系統狀態。
Q:Zookeeper有哪些應用場景?
Zookeeper可以作為服務協調的注冊中心。還可以做分布式鎖(如果沒有用過分布式鎖就不要說)
Q:Zookeeper為什么能做注冊中心?
Zookeeper的數據模型是樹型結構,由很多數據節點組成,zk將全量數據存儲在內存中,可謂是高性能,而且支持集群,可謂高可用。
另外支持事件監聽(watch命令)。
Zookeeper可以作為一個數據發布/訂閱系統。
Q:Zookeeper的節點有哪些類型?有什么區別?
臨時節點,永久節點。 更加細分就是臨時有序節點、臨時無序節點、永久有序節點、永久無序節點。
臨時節點: 當創建臨時節點的程序停掉之后,這個臨時節點就會消失,存儲的數據也沒有了。
Q:Zookeeper做為注冊中心,主要存儲哪些數據?存儲在哪里?
ip、端口,還有心跳機制。
數據存儲在Zookeeper的節點上面。
Q:心跳機制有什么用?
Q:Zookeeper的廣播模式有什么缺陷?
廣播風暴。
Q:講一下Zookeeper的讀寫機制。
Leader主機負責讀和寫。
Follower負責讀,並將寫操作轉發給Leader。Follower還參與Leader選舉投票,參與事務請求Proposal投票。
Observer充當觀察者的角色。Observer和Follower的唯一區別在於:Observer不參與任何投票。
Q:講一下Zookeeper的選舉機制。
Leader不可用時,會重新選舉Leader。超過半數的Follower選舉投票即可,Observer不參與投票。
Q:你們的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呢。
Q:zookeeper的集群節點,如果不是奇數的,可能會出現什么問題?
可能會出現腦裂。
假死:由於心跳超時(網絡原因導致的)認為master死了,但其實master還存活着。
腦裂:由於假死會發起新的master選舉,選舉出一個新的master,但舊的master網絡又通了,導致出現了兩個master ,有的客戶端連接到老的master 有的客戶端鏈接到新的master。
詳情見:https://my.oschina.net/wangen2009/blog/2994188
消息隊列
Q:為什么使用消息隊列?消息隊列有什么優點和缺點?Kafka、ActiveMQ、RabbitMq、RocketMQ 都有什么優點和缺點?
消息隊列解耦,削峰,限流。
Q:如何保證消息隊列的高可用?
Q:如何保證消息不被重復消費?(如何保證消息消費的冪等性)
Q:如何保證消息的可靠性傳輸?(如何處理消息丟失的問題)
Q:如何保證消息的順序性?
Q:如何解決消息隊列的延時以及過期失效問題?消息隊列滿了以后該怎么處理?有幾百萬消息持續積壓幾小時,說說怎么解決?
Q:如果讓你寫一個消息隊列,該如何進行架構設計啊?說一下你的思路。
Kafka
參考資料: Kafka 入門一篇文章就夠了
Q:講一下Kafka。
Kafka的簡單理解
Q:Kafka相對其他消息隊列,有什么特點?
持久化:Kafka的持久化能力比較好,通過磁盤持久化。而RabbitMQ是通過內存持久化的。
吞吐量:Rocket的並發量非常高。
消息處理:RabbitMQ的消息不支持批量處理,而RocketMQ和Kafka支持批量處理。
高可用:RabbitMQ采用主從模式。Kafka也是主從模式,通過Zookeeper管理,選舉Leader,還有Replication副本。
事務:RocketMQ支持事務,而Kafka和RabbitMQ不支持。
Q:Kafka有哪些模式?
如果一個生產者或者多個生產者產生的消息能夠被多個消費者同時消費的情況,這樣的消息隊列稱為"發布訂閱模式"的消息隊列
Q:Kafka作為消息隊列,有哪些優勢?
分布式的消息系統。
高吞吐量。即使存儲了許多TB的消息,它也保持穩定的性能。
數據保留在磁盤上,因此它是持久的。
Q:Kafka為什么處理速度會很快?kafka的吞吐量為什么高?
零拷貝:Kafka 實現了"零拷貝"原理來快速移動數據,避免了內核之間的切換。
消息壓縮、分批發送:Kafka 可以將數據記錄分批發送,從生產者到文件系統(Kafka 主題日志)到消費者,可以端到端的查看這些批次的數據。
批處理能夠進行更有效的數據壓縮並減少 I/O 延遲。
順序讀寫:Kafka 采取順序寫入磁盤的方式,避免了隨機磁盤尋址的浪費。
Q:講一下Kafka中的零拷貝。
數據的拷貝從內存拷貝到kafka服務進程那塊,又拷貝到socket緩存那塊,整個過程耗費的時間比較高,kafka利用了Linux的sendFile技術(NIO),省去了進程切換和一次數據拷貝,讓性能變得更好。
Q:Kafka的偏移量是什么?
消費者每次消費數據的時候,消費者都會記錄消費的物理偏移量(offset)的位置。等到下次消費時,他會接着上次位置繼續消費
Q:Kafka的生產者,是如何發送消息的?
生產者的消息是先被寫入分區中的緩沖區中,然后分批次發送給 Kafka Broker。
生產者的消息發送機制,有同步發送和異步發送。
同步發送消息都有個問題,那就是同一時間只能有一個消息在發送,這會造成許多消息無法直接發送,造成消息滯后,無法發揮效益最大化。
異步發送消息的同時能夠對異常情況進行處理,生產者提供了Callback 回調。
Q:Kafka生產者發送消息,有哪些分區策略?
Kafka 的分區策略指的就是將生產者發送到哪個分區的算法。
有順序輪詢、隨機輪詢、key-ordering 策略。
key-ordering 策略:Kafka 中每條消息都會有自己的key,一旦消息被定義了 Key,那么你就可以保證同一個 Key 的所有消息都進入到相同的分區里面,由於每個分區下的消息處理都是有順序的,故這個策略被稱為按消息鍵保序策略。
Q:Kafka為什么要分區?
實現負載均衡和水平擴展。
Kafka可以將主題(Topic)划分為多個分區(Partition),會根據分區規則選擇把消息存儲到哪個分區中,只要如果分區規則設置的合理,那么所有的消息將會被均勻的分布到不同的分區中,這樣就實現了負載均衡和水平擴展。另外,多個訂閱者可以從一個或者多個分區中同時消費數據,以支撐海量數據處理能力。
Q: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上面。。
Q:Kafka如何保證消息的順序性?
Kafka 可以保證同一個分區里的消息是有序的。也就是說消息發送到一個Partition 是有順序的。
Q:Kafka的消費者群組Consumer Group訂閱了某個Topic,假如這個Topic接收到消息並推送,那整個消費者群組能收到消息嗎?
http://kafka.apache.org/intro
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,會被傳遞到已訂閱的消費者群組里面的一個消費者實例。
Q:如何提高Kafka的消費速度?
擴容消費者。之所以消費延遲大,就是消費者處理能力有限,可以增加消費者的數量。
擴大分區。一個分區只能被消費者群組中的一個消費者消費。消費者擴大,分區最好多隨之擴大。
Q:Kafka消息消費者宕機了,怎么確認有沒有收到消息?
ack機制,如果接收方收到消息后,會返回一個確認字符。
Q:講一下Kafka的ack機制。
acks 參數指定了要有多少個分區副本接收消息,生產者才認為消息是寫入成功的。此參數對消息丟失的影響較大。
如果 acks = 0,就表示生產者也不知道自己產生的消息是否被服務器接收了,它才知道它寫成功了。如果發送的途中產生了錯誤,生產者也不知道,它也比較懵逼,因為沒有返回任何消息。這就類似於 UDP 的運輸層協議,只管發,服務器接受不接受它也不關心。
如果 acks = 1,只要集群的 Leader 接收到消息,就會給生產者返回一條消息,告訴它寫入成功。如果發送途中造成了網絡異常或者 Leader 還沒選舉出來等其他情況導致消息寫入失敗,生產者會受到錯誤消息,這時候生產者往往會再次重發數據。因為消息的發送也分為 同步 和 異步,Kafka 為了保證消息的高效傳輸會決定是同步發送還是異步發送。如果讓客戶端等待服務器的響應(通過調用 Future 中的 get() 方法),顯然會增加延遲,如果客戶端使用回調,就會解決這個問題。
如果 acks = all,這種情況下是只有當所有參與復制的節點都收到消息時,生產者才會接收到一個來自服務器的消息。不過,它的延遲比 acks =1 時更高,因為我們要等待不只一個服務器節點接收消息。
參考資料: https://juejin.im/post/5ddf5659518825782d599641
Q: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 個副本。雖然造成了數據冗余,但是帶來了數據的安全性。
詳情參考:https://blog.csdn.net/qq_34337272/article/details/104903388?fps=1&locationNum=2
Q:Kafka怎么保證可靠性?(存疑)
在Kafka中主要通過ISR機制來保證消息的可靠性。
ISR(in sync replica):是Kafka動態維護的一組同步副本,在ISR中有成員存活時,只有這個組的成員才可以成為leader,內部保存的為每次提交信息時必須同步的副本(acks = all時),每當leader掛掉時,在ISR集合中選舉出一個follower作為leader提供服務,當ISR中的副本被認為壞掉的時候,會被踢出ISR,當重新跟上leader的消息數據時,重新進入ISR。
詳情見: https://www.jianshu.com/p/ebeaa7593d83
Q: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)
詳情見:https://www.jianshu.com/p/f0449509fb11
Q:Kafka怎么處理重復消息?怎么避免重復消費?
一般情況下,kafka重復消費都是由於未正常提交offset造成的。
使用的是spring-Kafka,所以把Kafka消費者的配置enable.auto.commit設為false,禁止Kafka自動提交offset,從而使用spring-Kafka提供的offset提交策略。
spring-Kafka中的offset提交策略可以保證一批消息數據沒有完成消費的情況下,也能提交offset,從而避免了提交失敗而導致永遠重復消費的問題。
如何去重:將消息的唯一標識保存起來,每次消費時判斷是否處理過即可。
Q:Kafka消息是采用pull模式,還是push模式?
pull模式。
Q:pull模式和push模式,各有哪些特點?
pull模式,准確性?可以較大保證消費者能獲取到消息。
push模式,即時性?可以在broker獲取消息后馬上送達消費者。
Q:Kafka是如何存儲消息的?
Kafka使用日志文件的方式來保存生產者和發送者的消息,每條消息都有一個 offset 值來表示它在分區中的偏移量。
Kafka中存儲的一般都是海量的消息數據,為了避免日志文件過大,
一個分片並不是直接對應在一個磁盤上的日志文件,而是對應磁盤上的一個目錄。
數據存儲設計的特點在於以下幾點:
(1)Kafka把主題中一個分區划分成多個分段的小文件段,通過多個小文件段,就容易根據偏移量查找消息、定期清除和刪除已經消費完成的數據文件,減少磁盤容量的占用;
(2)采用稀疏索引存儲的方式構建日志的偏移量索引文件,並將其映射至內存中,提高查找消息的效率,同時減少磁盤IO操作;
(3)Kafka將消息追加的操作邏輯變成為日志數據文件的順序寫入,極大的提高了磁盤IO的性能;
參考:https://www.jianshu.com/p/3e54a5a39683
參考資料:https://blog.csdn.net/zhangxm_qz/article/details/87636094
Q:講一下Kafka集群的Leader選舉機制。
Kafka在Zookeeper上針對每個Topic都維護了一個ISR(in-sync replica---已同步的副本)的集合,集合的增減Kafka都會更新該記錄。如果某分區的Leader不可用,Kafka就從ISR集合中選擇一個副本作為新的Leader。
分庫分表
Q:數據庫如何處理海量數據?
分庫分表,主從架構,讀寫分離
Q:數據庫分庫分表,何時分?怎么分?
水平分庫/分表,垂直分庫/分表
水平分庫/表,各個庫和表的結構一模一樣。
垂直分庫/表,各個庫和表的結構不一樣。
Q:讀寫分離怎么做?
主機負責寫,從機負責讀。
系統設計
分布式、高並發場景
遇到高並發場景,可以使用Redis緩存、Redis限流、MQ異步、MQ削峰等。
Q:在實踐中,遇到過哪些並發的業務場景?
秒殺。比如搶商品,搶紅包。
Q:如何設計一個秒殺/搶券系統?
可以通過隊列配合異步處理實現秒殺。
使用redis的list,將商品push進隊列,pop出隊列。
異步操作不會阻塞,不會消耗太多時間。
Q:如何提高搶券系統的性能?
使用多個list。
使用多線程從隊列中拉取數據。
集群提高可用性。
MQ異步處理,削峰。
Q:秒殺怎么避免少賣或超賣?
redis是單進程單線程的,操作具有原子性,不會導致少賣或者超賣。
另外,也可以設置一個版本號version,樂觀鎖機制。
Q:考勤打卡,假如高峰期有幾萬人同時打卡,那么怎么應對這種高並發?
使用Redis緩存。
員工點擊簽到,可以在緩存中set狀態。將工號作為key,打卡狀態作為value,打卡成功為01,未打卡或者打卡失敗為00,
然后再將數據異步地寫入到數據庫里面就可以了。
Q:如何應對高峰期的超高並發量?
Redis限流。
Redis可以用計數器限流。使用INCR命令,每次都加一,處理完業務邏輯就減一。然后設置一個最大值,當達到最大值后就直接返回,不處理后續的邏輯。
Redis還可以用令牌桶限流。使用Redis隊列,每十個數據中push一個令牌桶,每個請求進入后會先從隊列中pop數據,如果是令牌就可以通行,不是令牌就直接返回。
系統架構與設計
Q:如何提高系統的並發能力?
使用分布式系統。
部署多台服務器,並做負載均衡。
使用緩存(Redis)集群。
數據庫分庫分表 + 讀寫分離。
引入消息中間件集群。
Q:設計一個紅包系統,需要考慮哪些問題,如何解決?(本質上也是秒殺系統)
Q:如果讓你設計一個消息隊列,你會怎么設計?