http://blog.csdn.net/lipeng_bigdata/article/details/51160359

UnderReplicatedBlocks是HDFS中关于块复制的一个重要数据结构。在HDFS的高性能、高容错性体系中,总有一些原因促使HDFS系统内进行块复制工作,比如基于高性能的负载均衡、基于容错性的数据块副本数恢复等。普遍的,任何工作都会有一个优先级的问题,特别是这里的数据块复制,不可能简单的按照先入先出或者其他简单策略,比方说,基于容错性的数据块副本数恢复,特别是数据块副本仅有一个的数据块副本数恢复,其优先级肯定要比基于高性能的负载均衡高,所以数据块复制要有个优先级的概念,那么,数据块复制的优先级怎么确定,怎么存储?一切答案均在UnderReplicatedBlocks中,本文我们将开始分析UnderReplicatedBlocks。

UnderReplicatedBlocks专门用于存储待优先级的需要复制的数据块。何谓数据块复制优先级,我们看下UnderReplicatedBlocks类中的几个静态成员变量及其说明就会得到答案,如下:

  1. /** The queue with the highest priority: {@value} */
  2. // 最高优先级队列的优先级值0
  3. static final int QUEUE_HIGHEST_PRIORITY = 0;
  4. /** The queue for blocks that are way below their expected value : {@value} */
  5. // 第二优先级队列的优先级值1:主要针对低于副本数要求很多的数据块
  6. static final int QUEUE_VERY_UNDER_REPLICATED = 1;
  7. /** The queue for "normally" under-replicated blocks: {@value} */
  8. // 第三优先级队列的优先级值2:主要针对低于副本数要求不是很多,即一般情况的数据块
  9. static final int QUEUE_UNDER_REPLICATED = 2;
  10. /** The queue for blocks that have the right number of replicas,
  11. * but which the block manager felt were badly distributed: {@value}
  12. */
  13. // 第四优先级队列的优先级值3:主要针对副本数满足要求,但是数据块管理器BlockManager感觉严重分布不均
  14. static final int QUEUE_REPLICAS_BADLY_DISTRIBUTED = 3;
  15. /** The queue for corrupt blocks: {@value} */
  16. // 第五优先级队列的优先级值4:主要针对损坏的数据块
  17. static final int QUEUE_WITH_CORRUPT_BLOCKS = 4;

数据块复制优先级共分为五种级别,从高到底依次如下:

1、QUEUE_HIGHEST_PRIORITY = 0:最高优先级

主要针对数据块副本数非常的、严重的不足的情况,当前副本数低于期望值,且仅有1个或者干脆没有,比如副本数仅有1个,或者副本数干脆为0,但是还存在退役副本,这种情况最危险,数据最容易丢失,所以复制的优先级也最高;

2、QUEUE_VERY_UNDER_REPLICATED = 1:第二优先级

主要针对数据块副本数比较不足的情况,比上面的情况好点,当前副本数低于期望值,但是副本数大于1,其判断公式为当前副本数curReplicas乘以3还小于期望副本数expectedReplicas,这种情况也比较危险,数据也容易丢失,所以复制的优先级也很高;

3、QUEUE_UNDER_REPLICATED = 2:第三优先级

主要针对数据块副本数低于期望值,但是还不是很严重、很危急的情况;

4、QUEUE_REPLICAS_BADLY_DISTRIBUTED = 3:第四优先级

主要针对数据块已经有足够的副本数,但是没有足够的机架的情况,这是负载均衡等策略需要的产物;

5、QUEUE_WITH_CORRUPT_BLOCKS = 4:第五优先级

主要针对损坏的数据块的情况,其副本数位0,但是还没有退役副本,所以优先级最低,话说,这种数据块还需要哦复制吗?留个小小的疑问吧!

通过上面的说明,我们可以简单总结下:

