﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-实验室宅男的一亩三分地</title><link>http://www.cppblog.com/whspecial/</link><description /><language>zh-cn</language><lastBuildDate>Tue, 07 Apr 2026 21:35:39 GMT</lastBuildDate><pubDate>Tue, 07 Apr 2026 21:35:39 GMT</pubDate><ttl>60</ttl><item><title>自控力读书笔记</title><link>http://www.cppblog.com/whspecial/archive/2015/04/27/210452.html</link><dc:creator>whspecial</dc:creator><author>whspecial</author><pubDate>Mon, 27 Apr 2015 07:34:00 GMT</pubDate><guid>http://www.cppblog.com/whspecial/archive/2015/04/27/210452.html</guid><wfw:comment>http://www.cppblog.com/whspecial/comments/210452.html</wfw:comment><comments>http://www.cppblog.com/whspecial/archive/2015/04/27/210452.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/whspecial/comments/commentRss/210452.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/whspecial/services/trackbacks/210452.html</trackback:ping><description><![CDATA[附上URL：http://book.douban.com/subject/10786473/<br /><br /><div><span style="font-size: 18px;"><strong>1，锻炼意志力的方法</strong></span></div><div><strong>A，每天冥想5分钟</strong></div><div><strong>B，锻炼</strong></div><div>对于锻炼有两个常见问题，第一个是&#8220;需要锻炼多久&#8221;，第二个是&#8220;什么锻炼最有效&#8221;，这两个问题的答案是&#8220;你想锻炼多久&#8221;，&#8220;你真的会去做什么样的锻炼&#8221;</div><div><strong>C，睡眠</strong></div><div>睡足觉能显著提高自控力，因为睡眠不足会导致大脑缺乏足够的能量进行自控。<br />如何改掉晚睡的坏习惯？</div><div>真正的问题并不是强迫自己去睡觉，而是强迫自己在一定时间之后就远离那些让自己无法睡觉的事情。<br /></div><div></div><div><span style="font-size: 18px;"><strong>2，意志力的规律</strong></span></div><div><strong>A，每天的意志力变化规律：</strong>早上的意志力最强，随着时间的推移而逐渐减弱。</div><div><strong>方案：</strong>需要将最重要的事情放在早上处理</div><div><strong>B，很多想不到的事情都是在消耗你的意志力：</strong>很多你认为不需要意志力的事情其实都在消耗你的意志，比如试图融入一家价值观和你不符合的公司，在糟糕的路况中上班，干坐着熬过无聊的会议等等。</div><div><strong>方案</strong>：尽量避免这些事情的发生</div><div><strong>C，压力和情绪低落会导致意志力涣散：</strong>由于大脑的调节功能，如果一个人感觉到压力和情绪低落，大脑会指引着你去做它认为能给你带来快乐的事情，这样会造成一个矛盾：有很多工作要完成的人，往往会选择去玩游戏来排解压力；需要控制支出的人会去大肆购物来排解压力，这样就造成了一个恶性循环。</div><div><strong>方案：<br /></strong>尝试有效的解压方法：锻炼，阅读，听音乐，和家人相处，按摩，散步，冥想，培养有创意的爱好；<br />放弃无效的解压方法：赌博，购物，抽烟，喝酒，暴饮暴食，玩游戏，上网，花两个小时以上看电影或者电视。<br />有效和无效的区别是？真正能缓解压力的不是释放多巴胺，而是增加大脑中改善情绪的化学物质，比如血清素／Y－氨基丁酸/催产素等等，这样才是治本的。</div><div><strong>D，不能自我谅解导致的自控力恶性循环：</strong>一次自控失败往往会导致整个自控计划的失败，是第一次放弃后产生的羞耻感，罪恶感，失控感和绝望感，会让人破罐子破摔。</div><div><strong>方案：</strong>寻求自我谅解，只要是凡人都会有失去自控力的时候，挫折本身并不可怕，可怕的是自暴自弃。<br /></div><div></div><div><span style="font-size: 18px;"><strong>3，意志力的误区</strong></span></div><div><strong>A，不要把支持目标实现的行为误认为是目标本身：</strong>比如在健身之后，有时会奖赏自己一瓶碳酸饮料，或者去吃烧烤，其实最终摄入的能量还要大于健身消耗的能量。</div><div><strong>方案：</strong>要弄清楚自己的目标，不要将目标和过程弄混了。</div><div><strong>B，误将渴望当做幸福：</strong>由于多巴胺分泌的因素，我们往往将某些快感当做了真正的幸福，比如吃垃圾食品，无节制的游戏等等。</div><div><strong>方案：</strong>我们需要区分让我们的生活真正有意义的真实奖励（有长久意义的，对生活有益的），和让我们分散精力，上瘾的虚假奖励（短暂无用的，仅仅是刺激多巴胺分泌的）。</div><div><strong>C，经常制定自控力计划而不施行：</strong>很多人会重复的制定计划，而不去执行计划，因为制定一个计划很容易，而且会让我们心情大好，但是如果真的付诸实践，带给我们的快感远远小于制定计划的快感。</div><div><strong>方案：</strong>需要避免一个意志力陷阱：即用&#8220;改变的承诺&#8221;而不是&#8220;改变&#8221;来改善我们的心情</div><div><strong>D，人类往往放弃未来更大的回报，而选择即刻的满足感：</strong>即刻奖励会激活更原始的奖励系统，即刺激多巴胺的分泌，而未来奖励是刺激人类最近才进化出来的前额皮质系统。人类在面临当前奖励和未来奖励的时候，两个奖励系统会进行斗争。</div><div><strong>方案：</strong>等待10分钟，因为这10分钟会降低即刻满足的快感，让大脑更理智的思考。如果10分钟之后依然想要，则可以选择即刻满足。</div><div></div><img src ="http://www.cppblog.com/whspecial/aggbug/210452.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/whspecial/" target="_blank">whspecial</a> 2015-04-27 15:34 <a href="http://www.cppblog.com/whspecial/archive/2015/04/27/210452.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>log4j 行号与文件名打印问号</title><link>http://www.cppblog.com/whspecial/archive/2014/03/11/206132.html</link><dc:creator>whspecial</dc:creator><author>whspecial</author><pubDate>Tue, 11 Mar 2014 07:57:00 GMT</pubDate><guid>http://www.cppblog.com/whspecial/archive/2014/03/11/206132.html</guid><wfw:comment>http://www.cppblog.com/whspecial/comments/206132.html</wfw:comment><comments>http://www.cppblog.com/whspecial/archive/2014/03/11/206132.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/whspecial/comments/commentRss/206132.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/whspecial/services/trackbacks/206132.html</trackback:ping><description><![CDATA[<div>转载自：http://www.blogjava.net/itspy/archive/2008/04/22/194686.html#Post<br /><br />log4j本来设置了要打印行号与文件名的,结果有的能打印出来,有的却是乱码,查了些文档之后才发现,原来打印问题是因为编绎时没有编绎进去调试信息,所以没办法打印.</div><div></div><div>但是我用的是Ant,如果在Ant编绎时,编绎进去调试信息呢,参考下面配置.</div><div></div><div>&lt;javac srcdir="src" destdir="bin" <span style="background-color: yellow;">debug="true" </span>&nbsp;classpathref="accrual.path" &gt;</div><div></div><div></div><div></div><div><br />参考文档</div><div>http://ant.apache.org/manual/CoreTasks/javac.html</div><div>Log4j配置</div><div>log4j.appender.C1.layout.ConversionPattern=%F(%L)-- %-4r %-5p [%t] %37c %3x - %m%n</div><img src ="http://www.cppblog.com/whspecial/aggbug/206132.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/whspecial/" target="_blank">whspecial</a> 2014-03-11 15:57 <a href="http://www.cppblog.com/whspecial/archive/2014/03/11/206132.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>将排序二叉树转换成双向链表</title><link>http://www.cppblog.com/whspecial/archive/2014/01/03/205123.html</link><dc:creator>whspecial</dc:creator><author>whspecial</author><pubDate>Thu, 02 Jan 2014 16:41:00 GMT</pubDate><guid>http://www.cppblog.com/whspecial/archive/2014/01/03/205123.html</guid><wfw:comment>http://www.cppblog.com/whspecial/comments/205123.html</wfw:comment><comments>http://www.cppblog.com/whspecial/archive/2014/01/03/205123.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/whspecial/comments/commentRss/205123.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/whspecial/services/trackbacks/205123.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 将排序二叉树转化成双向链表，应该是一道很常见的面试题目，网上的实现比较多，有用递归也有用中序遍历法的。看到一位外国友人的实现，还是比较清晰的，思路如下：1，如果左子树不为null，处理左子树&nbsp; &nbsp;1.a）递归转化左子树为双向链表；&nbsp; &nbsp;1.b）找出根结点的前驱节点（是左子树的最右的节点）&nbsp; &nbsp;1.c）将上一步找出的节点和根...&nbsp;&nbsp;<a href='http://www.cppblog.com/whspecial/archive/2014/01/03/205123.html'>阅读全文</a><img src ="http://www.cppblog.com/whspecial/aggbug/205123.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/whspecial/" target="_blank">whspecial</a> 2014-01-03 00:41 <a href="http://www.cppblog.com/whspecial/archive/2014/01/03/205123.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>UNIX网络编程读书笔记</title><link>http://www.cppblog.com/whspecial/archive/2013/10/31/204011.html</link><dc:creator>whspecial</dc:creator><author>whspecial</author><pubDate>Wed, 30 Oct 2013 16:32:00 GMT</pubDate><guid>http://www.cppblog.com/whspecial/archive/2013/10/31/204011.html</guid><wfw:comment>http://www.cppblog.com/whspecial/comments/204011.html</wfw:comment><comments>http://www.cppblog.com/whspecial/archive/2013/10/31/204011.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/whspecial/comments/commentRss/204011.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/whspecial/services/trackbacks/204011.html</trackback:ping><description><![CDATA[&nbsp; &nbsp;这一段在看《unix网络编程》，回顾之前做项目用到的一些东西，在这里总结一下：<br /><br />&nbsp;<strong> &nbsp;(1)TCP套接口编程</strong><br />&nbsp; &nbsp;这里介绍各个接口函数：<br />&nbsp; &nbsp;<span style="font-family: Arial; line-height: 24px; background-color: #ffffff;">1 文件描述符</span><br style="color: #444444; font-family: 'Open Sans', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;" />&nbsp; &nbsp;<span style="font-family: Arial; line-height: 24px; background-color: #ffffff;">－socket(int domain, int type, int protocol); //生成文件描述符</span><br style="color: #444444; font-family: 'Open Sans', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;" />&nbsp; &nbsp;<span style="font-family: Arial; line-height: 24px; background-color: #ffffff;">－bind(int sockfd, struct sockaddr *my_addr, int addrlen); //将本地的一个端口绑定到fd上，一般只需要在server端</span><br style="color: #444444; font-family: 'Open Sans', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;" />&nbsp; &nbsp;<span style="font-family: Arial; line-height: 24px; background-color: #ffffff;">2 服务端</span><br style="color: #444444; font-family: 'Open Sans', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;" />&nbsp; &nbsp;<span style="font-family: Arial; line-height: 24px; background-color: #ffffff;">－listen(int sockfd, int backlog); //有两个作用：1,将主动套接口变为被动套接口;2,设置最大连接数backlog</span><br style="color: #444444; font-family: 'Open Sans', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;" />&nbsp; &nbsp;<span style="font-family: Arial; line-height: 24px; background-color: #ffffff;">－accept(int sockfd, void *addr, int *addrlen); //为建立好的连接生成一个新的fd</span><br style="color: #444444; font-family: 'Open Sans', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;" />&nbsp; &nbsp;<span style="font-family: Arial; line-height: 24px; background-color: #ffffff;">3</span><span style="font-family: Arial; line-height: 24px; background-color: #ffffff;">&nbsp;客户端</span><br style="color: #444444; font-family: 'Open Sans', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;" />&nbsp; &nbsp;<span style="font-family: Arial; line-height: 24px; background-color: #ffffff;">－connect(int sockfd, struct sockaddr *serv_addr, int addrlen); //进行socket连接</span><br style="color: #444444; font-family: 'Open Sans', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;" />&nbsp; &nbsp;<span style="font-family: Arial; line-height: 24px; background-color: #ffffff;">4 通信</span><br style="color: #444444; font-family: 'Open Sans', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;" />&nbsp; &nbsp;<span style="font-family: Arial; line-height: 24px; background-color: #ffffff;">－send(int sockfd, const void *msg, int len, unsigned int flags); //发送请求</span><br style="color: #444444; font-family: 'Open Sans', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;" />&nbsp; &nbsp;<span style="font-family: Arial; line-height: 24px; background-color: #ffffff;">－recv(int sockfd, void *buf, int len, unsigned int flags); //接收请求</span><span style="color: #444444; font-family: 'Open Sans', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><br /><br /><img src="http://www.cppblog.com/images/cppblog_com/whspecial/20120130150030502.png" width="654" height="672" alt="" /></span><br /><br />&nbsp;<strong> &nbsp;(2)I/O多路复用</strong><br />&nbsp;&nbsp;&nbsp;I/O多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取，它就通知该进程。按照《UNIX网络编程》的说法，I/O多路复用用于以下三种情况：<br /><span>&nbsp; &nbsp;a)</span><span style="font-family: 'Open Sans', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">一个TCP服务器既要处理监听套接口，又要处理已连接套接口；<br /></span><span>&nbsp; &nbsp;b)</span><span style="font-family: 'Open Sans', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">一个服务器既要处理TCP，又要处理UDP；<br /></span><span>&nbsp; &nbsp;c)</span><span style="font-family: 'Open Sans', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;">当客户端处理多个描述字（比如处理交互式输入和网络套接口）</span><span style="color: #444444; font-family: 'Open Sans', Helvetica, Arial, sans-serif; line-height: 24px; background-color: #ffffff;"><br /></span>&nbsp; &nbsp;目前被广泛使用的是select和epoll：<br />&nbsp; &nbsp;2.1,select<br />&nbsp;&nbsp;&nbsp;int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout)<br />&nbsp; &nbsp;第一个参数指定最大的fd数目，中间三个分别是被监控的读、写、异常的fd集，最后一个是超时时间。select函数会阻塞等待，直到监控的fd集中有fd就绪，或者已经超时。<br />&nbsp; &nbsp;2.2,epoll<br />&nbsp; &nbsp;epoll相比于select，主要的好处在于它不像select一样去轮询fd集，而是由内核去触发；另外它支持更大的fd个数<br /><br />&nbsp;<strong> &nbsp;(3)网络服务器模型</strong><br />&nbsp; &nbsp;其实网络服务器模型还是比较复杂的，有一篇比较经典的文章叫做c10K problem，链接如下：<a href="http://www.kegel.com/c10k.html">http://www.kegel.com/c10k.html</a><br />&nbsp; &nbsp;这里记录的是很简单的几种多线程TCP服务器模型，顺便可以比较下：<br />&nbsp; &nbsp;2.1 主线程accept，为每个client创建一个线程<br />&nbsp; &nbsp;2.2 使用线程池，全部accept，当有连接来的时候其中某个线程进行处理<br />&nbsp; &nbsp;2.3 使用线程池，主线程accept，当有连接来的时候主线程将其放入队列，由工作线程进行处理（生产者-消费者模型）<br />&nbsp; &nbsp;1方案过于频繁地进行线程创建销毁，2方案在一个连接过来时会带来惊群现象，3方案会比前两个方案要好一些。<img src ="http://www.cppblog.com/whspecial/aggbug/204011.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/whspecial/" target="_blank">whspecial</a> 2013-10-31 00:32 <a href="http://www.cppblog.com/whspecial/archive/2013/10/31/204011.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>跨机房的hadoop集群</title><link>http://www.cppblog.com/whspecial/archive/2013/10/27/203940.html</link><dc:creator>whspecial</dc:creator><author>whspecial</author><pubDate>Sun, 27 Oct 2013 15:28:00 GMT</pubDate><guid>http://www.cppblog.com/whspecial/archive/2013/10/27/203940.html</guid><wfw:comment>http://www.cppblog.com/whspecial/comments/203940.html</wfw:comment><comments>http://www.cppblog.com/whspecial/archive/2013/10/27/203940.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/whspecial/comments/commentRss/203940.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/whspecial/services/trackbacks/203940.html</trackback:ping><description><![CDATA[<div><p style="text-indent:21.0pt;line-height:150%"><span style="font-family:宋体;">这是来自于阿里技术嘉年华的一个分享，因为在百度也考虑过类似的事情，所以听得比较有感悟，这里把相关内容整理一下。</span></p>  <p style="text-indent:21.0pt;line-height:150%"><span style="font-family:宋体;">首先尊重版权，还是把原链接和作者贴上：</span></p>  <p style="line-height:150%"><a href="http://adc.alibabatech.org/carnival/history/schedule/2013/detail/main/286?video=0">http://adc.alibabatech.org/carnival/history/schedule/2013/detail/main/286?video=0</a></p>  <p style="text-indent:21.0pt;line-height:150%"><span style="font-family:宋体;">来自于阿里吴威工程师的分享</span></p>  <p style="line-height:150%">&nbsp;</p>  <p style="text-indent:21.0pt;line-height:150%"><span style="font-family:宋体;">首先需要说明一点，跨机房</span>hadoop<span style="font-family:宋体;">可能应用场景并不是很多，国内像</span>BAT<span style="font-family:宋体;">这种巨头也许需要，但是大部分的中小公司也许并不需要这个，也许这是个屠龙之技，呵呵。</span></p>  <p style="text-indent:21.0pt;line-height:150%"><span style="font-family:宋体;">把这个问题分三段来讲，第一段是问题出现的背景，第二段是解决该问题的难点，第三段是最终的解决方案。</span></p>  <p style="margin-left:36.0pt;text-indent:-36.0pt;line-height:150%;"><strong>（一）&nbsp;</strong><strong><span style="font-family:宋体;">背景：</span></strong></p>  <p style="line-height:150%"><span style="font-family:宋体;">先要看下为什么需要做一个跨机房的大集群？</span></p>  <p style="text-indent:21.0pt;line-height:150%"><span style="font-family:宋体;">大集群的优点在于数据管理和授权容易（这个问题在一个多部门的大公司还是很重要的）；跨部门的使用数据容易，无需重复拉取数据。</span></p>  <p style="text-indent:21.0pt;line-height:150%"><span style="font-family:宋体;">在集群达到一定规模时，单机房（机房内的容量是有限的）已经无法满足集群的需求了，要想一劳永逸的解决问题，需要建设一个跨机房的</span>hadoop<span style="font-family:宋体;">集群。</span></p>  <p style="line-height:150%"><strong><span style="font-family:宋体;">（二）技术挑战：</span></strong></p>  <p style="line-height:150%"><strong>2.1 NameNode</strong><strong><span style="font-family:宋体;">的性能问题：</span></strong></p>  <p style="line-height:150%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family:宋体;">在管理一个巨大的</span>hadoop<span style="font-family:宋体;">集群时，由于原始的</span>Namenode<span style="font-family:宋体;">是单节点，因此会成为一个性能瓶颈，遇到的性能问题主要包括两方面：存储容量问题（存储元数据）和计算压力（处理</span>rpc<span style="font-family:宋体;">请求，修改内存树时候需要全局锁）问题。</span></p>  <p style="line-height:150%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family:宋体;">其中存储容量问题可以依赖内存的垂直扩展来解决，但是计算压力却很难通过提升硬件来解决（因为目前厂商的主要发展方向是多核，而非提高主频）</span></p>  <p style="line-height:150%"><strong>2.2</strong><strong><span style="font-family:宋体;">机房之间的网络限制：</span></strong></p>  <p style="line-height:150%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family:宋体;">机房之间的网络永远是个硬件条件的限制，跨机房的网络传输带来了数据延时和带宽限制：</span></p>  <p style="margin-left:39.0pt;text-indent:-18.0pt;line-height:150%;">1，&nbsp;<span style="font-family:宋体;">延时一般是在</span>10ms<span style="font-family:宋体;">之内，而</span>hadoop<span style="font-family:宋体;">上大部分运行的是离线作业，基本可接受</span></p>  <p style="margin-left:39.0pt;text-indent:-18.0pt;line-height:150%;">2，&nbsp;<span style="font-family:宋体;">带宽限制的问题比较大，因为单机房内的点对点带宽一般是在</span>1Gbps<span style="font-family:宋体;">，而机房之间的带宽确在</span>20Mbps<span style="font-family:宋体;">左右，非常有限。</span></p>  <p style="line-height:150%"><strong>2.3</strong><strong><span style="font-family:宋体;">资源组之间的管理</span></strong></p>  <p style="line-height:150%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family:宋体;">每个部门可以看做一个资源组，它们可能会互相使用对方的数据，因此如何规划计算和存储的位置就很重要，否则会在多个机房之间出现大量的数据拷贝。</span></p>  <p style="line-height:150%"><strong><span style="font-family:宋体;">（三）解决方案：</span></strong></p>  <p style="text-indent:21.0pt;line-height:150%"><span style="font-family:宋体;">先看下整个跨集群</span>hadoop<span style="font-family:宋体;">的架构图：</span></p>  <p style="line-height:150%"><img src="http://www.cppblog.com/images/cppblog_com/whspecial/QQ图片20131027233042.jpg" width="558" height="315" alt="" /><br /></p>  <p style="line-height:150%">&nbsp;</p>  <p style="text-indent:18.0pt;line-height:150%"><span style="font-family:宋体;">重点介绍里面三点，也就是和上面三个问题相对应的：</span></p>  <p style="margin-left:18.0pt;text-indent:-18.0pt;line-height:150%;">1，&nbsp;<span style="font-family:宋体;">可以看到这里画出了两个</span>NN<span style="font-family:宋体;">（</span>namenode<span style="font-family:宋体;">），它们实际上还是属于一个</span>hadoop<span style="font-family:宋体;">集群，这是业界里的一个解决方案：</span>HDFS Fedaration<span style="font-family:宋体;">，它为了解决元数据节点性能问题；</span></p>  <p style="margin-left:18.0pt;text-indent:-18.0pt;line-height:150%;">2，&nbsp;<span style="font-family:宋体;">可以看到这里有一个</span>cross node<span style="font-family:宋体;">节点，它是用来在两个机房之间同步数据的，它的设计考虑到了机房间的网络限制；</span></p>  <p style="margin-left:18.0pt;text-indent:-18.0pt;line-height:150%;">3，&nbsp;<span style="font-family:宋体;">最后是</span>groupA<span style="font-family:宋体;">、</span>groupB<span style="font-family:宋体;">，这是为了解决数据产出方和使用方关系来用的。</span></p>  <p style="line-height:150%"><strong>3.1 Federation</strong></p>  <p style="text-indent:18.0pt;line-height:150%">Federation<span style="font-family:宋体;">相关资料见：</span></p>  <p style="text-indent:18.0pt;line-height:150%"><a href="http://hadoop.apache.org/docs/current/hadoop-project-dist/hadoop-hdfs/Federation.html#HDFS_Federation">http://hadoop.apache.org/docs/current/hadoop-project-dist/hadoop-hdfs/Federation.html#HDFS_Federation</a></p>  <p style="text-indent:18.0pt;line-height:150%"><img src="http://www.cppblog.com/images/cppblog_com/whspecial/QQ图片20131027233052.jpg" width="547" height="381" alt="" /><br /></p>  <p style="text-indent:21.0pt;line-height:150%"><span style="font-family:宋体;">为了水平扩展</span>Namenode<span style="font-family:宋体;">，</span>federation<span style="font-family:宋体;">使用了多个互相独立的</span>namenode<span style="font-family:宋体;">。它们之间互相不需要通信，每个</span>datenode<span style="font-family:宋体;">需要向全部</span>namenode<span style="font-family:宋体;">注册并发送信息。</span></p>  <p style="text-indent:21.0pt;line-height:150%">BlockPool<span style="font-family:宋体;">是属于一个</span>namenode<span style="font-family:宋体;">的</span>block<span style="font-family:宋体;">集合，每个</span>blockpool<span style="font-family:宋体;">之间也是互相独立的。</span></p>  <p style="line-height:150%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family:宋体;">在</span>federation<span style="font-family:宋体;">里，有一个需要关注的问题，就是多个</span>namenode<span style="font-family:宋体;">的地址如何对用户进行透明？它采用的解决方案是目录树挂载的方案（社区有个</span>viewFS<span style="font-family:宋体;">，应该就是为了解决这个问题）：熟悉</span>linux<span style="font-family:宋体;">或者</span>nfs<span style="font-family:宋体;">的朋友应该都知道</span>mount<span style="font-family:宋体;">这个概念，目录树挂载就是这个意思。</span></p>  <p style="text-indent:21.0pt;line-height:150%"><span style="font-family:宋体;">不过使用目录树挂载也存在着一个问题，就是各个子目录下的存储资源需要人为的介入管理，不能出现严重的不均。</span></p>  <p style="line-height:150%"><strong>3.2 crossNode</strong></p>  <p style="line-height:150%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family:宋体;">机房间的网络限制要求不能出现大规模、长时间的数据拷贝，需要一个专门管理机房间数据拷贝的进程，叫做</span>crossNode<span style="font-family:宋体;">。它是独立部署的一个节点，和元数据节点是分离的。</span></p>  <p style="line-height:150%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family:宋体;">它能提供的功能概括来说主要包括以下三点：</span></p>  <p style="margin-left:39.0pt;text-indent:-18.0pt;line-height:150%;">a）&nbsp;<span style="font-family:宋体;">根据预置的跨机房文件，进行数据拷贝</span></p>  <p style="margin-left:39.0pt;text-indent:-18.0pt;line-height:150%;">b）&nbsp;<span style="font-family:宋体;">处理实时的数据拷贝请求</span></p>  <p style="margin-left:39.0pt;text-indent:-18.0pt;line-height:150%;">c）&nbsp;<span style="font-family:宋体;">进行跨机房的数据流量控制</span></p>  <p style="margin-left:21.0pt;line-height:150%"><span style="font-family:宋体;">如何得知跨机房文件列表？</span></p>  <p style="line-height:150%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family:宋体;">由于离线任务基本都是定时触发的，可以根据对历史作业的分析来形成一个跨机房文件列表</span></p>  <p style="margin-left:18.0pt;text-indent:-18.0pt;line-height:150%;"><strong>3.3<span style="font-weight: normal; font-size: 7pt; line-height: normal; font-family: 'Times New Roman';">&nbsp;&nbsp; </span></strong><strong><span style="font-family:宋体;">资源组之间的管理</span></strong></p>  <p style="text-indent:18.0pt;line-height:150%"><span style="font-family:宋体;">各个资源组之间存在数据的依赖，我们希望通过资源组管理，能实现大部分任务在本机房内产出数据，只有少量跨机房产出数据；大部分任务读取本机房的数据副本，只有少量跨机房读取数据。</span></p>  <p style="text-indent:18.0pt;line-height:150%"><span style="font-family:宋体;">为了标识资源组之间的数据依赖性，定义一个资源组之间的距离概念：一个资源组访问另一个资源组的数据量越多，则两者的距离越近，应该将距离接近的资源组放在同一个机房内。</span></p>  <p style="text-indent:18.0pt;line-height:150%"><span style="font-family:宋体;">为了让计算和产出尽可能地靠近，使用一个</span>MRProxy<span style="font-family:宋体;">，对于不同类型的任务做不同处理：</span></p>  <p style="margin-left:51.75pt;text-indent:-33.75pt;line-height:150%;">a）<span style="font-size: 7pt; line-height: normal; font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-family:宋体;">离线计算：跨机房列表中的数据正在传输中（</span>DC1-&gt;DC2<span style="font-family:宋体;">），</span>DC2<span style="font-family:宋体;">上的</span> Job <span style="font-family:宋体;">被暂停调度，等待传输完毕</span> </p>  <p style="margin-left:51.75pt;text-indent:-33.75pt;line-height:150%;">b）<span style="font-size: 7pt; line-height: normal; font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Ad-hoc<span style="font-family:宋体;">查询：</span>DC2<span style="font-family:宋体;">上的</span> Job <span style="font-family:宋体;">需要读</span>DC1<span style="font-family:宋体;">上的数据，</span>Job<span style="font-family:宋体;">暂停调度，通知</span> CrossNode<span style="font-family:宋体;">，数据传输完毕后继续调度</span> </p>  <p style="margin-left:51.75pt;text-indent:-33.75pt;line-height:150%;">c）<span style="font-size: 7pt; line-height: normal; font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-family:宋体;">特殊情况：跨机房数据</span> Join<span style="font-family:宋体;">，</span>DC1<span style="font-family:宋体;">大表，</span>DC2<span style="font-family:宋体;">小表，</span>Job <span style="font-family:宋体;">调度到</span>DC1<span style="font-family:宋体;">上，跨机房直接读取</span>DC2<span style="font-family:宋体;">数据，无需等待</span> </p>  <p style="margin-left:18.0pt;line-height:150%">&nbsp;</p>  <p style="text-indent:18.0pt;line-height:150%"><span style="font-family:宋体;">由于是根据视频和</span>ppt<span style="font-family:宋体;">整理，并没有代码或者文档，所以可能有些地方的理解有偏差，欢迎来提意见</span>~</p></div><img src ="http://www.cppblog.com/whspecial/aggbug/203940.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/whspecial/" target="_blank">whspecial</a> 2013-10-27 23:28 <a href="http://www.cppblog.com/whspecial/archive/2013/10/27/203940.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>KFS代码分析2（meta元数据持久化）</title><link>http://www.cppblog.com/whspecial/archive/2013/10/24/203894.html</link><dc:creator>whspecial</dc:creator><author>whspecial</author><pubDate>Wed, 23 Oct 2013 17:03:00 GMT</pubDate><guid>http://www.cppblog.com/whspecial/archive/2013/10/24/203894.html</guid><wfw:comment>http://www.cppblog.com/whspecial/comments/203894.html</wfw:comment><comments>http://www.cppblog.com/whspecial/archive/2013/10/24/203894.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/whspecial/comments/commentRss/203894.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/whspecial/services/trackbacks/203894.html</trackback:ping><description><![CDATA[<p style="line-height:150%"><span style="font-size: 9pt; line-height: 150%; font-family: 宋体;">KFS</span><span style="font-size: 9pt; line-height: 150%; font-family: 宋体;">的元数据持久化是依赖checkpoint和operation log结合来工作的，其中checkpoint顾名思义保存的是某个点内存的状态，operation log记录的是对元数据修改的操作日志。</span></p>  <h1><span style="font-size:12.0pt;font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">使用</span><span style="font-size:12.0pt;">checkpoint+log</span><span style="font-size:12.0pt;font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">的设计<br /></span><span style="font-weight: normal;"><span style="font-size: 9pt; line-height: 150%; font-family: 宋体;">（1）<span style="font-size: 7pt; line-height: normal; font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt; line-height: 150%; font-family: 宋体;">元数据信息必须要持久化，否则掉电或者人工重启之后该信息丢失<br /></span><span style="font-size: 9pt; line-height: 150%; font-family: 宋体;">（2）<span style="font-size: 7pt; line-height: normal; font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt; line-height: 150%; font-family: 宋体;">便于快速重启，可以从最近的一个cp中快速构建内存状态，加上该cp之后的log就可以完整地构建内存<br /><br /></span></span></h1>  <h1><span style="font-size:12.0pt;font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">读写</span><span style="font-size:12.0pt;">checkpoint</span><span style="font-size:12.0pt;font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">和</span><span style="font-size:12.0pt;">log</span><span style="font-size:12.0pt;font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">的过程<br /><br /></span><strong style="line-height: 150%; font-size: 14px;">Metaserver</strong><strong style="line-height: 150%; font-size: 14px;"><span style="font-family:宋体;">启动时的内存构建：</span></strong></h1>  <p style="line-height:150%"><span style="font-family:宋体;">在</span>Startup.cc<span style="font-family:宋体;">调用</span>rebuild<span style="font-family:宋体;">函数</span></p>  <p style="margin-left:36.0pt;text-indent:-36.0pt;line-height:150%;">（1）<span style="font-size: 7pt; line-height: normal; font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-family:宋体;">如果之前已经有了</span>checkpoint<span style="font-family:宋体;">，从</span>checkpoint<span style="font-family:宋体;">里重建内存树，否则新建一棵内存树</span></p>  <p style="margin-left:36.0pt;text-indent:-36.0pt;line-height:150%;">（2）<span style="font-size: 7pt; line-height: normal; font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-family:宋体;">在内存中</span>replay<span style="font-family:宋体;">该</span>checkpoint<span style="font-family:宋体;">之后的所有</span>operation log<br /><br /></p>  <p style="line-height:150%"><strong>MetaServer</strong><strong><span style="font-family:宋体;">运行时写入新的</span>checkpoint</strong><strong><span style="font-family:宋体;">：<br /></span></strong></p>  <p style="line-height:150%">logcompactor_main.cc<span style="font-family:宋体;">的</span>main<span style="font-family:宋体;">函数调用，应该是以调用另一个进程的方式来执行，猜想是</span>Metaserver<span style="font-family:宋体;">进程会定时调用该进程</span></p>  <p style="margin-left:36.0pt;text-indent:-36.0pt;line-height:150%;">（1）<span style="font-size: 7pt; line-height: normal; font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-family:宋体;">根据旧的</span>checkpoint<span style="font-family:宋体;">在内存中生成状态</span></p>  <p style="margin-left:36.0pt;text-indent:-36.0pt;line-height:150%;">（2）<span style="font-size: 7pt; line-height: normal; font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-family:宋体;">在内存中</span>replay<span style="font-family:宋体;">之后的</span>op log</p>  <p style="margin-left:36.0pt;text-indent:-36.0pt;line-height:150%;">（3）<span style="font-size: 7pt; line-height: normal; font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-family:宋体;">将此时的内存状态写入新的</span>checkpoint<br /><br /></p>  <p style="line-height:150%"><strong>MetaServer</strong><strong><span style="font-family:宋体;">运行时写入新的</span>log</strong><strong><span style="font-family:宋体;">：</span></strong></p>  <p style="line-height:150%"><span style="font-family:宋体;">由</span>logger.cc<span style="font-family:宋体;">来写入新</span>log<span style="font-family:宋体;">，看了代码应该是每次修改了元信息的操作，都会将这条</span>op log<span style="font-family:宋体;">写入磁盘，虽然性能不高，但是比较可靠（之前也自己写过日志库，使用的是两个</span>buffer<span style="font-family:宋体;">交换写入，这样比较高效一些）</span></p><img src ="http://www.cppblog.com/whspecial/aggbug/203894.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/whspecial/" target="_blank">whspecial</a> 2013-10-24 01:03 <a href="http://www.cppblog.com/whspecial/archive/2013/10/24/203894.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>KFS代码分析1（meta内存结构）</title><link>http://www.cppblog.com/whspecial/archive/2013/10/23/203879.html</link><dc:creator>whspecial</dc:creator><author>whspecial</author><pubDate>Tue, 22 Oct 2013 17:36:00 GMT</pubDate><guid>http://www.cppblog.com/whspecial/archive/2013/10/23/203879.html</guid><wfw:comment>http://www.cppblog.com/whspecial/comments/203879.html</wfw:comment><comments>http://www.cppblog.com/whspecial/archive/2013/10/23/203879.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/whspecial/comments/commentRss/203879.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/whspecial/services/trackbacks/203879.html</trackback:ping><description><![CDATA[<p><span style="font-family: Verdana; font-size: 10pt;">此处的KFS是指</span><span style="font-family: Verdana; font-size: 10pt; line-height: 18px; background-color: #ffffff;">Kosmos distributed file system，代码位于</span><span style="font-family: Verdana; font-size: 10pt;"><a href="http://sourceforge.net/projects/kosmosfs/">http://sourceforge.net/projects/kosmosfs/</a>，之后会写几篇相关的文章，以供后来者参考。</span><span style="font-family: Verdana; font-size: 10pt;"><br /></span><span style="font-family: 宋体;"><br />KFS里Meta的内存结构主要是一棵B+树，保存在内存里，具体分析如下：</span></p>  <h1><span style="font-size:12.0pt;">B-</span><span style="font-size:12.0pt;font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">树，</span><span style="font-size:12.0pt;">B+</span><span style="font-size:12.0pt;font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">树的定义</span></h1>  <p style="line-height:150%"><span style="font-family:宋体;">关于这些树的定义，最好还是参考算法导论等经典书，网路上的信息有些不是很准确，为了方便大家还是贴一个链接：</span></p>  <p style="line-height:150%"><a href="http://www.cnblogs.com/oldhorse/archive/2009/11/16/1604009.html">http://www.cnblogs.com/oldhorse/archive/2009/11/16/1604009.html</a></p>  <h1><span style="font-size:12.0pt;">KFS</span><span style="font-size:12.0pt;font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">为何选用</span><span style="font-size:12.0pt;">B+</span><span style="font-size:12.0pt;font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">树而非</span><span style="font-size:12.0pt;">B</span><span style="font-size:12.0pt;font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">树？</span></h1>  <p style="line-height:150%"><span style="font-family:宋体;">这是我个人的理解：</span></p>  <p style="line-height:150%"><span style="font-family:宋体;">虽然</span>B<span style="font-family:宋体;">树可以在非叶子节点命中，会缩短一些平均查找长度，但是</span>B+<span style="font-family:宋体;">树在这种应用一个优势就是每个节点都有指向</span>next<span style="font-family:宋体;">节点的指针，对于范围查询或者遍历操作很适合。对于文件系统的一个</span>ls<span style="font-family:宋体;">某个子目录的需求，用</span>B+<span style="font-family:宋体;">树可以较高效的解决。</span></p>  <h1><span style="font-size:12.0pt;">KFS</span><span style="font-size:12.0pt;font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">里</span><span style="font-size:12.0pt;">B+</span><span style="font-size:12.0pt; font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">树的类图<br /></span></h1><p style="line-height: 150%;"><span style="font-size: 11.0pt;line-height:150%"><img src="http://www.cppblog.com/images/cppblog_com/whspecial/image1.png" width="480" height="184" alt="" /><br />MetaNode</span><span style="font-size:11.0pt;line-height:150%;font-family: 宋体;">：</span>base class for both internal and leaf nodes</p>  <p style="line-height:150%"><span style="font-size: 11.0pt;line-height:150%">Meta</span><span style="font-size:11.0pt;line-height:150%;font-family: 宋体;">：</span>base class for data objects (leaf nodes)</p>  <p style="line-height:150%">Node<span style="font-family:宋体;">：</span>an internal node in the KFS search tree</p>  <p style="line-height:150%">MetaChunkInfo<span style="font-family:宋体;">：</span>chunk information for a given file offset </p>  <p style="line-height:150%">MetaDentry <span style="font-family:宋体;">：</span>Directory entry, mapping a file name to a file id</p>  <p style="line-height:150%">MetaFattr<span style="font-family:宋体;">：</span>File or directory attributes</p>  <h1><span style="font-size:12.0pt;font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">各节点的介绍<br /></span></h1>  <p style="line-height:150%"><strong><span style="font-family:宋体;">（</span>1</strong><strong><span style="font-family:宋体;">）</span>Meta</strong><span style="font-family:宋体;">类是子节点的父类，其最主要的成员变量是</span>fid<br /></p><p style="line-height:150%"><span style="font-family:宋体;">有三个叶子节点：</span>MetaChunkInfo<span style="font-family:宋体;">，</span>MetaDentry<span style="font-family:宋体;">，</span>MetaFattr<br /><br /></p>  <p style="line-height:150%"><strong><span style="font-family:宋体;">（</span>2</strong><strong><span style="font-family:宋体;">）</span>MetaDentry</strong><strong><span style="font-family:宋体;">：</span></strong><span style="font-family:宋体;">实现从文件名到</span>fid<span style="font-family:宋体;">的映射，对于每个文件（目录）都拥有</span>1<span style="font-family:宋体;">个</span>MetaDentry</p><p style="line-height:150%"><span style="font-family:宋体;">成员变量包括：</span></p>  <p style="line-height:150%">dir<span style="font-family:宋体;">：文件父目录的</span>fid</p>  <p style="line-height:150%">name<span style="font-family:宋体;">：</span>dentry<span style="font-family:宋体;">的名称，实际就是文件名<br /><br /></span></p>  <p style="line-height:150%"><strong><span style="font-family:宋体;">（</span>3</strong><strong><span style="font-family:宋体;">）</span>MetaFattr</strong><strong><span style="font-family: 宋体;">：</span></strong><span style="font-family:宋体;">实现从</span>fid<span style="font-family:宋体;">到文件属性的映射，对于每个文件（目录）都拥有一个</span>MetaFattr<span style="font-family:宋体;">。<br /></span></p><p style="line-height:150%"><span style="font-family:宋体;">成员变量包括：</span></p>  <p style="line-height:150%">Type<span style="font-family:宋体;">：文件还是目录</span></p>  <p style="line-height:150%">numReplicas<span style="font-family:宋体;">：文件有几份副本</span></p>  <p style="line-height:150%">mtime<span style="font-family:宋体;">：修改时间</span></p>  <p style="line-height:150%">ctime<span style="font-family:宋体;">：属性修改时间</span></p>  <p style="line-height:150%">crtime<span style="font-family:宋体;">：文件创建时间</span></p>  <p style="line-height:150%">chunkcount<span style="font-family:宋体;">：连续的</span>chunk<span style="font-family:宋体;">数目</span></p>  <p style="line-height:150%">filesize<span style="font-family:宋体;">：文件大小</span></p>  <p style="line-height:150%">nextChunkOffset<span style="font-family:宋体;">：最后一个</span>chunk<span style="font-family:宋体;">在文件的所处的</span>offset</p>  <p style="line-height:150%">mode_t mode<span style="font-family:宋体;">：文件属性（</span>rwx<span style="font-family:宋体;">位）</span></p>  <p style="line-height:150%">key<span style="font-family:宋体;">：由</span>KFS_FATTR<span style="font-family:宋体;">，</span>fid<span style="font-family:宋体;">来构成，可以通过</span>fid<span style="font-family:宋体;">直接找到保存文件属性的节点。<br /></span><br /> <strong><span style="font-family:宋体;">（</span>4</strong><strong><span style="font-family:宋体;">）</span>MetaChunkInfo</strong><strong><span style="font-family: 宋体;">：</span></strong><span style="font-family:宋体;">标志某个文件对应的</span>chunk<span style="font-family:宋体;">信息，如果一个文件包含多个</span>chunk<span style="font-family:宋体;">，那么需要有多个</span>MetaChunkInfo<span style="font-family:宋体;">。<br /></span></p><p style="line-height:150%"><span style="font-family:宋体;">成员变量包括：</span></p>  <p style="line-height:150%">offset<span style="font-family:宋体;">：</span>chunk<span style="font-family:宋体;">在文件中的偏移量，因为一个文件可能由多个</span>chunk<span style="font-family:宋体;">组成</span></p>  <p style="line-height:150%">chunkId<span style="font-family:宋体;">：</span>chunk<span style="font-family:宋体;">的</span>id<span style="font-family:宋体;">号</span></p>  <p style="line-height:150%">chunkVersion<span style="font-family:宋体;">：</span>chunk<span style="font-family:宋体;">的</span>version<span style="font-family:宋体;">值<br /><br /></span></p>  <p style="line-height:150%"><strong><span style="font-family:宋体;">（</span>5</strong><strong><span style="font-family:宋体;">）</span>Node</strong><strong><span style="font-family: 宋体;">：</span></strong><span style="font-family:宋体;">实现的是</span>B+<span style="font-family:宋体;">树的内部节点，这种节点仅仅作为索引用途，存储实际元数据信息的节点位于最底部的叶子节点。<br /></span></p><p style="line-height:150%"><span style="font-family:宋体;">成员变量包括：</span></p>  <p style="line-height:150%">NKEY = 32<span style="font-family:宋体;">：每个节点最多拥有的关键字数目，实际上也就是最多拥有的子节点数目，如果多余这个值节点进行分裂</span></p>  <p style="line-height:150%">NSPLIT = NKEY / 2<span style="font-family:宋体;">：分裂之后每个节点的关键字数目</span></p>  <p style="line-height:150%">NFEWEST = NKEY - NSPLIT<span style="font-family:宋体;">：每个节点最少拥有的关键字数目，如果少于这个值两个节点进行合并</span></p>  <p style="line-height:150%">count<span style="font-family:宋体;">：节点实际拥有的关键字数目</span></p>  <p style="line-height:150%">Key childKey[NKEY]<span style="font-family:宋体;">：节点存储的关键字列表</span></p>  <p style="line-height:150%">MetaNode *childNode[NKEY]<span style="font-family:宋体;">：节点指向子节点的指针列表</span></p>  <p style="line-height:150%">Node *next<span style="font-family:宋体;">：指向下一个同级节点的指针</span></p>  <p style="line-height:150%"><span style="font-family:宋体;">实际上每个内部节点的阶数为</span>32<span style="font-family:宋体;">，可以有</span>32<span style="font-family:宋体;">个子节点，而每个叶子节点只保存一个</span>key<span style="font-family:宋体;">值。</span></p>  <h1><span style="font-size:12.0pt;font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">三类子节点在</span><span style="font-size:12.0pt;">B+</span><span style="font-size:12.0pt;font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">树中如何分布？</span></h1>  <p style="line-height:150%"><span style="font-family:宋体;">可以想象，必定是将同一类的节点聚集在一起。因此对于排序函数就是先比较节点类型，然后再对节点内部的成员变量进行比较。</span>MetaDentry<span style="font-family:宋体;">是根据</span>dir<span style="font-family:宋体;">（父目录的</span>id<span style="font-family:宋体;">），</span>MetaFattr<span style="font-family:宋体;">是根据</span>fid<span style="font-family:宋体;">，</span>MetaChunkInfo<span style="font-family:宋体;">是根据</span>id<span style="font-family:宋体;">和</span>chunkId<span style="font-family:宋体;">来排序。</span></p>  <h1><span style="font-size:12.0pt;font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">一个不太相关的思考</span></h1>  <p style="line-height:150%"><span style="font-size: 10pt; line-height: 150%; font-family: 宋体;">看上面的三类子节点，我们可以发现chunk的位置信息并没有保存在B+树里，它是单独保存在一个Map数据结构里的，也不会在meta server里进行持久化，而是每次chunk启动时向meta server来报告。之所以不做持久化，可以这样来理解：</span></p>  <p style="line-height:150%"><span style="font-size: 10pt; line-height: 150%; font-family: 宋体;">只有Chunk服务器才能最终确定一个Chunk是否在它的硬盘上。Chunk服务器的错误可能会导致Chunk自动消失(比如，硬盘损坏了或者无法访问了)，亦或者操作人员可能会重命名一个Chunk服务器，还是由chunk server来报告比较靠谱。</span></p><img src ="http://www.cppblog.com/whspecial/aggbug/203879.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/whspecial/" target="_blank">whspecial</a> 2013-10-23 01:36 <a href="http://www.cppblog.com/whspecial/archive/2013/10/23/203879.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Dremel存储格式解析</title><link>http://www.cppblog.com/whspecial/archive/2013/08/14/202546.html</link><dc:creator>whspecial</dc:creator><author>whspecial</author><pubDate>Wed, 14 Aug 2013 15:17:00 GMT</pubDate><guid>http://www.cppblog.com/whspecial/archive/2013/08/14/202546.html</guid><wfw:comment>http://www.cppblog.com/whspecial/comments/202546.html</wfw:comment><comments>http://www.cppblog.com/whspecial/archive/2013/08/14/202546.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/whspecial/comments/commentRss/202546.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/whspecial/services/trackbacks/202546.html</trackback:ping><description><![CDATA[<p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp; &nbsp; Dremel是google推出的又一神器，paper中宣称能够在3s内分析1PB的数据，主要是面向交互式查询。这篇paper对嵌套类型的存储方式方面，思维确实有些跳跃，这篇文章主要讲讲这个，一方面是方便后来者理解，另一方面是让自己也整理下思路。<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;首先Dremel使用的是列存模型，对于基本类型列存较容易做到；但是对于嵌套类型，Dremel也能做到将其拆解成基本类型并进行列存，这是值得我们研究的。<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;直观看下嵌套类型按行存储和拆解后按列存储的对比效果：<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;"><a target="_blank" href="http://blog.51cto.com/attachment/201306/230027199.jpg" data_ue_src="http://blog.51cto.com/attachment/201306/230027199.jpg"><img src="http://blog.51cto.com/attachment/201306/230027199.jpg" title="11.jpg" width="450" height="205" border="0" hspace="0" vspace="0" data_ue_src="http://blog.51cto.com/attachment/201306/230027199.jpg" style="width: 450px; height: 205px;"  alt="" /></a></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;然后对于嵌套数据类型，Dremel里面定义了里面三种类型的字段<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;1，必须出现1次而且仅出现1次的字段：required<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;2，可能出现1次或者0次的字段：optional<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;3，可能出现0次或者N次字段：repeated<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;下面以paper的例子来讲述吧：</p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;"><a target="_blank" href="http://blog.51cto.com/attachment/201306/225240357.jpg" data_ue_src="http://blog.51cto.com/attachment/201306/225240357.jpg"><img src="http://blog.51cto.com/attachment/201306/225240357.jpg" title="2.jpg" width="450" height="360" border="0" hspace="0" vspace="0" data_ue_src="http://blog.51cto.com/attachment/201306/225240357.jpg" style="width: 450px; height: 360px;"  alt="" /></a></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;其中DocId是required字段，因此在r1,r2中必须出现1次；url字段是optional字段，因此在r1的第三个Name里未出现，在r1的前两个Name里出现了1次；Backward字段是repeated字段，因此在r1的Links里未出现，在r2的Links里出现了2次。<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;理解了上面这些，直接来看下Dremel是怎么来存它的吧：<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;"><a target="_blank" href="http://blog.51cto.com/attachment/201306/231032285.jpg" data_ue_src="http://blog.51cto.com/attachment/201306/231032285.jpg"><img src="http://blog.51cto.com/attachment/201306/231032285.jpg" title="3.jpg" width="450" height="363" border="0" hspace="0" vspace="0" data_ue_src="http://blog.51cto.com/attachment/201306/231032285.jpg" style="width: 450px; height: 363px;"  alt="" /></a></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;上表中的每条记录都有两个属性，"r"代表repetition&nbsp;level，"d"代表definition&nbsp;level，定义如下：<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;repetition&nbsp;level:what&nbsp;repeated&nbsp;field&nbsp;in&nbsp;the&nbsp;field&#8217;s&nbsp;path&nbsp;the&nbsp;value&nbsp;has&nbsp;repeated，记录该字段是在哪个repeated级别上重复的</p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;definition&nbsp;level:how&nbsp;many&nbsp;fields&nbsp;inpthat&nbsp;could&nbsp;be&nbsp;undefined&nbsp;(because&nbsp;they&nbsp;are&nbsp;optional&nbsp;or&nbsp;repeated)&nbsp;are&nbsp;actually&nbsp;present，记录该字段之上有多少个optional或者repeated字段实际是有值的（本来可以为null的）</p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;看到这里，各位可能已经在心里默念了：WTF！别急，可以结合一个例子来看：<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;"><strong>先看repetition&nbsp;level</strong>（下面以r替代），以Name.Language.Code为例：</p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;1)对第1个出现的值，其r始终为0，因此'en-us'的r为0<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;2)对于第2个值'en'，其上一个值是'en-us'，它们是在Language级别发生的重复，Name.Language是两级的repeated字段，因此r为2<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;3)对于第3个值null，是为了记录'en-gb'是出现在第三个Name而非第二个Name里，特意占位用的。null的上一个值是'en'，它们是在Name级别发生的重复，因此r是1<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;4)对于第4个值'en-gb'，其上一个值是null，它们也是在Name级别发生的重复，因此r是1<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;5)对于第5个值null，其上一个值是'en-gb'，它们出现在两个不同Document里，因此r是0<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;总结下，看repetition&nbsp;level注意两点：1,只比较该值和上一个值；2,只需要看这两个值的重复位置上有几个repeated字段<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;"><strong>再看definition&nbsp;level</strong>（下面以d替代），也以Name.Language.Code为例：</p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;1)对于'en-us'，其上的Name，Language都出现了，因此d为2（其实对于非null值的字段，其上的optional或者repeated字段肯定是出现了，所以都是相同的，只是null字段的d值有差别）<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;2)对于'en'，同理d也为2<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;3)对于null，其上只出现了Name，没有出现Language，因此d为1<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;4)对于'en-gb',d也为2<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;5)对于最后一个null，其上也只出现了Name，没有出现Language，因此d为1<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;"><br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;以上只是讲了dremel怎么去存嵌套类型，至于这种存法是怎么想出来的，真非我辈能理解的了。。。更多内容，请参考原著paper及网上解析。<br /></p><div></div><img src ="http://www.cppblog.com/whspecial/aggbug/202546.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/whspecial/" target="_blank">whspecial</a> 2013-08-14 23:17 <a href="http://www.cppblog.com/whspecial/archive/2013/08/14/202546.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Orcfile文件格式解析（2）</title><link>http://www.cppblog.com/whspecial/archive/2013/08/14/202545.html</link><dc:creator>whspecial</dc:creator><author>whspecial</author><pubDate>Wed, 14 Aug 2013 15:13:00 GMT</pubDate><guid>http://www.cppblog.com/whspecial/archive/2013/08/14/202545.html</guid><wfw:comment>http://www.cppblog.com/whspecial/comments/202545.html</wfw:comment><comments>http://www.cppblog.com/whspecial/archive/2013/08/14/202545.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/whspecial/comments/commentRss/202545.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/whspecial/services/trackbacks/202545.html</trackback:ping><description><![CDATA[<p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp; &nbsp; 上篇文章从整体介绍了Orcfile的存储格式，接下来重点介绍下Orc里用到的几种编码格式：</p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;字典编码：用于String类型的字段<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;Run-Length编码：用于int，long，short等类型的编码</p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;Bit编码：可以用于各种数据类型<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;"><strong>1，字典编码：</strong></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;对于String类型的每个字段分别保存一个字典，记录每个值在字典中的位置，保存字典的数据结构采用一棵红黑树。对于每个String字段，最终会有三个输出Stream，分别是StringOuptut(记录字典中的值)，LengthOutput(记录每个字典值的长度)，RowOutput(记录字段在字典中的位置)。</p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;思考1：为什么要用红黑树？<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;因为红黑树无论是插入，删除，查找的性能都比较平均，都是O(logN)，而且是平衡查找树，最坏情况也不会退化成O(N)<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;思考2：其实一般存储时还会使用LZO之类的压缩，它们本身就是一种字典压缩，为什么Orc里面要自己做字典压缩？<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;因为LZO之类的压缩窗口一般比较小（LZO默认是64KB），而Orc的字典压缩是以整个字段为范围来压缩的，压缩率会更好。<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;"><strong>2，Run-Length编码：</strong><br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;对于int,long,short类型的字段，使用Run-Length编码。该Run-Length能够对等差数列（完全相等也属于等差数列）进行压缩，该等差数列需要满足以下两个条件：</p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;1，至少包含3个元素</p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;2，差值在-128~127之间（因为差值用1Byte来表示）</p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;对于不满足等差数列的数字，Run-Length编码也能存储，但是没有压缩效果，Run-Length的具体存储如下：</p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;第一个Byte是Control&nbsp;Byte，取值在-128~127之间，其中-1~-128代表后面存储着1~128个不满足等差数列的数字，0~127代表后面存储着3~130个等差数列的数字；</p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;如果Control&nbsp;Byte&gt;=0，则后面跟着一个Byte存储差值，否则不存储该Byte；</p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;如果Control&nbsp;Byte&gt;=0，则后面跟着等差数列的第一个数，否则跟着-Control&nbsp;Byte个数字。</p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;例子：</p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;原始数字：12,12,12,12,12,10,7,13</p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;经过Run-Length的数字：<span style="color: #ff0000;">2</span>,<span style="color: #ffc000;">0</span>,12,<span style="color: #ff0000;">-3</span>,10,7,13</p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;红色代表Control&nbsp;Byte，黄色代表差值，黑色代表具体的数字。<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;"><strong>3，Bit编码：</strong></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">对所有类型的字段都可以采用Bit编码来表示该值是否为null。在写任何类型字段之前，先判断该字段值是够为null，如果为null则bit值存为0，否则存为1，对于为null的字段在实际编码时不需要存储了。经过Bit编码之后，可以对于8个bit组成一个Byte，再对其进行Run-Length编码。</p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;其实除了这三种编码格式之外，Orc对于hive的复杂类型array,map,list等，将其降维成基本类型来存储，这个也是值得借鉴的，如果有空之后会进行分析。</p><img src ="http://www.cppblog.com/whspecial/aggbug/202545.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/whspecial/" target="_blank">whspecial</a> 2013-08-14 23:13 <a href="http://www.cppblog.com/whspecial/archive/2013/08/14/202545.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Orcfile文件格式解析（1）</title><link>http://www.cppblog.com/whspecial/archive/2013/08/14/202544.html</link><dc:creator>whspecial</dc:creator><author>whspecial</author><pubDate>Wed, 14 Aug 2013 15:12:00 GMT</pubDate><guid>http://www.cppblog.com/whspecial/archive/2013/08/14/202544.html</guid><wfw:comment>http://www.cppblog.com/whspecial/comments/202544.html</wfw:comment><comments>http://www.cppblog.com/whspecial/archive/2013/08/14/202544.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/whspecial/comments/commentRss/202544.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/whspecial/services/trackbacks/202544.html</trackback:ping><description><![CDATA[<p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp; &nbsp; Orcfile(<em>Optimized&nbsp;Row&nbsp;Columnar</em>)是hive&nbsp;0.11版里引入的新的存储格式，是对之前的RCFile存储格式的优化。写这个的哥们来自于HortonWorks，代码写的很不错，比之前的rcfile强多了（据说rcfile是个中科院的童鞋跑去facebook写的，看来中国的计算机教育水平还是有限啊。。。囧，跑题了）</p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;先介绍下Orc的文件格式，截一张官方的图：<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;"><a target="_blank" href="http://blog.51cto.com/attachment/201306/234709762.png" data_ue_src="http://blog.51cto.com/attachment/201306/234709762.png"><img src="http://blog.51cto.com/attachment/201306/234709762.png" title="OrcFileLayout.png" data_ue_src="http://blog.51cto.com/attachment/201306/234709762.png"  alt="" /></a></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;可以看到每个Orc文件由1个或多个stripe组成，每个stripe250MB大小，这个Stripe实际相当于之前的rcfile里的RowGroup概念，不过大小由4MB-&gt;250MB，这样应该能提升顺序读的吞吐率。每个Stripe里有三部分组成，分别是Index&nbsp;Data,Row&nbsp;Data,Stripe&nbsp;Footer：</p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;1，Index&nbsp;Data：一个轻量级的index，默认是每隔1W行做一个索引。这里做的索引应该只是记录某行的各字段在Row&nbsp;Data中的offset，据说还包括每个Column的max和min值，具体没细看代码。</p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;2，Row&nbsp;Data：存的是具体的数据，和RCfile一样，先取部分行，然后对这些行按列进行存储。与RCfile不同的地方在于每个列进行了编码，分成多个Stream来存储，具体如何编码在下一篇解析里会讲。</p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;3，Stripe&nbsp;Footer：存的是各个Stream的类型，长度等信息。</p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;每个文件有一个File&nbsp;Footer，这里面存的是每个Stripe的行数，每个Column的数据类型信息等；每个文件的尾部是一个PostScript，这里面记录了整个文件的压缩类型以及FileFooter的长度信息等。在读取文件时，会seek到文件尾部读PostScript，从里面解析到File&nbsp;Footer长度，再读FileFooter，从里面解析到各个Stripe信息，再读各个Stripe，即从后往前读。</p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;接下来看下ORcfile相对于RCfile做了哪些改进，从Orc作者的ppt里截了张图，分别解释下各行：<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;"><a target="_blank" href="http://blog.51cto.com/attachment/201306/003840639.png" data_ue_src="http://blog.51cto.com/attachment/201306/003840639.png"><img src="http://blog.51cto.com/attachment/201306/003840639.png" title="图片1.png" width="500" height="345" border="0" hspace="0" vspace="0" data_ue_src="http://blog.51cto.com/attachment/201306/003840639.png" style="width: 500px; height: 345px;"  alt="" /></a></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;Hive&nbsp;type&nbsp;model:RCfile在底层存储时不保存类型，都当做Byte流来存储</p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;Separtor&nbsp;complex&nbsp;columns:Orc将复杂类型拆开存储</p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;Splits&nbsp;Found&nbsp;Quickly：不很理解<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;Default&nbsp;Column&nbsp;group&nbsp;size：不用解释了</p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;Files&nbsp;per&nbsp;a&nbsp;bucket：不很理解</p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;Store&nbsp;min，max，count，sum：存了这些便于快速地skip掉一个stripe</p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;Versioned&nbsp;metadata:不很理解<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;Run-Length&nbsp;Data-coding：整数类型做Run-Length变长编码</p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;Store&nbsp;Strings&nbsp;in&nbsp;dictionary：String类型做字典编码</p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;Store&nbsp;Row&nbsp;Count：每个Stripe会存储行数</p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;Skip&nbsp;Compressed&nbsp;blocks:可以直接skip掉压缩过的block</p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;Store&nbsp;internal&nbsp;indexes:存储了一个轻量级的index</p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;"><br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: sans-serif; font-size: 16px; line-height: normal;">&nbsp;&nbsp;&nbsp;&nbsp;整个Orc看下来，代码写的还是比较清晰明了的，而且我们也进行了测试，压缩效果比RCfile提升了不少，有兴趣的朋友可以来看下，之后会写第二篇解析，主要是讲Orc用到的几种编码格式。<br /></p><div></div><img src ="http://www.cppblog.com/whspecial/aggbug/202544.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/whspecial/" target="_blank">whspecial</a> 2013-08-14 23:12 <a href="http://www.cppblog.com/whspecial/archive/2013/08/14/202544.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>