這篇文章給大家分享的是有關(guān)如何解決HDFS Decommission緩慢問題的內(nèi)容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。
c3prc-xiami有大量raid單副本文件,decommission單個datanode速度很慢,觀察監(jiān)控指標,發(fā)現(xiàn):
網(wǎng)卡流量始終保持低速,60~80mb/s
磁盤io util也是單個磁盤100%在某一個時刻,也即是同一時間只有一個磁盤在工作
這樣下線速度就十分緩慢,一個datanode一天只能下4w個block。而一個datanode平均有20w個block,這個速度明顯不符合要求。
最開始認為是配置問題,我們分析了幾種配置,有一定的效果,提高到了6w個block每天,但還是慢。
最后我們從代碼層面著手,改變相關(guān)數(shù)據(jù)結(jié)構(gòu),使下線速度明顯提升,一個datanode下線平均1到2天就能下完。
分析配置public static final String DFS_NAMENODE_REPLICATION_MAX_STREAMS_KEY = "dfs.namenode.replication.max-streams";public static final int DFS_NAMENODE_REPLICATION_MAX_STREAMS_DEFAULT = 2;public static final String DFS_NAMENODE_REPLICATION_STREAMS_HARD_LIMIT_KEY = "dfs.namenode.replication.max-streams-hard-limit";public static final int DFS_NAMENODE_REPLICATION_STREAMS_HARD_LIMIT_DEFAULT = 4;
blockmanager.maxReplicationStreams這個變量有兩個作用:
(1)在選擇從哪里把塊復(fù)制出去的時候chooseSourceDatanode(),如果某個dn上已經(jīng)有>maxReplicationStreams的塊在被復(fù)制則不會再選中它作為源了。
(2)HeartbeatManager每次會想dn發(fā)送DNA_TRANSFER命令,會從DatanodeDescriptor.replicateBlocks取一定數(shù)量的block進行傳輸,而每個DN能夠啟動的DataTransfer線程數(shù)大不能超過maxReplicationStreams。
blockmanage.replicationStreamsHardLimit同上一個變量類似,只是在chooseSourceDatanode(),如果block的優(yōu)先級最高,這個dn還能再多復(fù)制2個(默認值分別是2,4),但是不能>replicationStreamsHardLimit。
所以在同一時刻最多replicationStreamsHardLimit被選出,而且是在同一個dn中。但是單純調(diào)整hardLimit并沒有多少效果。
真正控制一個dn往外拷貝數(shù)據(jù)還是maxReplicationStreams,dn通過心跳向NN報告正在進行Transfer的線程數(shù),而后NN向dn發(fā)送maxTransfer個DNA_transfer CMD:
//get datanode commandsfinal int maxTransfer = blockManager.getMaxReplicationStreams()
- xmitsInProgress;
xmitsInProgress=正在傳輸數(shù)量
對于每個dn:從待復(fù)制blocks隊列取出maxTransfers
public List<BlockTargetPair> getReplicationCommand(int maxTransfers) {
return replicateBlocks.poll(maxTransfers);
}
結(jié)論1:
通過調(diào)大上述2個參數(shù),從2到4,再調(diào)整到8,效果還是比較明顯,dn中的日志也反映出同一個時刻傳輸線程數(shù)有所增加。
但當調(diào)整為12或者更大時,就沒有多少效果了。整體網(wǎng)速也沒有上來。
目前調(diào)整為12是比較合理的。和單個dn磁盤數(shù)量對應(yīng)。
publicstatic final String DFS_NAMENODE_REPLICATION_MAX_STREAMS_KEY ="dfs.namenode.replication.max-streams";
publicstatic final int DFS_NAMENODE_REPLICATION_MAX_STREAMS_DEFAULT = 2;
publicstatic final String DFS_NAMENODE_REPLICATION_STREAMS_HARD_LIMIT_KEY ="dfs.namenode.replication.max-streams-hard-limit";
publicstatic final int DFS_NAMENODE_REPLICATION_STREAMS_HARD_LIMIT_DEFAULT = 4;
namenode中的blockmanager. ReplicationMonitor每3秒會computeDatanodeWork并且取一批block,然后告訴dn去復(fù)制這些blocks取的數(shù)量:
final int blocksToProcess = numlive
* this.blocksReplWorkMultiplier;
開始我認為調(diào)大能加快下線速度,但這個參數(shù)影響小,它的作用僅僅是把取出來的block放入DatanodeDescriptor的等待隊列中(replicateBlocks)
同過觀察NN日志發(fā)現(xiàn)如下問題:
ReplicationMonitor每次迭代會打印日志:
askdn_ip to replicate blk_xxx to another_dn.
并且數(shù)量為blocksToProcess。在同一時刻,這個dn_ip都是一樣的。
每次迭代(在一段時間內(nèi)循環(huán))多次要求同一個dn拷貝blocksToProcess(c3prc-xiaomi=800個)blocks,而dn每次最多拷貝maxReplicationStreams
也就是說NN做了很多無效工作,取的blocks都是同一個dn,當取到輪到下一個dn時,又是同樣的問題。并不是每個dn都在同時工作。觀察監(jiān)控發(fā)現(xiàn)dn間歇性往外拷貝數(shù)據(jù)。
ReplicationMonitor每次取blocksToProcess個blocks的時候,這些blocks可能是同一個dn上,甚至同一個dn的同一個磁盤上。
因此,要分析每次的取法。目的是能取出不連續(xù)的blocks,能讓不同dn,不同磁盤同時工作。
//分析數(shù)據(jù)結(jié)構(gòu)//和下線主要有關(guān)的代碼集中在blockmanager,以及UnderReplicatedBlocks
public final UnderReplicatedBlocks neededReplications = new UnderReplicatedBlocks();
所有需要做replicate的block都會放在blockmanager.neededReplications中。
UnderReplicatedBlocks是一個復(fù)合結(jié)構(gòu),保存者5個(LEVEL=5)帶有優(yōu)先級的隊列:
private final List<LightWeightHashSet<Block>> priorityQueues
對于只有一個副本的block,或者replica都在decommision節(jié)點上,它在優(yōu)先級最高的隊列中,raid副本下線就是這種情況。
對于每一個優(yōu)先級的隊列,實現(xiàn)是LightWeightLinkedSet,它是一個有序的hashset,元素收尾相連。
UnderReplicatedBlocks的實現(xiàn)是保證每個隊列的元素都會被取到,同時,每個隊列中的元素按順序依次被取出,不會讓某些block永遠沒機會被取出。
具體做法是為每一個隊列保存一個偏移:
private finalList<LightWeightHashSet<Block>> priorityQueues
如果取到最后一個隊列(LEVEL-1)末尾了,就重置所有隊列的偏移=0,從頭再取。
這5個隊列都是先進先被選,隊尾進,而且優(yōu)先級Level=0的更容易被取到。具體取的算法是在UnderReplicatedBlocks.chooseUnderReplicatedBlocks()中。
在進行decommission操作的時候,可能整個dn的塊都是要加入neededReplication隊列(raid集群如此,如果有3副本,那一個block有3個source.單副本的source dn只有一個)。這時候,加入某一個優(yōu)先級隊列(LightWeightLinkedSet)的blocks是有序的,而且連續(xù)上w個blocks屬于同一個dn,甚至連續(xù)在同一個磁盤上。由于從隊列是從頭到尾順序取,所以會有問題,尤其是對單副本的情況。
因此,我們想要隨機從優(yōu)先級隊列中取出block.但又要保證每個block被取到,所以還是要有序的。
//數(shù)據(jù)結(jié)構(gòu)改造-ShuffleAddSet//實際上,這么做是合理的,先進入隊列可以優(yōu)先被取到。但對于我們這種場景,并不要求取出的順序性和放入順序一致。如果能打亂順序,再取出就能使一次迭代取出的blocks盡可能在不同dn或者不同磁盤上。
LightWeightLinkedSet:UnderReplicatedBlocks默認采用的優(yōu)先級隊列的實現(xiàn)。本身是一個hashset繼承LightWeightHashSet,同時元素雙向連接,帶有Head tail
LightWeightHashSet:輕量級hashSet
ShuffleAddSet:Look like LightWeightLinkedSet
我們實現(xiàn)了一種ShuffleAddSet繼承LightWeightHashSet,盡可能表現(xiàn)和LightWeightLinkedSet一致,這樣對外UnderReplicatedBlocks不需要做過多修改。
ShuffleAddSet中有兩個隊列,而對外表現(xiàn)為一個優(yōu)先級隊列。
第一個隊列,同時也是Set和LightWeightLinkedSet是一樣的,雙向有序,外部調(diào)用方法取元素也是從這個Set取。
第二個隊列,緩存隊列cachedAddList,一開始用ArrayList、LinkedList,由于性能問題不使用了?,F(xiàn)在也是用LightWeightHashSet,HashSet具有天然無序性質(zhì)。
每當有新的元素加入,首先會放入cachedAddList中,隨后當?shù)谝粋€隊列數(shù)據(jù)空或者取到末尾,立即將cachedAddList數(shù)據(jù)shuffle,并拷貝到第一個隊列中,然后清空自己,繼續(xù)接收新元素。由于HashSet本身無序,因此少一步shuffle操作,直接從cachedAddList拷貝至第一個隊列即可。
需要注意的是取數(shù)據(jù)(調(diào)用迭代器取)和add操作必須是同步的,因為取的時候第一個隊列到達末尾或著空,會觸發(fā)shuffle and add操作,清空cachedlist。
綜上,第一個隊列只有為空或者取到末尾的時候,會從第二個隊里加入數(shù)據(jù),如果都為空說明整個優(yōu)先級隊列空。每從cachedlist加入一批,這一批就是隨機順序,雖然第一個隊列不是整個隊列都隨機打亂,總體上,第一個隊列還是是亂序的。
這樣做的問題:
(1)外部調(diào)用這個優(yōu)先級隊列的add操作,先進入隊列的不一定是先被調(diào)度,后加入cachedList的元素,也可能排在第一個隊列的前面先調(diào)度。
(2)極端情況,如果每次加入1個,然后再取1個元素,少量的元素,或者取出數(shù)量和頻率遠大于add數(shù)量,(比如cachedlist加入一個元素,第一個隊列立刻到達末尾了)實際上沒有達到隨機效果。
好在我們的場景不要求FIFO,而且每次Decommision初始加入UnderReplicatedBlocks的block數(shù)量很大,ReplicationMonitor每次取/處理的數(shù)量blockToProcess,相對而言(下線8臺節(jié)點,UnderReplicatedBlocks會達到180w)較小。發(fā)生shuffle and add不是很頻繁,也不是性能瓶頸。觀測到最長時間是200ms。
同時,我們將這個功能ShuffleAddSet作為一種可配置項目,UnderReplicatedBlocks可以在初始化時候選擇用ShuffleAddSet或者LightWeightLinkedSet
dfs.namenode.blockmanagement.queues.shuffle
如下:
private final List<LightWeightHashSet<Block>> priorityQueues
= new ArrayList<LightWeightHashSet<Block>>();public static final String NAMENODE_BLOCKMANAGEMENT_QUEUES_SHUFFLE = "dfs.namenode.blockmanagement.queues.shuffle";private Map<Integer, Integer> priorityToReplIdx = new HashMap<Integer, Integer>(LEVEL);new HdfsConfiguration(); boolean useShuffle = conf.getBoolean(NAMENODE_BLOCKMANAGEMENT_QUEUES_SHUFFLE, false); for (int i = 0; i < LEVEL; i++) { if (useShuffle) {
priorityQueues.add(new ShuffleAddSet<Block>());
} else {
priorityQueues.add(new LightWeightLinkedSet<Block>());
}
priorityToReplIdx.put(i, 0);
}
}
//性能問題//我們發(fā)現(xiàn)使用ShuffleAddSet時候,開始下線8臺dn時會卡住,主要是卡在DecommissionManager$Monitor,每次要檢查此dn上全部的blocks是否 underReplicated,這樣blocks很多拿寫鎖的時間會很長。
也會檢查ShuffleAddSet.contains(blocks),由于有兩個隊列,所以contain開銷會比之前大。
2019-04-12,12:09:35,876 INFO org.apache.hadoop.hdfs.server.namenode.FSNamesystem: Long read lock is held at 1555042175235. And released after 641 milliseconds.Call stack is:
java.lang.Thread.getStackTrace(Thread.java:1479)
org.apache.hadoop.util.StringUtils.getStackTrace(StringUtils.java:914)
org.apache.hadoop.hdfs.server.namenode.FSNamesystemLock.checkAndLogLongReadLockDuration(FSNamesystemLock.java:104)
org.apache.hadoop.hdfs.server.namenode.FSNamesystem.writeUnlock(FSNamesystem.java:1492)
org.apache.hadoop.hdfs.server.blockmanagement.BlockManager.isReplicationInProgress(BlockManager.java:3322)
org.apache.hadoop.hdfs.server.blockmanagement.DatanodeManager.checkDecommissionState(DatanodeManager.java:751)
org.apache.hadoop.hdfs.server.blockmanagement.DecommissionManager$Monitor.check(DecommissionManager.java:93)
org.apache.hadoop.hdfs.server.blockmanagement.DecommissionManager$Monitor.run(DecommissionManager.java:70)
java.lang.Thread.run(Thread.java:662)
通過修改isReplicationInProgress方法,類似處理blockreport,每隔一定數(shù)量放一次鎖的方式,緩解寫鎖時間太長導(dǎo)致其他rpc請求沒有響應(yīng)。
++processed;// Release lock per 5w blocks processed and has too many underReplicatedBlocks.if (processed == numReportBlocksPerIteration &&
namesystem.hasWriteLock() && underReplicatedBlocks > numReportBlocksPerIteration) {
namesystem.writeUnlock();
processed = 0;
namesystem.writeLock();
}
//結(jié)論//對于單副本較多的集群,可采用如下方式下線:
dfs.namenode.blockmanagement.queues.shuffle= truedfs.namenode.replication.max-streams= 12 默認是2,限制一個datanode復(fù)制數(shù)量
dfs.namenode.replication.max-streams-hard-limit=12 默認是4dfs.namenode.replication.work.multiplier.per.iteration= 4 默認2 / namenode一次調(diào)度的數(shù)量=該值×datanodes數(shù)量
開啟shuffle and add,并調(diào)整單個dn大復(fù)制數(shù)為物理磁盤數(shù)量,對于小集群可以調(diào)大work.multiplier一次處理4倍LiveDatanode數(shù)量block.使下線速度大化。
注意的問題:
每次操作下線2臺節(jié)點(refreshNodes),隔10分鐘再下2臺,一直到8臺。同時下線的dn最好不要超過8臺。不然DecommissionManager的開銷會很大,影響NN正常服務(wù)。
感謝各位的閱讀!關(guān)于“如何解決HDFS Decommission緩慢問題”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,讓大家可以學(xué)到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
新聞標題:如何解決HDFSDecommission緩慢問題-創(chuàng)新互聯(lián)
本文鏈接:http://www.rwnh.cn/article22/ddcgjc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)頁設(shè)計公司、外貿(mào)網(wǎng)站建設(shè)、域名注冊、全網(wǎng)營銷推廣、響應(yīng)式網(wǎng)站、用戶體驗
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容