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