代码拉取完成,页面将自动刷新
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[测试hexoEditer]]></title>
<url>%2Fnote%2F2019%2F03%2F02%2F%E6%B5%8B%E8%AF%95hexoEditer%2F</url>
<content type="text"><![CDATA[图片 图片2 HashMap前置123456789//初始化容量static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; //容器最大容量static final int MAXIMUM_CAPACITY = 1 << 30;//负载因子,在0.75的时候扩大。比如16的时候,12扩大 12/16=0.75static final float DEFAULT_LOAD_FACTOR = 0.75f;//Node超过8时,转换为红黑树。//查找由链表的O(n)转换为O(log(n)) 对数级static final int TREEIFY_THRESHOLD = 8; Node 123456789101112static class Node<K,V> implements Map.Entry<K,V> { final int hash; final K key; V value; Node<K,V> next; Node(int hash, K key, V value, Node<K,V> next) { this.hash = hash; this.key = key; this.value = value; this.next = next; } 数组: 1transient Node<K,V>[] table; put操作12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n, i; //为空则初始化Node[]数组 if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; //判读数组位置是否有Node占据,如果没有,直接复制 if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); //数据已占据,采取链表 else { Node<K,V> e; K k; //hash和key相等,则更新值就可以了 if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; else if (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else { //p相当于数组坐标的位置 //把数据插入链表 for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); //如果大于TREEIFY_THRESHOLD即是大于等于7,说明链表长度为8了,做转换操作 if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash); break; } //判断hash和key是否相等,如果相等,则进行更新操作 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } //修改次数 ++modCount; //数组到底用了多少个格子 //threshold记录的是当前数组格子用了多少,超出大小*负载因子则需要扩容 if (++size > threshold) resize(); afterNodeInsertion(evict); return null; } get操作1234public V get(Object key) { Node<K,V> e; return (e = getNode(hash(key), key)) == null ? null : e.value;} hash操作 1234static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);} resize123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081final Node<K,V>[] resize() { Node<K,V>[] oldTab = table; int oldCap = (oldTab == null) ? 0 : oldTab.length; int oldThr = threshold; int newCap, newThr = 0; if (oldCap > 0) { if (oldCap >= MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return oldTab; } //扩容数组,位移效率更高 else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY) newThr = oldThr << 1; // double threshold } else if (oldThr > 0) // initial capacity was placed in threshold newCap = oldThr; else { // zero initial threshold signifies using defaults newCap = DEFAULT_INITIAL_CAPACITY; newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); } if (newThr == 0) { float ft = (float)newCap * loadFactor; newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? (int)ft : Integer.MAX_VALUE); } threshold = newThr; @SuppressWarnings({"rawtypes","unchecked"}) //分配内存地址 Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap]; table = newTab; //雨露均沾 if (oldTab != null) { for (int j = 0; j < oldCap; ++j) { Node<K,V> e; //如果数组位置有值 if ((e = oldTab[j]) != null) { oldTab[j] = null; //数组下有链表进入 if (e.next == null) newTab[e.hash & (newCap - 1)] = e; //是二叉树,进行二叉树的拆分方式 else if (e instanceof TreeNode) ((TreeNode<K,V>)e).split(this, newTab, j, oldCap); //链表的拆分方式 else { // preserve order Node<K,V> loHead = null, loTail = null; Node<K,V> hiHead = null, hiTail = null; Node<K,V> next; do { next = e.next; if ((e.hash & oldCap) == 0) { if (loTail == null) loHead = e; else loTail.next = e; loTail = e; } else { if (hiTail == null) hiHead = e; else hiTail.next = e; hiTail = e; } //很体现循环遍历的地方 } while ((e = next) != null); if (loTail != null) { loTail.next = null; newTab[j] = loHead; } if (hiTail != null) { hiTail.next = null; newTab[j + oldCap] = hiHead; } } } } } return newTab; } 问题要点数据结构:链表+数组 12345678// 链表node{ object key object value Node next}//数组elemDate[] hash函数实现 1234567static final int hash(Object key) { int h; //低16位和高16位异或,右移后异或,保证hash分散,降低重复率。防止数组后面的链表过长,尽可能用齐数组 return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);} public native int hashCode(); 检查是否hash碰撞 12if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); 成员变量:threshold 记录数组用了多少。易混static final int TREEIFY_THRESHOLD = 8; 为链表转红黑树的大小。 123456//数组到底用了多少个格子++modCount;if (++size > threshold) resize();afterNodeInsertion(evict);return null; 12345678 newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);}if (newThr == 0) { float ft = (float)newCap * loadFactor; newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? (int)ft : Integer.MAX_VALUE);}threshold = newThr; resize()方法:Initializes or doubles table size 初始化或者双倍扩容 以双倍扩容 (n-1)&hash与也可以体现出来 123////雨露均沾,链表上的值,分配到新的数组上if (e.next == null) newTab[e.hash & (newCap - 1)] = e; 1234567if ((e.hash & oldCap) == 0) { if (loTail == null) loHead = e; else loTail.next = e; loTail = e; }]]></content>
</entry>
<entry>
<title><![CDATA[Hello World]]></title>
<url>%2Fnote%2F2019%2F03%2F01%2Fhello-world%2F</url>
<content type="text"><![CDATA[Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub. Quick StartCreate a new post1$ hexo new "My New Post" More info: Writing Run server1$ hexo server More info: Server Generate static files1$ hexo generate More info: Generating Deploy to remote sites1$ hexo deploy More info: Deployment]]></content>
</entry>
<entry>
<title><![CDATA[HashMap探究]]></title>
<url>%2Fnote%2F2019%2F03%2F01%2FHashMap%E6%8E%A2%E7%A9%B6%2F</url>
<content type="text"><![CDATA[HashMap前置123456789//初始化容量static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; //容器最大容量static final int MAXIMUM_CAPACITY = 1 << 30;//负载因子,在0.75的时候扩大。比如16的时候,12扩大 12/16=0.75static final float DEFAULT_LOAD_FACTOR = 0.75f;//Node超过8时,转换为红黑树。//查找由链表的O(n)转换为O(log(n)) 对数级static final int TREEIFY_THRESHOLD = 8; Node 123456789101112static class Node<K,V> implements Map.Entry<K,V> { final int hash; final K key; V value; Node<K,V> next; Node(int hash, K key, V value, Node<K,V> next) { this.hash = hash; this.key = key; this.value = value; this.next = next; } 数组: 1transient Node<K,V>[] table; put操作12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n, i; //为空则初始化Node[]数组 if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; //判读数组位置是否有Node占据,如果没有,直接复制 if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); //数据已占据,采取链表 else { Node<K,V> e; K k; //hash和key相等,则更新值就可以了 if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; else if (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else { //p相当于数组坐标的位置 //把数据插入链表 for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); //如果大于TREEIFY_THRESHOLD即是大于等于7,说明链表长度为8了,做转换操作 if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash); break; } //判断hash和key是否相等,如果相等,则进行更新操作 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } //修改次数 ++modCount; //数组到底用了多少个格子 //threshold记录的是当前数组格子用了多少,超出大小*负载因子则需要扩容 if (++size > threshold) resize(); afterNodeInsertion(evict); return null; } get操作1234public V get(Object key) { Node<K,V> e; return (e = getNode(hash(key), key)) == null ? null : e.value;} hash操作 1234static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);} resize123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081final Node<K,V>[] resize() { Node<K,V>[] oldTab = table; int oldCap = (oldTab == null) ? 0 : oldTab.length; int oldThr = threshold; int newCap, newThr = 0; if (oldCap > 0) { if (oldCap >= MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return oldTab; } //扩容数组,位移效率更高 else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY) newThr = oldThr << 1; // double threshold } else if (oldThr > 0) // initial capacity was placed in threshold newCap = oldThr; else { // zero initial threshold signifies using defaults newCap = DEFAULT_INITIAL_CAPACITY; newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); } if (newThr == 0) { float ft = (float)newCap * loadFactor; newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? (int)ft : Integer.MAX_VALUE); } threshold = newThr; @SuppressWarnings({"rawtypes","unchecked"}) //分配内存地址 Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap]; table = newTab; //雨露均沾 if (oldTab != null) { for (int j = 0; j < oldCap; ++j) { Node<K,V> e; //如果数组位置有值 if ((e = oldTab[j]) != null) { oldTab[j] = null; //数组下有链表进入 if (e.next == null) newTab[e.hash & (newCap - 1)] = e; //是二叉树,进行二叉树的拆分方式 else if (e instanceof TreeNode) ((TreeNode<K,V>)e).split(this, newTab, j, oldCap); //链表的拆分方式 else { // preserve order Node<K,V> loHead = null, loTail = null; Node<K,V> hiHead = null, hiTail = null; Node<K,V> next; do { next = e.next; if ((e.hash & oldCap) == 0) { if (loTail == null) loHead = e; else loTail.next = e; loTail = e; } else { if (hiTail == null) hiHead = e; else hiTail.next = e; hiTail = e; } //很体现循环遍历的地方 } while ((e = next) != null); if (loTail != null) { loTail.next = null; newTab[j] = loHead; } if (hiTail != null) { hiTail.next = null; newTab[j + oldCap] = hiHead; } } } } } return newTab; } 问题要点数据结构:链表+数组 12345678// 链表node{ object key object value Node next}//数组elemDate[] hash函数实现 1234567static final int hash(Object key) { int h; //低16位和高16位异或,右移后异或,保证hash分散,降低重复率。防止数组后面的链表过长,尽可能用齐数组 return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);} public native int hashCode(); 检查是否hash碰撞 12if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); 成员变量:threshold 记录数组用了多少。易混static final int TREEIFY_THRESHOLD = 8; 为链表转红黑树的大小。 123456//数组到底用了多少个格子++modCount;if (++size > threshold) resize();afterNodeInsertion(evict);return null; 12345678 newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);}if (newThr == 0) { float ft = (float)newCap * loadFactor; newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? (int)ft : Integer.MAX_VALUE);}threshold = newThr; resize()方法:Initializes or doubles table size 初始化或者双倍扩容 以双倍扩容 (n-1)&hash与也可以体现出来 123////雨露均沾,链表上的值,分配到新的数组上if (e.next == null) newTab[e.hash & (newCap - 1)] = e; 1234567if ((e.hash & oldCap) == 0) { if (loTail == null) loHead = e; else loTail.next = e; loTail = e; }]]></content>
<tags>
<tag>JDK</tag>
</tags>
</entry>
<entry>
<title><![CDATA[HDFS基础总结]]></title>
<url>%2Fnote%2F2019%2F03%2F01%2F%E2%80%98HDFS%E5%9F%BA%E7%A1%80%E6%80%BB%E7%BB%93%2F</url>
<content type="text"><![CDATA[HDFS HDFS(Hadoop Distributed File System)是Hadoop项目的核心子项目,是分布式计算中数据存储管理的基础,是基于流数据模式访问和处理超 大文件的需求而开发的,可以运行于廉价的商用服务器上。 HDFS 源于 Google 在2003年10月份发表的GFS(Google File System) 论文。 它其实就是 GFS 的一个克隆版本。 特点 随着互联网的快速发展,数据量越来越多,一台服务器的存储有限,所以数据应该分配到更多的机器的磁盘中,因此需要一种系统来管理多台机器上的文件,这就是分布式文件管 理系统 ,它具有高容错的特点,它可以部署在廉价的通用硬件上,提供高吞吐率的数据访 问,适合那些需要处理海量数据集的应用程序。 高容错性 数据自动保存多个副本。它通过增加副本的形式,提高容错性。 某一个副本丢失以后,它可以自动恢复,这是由 HDFS 内部机制实现的。 适合大数据处理 处理数据达到 GB、TB、甚至PB级别的数据 能够处理百万规模以上的文件数量,数量相当之大。 能够处理10K节点的规模。 适合批处理 它是通过移动计算而不是移动数据。 将数据暴露给计算框架 流式文件访问 一次写入,多次读取。文件一旦写入不能修改,只能追加。 它能保证数据的一致性。 可构建在廉价的机器上 它通过多副本机制,提高可靠性。 它提供了容错和恢复机制。比如某一个副本丢失,可以通过其它副本来恢复。 劣势低延时数据访问 比如毫秒级的来存储数据,它做不到。 它适合高吞吐率的场景,就是在某一时间内写入大量的数据。但是它在低延时的情况下是不行的,比如毫秒级以内读取数据,这样它是很难做到的。 小文件存储 存储大量小文件(这里的小文件是指小于HDFS系统的Block大小的文件(默认64M))的话,它会占用 NameNode大量的内存来存储文件、目录和块信息。这样是不可取的,因为NameNode的内存总是有限的。 小文件存储的寻道时间会超过读取时间,它违反了HDFS的设计目标。 并发写入,文件随机修改 一个文件只能有一个写,不允许多个线程同时写。 仅支持数据 append(追加),不支持文件的随机修改。 HDFS基本概念 HDFS(Hadoop Distributed File System):分布式文件系统,将一个文件分成多个块,分别存储(拷贝)到不同的节点上,它是Hadoop体系中数据存储管理的基础。它是一个高度容错的系统,能检测和应对硬件故障,用于在低成本的通用硬件上运行。HDFS简化了文件的一致性模型,通过流式数据访问,提供高吞吐量应用程序数据访问功能,适合带有大型数据集的应用程序。 机架HDFS集群,由分布在多个机架上的大量DataNode组成,不同机架之间节点通过交换机通信,HDFS通过机架感知策略,使NameNode能够确定每个DataNode所属的机架ID,使用副本存放策略,来改进数据的可靠性、可用性和网络带宽的利用率。 数据块(block)linux中每个磁盘有默认的数据块大小,这是对磁盘操作的最小单位,通常512字节。HDFS中也有数据块的概念。它是最基本的存储单元,默认为64M/128M,用户可以自行设置大小。 元数据指HDFS文件系统中,文件和目录的属性信息。HDFS实现时,采用了 镜像文件(Fsimage) + 日志文件(EditLog)的备份机制。文件的镜像文件中内容包括:修改时间、访问时间、数据块大小、组成文件的数据块的存储位置信息。目录的镜像文件内容包括:修改时间、访问控制权限等信息。日志文件记录的是:HDFS的更新操作。 NameNode启动的时候,会将镜像文件和日志文件的内容在内存中合并。把内存中的元数据更新到最新状态。 用户数据HDFS存储的大部分都是用户数据,以数据块的形式存放在DataNode上。 在HDFS中,NameNode 和 DataNode之间使用TCP协议进行通信。DataNode每3s向NameNode发送一个心跳。每10次心跳后,向NameNode发送一个数据块报告自己的信息,通过这些信息,NameNode能够重建元数据,并确保每个数据块有足够的副本。 fsimage元数据镜像文件:Namenode启动后,文件的元数据被加载到内存中,加载到内存后也会把这些元数据写入到本地的磁盘中,这个文件就是fsimage文件。 HDFS的目录树及文件/目录元信息是保存在内存中的,如果节点掉电或进程崩溃,数据将 不再存在,必须将上述信息保存到磁盘,Fslmage就是保存某一个时刻元数据的信息的磁盘文 件元数据镜像在内存中保存一份最新的。内存中的镜像=fsimage+fsedit。 fsedits元数据操作日志文件:客户端要对文件进行读写操作,在这些操作产生的日志就存在了fsedit文件中。对内存目录树的修改,也必须同步到磁盘元数据上,但每次修改都将内存元数据导出到磁盘,显然是不现实的,为此,namenode引入了镜像编辑日志,将每次的改动都保存在日志中,如果namenode机器宕机或者namenode进程挂掉后可以使用FSlmage和EditLog联合恢复内存元数据。 HDFS ClientNamenode存储着命名空间(namespace)和元数据(metadata) ,如果想对文件进行读写的话,首先需要通过Namenode来获取一些信息。客户端主要工作如下: 文件切分.文件上传 HDFS 的时候,Client 将文件切分成 一个一个的Block,然后进行存储。. 与NameNode交互,读取文件位置信息 与DataNode交互,读取或者写入数据 管理HDFS、访问HDFS(浏览器、shell、JavaAPI) NameNode 整个 Hadoop 集群中只有一个 NameNode。它是整个系统的“总管”,负责管理 HDFS 的目录树和相关的文件元数据信息。这些信息以“fsimage”(HDFS 元数据镜像文件) 和 “editlog”(HDFS 文件改动日志)两个文件形式存放在本地磁盘,当 HDFS 重启时重新构造出来的。此外,NameNode 还负责监控各个 DataNode 的健康状态,一旦发现某个 DataNode 宕掉,则将该 DataNode 移出 HDFS 并重新备份其上面的数据。 管理HDFS的名称空间 管理数据块的映射信息 配置副本策略 处理客户端的读写请求 DataNode般而言,每个 Slave 节点上安装一个 DataNode,它负责实际的数据存储,并将数据信息定期汇报NameNode。DataNode 以固定大小的block 为基本单位组织文件内容,默认情况下block 大小为 64MB。(当然现在的版本已经是128MB了),当用户上传一个大的文件到 HDFS 上时,该文件会被切分成 若干个block,分别存储到不同的DataNode ;同时,为了保证数据可靠,会将同一个block 以流水线方式写到若干个(默认是3,该参数可配置)不同的DataNode 上。这种文件切割 后存储的过程是对用户透明的。 存储实际的数据块 执行数据块的读/写操作 Secondary NameNodeSecondary NameNode 最重要的任务并不是为 NameNode 元数据进行热备份,而是定期合并 fsimage 和 edits 日志,并传输给 NameNode。这里需要注意的是,为了减小 NameNode 压力,NameNode 自己并不会合并 fsimage 和 edits,并将文件存储到磁盘上,而是交由 Secondary NameNode 完成。 辅助 NameNode,分担其工作量。 定期合并fsimage和fsedits,并推送给NameNode。 在紧急情况下,可辅助恢复 NameNode。 HDFS体系结构 HDFS具有主/从架构。HDFS集群由单个NameNode,管理文件系统命名空间的主服务器和管理客户端对文件的访问组成。此外,还有许多DataNode,通常是群集中每个节点一个,用于管理连接到它们运行的节点的存储。HDFS公开文件系统命名空间,并允许用户数据存储在文件中。在内部,文件被分成一个或多个块,这些块存储在一组DataNode中。NameNode执行文件系统命名空间操作,如打开,关闭和重命名文件和目录。它还确定了块到DataNode的映射。DataNode负责提供来自文件系统客户端的读写请求。DataNodes还执行块创建,删除。 进阶数据块大小设置 小于块大小的小文件不会占用整个HDFS块空间。但是较多的小文件会占用更多的NameNode的内存,文本处理时可能会有更大的网络开销。现在版本默认128M Q1:如果一个HDFS上的文件大小(file size) 小于块大小(block size) ,那么HDFS会实际占用Linux file system的多大空间? A1:实际的文件大小,而非一个块的大小。块大小还是必要的,一个显而易见的作用就是当文件通过append操作不断增长的过程中,可以通过来block size决定何时split文件。 1“The block size is a meta attribute. If you append tothe file later, it still needs to know when to split further - so it keeps that value as a mere metadata it can use to advise itself on write boundaries.” --Hadoop Community Q2:为什么64MB(或128MB或256MB)是最优选择? A2: 减小硬盘寻道时间 HDFS的设计前提是支持大容量的流式数据操作,所以即使是一般的读写操作涉及到的数据量也是比较大的。如果数据块设置的过小,那么读取的数据块就比较多,由于数据块在硬盘上非连续存储,普通硬盘因为需要移动磁头,所以随机寻址较慢,读越多的数据块就增大了总的硬盘寻道时间。当硬盘寻道时间比io时间还要长的多时,那么硬盘寻道时间就成了系统的一个瓶颈。合适的块大小有助于减少硬盘寻道时间,提高系统吞吐量。 减小NameNode内存消耗 对于HDFS,他只有一个Namenode节点,他的内存相对于Datanode来说,是极其有限的。然而,namenode需要在其内存FSImage文件中中记录在Datanode中的数据块信息,假如数据块大小设置过少,而需要维护的数据块信息就会过多,那Namenode的内存可能就会伤不起了。 Q3:为什么不能远大于64MB(或128MB或256MB)? A3: Map崩溃问题:系统需要重新启动,启动过程需要重新加载数据,数据块越大,数据加载时间越长,系统恢复过程越长。 健壮性HDFS的主要目标就是即使在出错的情况下也要保证数据存储的可靠性。常见的三种出错情况是:Namenode出错, Datanode出错和网络割裂(network partitions)。 磁盘数据错误,心跳检测和重新复制 每个Datanode节点周期性地向Namenode发送心跳信号。网络割裂可能导致一部分Datanode跟Namenode失去联系。Namenode通过心跳信号的缺失来检测这一情况,并将这些近期不再发送心跳信号Datanode标记为宕机,不会再将新的IO请求发给它们。任何存储在宕机Datanode上的数据将不再有效。Datanode的宕机可能会引起一些数据块的副本系数低于指定值,Namenode不断地检测这些需要复制的数据块,一旦发现就启动复制操作。在下列情况下,可能需要重新复制:某个Datanode节点失效,某个副本遭到损坏,Datanode上的硬盘错误,或者文件的副本系数增大。 集群均衡 HDFS的架构支持数据均衡策略。如果某个Datanode节点上的空闲空间低于特定的临界点,按照均衡策略系统就会自动地将数据从这个Datanode移动到其他空闲的Datanode。当对某个文件的请求突然增加,那么也可能启动一个计划创建该文件新的副本,并且同时重新平衡集群中的其他数据。这些均衡策略目前还没有实现。 数据完整性 从某个Datanode获取的数据块有可能是损坏的,损坏可能是由Datanode的存储设备错误、网络错误或者软件bug造成的。HDFS客户端软件实现了对HDFS文件内容的校验和(checksum)检查。当客户端创建一个新的HDFS文件,会计算这个文件每个数据块的校验和,并将校验和作为一个单独的隐藏文件保存在同一个HDFS名字空间下。当客户端获取文件内容后,它会检验从Datanode获取的数据跟相应的校验和文件中的校验和是否匹配,如果不匹配,客户端可以选择从其他Datanode获取该数据块的副本。 元数据磁盘错误 FsImage和Editlog是HDFS的核心数据结构。如果这些文件损坏了,整个HDFS实例都将失效。因而,Namenode可以配置成支持维护多个FsImage和Editlog的副本。任何对FsImage或者Editlog的修改,都将同步到它们的副本上。这种多副本的同步操作可能会降低Namenode每秒处理的名字空间事务数量。然而这个代价是可以接受的,因为即使HDFS的应用是数据密集的,它们也非元数据密集的。当Namenode重启的时候,它会选取最近的完整的FsImage和Editlog来使用。 参考:[]: https://www.cnblogs.com/codeOfLife/p/5375120.html “初步掌握HDFS的架构及原理”[]: https://www.yiibai.com/hadoop/hadoop_hdfs_overview.html “HDFS概念” [^]: https://www.cnblogs.com/Dhouse/p/6901028.html?utm_source=itdadao&utm_medium=referral HDFS数据块大小设置]]></content>
<categories>
<category>大数据</category>
<category>HDFS</category>
</categories>
<tags>
<tag>HDFS</tag>
<tag>总结</tag>
</tags>
</entry>
</search>
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。