歡迎關注微信公衆號:石杉的架構筆記(id:shishan100)node
個人新課**《C2C 電商系統微服務架構120天實戰訓練營》在公衆號儒猿技術窩**上線了,感興趣的同窗,能夠點擊下方連接瞭解詳情:安全
1、寫在前面微信
2、問題源起markdown
3、HDFS優雅的解決方案:網絡
(1)分段加鎖機制+內存雙緩衝機制多線程
(2)多線程併發吞吐量的百倍優化架構
(3)緩衝數據批量刷磁盤+網絡優化併發
上篇文章咱們已經初步給你們解釋了Hadoop HDFS的總體架構原理,相信你們都有了必定的認識和了解。app
若是沒看過上篇文章的同窗能夠看一下:《兄弟,用大白話告訴你小白都能聽懂的Hadoop架構原理》這篇文章。
本文咱們來看看,若是大量客戶端對NameNode發起高併發(好比每秒上千次)訪問來修改元數據,此時NameNode該如何抗住?
咱們先來分析一下,高併發請求NameNode會遇到什麼樣的問題。
你們如今都知道了,每次請求NameNode修改一條元數據(好比說申請上傳一個文件,那麼就須要在內存目錄樹中加入一個文件),都要寫一條edits log,包括兩個步驟:
寫入本地磁盤。
經過網絡傳輸給JournalNodes集羣。
可是若是對Java有必定了解的同窗都該知道多線程併發安全問題吧?
NameNode在寫edits log時的第一條原則:
必須保證每條edits log都有一個全局順序遞增的transactionId(簡稱爲txid),這樣才能夠標識出來一條一條的edits log的前後順序。
那麼若是要保證每條edits log的txid都是遞增的,就必須得加鎖。
每一個線程修改了元數據,要寫一條edits log的時候,都必須按順序排隊獲取鎖後,才能生成一個遞增的txid,表明此次要寫的edits log的序號。
好的,那麼問題來了,你們看看下面的圖。
若是每次都是在一個加鎖的代碼塊裏,生成txid,而後寫磁盤文件edits log,網絡請求寫入journalnodes一條edits log,會咋樣?
不用說,這個絕對完蛋了!
NameNode自己用多線程接收多個客戶端發送過來的併發的請求,結果多個線程竟然修改完內存中的元數據以後,排着隊寫edits log!
並且你要知道,寫本地磁盤 + 網絡傳輸給journalnodes,都是很耗時的啊!性能兩大殺手:磁盤寫 + 網絡寫!
若是HDFS的架構真要是這麼設計的話,基本上NameNode能承載的每秒的併發數量就不多了,可能就每秒處理幾十個併發請求處理撐死了!
因此說,針對這個問題,人家HDFS是作了很多的優化的!
首先你們想一下,既然我們不但願每一個線程寫edits log的時候,串行化排隊生成txid + 寫磁盤 + 寫JournalNode,那麼是否是能夠搞一個內存緩衝?
也就是說,多個線程能夠快速的獲取鎖,生成txid,而後快速的將edits log寫入內存緩衝。
接着就快速的釋放鎖,讓下一個線程繼續獲取鎖後,生成id + 寫edits log進入內存緩衝。
而後接下來有一個線程能夠將內存中的edits log刷入磁盤,可是在這個過程當中,仍是繼續容許其餘線程將edits log寫入內存緩衝中。
可是這裏又有一個問題了,若是針對同一塊內存緩衝,同時有人寫入,還同時有人讀取後寫磁盤,那也有問題,由於不能併發讀寫一塊共享內存數據!
因此HDFS在這裏採起了double-buffer雙緩衝機制來處理!將一塊內存緩衝分紅兩個部分:
其中一個部分能夠寫入
另一個部分用於讀取後寫入磁盤和JournalNodes。
你們可能感受文字敘述不太直觀,老規矩,我們來一張圖,按順序給你們闡述一下。
首先各個線程依次第一次獲取鎖,生成順序遞增的txid,而後將edits log寫入內存雙緩衝的區域1,接着就立馬第一次釋放鎖了。
趁着這個空隙,後面的線程就能夠再次立馬第一次獲取鎖,而後當即寫本身的edits log到內存緩衝。
寫內存那麼快,可能才耗時幾十微妙,接着就立馬第一次釋放鎖了。因此這個併發優化絕對是有效果的,你們有沒有感覺到?
接着各個線程競爭第二次獲取鎖,有線程獲取到鎖以後,就看看,有沒有誰在寫磁盤和網絡?
若是沒有,好,那麼這個線程是個幸運兒!直接交換雙緩衝的區域1和區域2,接着第二次釋放鎖。這個過程至關快速,內存裏判斷幾個條件,耗時不了幾微秒。
好,到這一步爲止,內存緩衝已經被交換了,後面的線程能夠立馬快速的依次獲取鎖,而後將edits log寫入內存緩衝的區域2,區域1中的數據被鎖定了,不能寫。
怎麼樣,是否是又感覺到了一點點多線程併發的優化?
接着,以前那個幸運兒線程,將內存緩衝的區域1中的數據讀取出來(此時沒人寫區域1了,都在寫區域2),將裏面的edtis log都寫入磁盤文件,以及經過網絡寫入JournalNodes集羣。
這個過程但是很耗時的!可是不要緊啊,人家作過優化了,在寫磁盤和網絡的過程當中,是不持有鎖的!
所以後面的線程能夠噼裏啪啦的快速的第一次獲取鎖後,立馬寫入內存緩衝的區域2,而後釋放鎖。
這個時候大量的線程均可以快速的寫入內存,沒有阻塞和卡頓!
怎麼樣?併發優化的感受感覺到了沒有!
那麼在幸運兒線程吭哧吭哧把數據寫磁盤和網絡的過程當中,排在後面的大量線程,快速的第一次獲取鎖,寫內存緩衝區域2,釋放鎖,以後,這些線程第二次獲取到鎖後會幹嗎?
他們會發現有人在寫磁盤啊,兄弟們!因此會當即休眠1秒,釋放鎖。
此時大量的線程併發過來的話,都會在這裏快速的第二次獲取鎖,而後發現有人在寫磁盤和網絡,快速的釋放鎖,休眠。
怎麼樣,這個過程沒有人長時間的阻塞其餘人吧!由於都會快速的釋放鎖,因此後面的線程仍是能夠迅速的第一次獲取鎖後寫內存緩衝!
again!併發優化的感受感覺到了沒有?
並且這時,必定會有不少線程發現,好像以前那個幸運兒線程的txid是排在本身以後的,那麼確定就把本身的edits log從緩衝裏寫入磁盤和網絡了。
這些線程甚至都不會休眠等待,直接就會返回後去幹別的事情了,壓根兒不會卡在這裏。這裏又感覺到併發的優化沒有?
而後那個幸運兒線程寫完磁盤和網絡以後,就會喚醒以前休眠的那些線程。
那些線程會依次排隊再第二次獲取鎖後進入判斷,咦!發現沒有人在寫磁盤和網絡了!
而後就會再判斷,有沒有排在本身以後的線程已經將本身的edtis log寫入磁盤和網絡了。
若是有的話,就直接返回了。
沒有的話,那麼就成爲第二個幸運兒線程,交換兩塊緩衝區,區域1和區域2交換一下。
而後釋放鎖,本身開始吭哧吭哧的將區域2的數據寫入磁盤和網絡。
可是這個時候沒有關係啊,後面的線程若是要寫edits log的,仍是能夠第一次獲取鎖後立馬寫內存緩衝再釋放鎖。以此類推。
其實這套機制仍是挺複雜的,涉及到了分段加鎖以及內存雙緩衝兩個機制。
經過這套機制,NameNode保證了多個線程在高併發的修改元數據以後寫edits log的時候,不會說一個線程一個線程的寫磁盤和網絡,那樣性能實在太差,併發能力太弱了!
因此經過上述那套複雜的機制,盡最大的努力保證,一個線程能夠批量的將一個緩衝中的多條edits log刷入磁盤和網絡。
在這個漫長的吭哧吭哧的過程當中,其餘的線程能夠快速的高併發寫入edits log到內存緩衝裏,不會阻塞其餘的線程寫edits log。
因此,正是依靠以上機制,最大限度優化了NameNode處理高併發訪問修改元數據的能力!
《【性能優化的祕密】Hadoop如何將TB級大文件的上傳性能提高上百倍?》,敬請期待
若有收穫,請幫忙轉發,您的鼓勵是做者最大的動力,謝謝!
一大波微服務、分佈式、高併發、高可用的****原創系列
文章正在路上,歡迎掃描下方二維碼,持續關注:
石杉的架構筆記(id:shishan100)
十餘年BAT架構經驗傾囊相授