大規模集羣下Hadoop NameNode如何承載每秒上千次的高併發訪問

本文咱們來看看,若是大量客戶端對NameNode發起高併發(好比每秒上千次)訪問來修改元數據,此時NameNode該如何抗住?node

2、問題源起web

咱們先來分析一下,高併發請求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,會咋樣?網絡

<figure style="margin: 1em 0px; color: rgb(26, 26, 26); font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif; font-size: medium; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">!](http://upload-images.jianshu.io/upload_images/6898826-31b8c59c002c1754.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)多線程

</figure>架構

不用說,這個絕對完蛋了!
NameNode自己用多線程接收多個客戶端發送過來的併發的請求,結果多個線程竟然修改完內存中的元數據以後,排着隊寫edits log!
並且你要知道,寫本地磁盤 + 網絡傳輸給journalnodes,都是很耗時的啊!性能兩大殺手:磁盤寫 + 網絡寫!
若是HDFS的架構真要是這麼設計的話,基本上NameNode能承載的每秒的併發數量就不多了,可能就每秒處理幾十個併發請求處理撐死了!
3、HDFS優雅的解決方案
因此說,針對這個問題,人家HDFS是作了很多的優化的!併發

首先你們想一下,既然我們不但願每一個線程寫edits log的時候,串行化排隊生成txid + 寫磁盤 + 寫JournalNode,那麼是否是能夠搞一個內存緩衝?
也就是說,多個線程能夠快速的獲取鎖,生成txid,而後快速的將edits log寫入內存緩衝。
接着就快速的釋放鎖,讓下一個線程繼續獲取鎖後,生成id + 寫edits log進入內存緩衝。
而後接下來有一個線程能夠將內存中的edits log刷入磁盤,可是在這個過程當中,仍是繼續容許其餘線程將edits log寫入內存緩衝中。
可是這裏又有一個問題了,若是針對同一塊內存緩衝,同時有人寫入,還同時有人讀取後寫磁盤,那也有問題,由於不能併發讀寫一塊共享內存數據!
因此HDFS在這裏採起了double-buffer雙緩衝機制來處理!將一塊內存緩衝分紅兩個部分:app

  • 其中一個部分能夠寫入分佈式

  • 另一個部分用於讀取後寫入磁盤和JournalNodes。

(1)分段加鎖機制 + 內存雙緩衝機制

首先各個線程依次第一次獲取鎖,生成順序遞增的txid,而後將edits log寫入內存雙緩衝的區域1,接着就立馬第一次釋放鎖了。
趁着這個空隙,後面的線程就能夠再次立馬第一次獲取鎖,而後當即寫本身的edits log到內存緩衝。
寫內存那麼快,可能才耗時幾十微妙,接着就立馬第一次釋放鎖了。因此這個併發優化絕對是有效果的,你們有沒有感覺到?
接着各個線程競爭第二次獲取鎖,有線程獲取到鎖以後,就看看,有沒有誰在寫磁盤和網絡?
若是沒有,好,那麼這個線程是個幸運兒!直接交換雙緩衝的區域1和區域2,接着第二次釋放鎖。這個過程至關快速,內存裏判斷幾個條件,耗時不了幾微秒。
好,到這一步爲止,內存緩衝已經被交換了,後面的線程能夠立馬快速的依次獲取鎖,而後將edits log寫入內存緩衝的區域2,區域1中的數據被鎖定了,不能寫。
怎麼樣,是否是又感覺到了一點點多線程併發的優化?
(2)多線程併發吞吐量的百倍優化
接着,以前那個幸運兒線程,將內存緩衝的區域1中的數據讀取出來(此時沒人寫區域1了,都在寫區域2),將裏面的edtis log都寫入磁盤文件,以及經過網絡寫入JournalNodes集羣。
這個過程但是很耗時的!可是不要緊啊,人家作過優化了,在寫磁盤和網絡的過程當中,是不持有鎖的!
所以後面的線程能夠噼裏啪啦的快速的第一次獲取鎖後,立馬寫入內存緩衝的區域2,而後釋放鎖。
這個時候大量的線程均可以快速的寫入內存,沒有阻塞和卡頓!
怎麼樣?併發優化的感受感覺到了沒有!

(3)緩衝數據批量刷磁盤 + 網絡的優化
那麼在幸運兒線程吭哧吭哧把數據寫磁盤和網絡的過程當中,排在後面的大量線程,快速的第一次獲取鎖,寫內存緩衝區域2,釋放鎖,以後,這些線程第二次獲取到鎖後會幹嗎?

他們會發現有人在寫磁盤啊,兄弟們!因此會當即休眠1秒,釋放鎖。

此時大量的線程併發過來的話,都會在這裏快速的第二次獲取鎖,而後發現有人在寫磁盤和網絡,快速的釋放鎖,休眠。

怎麼樣,這個過程沒有人長時間的阻塞其餘人吧!由於都會快速的釋放鎖,因此後面的線程仍是能夠迅速的第一次獲取鎖後寫內存緩衝!

again!併發優化的感受感覺到了沒有?

並且這時,必定會有不少線程發現,好像以前那個幸運兒線程的txid是排在本身以後的,那麼確定就把本身的edits log從緩衝裏寫入磁盤和網絡了。

這些線程甚至都不會休眠等待,直接就會返回後去幹別的事情了,壓根兒不會卡在這裏。這裏又感覺到併發的優化沒有?

而後那個幸運兒線程寫完磁盤和網絡以後,就會喚醒以前休眠的那些線程。

那些線程會依次排隊再第二次獲取鎖後進入判斷,咦!發現沒有人在寫磁盤和網絡了!

而後就會再判斷,有沒有排在本身以後的線程已經將本身的edtis log寫入磁盤和網絡了。

  • 若是有的話,就直接返回了。
  • 沒有的話,那麼就成爲第二個幸運兒線程,交換兩塊緩衝區,區域1和區域2交換一下。
  • 而後釋放鎖,本身開始吭哧吭哧的將區域2的數據寫入磁盤和網絡。

可是這個時候沒有關係啊,後面的線程若是要寫edits log的,仍是能夠第一次獲取鎖後立馬寫內存緩衝再釋放鎖。以此類推。

在此我向你們推薦一個架構學習交流裙。交流學習裙號:736220120 裏面會分享一些資深架構師錄製的視頻錄像:有Spring,MyBatis,Netty源碼分析,高併發、高性能、分佈式、微服務架構的原理,JVM性能優化、分佈式架構等這些成爲架構師必備的知識體系。還能領取免費的學習資源,目前受益良多。

相關文章
相關標籤/搜索