当前副本数低于期望值时,如果当前副本数为1,甚至存在退役副本的情况下为0时,其复制优先级最高,如果当前副本数为0且没有退役副本,则复制优先级最低;如果当前副本数大于1,但是乘以3还小于期望副本数,处于比较危险的情况,则优先级次之,否则是第三优先级。而当当前副本数等于或高于期望值时,则可能是没有足够机架的情况,此时的优先级比最低优先级稍高,为第四优先级。

UnderReplicatedBlocks也提供了根据数据块及其副本情况来获取复制优先级的getPriority()方法,代码如下:

  1. /** Return the priority of a block
  2. * 计算指定数据块复制优先级
  3. *
  4. * @param block a under replicated block
  5. * @param curReplicas current number of replicas of the block
  6. * @param expectedReplicas expected number of replicas of the block
  7. * @return the priority for the blocks, between 0 and ({@link #LEVEL}-1)
  8. */
  9. private int getPriority(Block block,
  10. int curReplicas,
  11. int decommissionedReplicas,
  12. int expectedReplicas) {
  13. // 参数校验:当前副本数curReplicas应大于等于0
  14. assert curReplicas >= 0 : "Negative replicas!";
  15. // 如果当前副本数curReplicas大于等于期望的副本数,则返回第四优先级队列的优先级值3
  16. if (curReplicas >= expectedReplicas) {
  17. // 数据块已经有足够的副本数,但是没有足够的机架
  18. // Block has enough copies, but not enough racks
  19. return QUEUE_REPLICAS_BADLY_DISTRIBUTED;
  20. } else if (curReplicas == 0) {// 如果当前副本数curReplicas为0
  21. // If there are zero non-decommissioned replicas but there are
  22. // some decommissioned replicas, then assign them highest priority
  23. // 如果decommissionedReplicas大于0,返回最高优先级队列的优先级值0
  24. // 即没有非退役副本,但是有一些退役的副本,那么我们需要分配给它们最高优先级
  25. if (decommissionedReplicas > 0) {
  26. return QUEUE_HIGHEST_PRIORITY;
  27. }
  28. // all we have are corrupt blocks
  29. // 没有非退役副本,也没有退役副本,我们就认为它是个损坏的数据块,复制优先级最低,为第五优先级队列的优先级值4
  30. return QUEUE_WITH_CORRUPT_BLOCKS;
  31. } else if (curReplicas == 1) {// 如果当前副本数curReplicas为1
  32. //only on replica -risk of loss
  33. // highest priority
  34. // 仅仅有一个副本,有丢失的风险,所以赋予最高优先级0
  35. return QUEUE_HIGHEST_PRIORITY;
  36. } else if ((curReplicas * 3) < expectedReplicas) {
  37. //there is less than a third as many blocks as requested;
  38. //this is considered very under-replicated
  39. // 如果如果当前副本数curReplicas乘以3还小于期望副本数expectedReplicas,返回第二优先级队列的优先级值1
  40. return QUEUE_VERY_UNDER_REPLICATED;
  41. } else {
  42. //add to the normal queue for under replicated blocks
  43. // 一般的低于副本数的优先级,返回第三优先级队列的优先级值2
  44. return QUEUE_UNDER_REPLICATED;
  45. }

思路和上述介绍一样,大体逻辑如下:

1、如果当前副本数curReplicas大于等于期望的副本数,则返回第四优先级队列的优先级值3--QUEUE_REPLICAS_BADLY_DISTRIBUTED;

2、如果当前副本数curReplicas为0,且如果decommissionedReplicas大于0,返回最高优先级队列的优先级值0,没有非退役副本,也没有退役副本,我们就认为它是个损坏的数据块,复制优先级最低,为第五优先级队列的优先级值4;

3、如果当前副本数curReplicas为1,仅仅有一个副本,有丢失的风险,所以赋予最高优先级0;

4、如果如果当前副本数curReplicas乘以3还小于期望副本数expectedReplicas,返回第二优先级队列的优先级值1;

5、一般的低于副本数的优先级,返回第三优先级队列的优先级值2。

UnderReplicatedBlocks还提供了涉及复制优先级队列的成员变量,如下:

  1. /** The total number of queues : {@value} */
  2. // 队列总数
  3. static final int LEVEL = 5;
  4. /** the queues themselves */
  5. // 队列集合
  6. private final List<LightWeightLinkedSet<Block>> priorityQueues
  7. = new ArrayList<LightWeightLinkedSet<Block>>();
  8. /** Stores the replication index for each priority */
  9. // 存储每个优先级对应的复制索引
  10. private Map<Integer, Integer> priorityToReplIdx = new HashMap<Integer, Integer>(LEVEL);

总的队列数目为5,而存储待复制不同优先级块集合的是priorityQueues列表,它是数据块集合LightWeightLinkedSet的列表,并且还提供了存储每种优先级对应的块复制索引的集合priorityToReplIdx,它是数字形式优先级priority到块在集合LightWeightLinkedSet中位置索引index的映射。

UnderReplicatedBlocks的构造函数如下:

  1. /** Create an object. */
  2. // 构造函数,创建一个对象
  3. UnderReplicatedBlocks() {
  4. // 5个LightWeightLinkedSet集合,存储到priorityQueues列表中,
  5. // 并将优先级与复制索引的映射存储到priorityToReplIdx中
  6. for (int i = 0; i < LEVEL; i++) {
  7. priorityQueues.add(new LightWeightLinkedSet<Block>());
  8. priorityToReplIdx.put(i, 0);
  9. }
  10. }

上来先构造5个LightWeightLinkedSet集合,并按照优先级由高到低的顺序,添加到列表priorityQueues中,并初始化每种块复制优先级对应的位置索引为0。

UnderReplicatedBlocks还提供了相应的添加、移除数据块及更新优先级方法,分别介绍如下:

1、添加数据块add()

  1. /** add a block to a under replication queue according to its priority
  2. * @param block a under replication block
  3. * @param curReplicas current number of replicas of the block
  4. * @param decomissionedReplicas the number of decommissioned replicas
  5. * @param expectedReplicas expected number of replicas of the block
  6. * @return true if the block was added to a queue.
  7. */
  8. synchronized boolean add(Block block,
  9. int curReplicas,
  10. int decomissionedReplicas,
  11. int expectedReplicas) {
  12. assert curReplicas >= 0 : "Negative replicas!";
  13. // 根据入参数据块及其副本情况计算块复制的优先级priLevel
  14. int priLevel = getPriority(block, curReplicas, decomissionedReplicas,
  15. expectedReplicas);
  16. // 如果块复制优先级priLevel小于5(即是一个正确有效的优先级),并且
  17. // 根据优先级priLevel从priorityQueues中取出相应块集合并将块添加入集合成功的话,返回true
  18. if(priLevel != LEVEL && priorityQueues.get(priLevel).add(block)) {
  19. if(NameNode.blockStateChangeLog.isDebugEnabled()) {
  20. NameNode.blockStateChangeLog.debug(
  21. "BLOCK* NameSystem.UnderReplicationBlock.add:"
  22. + block
  23. + " has only " + curReplicas
  24. + " replicas and need " + expectedReplicas
  25. + " replicas so is added to neededReplications"
  26. + " at priority level " + priLevel);
  27. }
  28. return true;
  29. }
  30. // 否则返回false
  31. return false;
  32. }

添加数据块的add()方法比较简单,首先根据入参数据块及其副本情况调用getPriority()方法计算块复制的优先级priLevel,然后如果块复制优先级priLevel小于5(即是一个正确有效的优先级),并且根据优先级priLevel从priorityQueues中取出相应块集合并将块添加入集合成功的话,返回true,表示添加成功,否则返回false,表示添加失败。

2、移除数据块remove()

  1. /** remove a block from a under replication queue */
  2. synchronized boolean remove(Block block,
  3. int oldReplicas,
  4. int decommissionedReplicas,
  5. int oldExpectedReplicas) {
  6. // 先根据入参数据块及其副本情况,调用getPriority()方法计算块复制优先级priLevel
  7. int priLevel = getPriority(block, oldReplicas,
  8. decommissionedReplicas,
  9. oldExpectedReplicas);
  10. // 调用两个参数的remove()方法,移除数据块
  11. return remove(block, priLevel);
  12. }
  1. /**
  2. * Remove a block from the under replication queues.
  3. *
  4. * The priLevel parameter is a hint of which queue to query
  5. * first: if negative or >= {@link #LEVEL} this shortcutting
  6. * is not attmpted.
  7. *
  8. * If the block is not found in the nominated queue, an attempt is made to
  9. * remove it from all queues.
  10. *
  11. * <i>Warning:</i> This is not a synchronized method.
  12. * @param block block to remove
  13. * @param priLevel expected privilege level
  14. * @return true if the block was found and removed from one of the priority queues
  15. */
  16. boolean remove(Block block, int priLevel) {
  17. // 如果优先级priLevel是正确有效的,且根据优先级priLevel从列表priorityQueues中
  18. // 取出数据块集合后,从中移除数据块成功的话,返回true,表示移除成功
  19. if(priLevel >= 0 && priLevel < LEVEL
  20. && priorityQueues.get(priLevel).remove(block)) {
  21. if(NameNode.blockStateChangeLog.isDebugEnabled()) {
  22. NameNode.blockStateChangeLog.debug(
  23. "BLOCK* NameSystem.UnderReplicationBlock.remove: "
  24. + "Removing block " + block
  25. + " from priority queue "+ priLevel);
  26. }
  27. return true;
  28. } else {
  29. // 否则,在给定优先级对应数据块集合中移除失败的话,尝试从所有优先级各自对应的队列中移除数据块,
  30. // 任何一个移除成功,均返回true,表示移除成功
  31. // Try to remove the block from all queues if the block was
  32. // not found in the queue for the given priority level.
  33. for (int i = 0; i < LEVEL; i++) {
  34. if (priorityQueues.get(i).remove(block)) {
  35. if(NameNode.blockStateChangeLog.isDebugEnabled()) {
  36. NameNode.blockStateChangeLog.debug(
  37. "BLOCK* NameSystem.UnderReplicationBlock.remove: "
  38. + "Removing block " + block
  39. + " from priority queue "+ i);
  40. }
  41. return true;
  42. }
  43. }
  44. }
  45. // 最后,如果还不行的话,则返回false,表示移除失败
  46. return false;
  47. }

首先,在四个参数的remove()方法中,先根据入参数据块及其副本情况,调用getPriority()方法计算块复制优先级priLevel,然后调用两个参数的remove()方法,移除数据块;

其次,在两个参数的remove()方法中,如果优先级priLevel是正确有效的,且根据优先级priLevel从列表priorityQueues中取出数据块集合后,从中移除数据块成功的话,返回true,表示移除成功;否则,在给定优先级对应数据块集合中移除失败的话,尝试从所有优先级各自对应的队列中移除数据块,任何一个移除成功,均返回true,表示移除成功;最后,如果还不行的话,则返回false,表示移除失败。

3、更新优先级update()

  1. /**
  2. * Recalculate and potentially update the priority level of a block.
  3. *
  4. * If the block priority has changed from before an attempt is made to
  5. * remove it from the block queue. Regardless of whether or not the block
  6. * is in the block queue of (recalculate) priority, an attempt is made
  7. * to add it to that queue. This ensures that the block will be
  8. * in its expected priority queue (and only that queue) by the end of the
  9. * method call.
  10. * @param block a under replicated block
  11. * @param curReplicas current number of replicas of the block
  12. * @param decommissionedReplicas  the number of decommissioned replicas
  13. * @param curExpectedReplicas expected number of replicas of the block
  14. * @param curReplicasDelta the change in the replicate count from before
  15. * @param expectedReplicasDelta the change in the expected replica count from before
  16. */
  17. synchronized void update(Block block, int curReplicas,
  18. int decommissionedReplicas,
  19. int curExpectedReplicas,
  20. int curReplicasDelta, int expectedReplicasDelta) {
  21. // curReplicas代表当前副本数,curReplicasDelta代表之前发生的副本数变化
  22. // curExpectedReplicas代表当前期望副本数,expectedReplicasDelta代表之前发生的期望副本数变化
  23. // 计算之前的副本数oldReplicas和之前的期望副本数oldExpectedReplicas
  24. int oldReplicas = curReplicas-curReplicasDelta;
  25. int oldExpectedReplicas = curExpectedReplicas-expectedReplicasDelta;
  26. // 计算当前的块复制优先级curPri
  27. int curPri = getPriority(block, curReplicas, decommissionedReplicas, curExpectedReplicas);
  28. // 计算之前的块复制优先级oldPri
  29. int oldPri = getPriority(block, oldReplicas, decommissionedReplicas, oldExpectedReplicas);
  30. if(NameNode.stateChangeLog.isDebugEnabled()) {
  31. NameNode.stateChangeLog.debug("UnderReplicationBlocks.update " +
  32. block +
  33. " curReplicas " + curReplicas +
  34. " curExpectedReplicas " + curExpectedReplicas +
  35. " oldReplicas " + oldReplicas +
  36. " oldExpectedReplicas  " + oldExpectedReplicas +
  37. " curPri  " + curPri +
  38. " oldPri  " + oldPri);
  39. }
  40. // 如果之前优先级oldPri合法且不等于当前优先级curPri
  41. if(oldPri != LEVEL && oldPri != curPri) {
  42. // 调用remove()方法移除数据块
  43. remove(block, oldPri);
  44. }
  45. // 如果当前优先级curPri合法,通过当前优先级curPri从priorityQueues列表中获取对应数据块集合并将数据块添加进去
  46. if(curPri != LEVEL && priorityQueues.get(curPri).add(block)) {
  47. if(NameNode.blockStateChangeLog.isDebugEnabled()) {
  48. NameNode.blockStateChangeLog.debug(
  49. "BLOCK* NameSystem.UnderReplicationBlock.update:"
  50. + block
  51. + " has only "+ curReplicas
  52. + " replicas and needs " + curExpectedReplicas
  53. + " replicas so is added to neededReplications"
  54. + " at priority level " + curPri);
  55. }
  56. }
  57. }

更新优先级update()方法用于当数据块副本数或期望副本数等发生变化时,调整数据块复制优先级,并调整其在UnderReplicatedBlocks中的相应存储位置。大体逻辑如下:

1、首先搞清楚几个参数:curReplicas代表当前副本数,curReplicasDelta代表之前发生的副本数变化,curExpectedReplicas代表当前期望副本数,expectedReplicasDelta代表之前发生的期望副本数变化;

2、计算之前的副本数oldReplicas和之前的期望副本数oldExpectedReplicas;

3、计算当前的块复制优先级curPri;

4、计算之前的块复制优先级oldPri;

5、如果之前优先级oldPri合法且不等于当前优先级curPri:调用remove()方法移除数据块;

6、如果当前优先级curPri合法,通过当前优先级curPri从priorityQueues列表中获取对应数据块集合并将数据块添加进去。

未完待续,更多精彩尽在《HDFS源码分析之UnderReplicatedBlocks(二)》

HDFS源码分析之UnderReplicatedBlocks(一)的更多相关文章

  1. HDFS源码分析之UnderReplicatedBlocks(二)

    UnderReplicatedBlocks还提供了一个数据块迭代器BlockIterator,用于遍历其中的数据块.它是UnderReplicatedBlocks的内部类,有三个成员变量,如下: // ...

  2. HDFS源码分析数据块复制监控线程ReplicationMonitor(一)

    ReplicationMonitor是HDFS中关于数据块复制的监控线程,它的主要作用就是计算DataNode工作,并将复制请求超时的块重新加入到待调度队列.其定义及作为线程核心的run()方法如下: ...

  3. HDFS源码分析数据块校验之DataBlockScanner

    DataBlockScanner是运行在数据节点DataNode上的一个后台线程.它为所有的块池管理块扫描.针对每个块池,一个BlockPoolSliceScanner对象将会被创建,其运行在一个单独 ...

  4. HDFS源码分析数据块复制监控线程ReplicationMonitor(二)

    HDFS源码分析数据块复制监控线程ReplicationMonitor(二)

  5. HDFS源码分析之LightWeightGSet

    LightWeightGSet是名字节点NameNode在内存中存储全部数据块信息的类BlocksMap需要的一个重要数据结构,它是一个占用较低内存的集合的实现,它使用一个数组array存储元素,使用 ...

  6. HDFS源码分析数据块汇报之损坏数据块检测checkReplicaCorrupt()

    无论是第一次,还是之后的每次数据块汇报,名字名字节点都会对汇报上来的数据块进行检测,看看其是否为损坏的数据块.那么,损坏数据块是如何被检测的呢?本文,我们将研究下损坏数据块检测的checkReplic ...

  7. HDFS源码分析之数据块及副本状态BlockUCState、ReplicaState

    关于数据块.副本的介绍,请参考文章<HDFS源码分析之数据块Block.副本Replica>. 一.数据块状态BlockUCState 数据块状态用枚举类BlockUCState来表示,代 ...

  8. HDFS源码分析EditLog之获取编辑日志输入流

    在<HDFS源码分析之EditLogTailer>一文中,我们详细了解了编辑日志跟踪器EditLogTailer的实现,介绍了其内部编辑日志追踪线程EditLogTailerThread的 ...

  9. HDFS源码分析EditLog之读取操作符

    在<HDFS源码分析EditLog之获取编辑日志输入流>一文中,我们详细了解了如何获取编辑日志输入流EditLogInputStream.在我们得到编辑日志输入流后,是不是就该从输入流中获 ...

随机推荐

  1. BZOJ1483 [HNOI2009]梦幻布丁 【链表 + 启发式合并】

    题目 N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色. 例如颜色分别为1,2,2,1的四个布丁一共有3段颜色. 输入格式 第一行给出N,M表示 ...

  2. VMware DRS概述及功能

    通过动态分配和平衡计算资源,使您的 IT 基础架构与业务目标一致.VMware Distributed Resource Scheduler (DRS) 可持续监控所有资源池的利用率,并根据业务需求在 ...

  3. delete zone and cfgsave on brocade by CMD

    brocade:user> cfgshowDefined configuration: cfg: cfg001 AMS_ESX_HBA1; AMS_ESX_HBA2; HUS_ESX_HBA1; ...

  4. Struts2.0中ActionInvocation使用

    Interceptor的接口定义没有什么特别的地方,除了init和destory方法以外,intercept方法是实现整个拦截器机制的核心方法.而它所依赖的参数ActionInvocation则是我们 ...

  5. UVA 10519 !! Really Strange !!

    //ans=2*n+(n-1)(n-2) n>=2#include <map> #include <set> #include <list> #include ...

  6. 详解TCP的三次握手四次断开

    本文将分别讲解经典的TCP协议建立连接(所谓的“3次握手”)和断开连接(所谓的“4次挥手”)的过程. 尽管TCP和UDP都使用相同的网络层(IP),TCP却向应用层提供与UDP完全不同的服务.TCP提 ...

  7. hdu 1546(dijkstra)

    Idiomatic Phrases Game Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/O ...

  8. PSR-2 编码风格规范

    本篇规范是 PSR-1 基本代码规范的继承与扩展. 本规范希望通过制定一系列规范化PHP代码的规则,以减少在浏览不同作者的代码时,因代码风格的不同而造成不便. 当多名程序员在多个项目中合作时,就需要一 ...

  9. AC日记——[HNOI2012]永无乡 bzoj 2733

    2733 思路: 启发式合并splay(n*log^2n): 来,上代码: #include <cstdio> #include <cstring> #include < ...

  10. 日志采集客户端 filebeat 安装部署

    linux----------------1. 下载wget https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-5.5.1- ...