叮。。。。。美團(tuán)來電。這次不是外賣而是電話面試。所報崗位為后端/服務(wù)端開發(fā),但是從我的復(fù)盤來看,這和 Java 后端開發(fā)的內(nèi)容差不多,除了部分的語言特性外,還是四大件基礎(chǔ)知識為重,下面我們來看看都問了啥,小心下次面你的時候就有這些問題哦
如果你問我,看了這些題就完事了?非也,這只是開始,你需要學(xué)習(xí)的還有很多,知道路子是怎么走才是重要的勒。
開始之前,我們先看提綱,大家默默的想一想,如果是你,你將怎么去回答這個問題,然后再看我的回答也許更佳哈。
1 一面(40分鐘)
自我介紹
老規(guī)矩,我叫啥,啥專業(yè),技術(shù)棧是啥,能做啥
怎么理解分布式
其實如果沒有去實習(xí),可能很少接觸分布式的內(nèi)容。對于面試官而言,也沒多期望你們對分布式的理解到多深的地步,只是希望你們能對其有個初步的了解即可。
不管是高登摩爾提出的摩爾定律還是Gordon Moore堅持的2版本是啥,總之如果你的系統(tǒng)需承載的計算量的增長速度大于摩爾定律的預(yù)測,那么在未來的某一個時間點,集中式系統(tǒng)將無法承載你所需的計算量。
在整個計算機(jī)系統(tǒng)發(fā)展的過程中,最實際的還是經(jīng)濟(jì)的元素。人們發(fā)現(xiàn)使用更加廉價的機(jī)器,組合在一起的分布式系統(tǒng),除了可以獲得超過CPU發(fā)展速度的性能以外,還可以有更好的性價比,所以得出如下結(jié)論:
無論是要以低價格獲得普通的性能,還是要以較高的價格獲 得極高的性能,分布式系統(tǒng)都能夠滿足。并且受規(guī)模效應(yīng)的影響,系統(tǒng)越大,性價比帶來 的收益越高。
隨著計算機(jī)的飛速發(fā)展,科學(xué)家們發(fā)現(xiàn)分布式系統(tǒng)相比于集中式系統(tǒng)的另一個很明顯的優(yōu)勢就是:具有更高的可用性。假設(shè)使用10個能夠承載10000流量相同的節(jié)點,其中的兩個節(jié)點掛了,只要實際的流量不超過8000,那么系統(tǒng)仍然正常運轉(zhuǎn)。
說這么多,分布式系統(tǒng)還是建立在「分治」和「冗余」的基礎(chǔ)上,這也就是分布式系統(tǒng)的本質(zhì)
那么分治是什么?
這和我們大腦解決問題類似,大問題分解為小問題,然后治理最后歸并。
為什么要這樣做?
小問題容易解決,解決了眾多的子問題,大問題也就更容易解決了
如果拆分的父子問題有依賴關(guān)系怎么辦?
大問題拆分的過程中,非常重要的即不同分支的子問題不能相互依賴,需要各自獨立,因為如果存在依賴關(guān)系,父子問題就失去了「歸并」的意義,那么在開發(fā)中,這就是「聚合度」和「內(nèi)聚度」的問題。
什么是聚合度和內(nèi)聚度?
所謂聚合度即軟件系統(tǒng)中各個模塊的相互依賴程度。比如在調(diào)用A方法的時候都需要同步調(diào)用方法B,顯然這樣的耦合度就高
所謂內(nèi)聚度即模塊之間具有共同點的相似程度,所以在拆分的時候要尤其注意這兩點。
什么是冗余?
這里的冗余不是代碼的冗余,而是容許系統(tǒng)在一定范圍內(nèi)出現(xiàn)故障,但對系統(tǒng)的影響很小。
如上圖將冗余的節(jié)點部署在單獨的服務(wù)器,完全是為了備用而做的冗余,如果不出現(xiàn)故障,那么資源是不是就浪費了,所以大多數(shù)情況會使用諸如雙主多活、讀寫分離之類的概念提高資源利用率
其實在生活中冗余也很常見,比如大部分的汽車系統(tǒng)中的底層控制系統(tǒng)也是有冗余機(jī)制,飛機(jī)發(fā)動機(jī)為什么是偶數(shù),也是同樣的道理。還有更多的關(guān)于后端的基礎(chǔ)總結(jié)要點則參考另一篇文章
寫個代碼熱熱身----棧實現(xiàn)隊列
class?MyQueue?{
public:
????stack<int>?stIn;
????stack<int>?stOut;
????/**?Initialize?your?data?structure?here.?*/
????MyQueue()?{
????}
????/**?Push?element?x?to?the?back?of?queue.?*/
????void?push(int?x)?{
????????stIn.push(x);
????}
????/**?Removes?the?element?from?in?front?of?queue?and?returns?that?element.?*/
????int?pop()?{
????????//?只有當(dāng)stOut為空的時候,再從stIn里導(dǎo)入數(shù)據(jù)(導(dǎo)入stIn全部數(shù)據(jù))
????????if?(stOut.empty())?{
????????????//?從stIn導(dǎo)入數(shù)據(jù)直到stIn為空
????????????while(!stIn.empty())?{
????????????????stOut.push(stIn.top());
????????????????stIn.pop();
????????????}
????????}
????????int?result?=?stOut.top();
????????stOut.pop();
????????return?result;
????}
????/**?Get?the?front?element.?*/
????int?peek()?{
????????int?res?=?this->pop();?//?直接使用已有的pop函數(shù)
????????stOut.push(res);?//?因為pop函數(shù)彈出了元素res,所以再添加回去
????????return?res;
????}
????/**?Returns?whether?the?queue?is?empty.?*/
????bool?empty()?{
????????return?stIn.empty()?&&?stOut.empty();
????}
};
介紹下第一個項目
注意:校招的的面試在我看來寫兩個項目差不多了,印象更深刻且自己的理解程度更好的放在上面。面試之前一定要搞懂這三個問題,且在排練的時候想想怎么能引面試官上鉤
項目的描述
自己在項目中擔(dān)任的角色,做了什么
在項目中遇到什么難點,怎么處理,有沒有測試過
說說快排思想?
如果我們的每一次分區(qū)操作都能正好將數(shù)組分成大小接近相等的兩個小區(qū)間,那么快排的時間復(fù)雜度和歸并是差不多的,所以其時間復(fù)雜度為O(nlogn)
這里特別注意所謂的每次分區(qū)操作有個前提條件,即選擇的pivot很合適,能掙好的將大區(qū)間對等的一分為二,如果原來的數(shù)據(jù)是一件排好序的,我們選擇最后一個元素為pivot,這樣每次分區(qū)得到的兩個區(qū)間是不均等,我們需要進(jìn)行大約n次分區(qū)操作,每次分區(qū)平均掃描n/2個元素,此時的復(fù)雜度將退化為0(n ^ 2)
注:一定要能手撕快排啊,這真是無數(shù)次會被考?。?!
public?class?QuickSort?{
????public?static?void?main(String[]?args)?{
????????int[]?arr?=?{?49,?38,?65,?97,?23,?22,?76,?1,?5,?8,?2,?0,?-1,?22?};
????????quickSort(arr,?0,?arr.length?-?1);
????????System.out.println("排序后:");
????????for?(int?i?:?arr)?{
????????????System.out.println(i);
????????}
????}
????private?static?void?quickSort(int[]?arr,?int?low,?int?high)?{
????????if?(low?????????????//?找尋基準(zhǔn)數(shù)據(jù)的正確索引
????????????int?index?=?getIndex(arr,?low,?high);
????????????//?進(jìn)行迭代對index之前和之后的數(shù)組進(jìn)行相同的操作使整個數(shù)組變成有序
????????????//quickSort(arr,?0,?index?-?1);?之前的版本,這種姿勢有很大的性能問題,謝謝大家的建議
????????????quickSort(arr,?low,?index?-?1);
????????????quickSort(arr,?index?+?1,?high);
????????}
????}
????private?static?int?getIndex(int[]?arr,?int?low,?int?high)?{
????????//?基準(zhǔn)數(shù)據(jù)
????????int?tmp?=?arr[low];
????????while?(low?????????????//?當(dāng)隊尾的元素大于等于基準(zhǔn)數(shù)據(jù)時,向前挪動high指針
????????????while?(low?=?tmp)?{
????????????????high--;
????????????}
????????????//?如果隊尾元素小于tmp了,需要將其賦值給low
????????????arr[low]?=?arr[high];
????????????//?當(dāng)隊首元素小于等于tmp時,向前挪動low指針
????????????while?(low?????????????????low++;
????????????}
????????????//?當(dāng)隊首元素大于tmp時,需要將其賦值給high
????????????arr[high]?=?arr[low];
????????}
????????//?跳出循環(huán)時low和high相等,此時的low或high就是tmp的正確索引位置
????????//?由原理部分可以很清楚的知道low位置的值并不是tmp,所以需要將tmp賦值給arr[low]
????????arr[low]?=?tmp;
????????return?low;?//?返回tmp的正確位置
????}
}
半連接了解吧,如果SYN半連接隊列滿了怎么處理,只能丟棄連接?
如果你看過我之前寫的文章,這個問題應(yīng)該就非常容易解決了。SYN半連接隊列已滿,通過開啟cookies功能就可以在不使用SYN隊列的情況下成功建立連接
syncookies是怎么操作的?
服務(wù)端返回 ACK 的時候會計算一個值放在發(fā)出的SYN+ACK報文中,當(dāng)客戶端返回ACK的時候取出該值進(jìn)行驗證,如果合法即連接成功
Linux如何開啟syncookies呢?
通過將參數(shù)tcp_cookies設(shè)置1即可。如果參數(shù)值為2,表示無條件開啟這個功能。如果為1表示僅當(dāng)SYN半連接隊列放不下的時候,再開啟。
那可不可以繞過三次握手?
還有這種操作?
三次握手中,一個較大的問題是HTTP請求必須在一次的RTT后才能發(fā)送。從而谷歌提出了TFO。想要繞過三次握手過程,如果是客戶端發(fā)起連接,想要在SYN的請求報文中放入數(shù)據(jù),需要得到服務(wù)端的認(rèn)可,所以事先需要有個約定。
首次建立連接走正常的三次握手,客戶端會在SYN報文中明確告訴服務(wù)器要使用TFO功能,服務(wù)端答應(yīng)后就會將客戶端的IP地址用自己的密鑰加密(cookie)攜帶在返回的SYN+ACK中,客戶端收到后將cookie緩存到本地
下次客戶端再建立連接,就可以在SYN中攜帶請求的數(shù)據(jù)+緩存的cookie
剛才你說的密鑰是不會變的?
當(dāng)然變化,不然就很容易產(chǎn)生SYN泛洪攻擊了。
Linux中如何打開TFO?
這里要注意了,需要客戶端和服務(wù)端都支持,TFO才會生效,當(dāng)tcp_fastopen設(shè)置為3的時候才生效
net.ipv4.tcp_fastopen=3
舉幾個使用緩存的例子
這里涉及面就非常廣了,什么數(shù)據(jù)庫緩存,如何選擇緩存的讀寫策略,如何做到緩存高可用,緩存穿透的怎么處理以及CDN相關(guān),我們先舉幾個緩存的例子
我們知道虛擬地址到物理地址轉(zhuǎn)換的時候需要通過一個叫做MMU的硬件,每次這樣的轉(zhuǎn)換無疑會造成性能的轉(zhuǎn)換,所以接住了TLB來緩存最近轉(zhuǎn)換過的,下一次轉(zhuǎn)換的時候,如果緩存存在就直接轉(zhuǎn)換即可
在路由尋址的時候,會有個映射表讓我們知道該訪問哪個目的地址,同樣存在于緩存中
HTTP緩存機(jī)制。通過緩存協(xié)商方式減少網(wǎng)絡(luò)傳輸?shù)臄?shù)據(jù)大小從而提升頁面展示的性能
緩存怎么個分類?
緩存分類分為三種,分別為靜態(tài)緩存、分布式緩存與本地緩存。
靜態(tài)緩存
通常使用靜態(tài)HTML實現(xiàn)靜態(tài)緩存。通過在Nginx上部署靜態(tài)緩存減少對于后臺應(yīng)用服務(wù)器的壓力。當(dāng)然你也可以存放數(shù)據(jù)庫,然后前端穿透查詢,但是對于后端的服務(wù)器壓力是非常大的。比如內(nèi)容系統(tǒng),通常在錄入文章的時候先渲染成靜態(tài)頁面,然后放在前端Ngix服務(wù)器上,這樣用戶會直接訪問前端靜態(tài)頁面,但是如果是動態(tài)請求,這樣就需要分布式緩存了
分布式緩存
面試中高頻的Redis和Memchache,就是通過集群的方式突破單機(jī)瓶頸
熱點本地緩存
熱點嘛,微博每天都有熱點,尤其是什么XX明星出軌,這樣的查詢通常會命中某一個緩存節(jié)點,短時間內(nèi)極高的緩存熱點查詢。這個時候就會在代碼中使用一些本地本地緩存方案。如hashmap
緩存的不足
從上面基本上知道,緩存的主要作用是提升訪問的速度,提升并發(fā)的能力。
緩存最好適用于讀多寫少且?guī)в幸欢ǖ臒狳c屬性
因為既然是緩存受限于存儲介質(zhì),不可能緩存所有數(shù)據(jù),只有當(dāng)數(shù)據(jù)有一定的熱點屬性才能有更高的緩存命中率
緩存讓系統(tǒng)更加復(fù)雜,容易出現(xiàn)數(shù)據(jù)不一致的現(xiàn)象
比如更新數(shù)據(jù)庫成功了,但是更新緩存失敗了。這個時候可以考慮固定時間清理或者手動清理。
給運維帶來一定的成本
運維小哥需要了解一些緩存組件的使用
一面小結(jié)
一面面試涉及到了項目,計算機(jī)網(wǎng)絡(luò),數(shù)據(jù)結(jié)構(gòu)和后端的基本知識,也基本覆蓋了校招的各科且有一定的是實踐性考察,不同公司面試當(dāng)然不一樣,看多了你會發(fā)現(xiàn)重點就是這些了,繼續(xù)復(fù)盤二面
2 二面
慢慢的到了10月,面試的越多不知道的越多,感覺需要學(xué)習(xí)的越多,當(dāng)然越戰(zhàn)越勇,面完一面是一面,積累一面是一面。團(tuán)團(tuán)既然給臉,那就面唄
寫個代碼-----數(shù)組中數(shù)字出現(xiàn)的次數(shù)
我擦,上來就是寫代碼,不過大家習(xí)慣就好,每個面試官套路不同,資本社會接受即可,寫唄,準(zhǔn)備這么久的你還怕啥。管他三七二十一,能上位運算就上位運算,盤它完事,小伙伴說能不能寫個python版本,那就py唄。如果這個題目還要猶豫,趕緊再去練練《劍指offer》和leetcode100題呀
class?Solution(object):
????def?singleNumbers(self,?nums):
????????"""
????????:type?nums:?List[int]
????????:rtype:?List[int]
????????"""
????????temp?=?0
????????for?i?in?nums:#得到用于區(qū)分的數(shù)字
????????????temp?^=?i
????????num?=?1
????????while?not?(num?&?temp):#找到用于區(qū)分的數(shù)字中從右到左的第一位為1的值
????????????num?=?num?<1
????????result1?=?0
????????result2?=?0
????????for?i?in?nums:
????????????if?num?&?i:#?劃分兩個數(shù)組
????????????????result1?^=?i#兩個數(shù)組分別取異或
????????????else:
????????????????result2?^=?i
????????return[result1,result2]?
說說TCP的收發(fā)包可通過哪些配置?
這個題就有點大了,我可以扯很久,而且只要問到TCP發(fā)送或者接受的類似問題,真是可以大干好幾百個回合。在這里就稍微詳細(xì)的說說,在以后的面試過程中,遇到此題講不在多說哈,還答不上來就打自己幾耳巴可好,別別,還是不能這么對自己,好了,不多BB了,上干貨
收包是數(shù)據(jù)到達(dá)網(wǎng)卡后交給應(yīng)用程序處理的過程,發(fā)包則調(diào)用相應(yīng)的發(fā)包函數(shù)將數(shù)據(jù)包從網(wǎng)卡發(fā)出的場景。
我們再看看類似的幾個問題
我明明是用了write和send進(jìn)行發(fā)包,但是總是發(fā)不出去
通過抓包發(fā)現(xiàn)數(shù)據(jù)包已經(jīng)到網(wǎng)卡了,但是應(yīng)用程序為什么就是收不到
調(diào)整了緩沖區(qū)大小,不失效的原因
和前面一樣,都是需要了解相應(yīng)的系統(tǒng)參數(shù)才能較好的回答這些問題且能體現(xiàn)你對網(wǎng)絡(luò)的了解深度
TCP數(shù)據(jù)包在發(fā)送的過程中會受到哪些影響?
這個圖畫了一個小時,一定要好好看看,順便記得給我一個贊,么么噠,下面我們詳細(xì)看看這個圖
當(dāng)我們通過write等發(fā)包函數(shù)進(jìn)行發(fā)包的時候,這些系統(tǒng)調(diào)用都是將數(shù)據(jù)包先從用戶緩沖區(qū)拷貝到TCP發(fā)送緩沖區(qū)中,可是這個TCP緩沖區(qū)是有大小限制的,正是這個限制就會產(chǎn)生各種問題。
TCP發(fā)送緩沖區(qū)的默認(rèn)受到 net.ipv4.tcp_wmen 控制
這三個參數(shù)分別的叫做是min,default,max。TCP發(fā)送緩沖區(qū)會在min和max中浮動,初始的大小為default。
為什么需要自動調(diào)整,是不是越大越好?
首先這個動態(tài)調(diào)整是由內(nèi)核來完成,不需要應(yīng)用程序的干預(yù),而且每次發(fā)包的大小不一樣,數(shù)據(jù)太小,緩沖區(qū)卻很大自然就浪費了內(nèi)存,所以通過動態(tài)調(diào)整的方式滿足發(fā)包的需要。
TCP緩沖區(qū)設(shè)置太小或者太大有什么標(biāo)志?
TCP的發(fā)送緩沖區(qū)設(shè)置太小,最明顯的特征即導(dǎo)致業(yè)務(wù)延遲。如何發(fā)現(xiàn)呢,可以通systemtap等工具在內(nèi)核打點完成觀察,如果發(fā)現(xiàn)了有sk_stream_wait_memory就認(rèn)為這發(fā)送緩沖區(qū)太小了,需要調(diào)大tcp_wemem.max
可不可以開發(fā)的時候指定需要發(fā)送的大?。?/p>
當(dāng)然可以呀,通過setsockopt中的SO_SNDBUF設(shè)置固定的緩沖區(qū)大小。但是設(shè)置了這個參數(shù),tcp_wmem就會失效,也就沒有了動態(tài)調(diào)整,設(shè)置的值不能超過net.core.wmem_max,如果超過了,內(nèi)核會強(qiáng)制設(shè)置為wmem_max。所以通常情況下,我們不會通過SO_SNDBUF設(shè)置TCP緩沖區(qū)的大小,設(shè)置太大浪費內(nèi)存,設(shè)置太小引起緩沖區(qū)不足的問題。
上面所說的tcp_wmem和wmem_max都是針對單個tcp連接,其單位是字節(jié)。系統(tǒng)中通常有非常多的tcp連接,連接太多可能導(dǎo)致內(nèi)存耗盡
我們能不能設(shè)置tcp消耗內(nèi)存的大?。克^上限
當(dāng)所消耗的內(nèi)存達(dá)到max就不會再發(fā)包
可以通過什么方式觀測到這種情況?
Linux內(nèi)核給我們早就埋了點,sock_exceed_buf_limit。觀察的時候只需要打開tracepoint即可
然后在日志中查看是否發(fā)生了事件
伴隨tcp層數(shù)據(jù)包的處理完畢來到IP層,在IP層需要考慮端口范圍的問題,太小可能導(dǎo)致無法創(chuàng)建連接。
net.ipv4.ip_local_port_range = 1024
為了實現(xiàn)tcp/ip數(shù)據(jù)流進(jìn)行流控,Linux內(nèi)核在IP層實現(xiàn)了qdisc(排隊規(guī)則)。我們平時見的比較多的TC即是基于qdisc的流控工具,實際上我們通過ipconfig看到的txqueuelen即qdisc的隊列長度。它太小就會導(dǎo)致數(shù)據(jù)包被丟失
我們?nèi)绾斡^察到這個現(xiàn)象?
如果發(fā)現(xiàn)dropped不為0,則很可能是txqueuelen太小導(dǎo)致,此時就需要調(diào)大這個值
經(jīng)過ip層后就進(jìn)入網(wǎng)卡了,此時需要發(fā)送的數(shù)據(jù)包走完TCP/IP協(xié)議棧
那么對于接受放而言,是怎么接受的?
同樣,先看看圖
從上圖可以發(fā)現(xiàn)其接受流程和發(fā)送流程基本相反。當(dāng)數(shù)據(jù)包到達(dá)接受方的網(wǎng)卡,就會觸發(fā)中斷,高速CPU讀取數(shù)據(jù)包,但是在高速網(wǎng)絡(luò)中,大量的數(shù)據(jù)包,如果每來一個數(shù)據(jù)包就觸發(fā)一次中斷勢必讓CPU的處理效率大大折扣,所以提出了NAPI技術(shù),讓CPU一次性輪詢多個數(shù)據(jù)包,通過批量的方式來提升效率,降低中斷帶來的性能開銷
在poll的時候,一次輪詢多少個?
這個poll通過sysctl選項控制
此值默認(rèn)大小為300,通常會增加此值到600使得一次性能處理更多的的數(shù)據(jù)包
可以無限增大此值?
當(dāng)然不行,系統(tǒng)存在太多進(jìn)程,CPU需要權(quán)衡自己的時間照顧其他的進(jìn)程,如果CPU在poll的時間太多,其他任務(wù)的調(diào)度就會延遲。
當(dāng)poll了數(shù)據(jù)包就會到IP層處理,隨后到達(dá)tcp層,從而遇到我們之前所說的TCP接受緩沖區(qū)
默認(rèn)通過tcp_rmem控制緩沖區(qū)的大小,通過適當(dāng)?shù)恼{(diào)整該值來獲取更好的網(wǎng)絡(luò)性能
TCP的默認(rèn)緩沖區(qū)大小會在min和max之間動態(tài)的調(diào)整,不過和發(fā)送緩沖區(qū)不同的是,這個動態(tài)調(diào)整時通過選項tcp_moderade_rcvbuf。通常我們都會打開它
為什么接受緩沖區(qū)時通過選項來自動調(diào)節(jié),而發(fā)送緩沖區(qū)不是
因為tcp接受緩沖區(qū)會直接影響tcp擁塞控制,從而影響對端的發(fā)包,所以通過選項控制的方式更加靈活的控制對端的發(fā)包行為
還有其他方式控制tcp的接受緩沖區(qū)?
當(dāng)然有,可以通過setsockopt()的配置選項SO_RECVBUF控制,如果設(shè)置了此值,那么tcp緩沖區(qū)的自動調(diào)整將關(guān)閉,總之只有當(dāng)tcp_moderate_rcvbuf 為 1,并且應(yīng)用程序沒有通過 SO_RCVBUF 來配置緩沖區(qū)大小的情況下,TCP 接收緩沖區(qū)才會動態(tài)調(diào)節(jié)
總結(jié):
TCP/IP協(xié)議棧復(fù)雜無比,此文介紹了很多配置選析,后續(xù)給大家總結(jié)一個常用的參數(shù)表
好了,這個題目也許啰嗦,但是你會發(fā)現(xiàn)確實有點東西哈,盤它呀,接著面試
小伙子對這個問題的理解比較深刻,差不多都答在了點上,你說說一致性哈希是什么?
說一致性哈希是什么,那我們肯定需要告訴面試官為啥要用一致性哈希,直接使用哈希不香?有什么問題嘛?
我先用個簡單的例子讓大伙看一波為啥使用一致性哈希
現(xiàn)在我們有一個分布式緩存,需要存放3w張美女照片,別問我干啥,就是存著
方案1:直接采用哈希算法,對每個圖片進(jìn)行分片
余數(shù)正好對應(yīng)每個機(jī)器節(jié)點
這樣子會出現(xiàn)什么問題?
通過哈希算法,每個key可以尋址對應(yīng)服務(wù)器,假設(shè)發(fā)過來的請求為key01,計算公式為hash(key01)%3
經(jīng)過計算后尋址編號為1的服務(wù)器節(jié)點,如下圖所示
此時加入新的服務(wù)器節(jié)點,就會出現(xiàn)路由失敗的情況。此時從三個節(jié)點變化為4個節(jié)點,之前的hash(kley01)%3=1就變化為 hash(key-01) % 4 = X,因為取模運算發(fā)生了變化,所以這個 X 大概率不是 1,這時你再查詢,就會找不到數(shù)據(jù)了,因為 key-01 對應(yīng)的數(shù)據(jù),存儲在節(jié)點 A 上,而不是節(jié)點 B。同樣的道理,如果我們需要下線 1 個服務(wù)器節(jié)點(也就是縮容),也會存在類似的可能查詢不到數(shù)據(jù)的問題
對于這個問題怎么解決呢
我們就需要遷移數(shù)據(jù),基于新的計算公式 hash(key-01) % 4 ,來重新對數(shù)據(jù)和節(jié)點做映射。需要注意的是,數(shù)據(jù)的遷移成本是非常高的。
如何解決這個問題---一致性哈希
一致性哈希也是使用了取模運算,但是和傳統(tǒng)的取摸運算不同,一致性哈希算法是對2^32 - 1進(jìn)行取模運算
即使這些數(shù)據(jù)均勻,但是數(shù)據(jù)的活躍度不同,存放熱點數(shù)據(jù)多的節(jié)點訪問量非常大,就很容易的達(dá)到CPU瓶頸。一致性哈希算法即將整個哈希值空間組織成一個虛擬的圓環(huán)---哈希環(huán)
通過哈希算法,將節(jié)點映射到哈希環(huán)上,通常是節(jié)點主機(jī)名,IP地址等如下圖
在尋址的過程就只需要兩步
將key作為參數(shù)執(zhí)行c-hash計算出哈希值,確定key在環(huán)的位置
從這個位置沿著哈希環(huán)順時針走,遇到的第一個節(jié)點即key對應(yīng)的節(jié)點
如下圖所示
從上圖可以發(fā)現(xiàn),key01對應(yīng)節(jié)點A,key02對應(yīng)節(jié)點C,如果此時B宕機(jī)了,只需要將key03定位到節(jié)點A即可,key01和key02并不需要改變。所以一致性哈希算法在增加和減少節(jié)點的時候,只需要重新定位一小部分?jǐn)?shù)據(jù)而不需要重新定位所有數(shù)據(jù),這樣就實現(xiàn)了"增加/減少節(jié)點需要大規(guī)模遷移"這個問題
如果節(jié)點比較少,是不是很容易就將請求數(shù)據(jù)打到一個節(jié)點呢?
這個問題即"數(shù)據(jù)傾斜問題",由于節(jié)點的不均勻是的大量你的請求訪問到節(jié)點A上,造成負(fù)載不均衡。
鑒于這個問題引入了虛擬節(jié)點,簡單的來說通過增加節(jié)點的個數(shù)來緩解節(jié)點的不均勻現(xiàn)象
所謂虛擬節(jié)點即對每個服務(wù)器節(jié)點計算多個哈希值,假設(shè)一個真實的節(jié)點有2個虛擬節(jié)點,此時我的三個節(jié)點就共有6個虛擬節(jié)點,如下圖所示
此時如果在環(huán)上順時針尋找虛擬節(jié)點,假設(shè)key01選擇虛擬節(jié)點nodeB02,那么此時將請求映射到真是節(jié)點B即可
所以通過虛擬節(jié)點擴(kuò)充節(jié)點數(shù)量的方式解決節(jié)點較少情況下數(shù)據(jù)傾斜的問題,代價非常小,只需要增加一個字典map維護(hù)真實節(jié)點和虛擬節(jié)點的映射關(guān)系即可
說說HTTP1.0 1.1 2 3的演進(jìn)
之前有一篇文章單獨說了這個問題,現(xiàn)在粗略總結(jié)下,希望讀者們再去查查相關(guān)資料
http/1.1相對于http/1.0 性能上的改進(jìn):
http/1.0 采用的是短連接,會有較大的三次握手和四次揮手的開銷
支持管道傳輸,
http/1.0
只有第一個響應(yīng)回來才能發(fā)這個請求,但是http/1.1
可以連續(xù)發(fā)送,并不需要等待其回來,減少了整體響應(yīng)時間
http/1.1性能瓶頸:
請求/響應(yīng)的頭部沒有經(jīng)改壓縮就發(fā)送,只能壓縮body部分
發(fā)送冗長的首部,每次互相發(fā)送相同的首部造成的浪費較多
服務(wù)器是按順序響應(yīng)的,會出現(xiàn)隊頭擁塞的情況
沒有請求優(yōu)先級控制
請求只能從客戶端開始,服務(wù)器只能被動響應(yīng)
http/2
采用了
HPACK
算法避免傳送相同的頭部,并且對頭部數(shù)據(jù)進(jìn)行了壓縮對傳輸?shù)臄?shù)據(jù)采用二進(jìn)制的方式傳輸,分為頭部幀和數(shù)據(jù)幀
對連接進(jìn)行了復(fù)用,只要訪問同一個域名下的資源,不必進(jìn)行TCP連接的重新建立
支持服務(wù)端push
可以根據(jù)優(yōu)先級進(jìn)行響應(yīng)
http/3 QUIC
采用了基于
UDP
的傳輸層協(xié)議,希望取代TCP
支持應(yīng)用層級別的重傳,而且在只丟失一個數(shù)據(jù)包的情況下不需要重傳,使用糾錯機(jī)制恢復(fù)丟失的包
談?wù)剶?shù)據(jù)庫的知識吧,盡量說說數(shù)據(jù)庫事務(wù)的四大特性
事務(wù)時一個可執(zhí)行的邏輯單元,該邏輯單元的操作要么都執(zhí)行,要么都不執(zhí)行,事務(wù)ACID的四個性質(zhì)
原子性:指事務(wù)包含的所有操作,要么都被執(zhí)行成功,如果中間有一條語句失敗,則進(jìn)行回滾。
一致性:指事務(wù)執(zhí)行前和執(zhí)行后都必須處于一致性狀態(tài)
隔離性:事務(wù)之間是相互獨立的,中間狀態(tài)對外是不可見的
持久性:數(shù)據(jù)的修改是永久的
多加索引一定很好嗎
索引主要用于加速查詢速度,但并發(fā)越多越好,因為索引需要占用物理空間的,且索引的維護(hù)需要時間的,所以如果建索引,一般來說,應(yīng)該查詢的次數(shù)大于插入的次數(shù),同時我們一般只對例如where或者h(yuǎn)aving子句中涉及的字段進(jìn)行設(shè)置索引,因為其它的地方就算建立了索引,一般也用不到,只是浪費。
請問TCP哪些保證了數(shù)據(jù)傳輸?shù)目煽啃?/p>
序列號、確認(rèn)重傳、超時重傳、擁塞控制(ps 看過我之前文章的小伙伴,問這個問題如果不能扯上一小時,自罰三杯)
三個線程順序打印A-Z
#include?
#include?
#include?
using?namespace?std;
char?ch?=?'A'?-?1;
pthread_cond_t??cond??=?PTHREAD_COND_INITIALIZER;
pthread_mutex_t?mutex?=?PTHREAD_MUTEX_INITIALIZER;
void*?print_fun(void?*?args){
????int?i?=?*((int*)args);
????char?next_ch?=?'A'?+?i;?
????while(next_ch?<=?'Z'){
????????pthread_mutex_lock(&mutex);
????????while(ch?+?1?!=?next_ch)pthread_cond_wait(&cond,?&mutex);
????????ch++;
????????cout<":"<endl;
????????next_ch?=?ch?+?3;
????????pthread_mutex_unlock(&mutex);
????????pthread_cond_broadcast(&cond);
????}
????return?nullptr;
}
int?main(){
????pthread_t?tids[3];
????int?pos?=?0;
????for(int?i?=?0;?i?3;?++i){
????????pthread_create(tids?+?i,?nullptr,?print_fun,?&pos);
????????sleep(1);
????????pos++;
????}
????for(int?i?=?0;?i?3;?++i){
????????pthread_join(tids[i],?nullptr);
????}
????return?0;
}
5 總結(jié)
請記下以下幾點:
公司招你去是干活了,不會因為你怎么怎么的而降低對你的要求標(biāo)準(zhǔn)。
工具上面寫代碼和手撕代碼完全不一樣。
珍惜每一次面試機(jī)會并學(xué)會復(fù)盤。
對于應(yīng)屆生主要考察的還是計算機(jī)基礎(chǔ)知識的掌握,項目要求沒有那么高,是自己做的就使勁摳細(xì)節(jié),做測試,只有這樣,才知道會遇到什么問題,遇到什么難點,如何解決的。從而可以侃侃而談了。
非科班也不要怕,怕了你就輸了!一定要多嘗試。
哈嘍,我是小林,就愛圖解計算機(jī)基礎(chǔ),如果覺得文章對你有幫助,歡迎分享給你的朋友,也給小林點個「在看」,這對小林非常重要,謝謝你們,給各位小姐姐小哥哥們抱拳了,我們下次見!
推薦閱讀
你不好奇 CPU 是如何執(zhí)行任務(wù)的?
學(xué)完計組后,我馬上在「我的世界」造了臺顯示器,你敢信?
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!