<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>淘宝共享数据平台 tbdata.org</title>
	<atom:link href="http://www.tbdata.org/feed" rel="self" type="application/rss+xml" />
	<link>http://www.tbdata.org</link>
	<description>西湖之滨，白云之上</description>
	<lastBuildDate>Thu, 16 Feb 2012 05:26:56 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Spark 性能测试报告</title>
		<link>http://www.tbdata.org/archives/2144</link>
		<comments>http://www.tbdata.org/archives/2144#comments</comments>
		<pubDate>Wed, 15 Feb 2012 08:20:59 +0000</pubDate>
		<dc:creator>玉泉</dc:creator>
				<category><![CDATA[dw架构]]></category>
		<category><![CDATA[所有]]></category>

		<guid isPermaLink="false">http://www.tbdata.org/?p=2144</guid>
		<description><![CDATA[如有不对或不足的地方请指正。 RDD可以很好地适用于支持数据并行的批量分析应用，包括数据挖掘，机器学习，图算法等，因为这些程序通常都会在很多记录上执行相同的操作。RDD不太适合那些异步更新共享状态的应用，例如并行web爬行器。因此，我们的目标是为大多数分析型应用提供有效的编程模型，而其他类型的应用交给专门的系统。 关于RDD详见： 弹性分布式数据集：一种基于内存的集群计算的容（二）：弹性分布式数据集（RDD） 硬件环境： 开发机器是 3台 Intel(R) Xeon(R) CPU E5440 @ 2.83GHz双核 2.8G 4G内存 操作系统： Red Hat Enterprise Linux Server release 5.7 (Tikanga) Spark配置： 三节点，每个节点2G内存，14 个维度，100个类别，10次迭代，使用不同大小样例文件分析。 结论1：定义0.8（数据量/2048/3）作为三节点的阈值，当运行数据在阈值内时性能成单调递增，当超过该阈值时，性能急剧下降，当超过阈值2%时性能下降53.11937%，当超过34.01326%，性能下降70.80896% 以下是测试数据： 序号 数据文件大小(M) 记录条数 耗时 数据文件/耗时 数据/内存 数据/内存/节点数 0 33.33 147,106 10 3.333344 0.016274 0.005425 1 100 441,319 13 7.692317 0.048828 0.016276 2 166.67 735,533 15 11.11118<div class="more"><a href="http://www.tbdata.org/archives/2144" class="more">阅读全文 &#62;</a></div>]]></description>
			<content:encoded><![CDATA[<p>如有不对或不足的地方请指正。</p>
<p>RDD可以很好地适用于支持数据并行的批量分析应用，包括数据挖掘，机器学习，图算法等，因为这些程序通常都会在很多记录上执行相同的操作。RDD不太适合那些异步更新共享状态的应用，例如并行web爬行器。因此，我们的目标是为大多数分析型应用提供有效的编程模型，而其他类型的应用交给专门的系统。</p>
<p>关于RDD详见：</p>
<p><a href="http://www.ninqing.net/2012/02/%E5%BC%B9%E6%80%A7%E5%88%86%E5%B8%83%E5%BC%8F%E6%95%B0%E6%8D%AE%E9%9B%86%EF%BC%9A%E4%B8%80%E7%A7%8D%E5%9F%BA%E4%BA%8E%E5%86%85%E5%AD%98%E7%9A%84%E9%9B%86%E7%BE%A4%E8%AE%A1%E7%AE%97%E7%9A%84%E5%AE%B9-2/">弹性分布式数据集：一种基于内存的集群计算的容（二）：弹性分布式数据集（RDD）</a></p>
<p>硬件环境：</p>
<p>开发机器是 3台 Intel(R) Xeon(R) CPU E5440 @ 2.83GHz双核 2.8G 4G内存</p>
<p>操作系统：</p>
<p>Red Hat Enterprise Linux Server release 5.7 (Tikanga)</p>
<p>Spark配置：</p>
<p>三节点，每个节点2G内存，14 个维度，100个类别，10次迭代，使用不同大小样例文件分析。</p>
<p>结论1：定义0.8（数据量/2048/3）作为三节点的阈值，当运行数据在阈值内时性能成单调递增，当超过该阈值时，性能急剧下降，当超过阈值2%时性能下降53.11937%，当超过34.01326%，性能下降70.80896%</p>
<p>以下是测试数据：</p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="48">序号</td>
<td width="132">数据文件大小(M)</td>
<td width="104">记录条数</td>
<td width="47">耗时</td>
<td width="113">数据文件/耗时</td>
<td width="85">数据/内存</td>
<td width="132">数据/内存/节点数</td>
</tr>
<tr>
<td width="48">0</td>
<td width="132">33.33</td>
<td width="104">147,106</td>
<td width="47">10</td>
<td width="113">3.333344</td>
<td width="85">0.016274</td>
<td width="132">0.005425</td>
</tr>
<tr>
<td width="48">1</td>
<td width="132">100</td>
<td width="104">441,319</td>
<td width="47">13</td>
<td width="113">7.692317</td>
<td width="85">0.048828</td>
<td width="132">0.016276</td>
</tr>
<tr>
<td width="48">2</td>
<td width="132">166.67</td>
<td width="104">735,533</td>
<td width="47">15</td>
<td width="113">11.11118</td>
<td width="85">0.081382</td>
<td width="132">0.027127</td>
</tr>
<tr>
<td width="48">3</td>
<td width="132">233.33</td>
<td width="104">1,029,746</td>
<td width="47">20</td>
<td width="113">11.66652</td>
<td width="85">0.113931</td>
<td width="132">0.037977</td>
</tr>
<tr>
<td width="48">4</td>
<td width="132">341.33</td>
<td width="104">1,506,371</td>
<td width="47">23</td>
<td width="113">14.8406</td>
<td width="85">0.166665</td>
<td width="132">0.055555</td>
</tr>
<tr>
<td width="48">5</td>
<td width="132">512</td>
<td width="104">2,259,557</td>
<td width="47">30</td>
<td width="113">17.06666</td>
<td width="85">0.25</td>
<td width="132">0.083333</td>
</tr>
<tr>
<td width="48">6</td>
<td width="132">682.67</td>
<td width="104">3,012,743</td>
<td width="47">42</td>
<td width="113">16.25402</td>
<td width="85">0.333335</td>
<td width="132">0.111112</td>
</tr>
<tr>
<td width="48">7</td>
<td width="132">853.33</td>
<td width="104">3,765,929</td>
<td width="47">45</td>
<td width="113">18.96291</td>
<td width="85">0.416665</td>
<td width="132">0.138888</td>
</tr>
<tr>
<td width="48">8</td>
<td width="132">1,024.00</td>
<td width="104">4,519,115</td>
<td width="47">57</td>
<td width="113">17.96494</td>
<td width="85">0.5</td>
<td width="132">0.166667</td>
</tr>
<tr>
<td width="48">9</td>
<td width="132">1,194.67</td>
<td width="104">5,272,301</td>
<td width="47">65</td>
<td width="113">18.37953</td>
<td width="85">0.583335</td>
<td width="132">0.194445</td>
</tr>
<tr>
<td width="48">10</td>
<td width="132">1,365.33</td>
<td width="104">6,025,487</td>
<td width="47">73</td>
<td width="113">18.70316</td>
<td width="85">0.666665</td>
<td width="132">0.222222</td>
</tr>
<tr>
<td width="48">11</td>
<td width="132">1,536.00</td>
<td width="104">6,778,673</td>
<td width="47">80</td>
<td width="113">19.20001</td>
<td width="85">0.75</td>
<td width="132">0.25</td>
</tr>
<tr>
<td width="48">12</td>
<td width="132">1,706.67</td>
<td width="104">7,531,859</td>
<td width="47">95</td>
<td width="113">17.96491</td>
<td width="85">0.833335</td>
<td width="132">0.277778</td>
</tr>
<tr>
<td width="48">13</td>
<td width="132">1,877.33</td>
<td width="104">8,285,044</td>
<td width="47">147</td>
<td width="113">12.77097</td>
<td width="85">0.916665</td>
<td width="132">0.305555</td>
</tr>
<tr>
<td width="48">14</td>
<td width="132">2,048.00</td>
<td width="104">9,038,230</td>
<td width="47">104</td>
<td width="113">19.6923</td>
<td width="85">1</td>
<td width="132">0.333333</td>
</tr>
<tr>
<td width="48">15</td>
<td width="132">2,218.66</td>
<td width="104">9,791,416</td>
<td width="47">113</td>
<td width="113">19.63417</td>
<td width="85">1.08333</td>
<td width="132">0.36111</td>
</tr>
<tr>
<td width="48">16</td>
<td width="132">2,389.33</td>
<td width="104">10,544,602</td>
<td width="47">124</td>
<td width="113">19.26881</td>
<td width="85">1.166665</td>
<td width="132">0.388888</td>
</tr>
<tr>
<td width="48">17</td>
<td width="132">2,560.01</td>
<td width="104">11,297,788</td>
<td width="47">175</td>
<td width="113">14.62861</td>
<td width="85">1.250005</td>
<td width="132">0.416668</td>
</tr>
<tr>
<td width="48">18</td>
<td width="132">2,730.66</td>
<td width="104">12,050,974</td>
<td width="47">184</td>
<td width="113">14.84056</td>
<td width="85">1.33333</td>
<td width="132">0.444443</td>
</tr>
<tr>
<td width="48">19</td>
<td width="132">2,901.34</td>
<td width="104">12,804,160</td>
<td width="47">164</td>
<td width="113">17.69109</td>
<td width="85">1.41667</td>
<td width="132">0.472223</td>
</tr>
<tr>
<td width="48">20</td>
<td width="132">3,072.00</td>
<td width="104">13,557,346</td>
<td width="47">155</td>
<td width="113">19.81934</td>
<td width="85">1.5</td>
<td width="132">0.5</td>
</tr>
<tr>
<td width="48">21</td>
<td width="132">3,242.67</td>
<td width="104">14,310,532</td>
<td width="47">162</td>
<td width="113">20.01647</td>
<td width="85">1.583335</td>
<td width="132">0.527778</td>
</tr>
<tr>
<td width="48">22</td>
<td width="132">3,413.34</td>
<td width="104">15,063,718</td>
<td width="47">166</td>
<td width="113">20.56231</td>
<td width="85">1.66667</td>
<td width="132">0.555557</td>
</tr>
<tr>
<td width="48">23</td>
<td width="132">3,754.68</td>
<td width="104">16,570,089</td>
<td width="47">179</td>
<td width="113">20.97585</td>
<td width="85">1.83334</td>
<td width="132">0.611113</td>
</tr>
<tr>
<td width="48">24</td>
<td width="132">4,266.68</td>
<td width="104">18,829,646</td>
<td width="47">189</td>
<td width="113">22.57501</td>
<td width="85">2.08334</td>
<td width="132">0.694447</td>
</tr>
<tr>
<td width="48">25</td>
<td width="132">4,500.01</td>
<td width="104">19,859,392</td>
<td width="47">209</td>
<td width="113">21.53114</td>
<td width="85">2.197271</td>
<td width="132">0.732424</td>
</tr>
<tr>
<td width="48">26</td>
<td width="132">4,666.68</td>
<td width="104">20,594,925</td>
<td width="47">202</td>
<td width="113">23.10235</td>
<td width="85">2.278652</td>
<td width="132">0.759551</td>
</tr>
<tr>
<td width="48">27</td>
<td width="132">4,766.68</td>
<td width="104">21,036,244</td>
<td width="47">202</td>
<td width="113">23.5974</td>
<td width="85">2.32748</td>
<td width="132">0.775827</td>
</tr>
<tr>
<td width="48">28</td>
<td width="132">4,866.68</td>
<td width="104">21,477,563</td>
<td width="47">226</td>
<td width="113">21.53396</td>
<td width="85">2.376309</td>
<td width="132">0.792103</td>
</tr>
<tr>
<td width="48">29</td>
<td width="132">4,966.68</td>
<td width="104">21,918,882</td>
<td width="47">220</td>
<td width="113">22.5758</td>
<td width="85">2.425137</td>
<td width="132">0.808379</td>
</tr>
<tr>
<td width="48">30</td>
<td width="132">5,066.68</td>
<td width="104">22,360,201</td>
<td width="47">458</td>
<td width="113">11.06261</td>
<td width="85">2.473965</td>
<td width="132">0.824655</td>
</tr>
<tr>
<td width="48">31</td>
<td width="132">5,120.01</td>
<td width="104">22,595,577</td>
<td width="47">463</td>
<td width="113">11.05834</td>
<td width="85">2.500005</td>
<td width="132">0.833335</td>
</tr>
<tr>
<td width="48">32</td>
<td width="132">6,656.01</td>
<td width="104">29,374,250</td>
<td width="47">1010</td>
<td width="113">6.59011</td>
<td width="85">3.250005</td>
<td width="132">1.083335</td>
</tr>
</tbody>
</table>
<p>性能趋势图：</p>
<p><a href="http://www.ninqing.net/2012/02/spark%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95%E6%8A%A5%E5%91%8A/"><img class="alignnone" src="http://www.ninqing.net/wp-content/uploads/2012/02/clip_image002.png" alt="" width="653" height="243" /></a></p>
<p>Spark配置：</p>
<p>一节点， 2G内存，14 个维度，100个类别，10次迭代。</p>
<p>结论2：定义0.9（数据量/2048）作为三节点的阈值，当运行数据在阈值内时性能成单调递增，当超过该阈值时，性能急剧下降，当超过阈值8.3334961%时性能下降57.61797318%，当超过18.18167291%，性能下降66.4701143%</p>
<p>当超过36.36441116%，性能下降94.14757913%</p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="48">序号</td>
<td width="142">数据文件大小(M)</td>
<td width="88">记录条数</td>
<td width="44">耗时</td>
<td width="114">数据文件/耗时</td>
<td width="94">数据/内存</td>
</tr>
<tr>
<td width="48">0</td>
<td width="142">33.33</td>
<td width="88">147,106</td>
<td width="44">10</td>
<td width="114">3.333344</td>
<td width="94">0.016274</td>
</tr>
<tr>
<td width="48">1</td>
<td width="142">100.00</td>
<td width="88">441,319</td>
<td width="44">20</td>
<td width="114">5.000006</td>
<td width="94">0.048828</td>
</tr>
<tr>
<td width="48">2</td>
<td width="142">166.67</td>
<td width="88">735,533</td>
<td width="44">27</td>
<td width="114">6.17288</td>
<td width="94">0.081382</td>
</tr>
<tr>
<td width="48">3</td>
<td width="142">233.33</td>
<td width="88">1,029,746</td>
<td width="44">34</td>
<td width="114">6.862657</td>
<td width="94">0.113931</td>
</tr>
<tr>
<td width="48">4</td>
<td width="142">341.33</td>
<td width="88">1,506,371</td>
<td width="44">45</td>
<td width="114">7.585197</td>
<td width="94">0.166665</td>
</tr>
<tr>
<td width="48">5</td>
<td width="142">512.00</td>
<td width="88">2,259,557</td>
<td width="44">64</td>
<td width="114">7.999997</td>
<td width="94">0.25</td>
</tr>
<tr>
<td width="48">6</td>
<td width="142">682.67</td>
<td width="88">3,012,743</td>
<td width="44">85</td>
<td width="114">8.031401</td>
<td width="94">0.333335</td>
</tr>
<tr>
<td width="48">7</td>
<td width="142">853.33</td>
<td width="88">3,765,929</td>
<td width="44">102</td>
<td width="114">8.365989</td>
<td width="94">0.416665</td>
</tr>
<tr>
<td width="48">8</td>
<td width="142">1,024.00</td>
<td width="88">4,519,115</td>
<td width="44">118</td>
<td width="114">8.67798</td>
<td width="94">0.5</td>
</tr>
<tr>
<td width="48">9</td>
<td width="142">1,194.67</td>
<td width="88">5,272,301</td>
<td width="44">137</td>
<td width="114">8.720216</td>
<td width="94">0.583335</td>
</tr>
<tr>
<td width="48">10</td>
<td width="142">1,365.33</td>
<td width="88">6,025,487</td>
<td width="44">153</td>
<td width="114">8.923729</td>
<td width="94">0.666665</td>
</tr>
<tr>
<td width="48">11</td>
<td width="142">1,536.00</td>
<td width="88">6,778,673</td>
<td width="44">176</td>
<td width="114">8.727279</td>
<td width="94">0.75</td>
</tr>
<tr>
<td width="48">12</td>
<td width="142">1,706.67</td>
<td width="88">7,531,859</td>
<td width="44">193</td>
<td width="114">8.84283</td>
<td width="94">0.833335</td>
</tr>
<tr>
<td width="48">13</td>
<td width="142">1,877.33</td>
<td width="88">8,285,044</td>
<td width="44">223</td>
<td width="114">8.41853</td>
<td width="94">0.916665</td>
</tr>
<tr>
<td width="48">14</td>
<td width="142">2,048.00</td>
<td width="88">9,038,230</td>
<td width="44">574</td>
<td width="114">3.567944</td>
<td width="94">1</td>
</tr>
<tr>
<td width="48">15</td>
<td width="142">2,218.66</td>
<td width="88">9,791,416</td>
<td width="44">786</td>
<td width="114">2.822724</td>
<td width="94">1.08333</td>
</tr>
<tr>
<td width="48">16</td>
<td width="142">2,389.33</td>
<td width="88">10,544,602</td>
<td width="44">1134</td>
<td width="114">2.106995</td>
<td width="94">1.166665</td>
</tr>
<tr>
<td width="48">17</td>
<td width="142">2,560.01</td>
<td width="88">11,297,788</td>
<td width="44">5196</td>
<td width="114">0.492688</td>
<td width="94">1.250005</td>
</tr>
</tbody>
</table>
<p>性能趋势图：</p>
<p><a href="http://www.ninqing.net/2012/02/spark%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95%E6%8A%A5%E5%91%8A/"><img class="alignnone" src="http://www.ninqing.net/wp-content/uploads/2012/02/clip_image004.png" alt="" width="693" height="266" /></a></p>
<p>总论：在执行Spark程序的时候需预估所需的内存，当超过一定阈值后，性能将急剧下降。</p>
<p>版权声明：原创作品，转载请保持文章完整性，转载时请务必以超链接形式标明文章原始出版、作者信息和本声明。否则将追究法律责任。</p>
<p>博客地址：<a href="http://www.ninqing.net/">http://www.ninqing.net/</a></p>
<p>微博地址：<a href="http://weibo.com/ninqing">http://weibo.com/ninqing</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.tbdata.org/archives/2144/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Storm配置项详解</title>
		<link>http://www.tbdata.org/archives/2118</link>
		<comments>http://www.tbdata.org/archives/2118#comments</comments>
		<pubDate>Thu, 05 Jan 2012 07:11:21 +0000</pubDate>
		<dc:creator>tuhai</dc:creator>
				<category><![CDATA[云计算]]></category>
		<category><![CDATA[storm]]></category>
		<category><![CDATA[配置]]></category>

		<guid isPermaLink="false">http://www.tbdata.org/?p=2118</guid>
		<description><![CDATA[什么是Storm? Storm是twitter开源的一套实时数据处理框架，基于该框架你可以通过简单的编程来实现对数据流的实时处理变换。 Storm的配置文件一般存放在$STORM_HOME/conf下，通常名为storm.yaml，它符合yaml格式要求。 配置项详解: 以下是从storm的backtype.storm.Config类中搜集的所有storm支持的配置项(Based storm 0.6.0): 配置项 配置说明 storm.zookeeper.servers ZooKeeper服务器列表 storm.zookeeper.port ZooKeeper连接端口 storm.local.dir storm使用的本地文件系统目录(必须存在并且storm进程可读写) storm.cluster.mode Storm集群运行模式([distributed&#124;local]) storm.local.mode.zmq Local模式下是否使用ZeroMQ作消息系统，如果设置为false则使用java消息系统。默认为false storm.zookeeper.root ZooKeeper中Storm的根目录位置 storm.zookeeper.session.timeout 客户端连接ZooKeeper超时时间 storm.id 运行中拓扑的id,由storm name和一个唯一随机数组成。 nimbus.host nimbus服务器地址 nimbus.thrift.port nimbus的thrift监听端口 nimbus.childopts 通过storm-deploy项目部署时指定给nimbus进程的jvm选项 nimbus.task.timeout.secs 心跳超时时间，超时后nimbus会认为task死掉并重分配给另一个地址。 nimbus.monitor.freq.secs nimbus检查心跳和重分配任务的时间间隔.注意如果是机器宕掉nimbus会立即接管并处理。 nimbus.supervisor.timeout.secs supervisor的心跳超时时间,一旦超过nimbus会认为该supervisor已死并停止为它分发新任务. nimbus.task.launch.secs task启动时的一个特殊超时设置.在启动后第一次心跳前会使用该值来临时替代nimbus.task.timeout.secs. nimbus.reassign 当发现task失败时nimbus是否重新分配执行。默认为真，不建议修改。 nimbus.file.copy.expiration.secs nimbus判断上传/下载链接的超时时间，当空闲时间超过该设定时nimbus会认为链接死掉并主动断开 ui.port Storm UI的服务端口 drpc.servers DRPC服务器列表，以便DRPCSpout知道和谁通讯 drpc.port Storm DRPC的服务端口 supervisor.slots.ports supervisor上能够运行workers的端口列表.每个worker占用一个端口,且每个端口只运行一个worker.通过这项配置可以调整每台机器上运行的worker数.(调整slot数/每机) supervisor.childopts<div class="more"><a href="http://www.tbdata.org/archives/2118" class="more">阅读全文 &#62;</a></div>]]></description>
			<content:encoded><![CDATA[<h2>什么是Storm?</h2>
<p>Storm是twitter开源的一套实时数据处理框架，基于该框架你可以通过简单的编程来实现对数据流的实时处理变换。</p>
<p>Storm的配置文件一般存放在$STORM_HOME/conf下，通常名为storm.yaml，它符合<a title="yaml" href="http://www.yaml.org/" target="_blank">yaml</a>格式要求。</p>
<h2>配置项详解:</h2>
<p>以下是从storm的<em>backtype</em>.<em>storm</em>.<em>Config</em>类中搜集的所有storm支持的配置项(Based storm 0.6.0):</p>
<p><span id="more-2118"></span></p>
<table border="0" rules="NONE" cellspacing="0">
<col width="282" />
<col width="443" />
<tbody>
<tr>
<td align="LEFT" width="282" height="17"><strong>配置项</strong></td>
<td align="LEFT" width="443"><strong>配置说明</strong></td>
</tr>
<tr>
<td align="LEFT" height="17">storm.zookeeper.servers</td>
<td align="LEFT">ZooKeeper服务器列表</td>
</tr>
<tr>
<td align="LEFT" height="17">storm.zookeeper.port</td>
<td align="LEFT">ZooKeeper连接端口</td>
</tr>
<tr>
<td align="LEFT" height="17">storm.local.dir</td>
<td align="LEFT">storm使用的本地文件系统目录(必须存在并且storm进程可读写)</td>
</tr>
<tr>
<td align="LEFT" height="17">storm.cluster.mode</td>
<td align="LEFT">Storm集群运行模式([distributed|local])</td>
</tr>
<tr>
<td align="LEFT" height="32">storm.local.mode.zmq</td>
<td align="LEFT">Local模式下是否使用ZeroMQ作消息系统，如果设置为false则使用java消息系统。默认为false</td>
</tr>
<tr>
<td align="LEFT" height="17">storm.zookeeper.root</td>
<td align="LEFT">ZooKeeper中Storm的根目录位置</td>
</tr>
<tr>
<td align="LEFT" height="17">storm.zookeeper.session.timeout</td>
<td align="LEFT">客户端连接ZooKeeper超时时间</td>
</tr>
<tr>
<td align="LEFT" height="17">storm.id</td>
<td align="LEFT">运行中拓扑的id,由storm name和一个唯一随机数组成。</td>
</tr>
<tr>
<td align="LEFT" height="17">nimbus.host</td>
<td align="LEFT">nimbus服务器地址</td>
</tr>
<tr>
<td align="LEFT" height="17">nimbus.thrift.port</td>
<td align="LEFT">nimbus的thrift监听端口</td>
</tr>
<tr>
<td align="LEFT" height="17">nimbus.childopts</td>
<td align="LEFT">通过storm-deploy项目部署时指定给nimbus进程的jvm选项</td>
</tr>
<tr>
<td align="LEFT" height="17">nimbus.task.timeout.secs</td>
<td align="LEFT">心跳超时时间，超时后nimbus会认为task死掉并重分配给另一个地址。</td>
</tr>
<tr>
<td align="LEFT" height="32">nimbus.monitor.freq.secs</td>
<td align="LEFT">nimbus检查心跳和重分配任务的时间间隔.注意如果是机器宕掉nimbus会立即接管并处理。</td>
</tr>
<tr>
<td align="LEFT" height="32">nimbus.supervisor.timeout.secs</td>
<td align="LEFT">supervisor的心跳超时时间,一旦超过nimbus会认为该supervisor已死并停止为它分发新任务.</td>
</tr>
<tr>
<td align="LEFT" height="32">nimbus.task.launch.secs</td>
<td align="LEFT">task启动时的一个特殊超时设置.在启动后第一次心跳前会使用该值来临时替代nimbus.task.timeout.secs.</td>
</tr>
<tr>
<td align="LEFT" height="17">nimbus.reassign</td>
<td align="LEFT">当发现task失败时nimbus是否重新分配执行。默认为真，不建议修改。</td>
</tr>
<tr>
<td align="LEFT" height="32">nimbus.file.copy.expiration.secs</td>
<td align="LEFT">nimbus判断上传/下载链接的超时时间，当空闲时间超过该设定时nimbus会认为链接死掉并主动断开</td>
</tr>
<tr>
<td align="LEFT" height="17">ui.port</td>
<td align="LEFT">Storm UI的服务端口</td>
</tr>
<tr>
<td align="LEFT" height="17">drpc.servers</td>
<td align="LEFT">DRPC服务器列表，以便DRPCSpout知道和谁通讯</td>
</tr>
<tr>
<td align="LEFT" height="17">drpc.port</td>
<td align="LEFT">Storm DRPC的服务端口</td>
</tr>
<tr>
<td align="LEFT" height="47">supervisor.slots.ports</td>
<td align="LEFT">supervisor上能够运行workers的端口列表.每个worker占用一个端口,且每个端口只运行一个worker.通过这项配置可以调整每台机器上运行的worker数.(调整slot数/每机)</td>
</tr>
<tr>
<td align="LEFT" height="17">supervisor.childopts</td>
<td align="LEFT">在storm-deploy项目中使用,用来配置supervisor守护进程的jvm选项</td>
</tr>
<tr>
<td align="LEFT" height="32">supervisor.worker.timeout.secs</td>
<td align="LEFT">supervisor中的worker心跳超时时间,一旦超时supervisor会尝试重启worker进程.</td>
</tr>
<tr>
<td align="LEFT" height="47">supervisor.worker.start.timeout.secs</td>
<td align="LEFT">supervisor初始启动时，worker的心跳超时时间，当超过该时间supervisor会尝试重启worker。因为JVM初始启动和配置会带来的额外消耗，从而使得第一次心跳会超过supervisor.worker.timeout.secs的设定</td>
</tr>
<tr>
<td align="LEFT" height="32">supervisor.enable</td>
<td align="LEFT">supervisor是否应当运行分配给他的workers.默认为true,该选项用来进行Storm的单元测试,一般不应修改.</td>
</tr>
<tr>
<td align="LEFT" height="17">supervisor.heartbeat.frequency.secs</td>
<td align="LEFT">supervisor心跳发送频率(多久发送一次)</td>
</tr>
<tr>
<td align="LEFT" height="17">supervisor.monitor.frequency.secs</td>
<td align="LEFT">supervisor检查worker心跳的频率</td>
</tr>
<tr>
<td align="LEFT" height="32">worker.childopts</td>
<td align="LEFT">supervisor启动worker时使用的jvm选项.所有的”%ID%”字串会被替换为对应worker的标识符</td>
</tr>
<tr>
<td align="LEFT" height="17">worker.heartbeat.frequency.secs</td>
<td align="LEFT">worker的心跳发送时间间隔</td>
</tr>
<tr>
<td align="LEFT" height="17">task.heartbeat.frequency.secs</td>
<td align="LEFT">task汇报状态心跳时间间隔</td>
</tr>
<tr>
<td align="LEFT" height="47">task.refresh.poll.secs</td>
<td align="LEFT">task与其他tasks之间链接同步的频率.(如果task被重分配,其他tasks向它发送消息需要刷新连接).一般来讲，重分配发生时其他tasks会理解得到通知。该配置仅仅为了防止未通知的情况。</td>
</tr>
<tr>
<td align="LEFT" height="17">topology.debug</td>
<td align="LEFT">如果设置成true，Storm将记录发射的每条信息。</td>
</tr>
<tr>
<td align="LEFT" height="32">topology.optimize</td>
<td align="LEFT">master是否在合适时机通过在单个线程内运行多个task以达到优化topologies的目的.</td>
</tr>
<tr>
<td align="LEFT" height="47">topology.workers</td>
<td align="LEFT">执行该topology集群中应当启动的进程数量.每个进程内部将以线程方式执行一定数目的tasks.topology的组件结合该参数和并行度提示来优化性能</td>
</tr>
<tr>
<td align="LEFT" height="77">topology.ackers</td>
<td align="LEFT">topology中启动的acker任务数.Acker保存由spout发送的tuples的记录，并探测tuple何时被完全处理.当Acker探测到tuple被处理完毕时会向spout发送确认信息.通常应当根据topology的吞吐量来确定acker的数目，但一般不需要太多.当设置为0时,相当于禁用了消息可靠性,storm会在spout发送tuples后立即进行确认.</td>
</tr>
<tr>
<td align="LEFT" height="47">topology.message.timeout.secs</td>
<td align="LEFT">topology中spout发送消息的最大处理超时时间.如果一条消息在该时间窗口内未被成功ack,Storm会告知spout这条消息失败。而部分spout实现了失败消息重播功能。</td>
</tr>
<tr>
<td align="LEFT" height="32">topology.kryo.register</td>
<td align="LEFT">注册到Kryo(Storm底层的序列化框架)的序列化方案列表.序列化方案可以是一个类名,或者是com.esotericsoftware.kryo.Serializer的实现.</td>
</tr>
<tr>
<td align="LEFT" height="32">topology.skip.missing.kryo.registrations</td>
<td align="LEFT">Storm是否应该跳过它不能识别的kryo序列化方案.如果设置为否task可能会装载失败或者在运行时抛出错误.</td>
</tr>
<tr>
<td align="LEFT" height="32">topology.max.task.parallelism</td>
<td align="LEFT">在一个topology中能够允许的最大组件并行度.该项配置主要用在本地模式中测试线程数限制.</td>
</tr>
<tr>
<td align="LEFT" height="32">topology.max.spout.pending</td>
<td align="LEFT">一个spout task中处于pending状态的最大的tuples数量.该配置应用于单个task,而不是整个spouts或topology.</td>
</tr>
<tr>
<td align="LEFT" height="17">topology.state.synchronization.timeout.secs</td>
<td align="LEFT">组件同步状态源的最大超时时间(保留选项,暂未使用)</td>
</tr>
<tr>
<td align="LEFT" height="17">topology.stats.sample.rate</td>
<td align="LEFT">用来产生task统计信息的tuples抽样百分比</td>
</tr>
<tr>
<td align="LEFT" height="17">topology.fall.back.on.java.serialization</td>
<td align="LEFT">topology中是否使用java的序列化方案</td>
</tr>
<tr>
<td align="LEFT" height="17">zmq.threads</td>
<td align="LEFT">每个worker进程内zeromq通讯用到的线程数</td>
</tr>
<tr>
<td align="LEFT" height="32">zmq.linger.millis</td>
<td align="LEFT">当连接关闭时,链接尝试重新发送消息到目标主机的持续时长.这是一个不常用的高级选项,基本上可以忽略.</td>
</tr>
<tr>
<td align="LEFT" height="32">java.library.path</td>
<td align="LEFT">JVM启动(如Nimbus,Supervisor和workers)时的java.library.path设置.该选项告诉JVM在哪些路径下定位本地库.</td>
</tr>
</tbody>
</table>
]]></content:encoded>
			<wfw:commentRss>http://www.tbdata.org/archives/2118/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>数据倾斜总结</title>
		<link>http://www.tbdata.org/archives/2109</link>
		<comments>http://www.tbdata.org/archives/2109#comments</comments>
		<pubDate>Thu, 01 Dec 2011 07:16:44 +0000</pubDate>
		<dc:creator>jijiannan</dc:creator>
				<category><![CDATA[所有]]></category>

		<guid isPermaLink="false">http://www.tbdata.org/?p=2109</guid>
		<description><![CDATA[   在做Shuffle阶段的优化过程中，遇到了数据倾斜的问题，造成了对一些情况下优化效果不明显。主要是因为在Job完成后的所得到的Counters是整个Job的总和，优化是基于这些Counters得出的平均值，而由于数据倾斜的原因造成map处理数据量的差异过大，使得这些平均值能代表的价值降低。Hive的执行是分阶段的，map处理数据量的差异取决于上一个stage的reduce输出，所以如何将数据均匀的分配到各个reduce中，就是解决数据倾斜的根本所在。规避错误来更好的运行比解决错误更高效。在查看了一些资料后，总结如下。 1数据倾斜的原因 1.1操作： 关键词 情形 后果 Join 其中一个表较小， 但是key集中 分发到某一个或几个Reduce上的数据远高于平均值 大表与大表，但是分桶的判断字段0值或空值过多 这些空值都由一个reduce处理，灰常慢 group by group by 维度过小， 某值的数量过多 处理某值的reduce灰常耗时 Count Distinct 某特殊值过多 处理此特殊值的reduce耗时 1.2原因： 1)、key分布不均匀 2)、业务数据本身的特性 3)、建表时考虑不周 4)、某些SQL语句本身就有数据倾斜 1.3表现： 任务进度长时间维持在99%（或100%），查看任务监控页面，发现只有少量（1个或几个）reduce子任务未完成。因为其处理的数据量和其他reduce差异过大。 单一reduce的记录数与平均记录数差异过大，通常可能达到3倍甚至更多。 最长时长远大于平均时长。 2数据倾斜的解决方案 2.1参数调节： hive.map.aggr = true Map 端部分聚合，相当于Combiner hive.groupby.skewindata=true 有数据倾斜的时候进行负载均衡，当选项设定为 true，生成的查询计划会有两个 MR Job。第一个 MR Job 中，Map 的输出结果集合会随机分布到 Reduce 中，每个 Reduce 做部分聚合操作，并输出结果，这样处理的结果是相同的 Group By<div class="more"><a href="http://www.tbdata.org/archives/2109" class="more">阅读全文 &#62;</a></div>]]></description>
			<content:encoded><![CDATA[<h1>   <span class="Apple-style-span" style="font-size: 13px; font-weight: normal;">在做Shuffle阶段的优化过程中，遇到了数据倾斜的问题，造成了对一些情况下优化效果不明显。主要是因为在Job完成后的所得到的Counters是整个Job的总和，优化是基于这些Counters得出的平均值，而由于数据倾斜的原因造成map处理数据量的差异过大，使得这些平均值能代表的价值降低。Hive的执行是分阶段的，map处理数据量的差异取决于上一个stage的reduce输出，所以如何将数据均匀的分配到各个reduce中，就是解决数据倾斜的根本所在。规避错误来更好的运行比解决错误更高效。在查看了一些资料后，总结如下。<span id="more-2109"></span></span></h1>
<h5>1数据倾斜的原因</h5>
<h6>1.1操作：</h6>
<div align="center">
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td valign="top" width="104">
<p align="center"><strong>关键词</strong></p>
</td>
<td valign="top" width="180">
<p align="center"><strong>情形</strong></p>
</td>
<td valign="top" width="208">
<p align="center"><strong>后果</strong></p>
</td>
</tr>
<tr>
<td rowspan="2" width="104">
<p align="center"><strong>Join</strong></p>
</td>
<td valign="top" width="180">
<p align="center">其中一个表较小，</p>
<p align="center">但是key集中</p>
</td>
<td valign="top" width="208">
<p align="center">分发到某一个或几个Reduce上的数据远高于平均值</p>
</td>
</tr>
<tr>
<td valign="top" width="180">
<p align="center">大表与大表，但是分桶的判断字段0值或空值过多</p>
</td>
<td valign="top" width="208">
<p align="center">这些空值都由一个reduce处理，灰常慢</p>
</td>
</tr>
<tr>
<td valign="top" width="104">
<p align="center"><strong>group by</strong></p>
</td>
<td valign="top" width="180">
<p align="center">group by 维度过小，</p>
<p align="center">某值的数量过多</p>
</td>
<td valign="top" width="208">
<p align="center">处理某值的reduce灰常耗时</p>
</td>
</tr>
<tr>
<td valign="top" width="104">
<p align="center"><strong>Count Distinct</strong></p>
</td>
<td valign="top" width="180">
<p align="center">某特殊值过多</p>
</td>
<td valign="top" width="208">
<p align="center">处理此特殊值的reduce耗时</p>
</td>
</tr>
</tbody>
</table>
</div>
<h6>1.2原因：</h6>
<p>1)、key分布不均匀</p>
<p>2)、业务数据本身的特性</p>
<p>3)、建表时考虑不周</p>
<p>4)、某些SQL语句本身就有数据倾斜</p>
<h6>1.3表现：</h6>
<p>任务进度长时间维持在99%（或100%），查看任务监控页面，发现只有少量（1个或几个）reduce子任务未完成。因为其处理的数据量和其他reduce差异过大。</p>
<p>单一reduce的记录数与平均记录数差异过大，通常可能达到3倍甚至更多。 最长时长远大于平均时长。</p>
<h5>2数据倾斜的解决方案</h5>
<p>2.1参数调节：</p>
<p><strong>hive.map.aggr = true</strong></p>
<p>Map 端部分聚合，相当于Combiner</p>
<p><strong>hive.groupby.skewindata</strong><strong>=true</strong></p>
<p>有数据倾斜的时候进行负载均衡，当选项设定为 true，生成的查询计划会有两个 MR Job。第一个 MR Job 中，Map 的输出结果集合会随机分布到 Reduce 中，每个 Reduce 做部分聚合操作，并输出结果，这样处理的结果是相同的 Group By Key 有可能被分发到不同的 Reduce 中，从而达到负载均衡的目的；第二个 MR Job 再根据预处理的数据结果按照 Group By Key 分布到 Reduce 中（这个过程可以保证相同的 Group By Key 被分布到同一个 Reduce 中），最后完成最终的聚合操作。</p>
<p>2.2 SQL语句调节：</p>
<p><strong>如何</strong><strong>Join</strong><strong>：</strong><strong></strong></p>
<p>关于驱动表的选取，选用join key分布最均匀的表作为驱动表<strong></strong></p>
<p>做好列裁剪和filter操作，以达到两表做join的时候，数据量相对变小的效果。</p>
<p><strong>大小表</strong><strong>Join</strong><strong>：</strong><strong></strong></p>
<p>使用map join让小的维度表（1000条以下的记录条数） 先进内存。在map端完成reduce.<strong></strong></p>
<p><strong>大表</strong><strong>Join</strong><strong>大表：</strong><strong></strong></p>
<p>把空值的key变成一个字符串加上随机数，把倾斜的数据分到不同的reduce上，由于null值关联不上，处理后并不影响最终结果。</p>
<p><strong>count distinct</strong><strong>大量相同特殊值</strong><strong></strong></p>
<p>count distinct时，将值为空的情况单独处理，如果是计算count distinct，可以不用处理，直接过滤，在最后结果中加1。如果还有其他计算，需要进行group by，可以先将值为空的记录单独处理，再和其他计算结果进行union。</p>
<p><strong>group by</strong><strong>维度过小：</strong><strong></strong></p>
<p>采用sum() group by的方式来替换count(distinct)完成计算。</p>
<p><strong>特殊情况特殊处理：</strong><strong></strong></p>
<p>在业务逻辑优化效果的不大情况下，有些时候是可以将倾斜的数据单独拿出来处理。最后union回去。</p>
<h5>3典型的业务场景</h5>
<h3>3.1空值产生的数据倾斜</h3>
<p><strong>场景：</strong>如日志中，常会有信息丢失的问题，比如日志中的 user_id，如果取其中的 user_id 和 用户表中的user_id 关联，会碰到数据倾斜的问题。</p>
<p><strong>解决方法</strong><strong>1</strong><strong>：</strong> user_id为空的不参与关联（红色字体为修改后）</p>
<table width="100%" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>
<pre>select * from log a</pre>
<pre>  join users b</pre>
<pre>  on a.user_id is not null</pre>
<pre>  and a.user_id = b.user_id</pre>
<pre>union all</pre>
<pre>select * from log a</pre>
<pre>  where a.user_id is null;</pre>
<p>&nbsp;</p>
</div>
</td>
</tr>
</tbody>
</table>
<p><strong>解决方法</strong><strong>2 </strong><strong>：</strong>赋与空值分新的key值</p>
<table width="100%" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>
<pre>select *</pre>
<pre>  from log a</pre>
<pre>  left outer join users b</pre>
<pre>  on case when a.user_id is null then concat(‘hive’,rand() ) else a.user_id end = b.user_id;</pre>
<p>&nbsp;</p>
</div>
</td>
</tr>
</tbody>
</table>
<p><strong>结论：</strong>方法2比方法1效率更好，不但io少了，而且作业数也少了。解决方法1中 log读取两次，jobs是2。解决方法2 job数是1 。这个优化适合无效 id (比如 -99 , ’’, null 等) 产生的倾斜问题。把空值的 key 变成一个字符串加上随机数，就能把倾斜的数据分到不同的reduce上 ,解决数据倾斜问题。</p>
<h3>3.2不同数据类型关联产生数据倾斜</h3>
<p><strong>场景：</strong>用户表中user_id字段为int，log表中user_id字段既有string类型也有int类型。当按照user_id进行两个表的Join操作时，默认的Hash操作会按int型的id来进行分配，这样会导致所有string类型id的记录都分配到一个Reducer中。</p>
<p><strong>解决方法：</strong>把数字类型转换成字符串类型</p>
<table width="100%" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>
<pre>select * from users a</pre>
<pre>  left outer join logs b</pre>
<pre>  on a.usr_id = cast(b.user_id as string)</pre>
<p>&nbsp;</p>
</div>
</td>
</tr>
</tbody>
</table>
<h3>3.3小表不小不大，怎么用 map join 解决倾斜问题</h3>
<p>使用 map join 解决小表(记录数少)关联大表的数据倾斜问题，这个方法使用的频率非常高，但如果小表很大，大到map join会出现bug或异常，这时就需要特别的处理。 <strong>以下例子</strong><strong>:</strong></p>
<table width="100%" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>
<pre>select * from log a</pre>
<pre>  left outer join users b</pre>
<pre>  on a.user_id = b.user_id;</pre>
<p>&nbsp;</p>
</div>
</td>
</tr>
</tbody>
</table>
<p>users 表有 600w+ 的记录，把 users 分发到所有的 map 上也是个不小的开销，而且 map join 不支持这么大的小表。如果用普通的 join，又会碰到数据倾斜的问题。</p>
<p><strong>解决方法：</strong></p>
<table width="100%" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>
<pre>select /*+mapjoin(x)*/* from log a</pre>
<pre>  left outer join (</pre>
<pre>    select  /*+mapjoin(c)*/d.*</pre>
<pre>      from ( select distinct user_id from log ) c</pre>
<pre>      join users d</pre>
<pre>      on c.user_id = d.user_id</pre>
<pre>    ) x</pre>
<pre>  on a.user_id = b.user_id;</pre>
<p>&nbsp;</p>
</div>
</td>
</tr>
</tbody>
</table>
<p>假如，log里user_id有上百万个，这就又回到原来map join问题。所幸，每日的会员uv不会太多，有交易的会员不会太多，有点击的会员不会太多，有佣金的会员不会太多等等。所以这个方法能解决很多场景下的数据倾斜问题。</p>
<h5>4总结</h5>
<p>使map的输出数据更均匀的分布到reduce中去，是我们的最终目标。由于Hash算法的局限性，按key Hash会或多或少的造成数据倾斜。大量经验表明数据倾斜的原因是人为的建表疏忽或业务逻辑可以规避的。在此给出较为通用的步骤：</p>
<p>1、采样log表，哪些user_id比较倾斜，得到一个结果表tmp1。由于对计算框架来说，所有的数据过来，他都是不知道数据分布情况的，所以采样是并不可少的。</p>
<p>2、数据的分布符合社会学统计规则，贫富不均。倾斜的key不会太多，就像一个社会的富人不多，奇特的人不多一样。所以tmp1记录数会很少。把tmp1和users做map join生成tmp2,把tmp2读到distribute file cache。这是一个map过程。</p>
<p>3、map读入users和log，假如记录来自log,则检查user_id是否在tmp2里，如果是，输出到本地文件a,否则生成&lt;user_id,value&gt;的key,value对，假如记录来自member,生成&lt;user_id,value&gt;的key,value对，进入reduce阶段。</p>
<p>4、最终把a文件，把Stage3 reduce阶段输出的文件合并起写到hdfs。</p>
<p>&nbsp;</p>
<p>如果确认业务需要这样倾斜的逻辑，考虑以下的优化方案：</p>
<p>1、对于join，在判断小表不大于1G的情况下，使用map join</p>
<p>2、对于group by或distinct，设定 hive.groupby.skewindata=true</p>
<p>3、尽量使用上述的SQL语句调节进行优化</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tbdata.org/archives/2109/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>动态实时跟踪你的java程序</title>
		<link>http://www.tbdata.org/archives/1851</link>
		<comments>http://www.tbdata.org/archives/1851#comments</comments>
		<pubDate>Mon, 15 Aug 2011 06:48:28 +0000</pubDate>
		<dc:creator>聚石</dc:creator>
				<category><![CDATA[java]]></category>
		<category><![CDATA[aop]]></category>
		<category><![CDATA[asm]]></category>
		<category><![CDATA[aspectj]]></category>
		<category><![CDATA[bcel]]></category>
		<category><![CDATA[bytecode]]></category>
		<category><![CDATA[cglib]]></category>
		<category><![CDATA[jvm]]></category>
		<category><![CDATA[proxy]]></category>
		<category><![CDATA[transform]]></category>

		<guid isPermaLink="false">http://www.tbdata.org/?p=1851</guid>
		<description><![CDATA[之前有写 基于AOP的日志调试 讨论一种跟踪Java程序的方法, 但不是很完美.后来发现了 Btrace , 由于它借助动态字节码注入技术 , 实现优雅且功能强大. 只不过, 用起来总是磕磕绊绊的, 时常为了跟踪某个问题, 却花了大把的时间调试Btrace的脚本. 为此, 我尝试将几种跟踪模式固化成脚本模板, 待用的时候去调整一下正则表达式之类的. 跟踪过程往往是假设与验证的螺旋迭代过程, 反复的用BTrace跟踪目标进程, 总有那么几次莫名其妙的不可用, 最后不得不重启目标进程. 若真是线上不能停的服务, 我想这种方式还是不靠谱啊. 为此, 据决定自己的搞个用起来简单, 又能良好支持反复跟踪而不用重启目标进程的工具. AOP AOP是Btrace, jip1等众多监测工具的核心思想, 用一段代码最容易说明: public void say(String words){ Trace.enter(); System.out.println(words); Trace.exit(); } 如上, Trace.enter() 和 Trace.exit() 将say(words)内的代码环抱起来, 对方法进出的进行切面的处理, 便可获取运行时的上下文, 如: 调用栈 当前线程 时间消耗 参数与返回值 当前实例状态 实现的选择 实现切面的方式, 我知道的有以下几种: 代理(装饰器)模式 设计模式中装饰器模式和代理模式, 尽管解决的问题域不同, 代码实现是非常相似,<div class="more"><a href="http://www.tbdata.org/archives/1851" class="more">阅读全文 &#62;</a></div>]]></description>
			<content:encoded><![CDATA[<p>之前有写 <a href="http://www.tbdata.org/archives/492">基于AOP的日志调试</a> 讨论一种跟踪Java程序的方法, 但不是很完美.后来发现了 <a href="http://kenai.com/projects/btrace">Btrace</a> , 由于它借助动态字节码注入技术 , 实现优雅且功能强大.<br />
只不过, 用起来总是磕磕绊绊的, 时常为了跟踪某个问题, 却花了大把的时间调试Btrace的脚本. 为此, 我尝试将几种跟踪模式固化成脚本模板, 待用的时候去调整一下正则表达式之类的.<br />
跟踪过程往往是假设与验证的螺旋迭代过程, 反复的用BTrace跟踪目标进程, 总有那么几次莫名其妙的不可用, 最后不得不重启目标进程. 若真是线上不能停的服务, 我想这种方式还是不靠谱啊.<br />
为此, 据决定自己的搞个用起来简单, 又能良好支持反复跟踪而不用重启目标进程的工具.</p>
<h1>AOP</h1>
<p>AOP是Btrace, jip<sup><a href="https://github.com/zhongl/jmxero/wiki/%E5%8A%A8%E6%80%81%E5%AE%9E%E6%97%B6%E8%B7%9F%E8%B8%AA%E4%BD%A0%E7%9A%84java%E7%A8%8B%E5%BA%8F#wiki-fn1">1</a></sup>等众多监测工具的核心思想, 用一段代码最容易说明:</p>
<pre><code>
public void say(String words){
  Trace.enter();
  System.out.println(words);
  Trace.exit();
}
</code></pre>
<p>如上, Trace.enter() 和 Trace.exit() 将say(words)内的代码环抱起来, 对方法进出的进行切面的处理, 便可获取运行时的上下文, 如:</p>
<ul>
<li>调用栈</li>
<li>当前线程</li>
<li>时间消耗</li>
<li>参数与返回值</li>
<li>当前实例状态</li>
</ul>
<h1>实现的选择</h1>
<p>实现切面的方式, 我知道的有以下几种:</p>
<h2>代理(装饰器)模式</h2>
<p>设计模式中装饰器模式和代理模式, 尽管解决的问题域不同, 代码实现是非常相似, 均可以实现切面处理, 这里视为等价. 依旧用代码说明:</p>
<pre><code>
interface Person {
  void say(String words);
}

class Officer implements Person {
  public void say(String words) { lie(words); }
  private void lie(String words) {...}
}

class Proxy implements Person {
  private final Officer officer;
  public Proxy(Officer officer) { this.officer = officer; }
  public void say(String words) {
    enter();
    officer.say(words);
    exit();
  }
  private void enter() { ... }
  private void exit() { ... }
}

Person p = new Proxy(new Officer());
</code></pre>
<p>很明显, 上述enter() 和exit()是实现切面的地方, 通过获取Officer的Proxy实例, 便可对Officer实例的行为进行跟踪. 这种方式实现起来最简单, 也最直接.</p>
<h2>Java Proxy</h2>
<p>Java Proxy是JDK内置的代理API, 借助反射机制实现. 用它来是完成切面则会是:</p>
<pre><code>
class ProxyInvocationHandler implements InvocationHandler {
  private final Object target;
  public ProxyInvocationHandler(Object target) { this.target = target;}
  public Object handle(Object proxy, Method method, Object[] args) {
    enter();
    method.invoke(target, args);
    exit();
  }
  private void enter() { ... }
  private void exit() { ... }
}
ClassLoader loader = ...
Class&lt;?&gt;[]  interfaces = {Person.class};
Person p = (Person)Proxy.newInstance(loader, interfaces, new ProxyInvocationHandler(new Officer()));
</code></pre>
<p>相比较上一中方法, 这种不太易读, 但更为通用, 对具体实现依赖很少.</p>
<h2>AspectJ</h2>
<p>AspectJ<sup><a href="https://github.com/zhongl/jmxero/wiki/%E5%8A%A8%E6%80%81%E5%AE%9E%E6%97%B6%E8%B7%9F%E8%B8%AA%E4%BD%A0%E7%9A%84java%E7%A8%8B%E5%BA%8F#wiki-fn3">3</a></sup>是基于字节码操作的AOP实现, 相比较Java proxy, 它会显得对调用更”透明”, 编写更简明(类似DSL), 性能更好. 如下代码:</p>
<pre><code>
pointcut say(): execute(* say(..))
before(): say() { ... }
after() : say() { ... }
</code></pre>
<p>Aspectj实现切面的时机有两种: 静态编译和类加载期编织(load-time weaving). 并且它对IDE的支持很丰富.</p>
<h2>CGlib</h2>
<p>与AspectJ一样CGlib<sup><a href="https://github.com/zhongl/jmxero/wiki/%E5%8A%A8%E6%80%81%E5%AE%9E%E6%97%B6%E8%B7%9F%E8%B8%AA%E4%BD%A0%E7%9A%84java%E7%A8%8B%E5%BA%8F#wiki-fn4">4</a></sup>也是操作字节码来实现AOP的, 使用上与Java Proxy非常相似, 只是不像Java Proxy对接口有依赖, 我们熟知的Spring, Guice之类的IoC容器实现AOP都是使用它来完成的.</p>
<pre><code>
class Callback implements MethodInterceptor {
  public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable {
    enter();
    proxy.invokeSuper(obj, args);
    exit();
  }
  private void enter() { ... }
  private void exit() { ... }
}
Enhancer e = new Enhancer();
e.setSuperclass(Officer.class);
e.setCallback(new Callback());
Person p = e.create();
</code></pre>
<h1>字节码操纵</h1>
<p>上面四种方法各有适用的场景, 但唯独对运行着的Java进程进行动态的跟踪支持不了, 当然也许是我了解的不够深入, 若有基于上述方案的办法还请不吝赐教.</p>
<p>还是回到Btrace<sup><a href="https://github.com/zhongl/jmxero/wiki/%E5%8A%A8%E6%80%81%E5%AE%9E%E6%97%B6%E8%B7%9F%E8%B8%AA%E4%BD%A0%E7%9A%84java%E7%A8%8B%E5%BA%8F#wiki-fn5">5</a></sup>的思路上来, 在理解了它借助java.lang.Instrumentation进行字节码注入的实现原理<sup><a href="https://github.com/zhongl/jmxero/wiki/%E5%8A%A8%E6%80%81%E5%AE%9E%E6%97%B6%E8%B7%9F%E8%B8%AA%E4%BD%A0%E7%9A%84java%E7%A8%8B%E5%BA%8F#wiki-fn6">6</a></sup>后, 实现动态变化跟踪方式或目标应该没有问题.</p>
<p>借下来的问题, 如何操作(注入)字节码实现切面的处理. 可喜的是, “构建自己的监测工具”<sup><a href="https://github.com/zhongl/jmxero/wiki/%E5%8A%A8%E6%80%81%E5%AE%9E%E6%97%B6%E8%B7%9F%E8%B8%AA%E4%BD%A0%E7%9A%84java%E7%A8%8B%E5%BA%8F#wiki-fn7">7</a></sup>一文给我提供了一个很好的切入点. 在此基础上, 经过一些对ASM<sup><a href="https://github.com/zhongl/jmxero/wiki/%E5%8A%A8%E6%80%81%E5%AE%9E%E6%97%B6%E8%B7%9F%E8%B8%AA%E4%BD%A0%E7%9A%84java%E7%A8%8B%E5%BA%8F#wiki-fn8">8</a></sup>的深入研究, 可以实现:</p>
<ul>
<li>方法调用进入时, 获取当前实例(this) 和 参数值列表;</li>
<li>方法调用出去时, 获取返回值;</li>
<li>方法异常抛出时, 触发回调并获取异常实例.</li>
</ul>
<p>其切面实现的核心代码如下:</p>
<pre><code>
 private static class ProbeMethodAdapter extends AdviceAdapter {

    protected ProbeMethodAdapter(MethodVisitor mv, int access, String name, String desc, String className) {
      super(mv, access, name, desc);
      start = new Label();
      end = new Label();
      methodName = name;
      this.className = className;
    }

    @Override
    public void visitMaxs(int maxStack, int maxLocals) {
      mark(end);
      catchException(start, end, Type.getType(Throwable.class));
      dup();
      push(className);
      push(methodName);
      push(methodDesc);
      loadThis();
      invokeStatic(Probe.TYPE, Probe.EXIT);
      visitInsn(ATHROW);
      super.visitMaxs(maxStack, maxLocals);
    }

    @Override
    protected void onMethodEnter() {
      push(className);
      push(methodName);
      push(methodDesc);
      loadThis();
      loadArgArray();
      invokeStatic(Probe.TYPE, Probe.ENTRY);
      mark(start);
    }

    @Override
    protected void onMethodExit(int opcode) {
      if (opcode == ATHROW) return; // do nothing, @see visitMax
      prepareResultBy(opcode);
      push(className);
      push(methodName);
      push(methodDesc);
      loadThis();
      invokeStatic(Probe.TYPE, Probe.EXIT);
    }

    private void prepareResultBy(int opcode) {
      if (opcode == RETURN) { // void
        push((Type) null);
      } else if (opcode == ARETURN) { // object
        dup();
      } else {
        if (opcode == LRETURN || opcode == DRETURN) { // long or double
          dup2();
        } else {
          dup();
        }
        box(Type.getReturnType(methodDesc));
      }
    }

    private final String className;
    private final String methodName;
    private final Label start;
    private final Label end;
}
</code></pre>
<p>更多参考请见这里的 <a href="https://github.com/zhongl/jsmx/tree/ztrace">Demo</a> , 它是javaagent, 在伴随宿主进程启动后, 提供MBean可用jconsole进行动态跟踪的管理.</p>
<h1>后续的方向</h1>
<ol>
<li>提供基于Web的远程交互界面;</li>
<li>提供基于Shell的本地命令行接口;</li>
<li>提供Profile统计和趋势输出;</li>
<li>提供跟踪日志定位与分析.</li>
</ol>
<h1>参考</h1>
<ol>
<li><a href="http://jiprof.sourceforge.net/">The Java Interactive Profiler</a></li>
<li><a href="http://download.oracle.com/javase/6/docs/api/java/lang/reflect/Proxy.html">Proxy Javadoc</a></li>
<li><a href="http://www.eclipse.org/aspectj/">Aspectj</a></li>
<li><a href="http://cglib.sourceforge.net/">CGlib</a></li>
<li><a href="http://kenai.com/projects/btrace/pages/UserGuide">BTrace User’s Guide</a></li>
<li><a href="http://kenwublog.com/btrace-theory-analysis">java动态跟踪分析工具BTrace实现原理</a></li>
<li><a href="http://www.ibm.com/developerworks/cn/java/j-jip/index.html?ca=drs">构建自己的监测工具</a></li>
<li><a href="http://download.forge.objectweb.org/asm/asm-guide.pdf">ASM Guide</a></li>
<li><a href="http://www.ibm.com/developerworks/cn/java/j-lo-profiling/index.html?ca=drs">常用 Java Profiling 工具的分析与比较</a></li>
<li><a href="https://www.ibm.com/developerworks/java/library/j-aopwork10/?S_TACT=105AGX52&amp;S_CMP=cn-a-j">AOP@Work: Performance monitoring with AspectJ</a></li>
<li><a title="2nd" href="http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html">The JavaTM Virtual Machine Specification</a></li>
<li><a href="http://www.slideshare.net/cafusic/jvm20101228">来自rednaxelafx的JVM分享</a>, 他的 <a href="http://rednaxelafx.iteye.com/">Blog</a></li>
<li><a href="http://commons.apache.org/bcel/manual.html">BCEL</a></li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://www.tbdata.org/archives/1851/feed</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>MapR初体验</title>
		<link>http://www.tbdata.org/archives/1833</link>
		<comments>http://www.tbdata.org/archives/1833#comments</comments>
		<pubDate>Sun, 07 Aug 2011 13:57:53 +0000</pubDate>
		<dc:creator>tuhai</dc:creator>
				<category><![CDATA[云计算]]></category>
		<category><![CDATA[mapr 入门]]></category>

		<guid isPermaLink="false">http://www.tbdata.org/?p=1833</guid>
		<description><![CDATA[一、MapR是什么？

MapR是MapR Technologies, Inc的一个产品，号称下一代Hadoop，使Hadoop变为一个速度更快、可靠性更高、更易于管理、使用更加方便的分布式计算服务和存储平台，同时性能也不断提高。它将极大的扩大了Hadoop的使用范围和方式。它包含了开源社区的许多流行的工具和功能，例如Hbase、Hive。它还100%和Apache Hadoop的API兼容。它能够为客户节约一半的硬件资源消耗，使更多的组织能够利用海量数据分析的力量提高竞争优势。目前有两个版本，M3和M5，其中M3是免费的，M5为收费版，有试用期。具体功能差别见：http://www.mapr.com/products/mapr-editions.html。]]></description>
			<content:encoded><![CDATA[<p style="text-align: right">作者:钟龙伟</p>
<p><strong>一、</strong><strong>MapR</strong><strong>是什么？</strong><strong> </strong></p>
<p>MapR是MapR Technologies, Inc的一个产品，号称下一代Hadoop，使Hadoop变为一个速度更快、可靠性更高、更易于管理、使用更加方便的分布式计算服务和存储平台，同时性能也不断提高。它将极大的扩大了Hadoop的使用范围和方式。它包含了开源社区的许多流行的工具和功能，例如Hbase、Hive。它还100%和Apache Hadoop的API兼容。它能够为客户节约一半的硬件资源消耗，使更多的组织能够利用海量数据分析的力量提高竞争优势。目前有两个版本，M3和M5，其中M3是免费的，M5为收费版，有试用期。具体功能差别见：<a href="http://www.mapr.com/products/mapr-editions.html">http://www.mapr.com/products/mapr-editions.html</a>。</p>
<p><span id="more-1833"></span></p>
<p>1、MapR的整体结构：</p>
<p><a href="http://www.tbdata.org/wp-content/uploads/2011/08/mapr2.png"><img class="alignnone size-full wp-image-1837" src="http://www.tbdata.org/wp-content/uploads/2011/08/mapr2.png" alt="Mapr架构图" width="731" height="691" /></a></p>
<p><strong>二、为什么要</strong><strong>MapR</strong><strong>？</strong></p>
<p>MapR比之其他Hadoop发行版具有三大优越性：EASY、DEPENDABLE、FAST。</p>
<p><strong>Easy</strong><strong>：</strong><strong> </strong></p>
<ul>
<li>有强大的管理工具：MapR Control System，方便的查看集群的状态，作业执行情况，各项配置等；</li>
<li>Mount Hadoop with Direct Access NFS™ . This allows users to mount the entire Hadoop cluster as an NFS volume . Users can simply browse files, automatically open associated applications with a mouse click, or drag-and-drop files and directories into and out of the cluster. Additionally, standard command-line tools and UNIX applications and utilities (such as Grep, Tar, Sort, or Tail) can be used directly on data in the cluster. With other Hadoop distributions, the user must copy the data out of the cluster in order to use standard tools . 不同于HDFS的一次写入，MapR允许修改、重写文件，可并发读写任一文件。</li>
<li>安装配置简单，完整发布版包括：HBase, Pig, Hive, Mahout, HBase, Cascading, ZooKeeper等。（但其实M3没有默认安装HBase, Pig, Hive, Mahout，需要自己另行安装）</li>
<li>和Hadoop &#8211; MapReduce, HDFS ,HBase 100% API兼容。</li>
</ul>
<p><strong>Dependable:</strong></p>
<ul>
<li>分布式的NameNode，避免单点失败；（M5才有）</li>
<li>镜像，MapR的镜像让你可以根据你的Recovery Time objectives设定策略，自动镜像你集群内、集群间或者站点间的数据，（M5才有）</li>
<li>快照，采用redirect-on-write技术，速度快，可以设定快照计划或者随机使用快照，操作简单。（M5才有）</li>
<li>使多个作业安全地共享集群资源，MapR可以给用户设定资源配额，跟踪容量的使用情况。</li>
</ul>
<p><strong>Fast</strong><strong>：</strong><strong> </strong></p>
<ul>
<li>比其他的发行版快，而且节约一半的硬件资源；</li>
<li>Higher MapReduce and HBase throughput (2X – 5X)</li>
<li>Optimized shuffle</li>
<li>Higher random I/O (5X to 100X)</li>
<li>自动、透明的压缩节省了网络和磁盘开销；</li>
<li>Lockless architecture scales linearly with number of cores and nodes</li>
</ul>
<p>在MapR的官方网站<a href="http://www.mapr.com/products/why-mapr.html">http://www.mapr.com/products/why-mapr.html</a>上有更详细的介绍，有兴趣可以进一步了解。</p>
<p><strong>三、如何使用</strong><strong>MapR</strong></p>
<p>MapR 可以像hadoop那样支持单点安装伪集群，但在集群环境下才能真正体现它的威力。我这里在四台服务器上安装MapR Edition 3 ，主要介绍M3-RHEL or CentOS安装。</p>
<p><strong>Hardware </strong><strong>Requirements</strong><strong> </strong></p>
<p>每个节点必须满足下表所列的要求：</p>
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td valign="top"><strong>Minimum Requirements </strong></td>
<td valign="top"><strong>Recommended </strong></td>
</tr>
<tr>
<td valign="top">
<ul>
<li>64-bit processor</li>
<li>4G DRAM</li>
<li>1 network interface</li>
</ul>
</td>
<td valign="top">
<ul>
<li>64-bit processor</li>
<li>32G DRAM</li>
<li>2 GigE network        interfaces</li>
</ul>
</td>
</tr>
</tbody>
</table>
<p><strong>Disk</strong></p>
<ul>
<li>At      least one free unmounted drive or partition, 50 GB or more（参见安装前准备－&gt;disksetup）</li>
<li>At      least 10 GB of free space on the operating system partition</li>
</ul>
<p><strong>Software</strong></p>
<p>确保在所有的节点上安装的是６４位的操作系统，目前ＭａｐＲ仅支持以下操作系统：</p>
<ul>
<li>CentOS      5.x</li>
<li>Red      Hat Enterprise Linux 5.x or 6.0</li>
<li>Ubuntu      9.04 or above</li>
</ul>
<p>主机名唯一（使用虚拟机的需注意这一点）</p>
<p>每个节点安装JDK：</p>
<ul>
<li>Sun      Java JDK 6（一定要是sun的，系统已安装了openjdk的要注意设置ｓｕｎ的为默认jdk）</li>
</ul>
<p><strong>安装前准备</strong><strong> </strong></p>
<p>最好用root用户执行整个过程，我之前用其他用户碰到了一些问题。这里统一默认使用root用户。</p>
<p><strong>规划：</strong><strong> </strong></p>
<p>确定集群规模，以及在每台机器上安装的服务。详见：</p>
<p><a href="http://www.mapr.com/doc/display/MapR/Planning+Roles">http://www.mapr.com/doc/display/MapR/Planning+Roles</a></p>
<p>我这里是：</p>
<p>Node1：CLDB，FileServer，JobTracker，NFS，TaskTracker，WebServer，ZooKeeer。</p>
<p>Node2：FileServer，TaskTracker，ZooKeeer。</p>
<p>Node3：FileServer，TaskTracker，ZooKeeer。</p>
<p>Node4：FileServer，TaskTracker。</p>
<p><strong>disksetup</strong><strong>：</strong></p>
<p>setting up disks for MapR是一个重要步骤，查看机器是否有这样一块磁盘空间：具体参考：<a href="http://www.mapr.com/doc/display/MapR/Setting+Up+Disks+for+MapR">http://www.mapr.com/doc/display/MapR/Setting+Up+Disks+for+MapR</a>。</p>
<p><tt>在每台机器上创建文件/tmp/disks.txt，</tt>列出本机所有可被MapR使用的磁盘。</p>
<p>由于我的机器上没有符合要求的磁盘空间，我的做法是：（这种方法不能很好发挥MapR威力，仅用来测试）</p>
<p>/sbin/modprobe loop</p>
<p>mkdir /disk1/mapr-disks</p>
<p>dd if=/dev/zero of=/disk1/mapr-disks/loop0 bs=1G count=20</p>
<p>/sbin/losetup -f</p>
<p>/sbin/losetup /dev/loop0 /disk1/mapr-disks/loop0</p>
<p>echo /dev/loop0 &gt; /tmp/disks.txt</p>
<p><strong>SSH keyless</strong><strong>：</strong><strong> </strong></p>
<p>确保服务器之间SSH无密码访问，也很简单，就是在每台服务器执行“ssh-keygen -t rsa”，回车不输入密码，生成密钥和公钥，在某台机器上的／root／.ssh目录下新建authorized_keys文件，将所有机器的公钥内容添加到authorized_keys，再将authorized_keys复制到其他机器的／root／.ssh目录下。在每台机器上SSH登录所有机器（包括自己），第一次会要求输入密码，并记录为已知主机，下次就不用再输了。</p>
<p><strong>安装</strong><strong> </strong></p>
<p>1、在所有节点的<tt>/etc/yum.repos.d/</tt><tt>下创建maprtech.repo文件,</tt> 内容为：（注意<tt>baseurl</tt><tt>中的archive，官方网站上的文档有错</tt>）</p>
<p><tt>[maprtech]</tt></p>
<p><tt>name=MapR Technologies</tt></p>
<p><tt>baseurl=http://archive.mapr.com/redhat</tt></p>
<p><tt>enabled=1</tt></p>
<p><tt>gpgcheck=0</tt></p>
<p><tt>protect=1</tt></p>
<p><tt> </tt></p>
<p><tt>2</tt><tt>、</tt>在 node 1, 执行命令：</p>
<p>yum install mapr-cldb mapr-fileserver mapr-jobtracker mapr-nfs mapr-tasktracker mapr-webserver mapr-zookeeper</p>
<p>在 node 2和node 3, 执行命令:</p>
<p>yum install mapr-fileserver mapr-tasktracker mapr-zookeeper</p>
<p>在nodes 4, 执行命令:</p>
<p>yum install mapr-fileserver mapr-tasktracker</p>
<p>3、配置和磁盘格式化，在所有节点执行:</p>
<p>/opt/mapr/server/configure.sh -C &lt;node1IP&gt; -Z &lt;node1IP&gt;,&lt;node2IP&gt;,&lt;node3IP&gt;</p>
<p>/opt/mapr/server/disksetup -F /tmp/disks.txt</p>
<p>4、启动zookeeper，在node1, node2,node3,执行命令：</p>
<p>/etc/init.d/mapr-zookeeper start</p>
<p>注意：MapR中用的是zookeeper3.3.2，有个bug，启动不成功之后再次启动会报错，详见：<a href="https://issues.apache.org/jira/browse/ZOOKEEPER-1061">https://issues.apache.org/jira/browse/ZOOKEEPER-1061</a>。</p>
<p>5、在node1执行：<span style="text-decoration: underline"> </span></p>
<p>/etc/init.d/mapr-warden start</p>
<p>在node1用以下命令给用户授权：</p>
<p>/opt/mapr/bin/maprcli acl edit -type cluster -user root:fc</p>
<p>6、许可证书：</p>
<pre>在安装证书之前NFS是不可用的，所以在日志中会看到很多ERROR，有两种方法安装证书：通过可视化页面MapR Control System或者命令行。</pre>
<p>MapR Control System方法参考：</p>
<p><a href="http://www.mapr.com/doc/display/MapR/M3+-+RHEL+or+CentOS">http://www.mapr.com/doc/display/MapR/M3+-+RHEL+or+CentOS</a>的第11步。</p>
<pre>命令行：先在<a href="http://mapr.com/register/login.html">http://mapr.com/register/login.html</a>注册用户，然后注册一个集群，Cluster ID可以在node1上执行：maprcli license showid得到，注册完后得到证书，在node1上新建文件lice保存证书内容，执行：</pre>
<pre>maprcli license add -is_file true –license lice</pre>
<pre>证书安装成功，可通过maprcli license list查看。</pre>
<pre>7、启动NFS，在node1执行：</pre>
<pre>/opt/mapr/bin/maprcli node services -nodes node1IP -nfs start</pre>
<p>8、在node2、node3、node4执行：</p>
<p>etc/init.d/mapr-warden start</p>
<pre>9、通过<a href="https://node1ip:8443/">https://node1IP:8443</a>，登录MapR Control System，Enjoy it ！</pre>
<pre>10、在终端进入hadoop下bin目录，输入hadoop mfs -ls / ,查看文件系统；</pre>
<p>11、在终端执行</p>
<p>hadoop jar /opt/mapr/hadoop/hadoop-0.20.2/hadoop-0.20.2-dev-examples.jar pi 2 50</p>
<pre><span style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;line-height: 19px">
</span></pre>
<pre><span style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;line-height: 19px">更多信息参考：http://www.mapr.com/doc/display/MapR/Home</span></pre>
]]></content:encoded>
			<wfw:commentRss>http://www.tbdata.org/archives/1833/feed</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>淘宝数据魔方技术架构解析</title>
		<link>http://www.tbdata.org/archives/1789</link>
		<comments>http://www.tbdata.org/archives/1789#comments</comments>
		<pubDate>Wed, 03 Aug 2011 14:37:57 +0000</pubDate>
		<dc:creator>朋春</dc:creator>
				<category><![CDATA[展现]]></category>
		<category><![CDATA[开发技术]]></category>

		<guid isPermaLink="false">http://www.tbdata.org/?p=1789</guid>
		<description><![CDATA[（本文首发于《程序员》8月刊，略有调整。你可通过pengchun#taobao.com联系到作者。） 淘宝网拥有国内最具商业价值的海量数据。截至当前，每天有超过30亿的店铺、商品浏览记录，10亿在线商品数，上千万的成交、收藏和评价数据。如何从这些数据中挖掘出真正的商业价值，进而帮助淘宝、商家进行企业的数据化运营，帮助消费者进行理性的购物决策，是淘宝数据平台与产品部的使命。 为此，我们进行了一系列数据产品的研发，比如为大家所熟知的量子统计、数据魔方和淘宝指数等。尽管从业务层面来讲，数据产品的研发难度并不高；但在“海量”的限定下，数据产品的计算、存储和检索难度陡然上升。本文将以数据魔方为例，向大家介绍淘宝在海量数据产品技术架构方面的探索。 淘宝海量数据产品技术架构 数据产品的一个最大特点是数据的非实时写入，正因为如此，我们可以认为，在一定的时间段内，整个系统的数据是只读的。这为我们设计缓存奠定了非常重要的基础。 图1 淘宝海量数据产品技术架构 按照数据的流向来划分，我们把淘宝数据产品的技术架构分为五层（如图1所示），分别是数据源、计算层、存储层、查询层和产品层。位于架构顶端的是我们的数据来源层，这里有淘宝主站的用户、店铺、商品和交易等数据库，还有用户的浏览、搜索等行为日志等。这一系列的数据是数据产品最原始的生命力所在。 在数据源层实时产生的数据，通过淘宝自主研发的数据传输组件DataX、DbSync和Timetunnel准实时地传输到一个有1500个节点的Hadoop集群上，这个集群我们称之为“云梯”，是计算层的主要组成部分。在“云梯”上，我们每天有大约40000个作业对1.5PB的原始数据按照产品需求进行不同的MapReduce计算。这一计算过程通常都能在凌晨两点之前完成。相对于前端产品看到的数据，这里的计算结果很可能是一个处于中间状态的结果，这往往是在数据冗余与前端计算之间做了适当平衡的结果。 不得不提的是，一些对实效性要求很高的数据，例如针对搜索词的统计数据，我们希望能尽快推送到数据产品前端。这种需求再采用“云梯”来计算效率将是比较低的，为此我们做了流式数据的实时计算平台，称之为“银河”。“银河”也是一个分布式系统，它接收来自TimeTunnel的实时消息，在内存中做实时计算，并把计算结果在尽可能短的时间内刷新到NoSQL存储设备中，供前端产品调用。 容易理解，“云梯”或者“银河”并不适合直接向产品提供实时的数据查询服务。这是因为，对于“云梯”来说，它的定位只是做离线计算的，无法支持较高的性能和并发需求；而对于“银河”而言，尽管所有的代码都掌握在我们手中，但要完整地将数据接收、实时计算、存储和查询等功能集成在一个分布式系统中，避免不了分层，最终仍然落到了目前的架构上。 为此，我们针对前端产品设计了专门的存储层。在这一层，我们有基于MySQL的分布式关系型数据库集群MyFOX和基于HBase的NoSQL存储集群Prom，在后面的文字中，我将重点介绍这两个集群的实现原理。除此之外，其他第三方的模块也被我们纳入存储层的范畴。 存储层异构模块的增多，对前端产品的使用带来了挑战。为此，我们设计了通用的数据中间层——glider——来屏蔽这个影响。glider以HTTP协议对外提供restful方式的接口。数据产品可以通过一个唯一的URL获取到它想要的数据。 以上是淘宝海量数据产品在技术架构方面的一个概括性的介绍，接下来我将重点从四个方面阐述数据魔方设计上的特点。 关系型数据库仍然是王道 关系型数据库（RDBMS）自20世纪70年代提出以来，在工业生产中得到了广泛的使用。经过三十多年的长足发展，诞生了一批优秀的数据库软件，例如Oracle、MySQL、DB2、Sybase和SQL Server等。 图2 MyFOX中的数据增长曲线 尽管相对于非关系型数据库而言，关系型数据库在分区容忍性（Tolerance to Network Partitions）方面存在劣势，但由于它强大的语义表达能力以及数据之间的关系表达能力，在数据产品中仍然占据着不可替代的作用。 淘宝数据产品选择MySQL的MyISAM引擎作为底层的数据存储引擎。在此基础上，为了应对海量数据，我们设计了分布式MySQL集群的查询代理层——MyFOX，使得分区对前端应用透明。 图3 MyFOX的数据查询过程 目前，存储在MyFOX中的统计结果数据已经达到10TB，占据着数据魔方总数据量的95%以上，并且正在以每天超过6亿的增量增长着（如图2所示）。这些数据被我们近似均匀地分布到20个MySQL节点上，在查询时，经由MyFOX透明地对外服务（如图3所示）。 图4 MyFOX节点结构 值得一提的是，在MyFOX现有的20个节点中，并不是所有节点都是“平等”的。一般而言，数据产品的用户更多地只关心“最近几天”的数据，越早的数据，越容易被冷落。为此，出于硬件成本考虑，我们在这20个节点中分出了“热节点”和“冷节点”（如图4所示）。 顾名思义，“热节点”存放最新的、被访问频率较高的数据。对于这部分数据，我们希望能给用户提供尽可能快的查询速度，所以在硬盘方面，我们选择了每分钟15000转的SAS硬盘，按照一个节点两台机器来计算，单位数据的存储成本约为4.5W/TB。相对应地，“冷数据”我们选择了每分钟7500转的SATA硬盘，单碟上能够存放更多的数据，存储成本约为1.6W/TB。 将冷热数据进行分离的另外一个好处是可以有效降低提高内存磁盘比。从图4可以看出，“热节点”上单机只有24GB内存，而磁盘装满大约有1.8TB（300 * 12 * 0.5 / 1024），内存磁盘比约为4:300，远远低于MySQL服务器的一个合理值。内存磁盘比过低导致的后果是，总有一天，即使所有内存用完也存不下数据的索引了——这个时候，大量的查询请求都需要从磁盘中读取索引，效率大打折扣。 NoSQL是SQL的有益补充 在MyFOX出现之后，一切都看起来那么完美，开发人员甚至不会意识到MyFOX的存在，一条不用任何特殊修饰的SQL语句就可以满足需求。这个状态持续了很长一段时间，直到有一天，我们碰到了传统的关系型数据库无法解决的问题——全属性选择器（如图5所示）。 图5 全属性选择器 这是一个非常典型的例子。为了说明问题，我们仍然以关系型数据库的思路来描述。对于笔记本电脑这个类目，用户某一次查询所选择的过滤条件可能包括“笔记本尺寸”、“笔记本定位”、“硬盘容量”等一系列属性（字段），并且在每个可能用在过滤条件的属性上，属性值的分布是极不均匀的。在图5中我们可以看到，笔记本电脑的尺寸这一属性有着10个枚举值，而“蓝牙功能”这个属性值是个布尔值，数据的筛选性非常差。 在用户所选择的过滤条件不确定的情况下，解决全属性问题的思路有两个：一个是穷举所有可能的过滤条件组合，在“云梯”上进行预先计算，存入数据库供查询；另一个是存储原始数据，在用户查询时根据过滤条件筛选出相应的记录进行现场计算。很明显，由于过滤条件的排列组合几乎是无法穷举的，第一种方案在现实中是不可取的；而第二种方案中，原始数据存储在什么地方？如果仍然用关系型数据库，那么你打算怎样为这个表建立索引？ 这一系列问题把我们引到了“创建定制化的存储、现场计算并提供查询服务的引擎”的思路上来，这就是Prometheus（如图6所示）。 图6 Prom的存储结构 从图6可以看出，我们选择了HBase作为Prom的底层存储引擎。之所以选择HBase，主要是因为它是建立在HDFS之上的，并且对于MapReduce有良好的编程接口。尽管Prom是一个通用的、解决共性问题的服务框架，但在这里，我们仍然以全属性选择为例，来说明Prom的工作原理。这里的原始数据是前一天在淘宝上的交易明细，在HBase集群中，我们以属性对（属性与属性值的组合）作为row-key进行存储。而row-key对应的值，我们设计了两个column-family，即存放交易ID列表的index字段和原始交易明细的data字段。在存储的时候，我们有意识地让每个字段中的每一个元素都是定长的，这是为了支持通过偏移量快速地找到相应记录，避免复杂的查找算法和磁盘的大量随机读取请求。 图7 Prom查询过程 图7用一个典型的例子描述的Prom在提供查询服务时的工作原理，限于篇幅，这里不做详细描述。值得一提的是，Prom支持的计算并不仅限于求和SUM运算，统计意义上的常用计算都是支持的。在现场计算方面，我们对Hbase进行了扩展，Prom要求每个节点返回的数据是已经经过“本地计算”的局部最优解，最终的全局最优解只是各个节点返回的局部最优解的一个简单汇总。很显然，这样的设计思路是要充分利用各个节点的并行计算能力，并且避免大量明细数据的网络传输开销。 用中间层隔离前后端 上文提到过，MyFOX和Prom为数据产品的不同需求提供了数据存储和底层查询的解决方案，但随之而来的问题是，各种异构的存储模块给前端产品的使用带来了很大的挑战。并且，前端产品的一个请求所需要的数据往往不可能只从一个模块获取。 举个例子，我们要在数据魔方中看昨天做热销的商品，首先从MyFOX中拿到一个热销排行榜的数据，但这里的“商品”只是一个ID，并没有ID所对应的商品描述、图片等数据。这个时候我们要从淘宝主站提供的接口中去获取这些数据，然后一一对应到热销排行榜中，最终呈现给用户。 图8 glider的技术架构 有经验的读者一定可以想到，从本质上来讲，这就是广义上的异构“表”之间的JOIN操作。那么，谁来负责这个事情呢？很容易想到，在存储层与前端产品之间增加一个中间层，它负责各个异构“表”之间的数据JOIN和UNION等计算，并且隔离前端产品和后端存储，提供统一的数据查询服务。这个中间层就是glider（如图8所示）。 缓存是系统化的工程 除了起到隔离前后端以及异构“表”之间的数据整合的作用之外，glider的另外一个不容忽视的作用便是缓存管理。上文提到过，在特定的时间段内，我们认为数据产品中的数据是只读的，这是利用缓存来提高性能的理论基础。 在图8中我们看到，glider中存在两层缓存，分别是基于各个异构“表”（datasource）的二级缓存和整合之后基于独立请求的一级缓存。除此之外，各个异构“表”内部可能还存在自己的缓存机制。细心的读者一定注意到了图3中MyFOX的缓存设计，我们没有选择对汇总计算后的最终结果进行缓存，而是针对每个分片进行缓存，其目的在于提高缓存的命中率，并且降低数据的冗余度。 大量使用缓存的最大问题就是数据一致性问题。如何保证底层数据的变化在尽可能短的时间内体现给最终用户呢？这一定是一个系统化的工程，尤其对于分层较多的系统来说。 图9 缓存控制体系 图9向我们展示了数据魔方在缓存控制方面的设计思路。用户的请求中一定是带了缓存控制的“命令”的，这包括URL中的query string，和HTTP头中的“If-None-Match”信息。并且，这个缓存控制“命令”一定会经过层层传递，最终传递到底层存储的异构“表”模块。各异构“表”除了返回各自的数据之外，还会返回各自的数据缓存过期时间（ttl），而glider最终输出的过期时间是各个异构“表”过期时间的最小值。这一过期时间也一定是从底层存储层层传递，最终通过HTTP头返回给用户浏览器的。 缓存系统不得不考虑的另一个问题是缓存穿透与失效时的雪崩效应。缓存穿透是指查询一个一定不存在的数据，由于缓存是不命中时被动写的，并且出于容错考虑，如果从存储层查不到数据则不写入缓存，这将导致这个不存在的数据每次请求都要到存储层去查询，失去了缓存的意义。<div class="more"><a href="http://www.tbdata.org/archives/1789" class="more">阅读全文 &#62;</a></div>]]></description>
			<content:encoded><![CDATA[<p><em>（本文首发于《程序员》8月刊，略有调整。你可通过pengchun#taobao.com联系到作者。）</em></p>
<p><em><br />
</em></p>
<p>淘宝网拥有国内最具商业价值的海量数据。截至当前，每天有超过30亿的店铺、商品浏览记录，10亿在线商品数，上千万的成交、收藏和评价数据。如何从这些数据中挖掘出真正的商业价值，进而帮助淘宝、商家进行企业的数据化运营，帮助消费者进行理性的购物决策，是淘宝数据平台与产品部的使命。</p>
<p>为此，我们进行了一系列数据产品的研发，比如为大家所熟知的量子统计、数据魔方和淘宝指数等。尽管从业务层面来讲，数据产品的研发难度并不高；但在“海量”的限定下，数据产品的计算、存储和检索难度陡然上升。本文将以数据魔方为例，向大家介绍淘宝在海量数据产品技术架构方面的探索。</p>
<p><span id="more-1789"></span></p>
<h2><strong>淘宝海量数据产品技术架构</strong></h2>
<p>数据产品的一个最大特点是数据的非实时写入，正因为如此，我们可以认为，在一定的时间段内，整个系统的数据是只读的。这为我们设计缓存奠定了非常重要的基础。</p>
<p><a href="http://www.tbdata.org/wp-content/uploads/2011/08/architecture.png"><img class="aligncenter size-large wp-image-1791" src="http://www.tbdata.org/wp-content/uploads/2011/08/architecture-1024x559.png" alt="" width="600" height="326" /></a></p>
<dl>
<dd>
<p style="text-align: center">图1 淘宝海量数据产品技术架构</p>
</dd>
</dl>
<p>按照数据的流向来划分，我们把淘宝数据产品的技术架构分为五层（如图1所示），分别是数据源、计算层、存储层、查询层和产品层。位于架构顶端的是我们的数据来源层，这里有淘宝主站的用户、店铺、商品和交易等数据库，还有用户的浏览、搜索等行为日志等。这一系列的数据是数据产品最原始的生命力所在。</p>
<p>在数据源层实时产生的数据，通过淘宝自主研发的数据传输组件DataX、DbSync和Timetunnel准实时地传输到一个有1500个节点的Hadoop集群上，这个集群我们称之为“云梯”，是计算层的主要组成部分。在“云梯”上，我们每天有大约40000个作业对1.5PB的原始数据按照产品需求进行不同的MapReduce计算。这一计算过程通常都能在凌晨两点之前完成。相对于前端产品看到的数据，这里的计算结果很可能是一个处于中间状态的结果，这往往是在数据冗余与前端计算之间做了适当平衡的结果。</p>
<p>不得不提的是，一些对实效性要求很高的数据，例如针对搜索词的统计数据，我们希望能尽快推送到数据产品前端。这种需求再采用“云梯”来计算效率将是比较低的，为此我们做了流式数据的实时计算平台，称之为“银河”。“银河”也是一个分布式系统，它接收来自TimeTunnel的实时消息，在内存中做实时计算，并把计算结果在尽可能短的时间内刷新到NoSQL存储设备中，供前端产品调用。</p>
<p>容易理解，“云梯”或者“银河”并不适合直接向产品提供实时的数据查询服务。这是因为，对于“云梯”来说，它的定位只是做离线计算的，无法支持较高的性能和并发需求；而对于“银河”而言，尽管所有的代码都掌握在我们手中，但要完整地将数据接收、实时计算、存储和查询等功能集成在一个分布式系统中，避免不了分层，最终仍然落到了目前的架构上。</p>
<p>为此，我们针对前端产品设计了专门的存储层。在这一层，我们有基于MySQL的分布式关系型数据库集群MyFOX和基于HBase的NoSQL存储集群Prom，在后面的文字中，我将重点介绍这两个集群的实现原理。除此之外，其他第三方的模块也被我们纳入存储层的范畴。</p>
<p>存储层异构模块的增多，对前端产品的使用带来了挑战。为此，我们设计了通用的数据中间层——glider——来屏蔽这个影响。glider以HTTP协议对外提供restful方式的接口。数据产品可以通过一个唯一的URL获取到它想要的数据。</p>
<p>以上是淘宝海量数据产品在技术架构方面的一个概括性的介绍，接下来我将重点从四个方面阐述数据魔方设计上的特点。</p>
<h2><strong>关系型数据库仍然是王道</strong></h2>
<p>关系型数据库（RDBMS）自20世纪70年代提出以来，在工业生产中得到了广泛的使用。经过三十多年的长足发展，诞生了一批优秀的数据库软件，例如Oracle、MySQL、DB2、Sybase和SQL Server等。</p>
<dl>
<dt><a href="http://www.tbdata.org/wp-content/uploads/2011/08/growth.png"><img class="aligncenter size-large wp-image-1798" src="http://www.tbdata.org/wp-content/uploads/2011/08/growth-1024x426.png" alt="" width="600" height="250" /></a> </dt>
<dd>
<p style="text-align: center">图2 MyFOX中的数据增长曲线</p>
</dd>
</dl>
<p>尽管相对于非关系型数据库而言，关系型数据库在分区容忍性（Tolerance to Network Partitions）方面存在劣势，但由于它强大的语义表达能力以及数据之间的关系表达能力，在数据产品中仍然占据着不可替代的作用。</p>
<p>淘宝数据产品选择MySQL的MyISAM引擎作为底层的数据存储引擎。在此基础上，为了应对海量数据，我们设计了分布式MySQL集群的查询代理层——MyFOX，使得分区对前端应用透明。</p>
<dl>
<dt><a href="http://www.tbdata.org/wp-content/uploads/2011/08/myfox1.png"><img class="aligncenter size-large wp-image-1801" src="http://www.tbdata.org/wp-content/uploads/2011/08/myfox1-1024x577.png" alt="" width="600" height="338" /></a> </dt>
<dd>
<p style="text-align: center">图3 MyFOX的数据查询过程</p>
</dd>
</dl>
<p>目前，存储在MyFOX中的统计结果数据已经达到10TB，占据着数据魔方总数据量的95%以上，并且正在以每天超过6亿的增量增长着（如图2所示）。这些数据被我们近似均匀地分布到20个MySQL节点上，在查询时，经由MyFOX透明地对外服务（如图3所示）。</p>
<dl>
<dt><a href="http://www.tbdata.org/wp-content/uploads/2011/08/myfox2.png"><img class="aligncenter size-large wp-image-1805" src="http://www.tbdata.org/wp-content/uploads/2011/08/myfox2-1024x592.png" alt="" width="600" height="347" /></a> </dt>
<dd>
<p style="text-align: center">图4 MyFOX节点结构</p>
</dd>
</dl>
<p>值得一提的是，在MyFOX现有的20个节点中，并不是所有节点都是“平等”的。一般而言，数据产品的用户更多地只关心“最近几天”的数据，越早的数据，越容易被冷落。为此，出于硬件成本考虑，我们在这20个节点中分出了“热节点”和“冷节点”（如图4所示）。</p>
<p>顾名思义，“热节点”存放最新的、被访问频率较高的数据。对于这部分数据，我们希望能给用户提供尽可能快的查询速度，所以在硬盘方面，我们选择了每分钟15000转的SAS硬盘，按照一个节点两台机器来计算，单位数据的存储成本约为4.5W/TB。相对应地，“冷数据”我们选择了每分钟7500转的SATA硬盘，单碟上能够存放更多的数据，存储成本约为1.6W/TB。</p>
<p>将冷热数据进行分离的另外一个好处是可以有效<span style="text-decoration: line-through">降低</span><span style="color: #ff0000"><strong>提高</strong></span>内存磁盘比。从图4可以看出，“热节点”上单机只有24GB内存，而磁盘装满大约有1.8TB（300 * 12 * 0.5 / 1024），内存磁盘比约为4:300，远远低于MySQL服务器的一个合理值。内存磁盘比过低导致的后果是，总有一天，即使所有内存用完也存不下数据的索引了——这个时候，大量的查询请求都需要从磁盘中读取索引，效率大打折扣。</p>
<h2><strong>NoSQL是SQL的有益补充</strong></h2>
<p>在MyFOX出现之后，一切都看起来那么完美，开发人员甚至不会意识到MyFOX的存在，一条不用任何特殊修饰的SQL语句就可以满足需求。这个状态持续了很长一段时间，直到有一天，我们碰到了传统的关系型数据库无法解决的问题——全属性选择器（如图5所示）。</p>
<dl>
<dt>
<p style="text-align: center"><a href="http://www.tbdata.org/wp-content/uploads/2011/08/prom0.png"><img class="size-full wp-image-1808" src="http://www.tbdata.org/wp-content/uploads/2011/08/prom0.png" alt="" width="600" height="314" /></a></p>
</dt>
<dd>
<p style="text-align: center">图5 全属性选择器</p>
</dd>
</dl>
<p>这是一个非常典型的例子。为了说明问题，我们仍然以关系型数据库的思路来描述。对于笔记本电脑这个类目，用户某一次查询所选择的过滤条件可能包括“笔记本尺寸”、“笔记本定位”、“硬盘容量”等一系列属性（字段），并且在每个可能用在过滤条件的属性上，属性值的分布是极不均匀的。在图5中我们可以看到，笔记本电脑的尺寸这一属性有着10个枚举值，而“蓝牙功能”这个属性值是个布尔值，数据的筛选性非常差。</p>
<p>在用户所选择的过滤条件不确定的情况下，解决全属性问题的思路有两个：一个是穷举所有可能的过滤条件组合，在“云梯”上进行预先计算，存入数据库供查询；另一个是存储原始数据，在用户查询时根据过滤条件筛选出相应的记录进行现场计算。很明显，由于过滤条件的排列组合几乎是无法穷举的，第一种方案在现实中是不可取的；而第二种方案中，原始数据存储在什么地方？如果仍然用关系型数据库，那么你打算怎样为这个表建立索引？</p>
<p>这一系列问题把我们引到了“创建定制化的存储、现场计算并提供查询服务的引擎”的思路上来，这就是Prometheus（如图6所示）。</p>
<dl>
<dt><a href="http://www.tbdata.org/wp-content/uploads/2011/08/prom1.png"><img class="aligncenter size-large wp-image-1812" src="http://www.tbdata.org/wp-content/uploads/2011/08/prom1-1024x543.png" alt="" width="600" height="318" /></a> </dt>
<dd>
<p style="text-align: center">图6 Prom的存储结构</p>
</dd>
</dl>
<p>从图6可以看出，我们选择了HBase作为Prom的底层存储引擎。之所以选择HBase，主要是因为它是建立在HDFS之上的，并且对于MapReduce有良好的编程接口。尽管Prom是一个通用的、解决共性问题的服务框架，但在这里，我们仍然以全属性选择为例，来说明Prom的工作原理。这里的原始数据是前一天在淘宝上的交易明细，在HBase集群中，我们以属性对（属性与属性值的组合）作为row-key进行存储。而row-key对应的值，我们设计了两个column-family，即存放交易ID列表的index字段和原始交易明细的data字段。在存储的时候，我们有意识地让每个字段中的每一个元素都是定长的，这是为了支持通过偏移量快速地找到相应记录，避免复杂的查找算法和磁盘的大量随机读取请求。</p>
<dl>
<dt><a href="http://www.tbdata.org/wp-content/uploads/2011/08/prom2.png"><img class="aligncenter size-large wp-image-1816" src="http://www.tbdata.org/wp-content/uploads/2011/08/prom2-1024x548.png" alt="" width="600" height="321" /></a> </dt>
<dd>
<p style="text-align: center">图7 Prom查询过程</p>
</dd>
</dl>
<p>图7用一个典型的例子描述的Prom在提供查询服务时的工作原理，限于篇幅，这里不做详细描述。值得一提的是，Prom支持的计算并不仅限于求和SUM运算，统计意义上的常用计算都是支持的。在现场计算方面，我们对Hbase进行了扩展，Prom要求每个节点返回的数据是已经经过“本地计算”的局部最优解，最终的全局最优解只是各个节点返回的局部最优解的一个简单汇总。很显然，这样的设计思路是要充分利用各个节点的并行计算能力，并且避免大量明细数据的网络传输开销。</p>
<h2><strong>用中间层隔离前后端</strong></h2>
<p>上文提到过，MyFOX和Prom为数据产品的不同需求提供了数据存储和底层查询的解决方案，但随之而来的问题是，各种异构的存储模块给前端产品的使用带来了很大的挑战。并且，前端产品的一个请求所需要的数据往往不可能只从一个模块获取。</p>
<p>举个例子，我们要在数据魔方中看昨天做热销的商品，首先从MyFOX中拿到一个热销排行榜的数据，但这里的“商品”只是一个ID，并没有ID所对应的商品描述、图片等数据。这个时候我们要从淘宝主站提供的接口中去获取这些数据，然后一一对应到热销排行榜中，最终呈现给用户。</p>
<dl>
<dt><a href="http://www.tbdata.org/wp-content/uploads/2011/08/glider.png"><img class="aligncenter size-large wp-image-1818" src="http://www.tbdata.org/wp-content/uploads/2011/08/glider-1024x582.png" alt="" width="600" height="341" /></a> </dt>
<dd>
<p style="text-align: center">图8 glider的技术架构</p>
</dd>
</dl>
<p>有经验的读者一定可以想到，从本质上来讲，这就是广义上的异构“表”之间的JOIN操作。那么，谁来负责这个事情呢？很容易想到，在存储层与前端产品之间增加一个中间层，它负责各个异构“表”之间的数据JOIN和UNION等计算，并且隔离前端产品和后端存储，提供统一的数据查询服务。这个中间层就是glider（如图8所示）。</p>
<h2><strong>缓存是系统化的工程</strong></h2>
<p>除了起到隔离前后端以及异构“表”之间的数据整合的作用之外，glider的另外一个不容忽视的作用便是缓存管理。上文提到过，在特定的时间段内，我们认为数据产品中的数据是只读的，这是利用缓存来提高性能的理论基础。</p>
<p>在图8中我们看到，glider中存在两层缓存，分别是基于各个异构“表”（datasource）的二级缓存和整合之后基于独立请求的一级缓存。除此之外，各个异构“表”内部可能还存在自己的缓存机制。细心的读者一定注意到了图3中MyFOX的缓存设计，我们没有选择对汇总计算后的最终结果进行缓存，而是针对每个分片进行缓存，其目的在于提高缓存的命中率，并且降低数据的冗余度。</p>
<p>大量使用缓存的最大问题就是数据一致性问题。如何保证底层数据的变化在尽可能短的时间内体现给最终用户呢？这一定是一个系统化的工程，尤其对于分层较多的系统来说。</p>
<dl>
<dt><a href="http://www.tbdata.org/wp-content/uploads/2011/08/cache.png"><img class="aligncenter size-large wp-image-1820" src="http://www.tbdata.org/wp-content/uploads/2011/08/cache-1024x589.png" alt="" width="600" height="345" /></a></dt>
<dd>
<p style="text-align: center">图9 缓存控制体系</p>
</dd>
</dl>
<p>图9向我们展示了数据魔方在缓存控制方面的设计思路。用户的请求中一定是带了缓存控制的“命令”的，这包括URL中的query string，和HTTP头中的“If-None-Match”信息。并且，这个缓存控制“命令”一定会经过层层传递，最终传递到底层存储的异构“表”模块。各异构“表”除了返回各自的数据之外，还会返回各自的数据缓存过期时间（ttl），而glider最终输出的过期时间是各个异构“表”过期时间的最小值。这一过期时间也一定是从底层存储层层传递，最终通过HTTP头返回给用户浏览器的。</p>
<p>缓存系统不得不考虑的另一个问题是缓存穿透与失效时的雪崩效应。缓存穿透是指查询一个一定不存在的数据，由于缓存是不命中时被动写的，并且出于容错考虑，如果从存储层查不到数据则不写入缓存，这将导致这个<span style="color: #ff0000">不</span>存在的数据每次请求都要到存储层去查询，失去了缓存的意义。</p>
<p>有很多种方法可以有效地解决缓存穿透问题，最常见的则是采用布隆过滤器，将所有可能存在的数据哈希到一个足够大的bitmap中，一个一定不存在的数据会被这个bitmap拦截掉，从而避免了对底层存储系统的查询压力。在数据魔方里，我们采用了一个更为简单粗暴的方法，如果一个查询返回的数据为空（不管是数据不存在，还是系统故障），我们仍然把这个空结果进行缓存，但它的过期时间会很短，最长不超过五分钟。</p>
<p>缓存失效时的雪崩效应对底层系统的冲击非常可怕。遗憾的是，这个问题目前并没有很完美的解决方案。大多数系统设计者考虑用加锁或者队列的方式保证缓存的单线程（进程）写，从而避免失效时大量的并发请求落到底层存储系统上。在数据魔方中，我们设计的缓存过期机制理论上能够将各个客户端的数据失效时间均匀地分布在时间轴上，一定程度上能够避免缓存同时失效带来的雪崩效应。</p>
<h2><strong>结束语</strong></h2>
<p>正是基于本文所描述的架构特点，数据魔方目前已经能够提供压缩前80TB的数据存储空间，数据中间层glider支持每天4000万的查询请求，平均响应时间在28毫秒（6月1日数据），足以满足未来一段时间内的业务增长需求。</p>
<p>尽管如此，整个系统中仍然存在很多不完善的地方。一个典型的例子莫过于各个分层之间使用短连接模式的HTTP协议进行通信。这样的策略直接导致在流量高峰期单机的TCP连接数非常高。所以说，一个良好的架构固然能够在很大程度上降低开发和维护的成本，但它自身一定是随着数据量和流量的变化而不断变化的。我相信，过不了几年，淘宝数据产品的技术架构一定会是另外的样子。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tbdata.org/archives/1789/feed</wfw:commentRss>
		<slash:comments>29</slash:comments>
		</item>
		<item>
		<title>HS4J Kit 介绍</title>
		<link>http://www.tbdata.org/archives/1780</link>
		<comments>http://www.tbdata.org/archives/1780#comments</comments>
		<pubDate>Wed, 08 Jun 2011 01:40:50 +0000</pubDate>
		<dc:creator>聚石</dc:creator>
				<category><![CDATA[java]]></category>
		<category><![CDATA[高性能服务器]]></category>
		<category><![CDATA[hs4j]]></category>
		<category><![CDATA[orm]]></category>

		<guid isPermaLink="false">http://www.tbdata.org/?p=1780</guid>
		<description><![CDATA[HS4J Kit是HS4J的贡献项目, 它的灵感来自ORM(对象关系映射), 通过使用Annotation(注解)对领域对象进行声明, 即可实现对HS4J的调用, 省去编写和维护较为底层的模板式代码. 举例而言, 在一个管理用户信息的业务场景中通常都会有: 01 class User { 02 Long id; 03 String name; 04 Integer age; 05 } 06 07 interface UserRepository { 08 void add(User user); 09 void delete(User user); 10 User findBy(long id); 11 void update(User user); 12 } 01 CREATE TABLE `user_t` ( 02 `id` int(10) unsigned<div class="more"><a href="http://www.tbdata.org/archives/1780" class="more">阅读全文 &#62;</a></div>]]></description>
			<content:encoded><![CDATA[<p>HS4J Kit是<a title="HS4J" href="http://rdc.taobao.com/team/jm/archives/545" target="_blank">HS4J</a>的<a rel="nofollow" href="https://github.com/killme2008/hs4j/tree/master/contributes">贡献项目</a>, 它的灵感来自ORM(对象关系映射), 通过使用Annotation(注解)对领域对象进行声明, 即可实现对HS4J的调用, 省去编写和维护较为底层的模板式代码.</p>
<p>举例而言, 在一个管理用户信息的业务场景中通常都会有:</p>
<pre style="color: #f8f8f8;background-color: #141414;font-size: 10pt;font-family: '??'"><span style="color: #777777">01 </span><span style="color: #a56a30;font-weight: bold">class</span> User <span style="color: #f8f8f8">{</span>
<span style="color: #777777">02 </span>  Long id<span style="color: #f8f8f8">;</span>
<span style="color: #777777">03 </span>  String name<span style="color: #f8f8f8">;</span>
<span style="color: #777777">04 </span>  Integer age<span style="color: #f8f8f8">;</span>
<span style="color: #777777">05 </span><span style="color: #f8f8f8">}</span>
<span style="color: #777777">06 </span>
<span style="color: #777777">07 </span><span style="color: #a56a30;font-weight: bold">interface</span> UserRepository <span style="color: #f8f8f8">{</span>
<span style="color: #777777">08 </span>  <span style="color: #c7ca87">void</span> <span style="color: #3b84cc">add</span><span style="color: #f8f8f8">(</span>User user<span style="color: #f8f8f8">);</span>
<span style="color: #777777">09 </span>  <span style="color: #c7ca87">void</span> <span style="color: #3b84cc">delete</span><span style="color: #f8f8f8">(</span>User user<span style="color: #f8f8f8">);</span>
<span style="color: #777777">10 </span>  User <span style="color: #3b84cc">findBy</span><span style="color: #f8f8f8">(</span><span style="color: #c7ca87">long</span> id<span style="color: #f8f8f8">);</span>
<span style="color: #777777">11 </span>  <span style="color: #c7ca87">void</span> <span style="color: #3b84cc">update</span><span style="color: #f8f8f8">(</span>User user<span style="color: #f8f8f8">);</span>
<span style="color: #777777">12 </span><span style="color: #f8f8f8">}</span></pre>
<pre><span style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;line-height: 19px">
</span></pre>
<pre><span style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;line-height: 19px">
</span></pre>
<pre style="color: #f8f8f8;background-color: #141414;font-size: 10pt;font-family: '??'"><span style="color: #777777">01 </span>CREATE TABLE `user_t` <span style="color: #f8f8f8">(</span>
<span style="color: #777777">02 </span>  `<span style="color: #c7ca87">id</span>` <span style="color: #c7ca87">int</span><span style="color: #f8f8f8">(</span><span style="color: #face43">10</span><span style="color: #f8f8f8">)</span> <span style="color: #c7ca87">unsigned</span> NOT NULL<span style="color: #f8f8f8">,</span>
<span style="color: #777777">03 </span>  `name` varchar<span style="color: #f8f8f8">(</span><span style="color: #face43">50</span><span style="color: #f8f8f8">)</span> <span style="color: #c7ca87">DEFAULT</span> NULL<span style="color: #f8f8f8">,</span>
<span style="color: #777777">04 </span>  `age` <span style="color: #c7ca87">int</span><span style="color: #f8f8f8">(</span><span style="color: #face43">3</span><span style="color: #f8f8f8">)</span> <span style="color: #c7ca87">DEFAULT</span> NULL<span style="color: #f8f8f8">,</span>
<span style="color: #777777">05 </span>  PRIMARY KEY <span style="color: #f8f8f8">(</span>`<span style="color: #c7ca87">id</span>`<span style="color: #f8f8f8">),</span>
<span style="color: #777777">06 </span>  KEY `NAME` <span style="color: #f8f8f8">(</span>`name`<span style="color: #f8f8f8">),</span>
<span style="color: #777777">07 </span>  KEY `AGE` <span style="color: #f8f8f8">(</span>`age`<span style="color: #f8f8f8">)</span>
<span style="color: #777777">08 </span><span style="color: #f8f8f8">)</span> ENGINE<span style="color: #f8f8f8">=</span>InnoDB</pre>
<pre><span style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;line-height: 19px">若直接使用HS4J提供的API, 代码会如HS4J <a href="http://code.google.com/p/hs4j/wiki/GettingStarted">Getting Started</a>的示例一样, 模式化的重复. 对象结构简单还不是问题, 一旦对象字段十多个, 而且还老是要变化, 可想而知噩梦才刚刚开始.</span></pre>
<p>使用<tt>Kit</tt>提供的注解来声明的话, 一切<tt>HS4J</tt>的调用操作全部将透明化:</p>
<pre style="color: #f8f8f8;background-color: #141414;font-size: 10pt;font-family: '??'"><span style="color: #777777">01 </span><span style="color: #30a630">@Repository</span><span style="color: #f8f8f8">(</span>database<span style="color: #f8f8f8">=</span><span style="color: #d9ff77">"test"</span><span style="color: #f8f8f8">,</span> table<span style="color: #f8f8f8">=</span><span style="color: #d9ff77">"user_t"</span><span style="color: #f8f8f8">)</span>
<span style="color: #777777">02 </span><span style="color: #a56a30;font-weight: bold">interface</span> UserRepository <span style="color: #f8f8f8">{</span>
<span style="color: #777777">03 </span>  <span style="color: #30a630">@HandlerSocket</span><span style="color: #f8f8f8">(</span>INSERT<span style="color: #f8f8f8">)</span>
<span style="color: #777777">04 </span>  <span style="color: #30a630">@EntityClass</span><span style="color: #f8f8f8">(</span>User<span style="color: #f8f8f8">.</span><span style="color: #a56a30;font-weight: bold">class</span><span style="color: #f8f8f8">)</span>
<span style="color: #777777">05 </span>  <span style="color: #c7ca87">void</span> <span style="color: #3b84cc">add</span><span style="color: #f8f8f8">(</span>User user<span style="color: #f8f8f8">);</span>
<span style="color: #777777">06 </span>  <span style="color: #f8f8f8">...</span>
<span style="color: #777777">07 </span><span style="color: #f8f8f8">}</span></pre>
<pre><span style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;line-height: 19px">
</span></pre>
<pre><span style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;line-height: 19px">接下来要写的代码就是:</span></pre>
<pre style="color: #f8f8f8;background-color: #141414;font-size: 10pt;font-family: '??'"><span style="color: #777777">01 </span>HSClient hsClient <span style="color: #f8f8f8">=</span> <span style="color: #a56a30;font-weight: bold">new</span> <span style="color: #3b84cc">HSClientImpl</span><span style="color: #f8f8f8">(</span><span style="color: #a56a30;font-weight: bold">new</span> <span style="color: #3b84cc">InetSocketAddress</span><span style="color: #f8f8f8">(</span><span style="color: #face43">9999</span><span style="color: #f8f8f8">));</span>
<span style="color: #777777">02 </span>ProxyFactory proxyFactory <span style="color: #f8f8f8">=</span> <span style="color: #a56a30;font-weight: bold">new</span> <span style="color: #3b84cc">HandlerSocketProxyFactory</span><span style="color: #f8f8f8">(</span>hsClient<span style="color: #f8f8f8">);</span>
<span style="color: #777777">03 </span>UserRepository proxy <span style="color: #f8f8f8">=</span> proxyFactory<span style="color: #f8f8f8">.</span><span style="color: #3b84cc">newProxyOf</span><span style="color: #f8f8f8">(</span>UserRepository<span style="color: #f8f8f8">.</span><span style="color: #a56a30;font-weight: bold">class</span><span style="color: #f8f8f8">);</span>
<span style="color: #777777">04 </span>
<span style="color: #777777">05 </span>proxy<span style="color: #f8f8f8">.</span><span style="color: #3b84cc">add</span><span style="color: #f8f8f8">(</span><span style="color: #a56a30;font-weight: bold">new</span> <span style="color: #3b84cc">User</span><span style="color: #f8f8f8">(</span><span style="color: #face43">1L</span><span style="color: #f8f8f8">,</span> <span style="color: #d9ff77">"killme2008"</span><span style="color: #f8f8f8">,</span> <span style="color: #face43">18</span><span style="color: #f8f8f8">));</span>
<span style="color: #777777">06 </span>
<span style="color: #777777">07 </span>hsClient<span style="color: #f8f8f8">.</span><span style="color: #3b84cc">shutdown</span><span style="color: #f8f8f8">();</span></pre>
<pre><span style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;line-height: 19px">抛开必要的初始化和销毁的代码, 除了一行必须的业务代码没有任何多余的.</span></pre>
<p>这里没有配置表的列(<tt>Columns</tt>), 而是通过<tt>EntityClass</tt>来告诉<tt>Kit</tt>默认使用<tt>User</tt>的所有字段映射为表的所有列, 前提是<tt>User</tt>的字段名与表列名一致.</p>
<p>若列名与字段名无法一致的时候, 可以<tt>ColumnName</tt>来声明:</p>
<pre style="color: #f8f8f8;background-color: #141414;font-size: 10pt;font-family: '??'"><span style="color: #777777">01 </span><span style="color: #a56a30;font-weight: bold">class</span> User <span style="color: #f8f8f8">{</span>
<span style="color: #777777">02 </span>  <span style="color: #30a630">@ColumnName</span><span style="color: #f8f8f8">(</span><span style="color: #d9ff77">"uid"</span><span style="color: #f8f8f8">)</span>
<span style="color: #777777">03 </span>  Long id<span style="color: #f8f8f8">;</span>
<span style="color: #777777">04 </span>  <span style="color: #f8f8f8">...</span>
<span style="color: #777777">05 </span><span style="color: #f8f8f8">}</span></pre>
<pre><span style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;line-height: 19px">查询方法怎么声明呢?</span></pre>
<pre style="color: #f8f8f8;background-color: #141414;font-size: 10pt;font-family: '??'"><span style="color: #777777">01 </span><span style="color: #30a630">@HandlerSocket</span><span style="color: #f8f8f8">(</span>FIND<span style="color: #f8f8f8">)</span>
<span style="color: #777777">02 </span><span style="color: #30a630">@EntityClass</span><span style="color: #f8f8f8">(</span>User<span style="color: #f8f8f8">.</span><span style="color: #a56a30;font-weight: bold">class</span><span style="color: #f8f8f8">)</span>
<span style="color: #777777">03 </span>ResultIterator<span style="color: #f8f8f8">&lt;</span>User<span style="color: #f8f8f8">&gt;</span> <span style="color: #3b84cc">findBy</span><span style="color: #f8f8f8">(</span><span style="color: #30a630">@Operator</span><span style="color: #f8f8f8">(</span>EQ<span style="color: #f8f8f8">)</span> <span style="color: #c7ca87">long</span> id<span style="color: #f8f8f8">);</span></pre>
<pre><span style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;line-height: 19px">业务代码会是:</span></pre>
<pre style="color: #f8f8f8;background-color: #141414;font-size: 10pt;font-family: '??'"><span style="color: #777777">01 </span>ResultIterator<span style="color: #f8f8f8">&lt;</span>User<span style="color: #f8f8f8">&gt;</span> itr <span style="color: #f8f8f8">=</span> proxy<span style="color: #f8f8f8">.</span><span style="color: #3b84cc">findBy</span><span style="color: #f8f8f8">(</span><span style="color: #face43">1L</span><span style="color: #f8f8f8">);</span>
<span style="color: #777777">02 </span><span style="color: #a56a30;font-weight: bold">while</span> <span style="color: #f8f8f8">(</span>itr<span style="color: #f8f8f8">.</span><span style="color: #3b84cc">next</span><span style="color: #f8f8f8">()) {</span>
<span style="color: #777777">03 </span>  User user <span style="color: #f8f8f8">=</span> itr<span style="color: #f8f8f8">.</span><span style="color: #3b84cc">get</span><span style="color: #f8f8f8">();</span>
<span style="color: #777777">04 </span>  <span style="color: #f8f8f8">...</span>
<span style="color: #777777">05 </span><span style="color: #f8f8f8">}</span></pre>
<pre><span style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;line-height: 19px">缺省情况下, 使用表的主键作为<tt>Index</tt>的<tt>key</tt>. 与<tt>add</tt>不同的是:</span></pre>
<ol>
<li>使用<tt>ResultIterator&lt;User&gt;</tt>作为返回值类型, 其实目的是为了简单的封装<tt>ResultSet</tt>;</li>
<li>使用<tt>Operator</tt>对参数<tt>id</tt>进行了注解, 告诉<tt>HS4J</tt>对<tt>id</tt>使用<tt>EQ</tt>操作.</li>
</ol>
<p>查询场景通常会有分页的需求, 这就会使用到<tt>Offset</tt>和<tt>Limit</tt>两个注解:</p>
<pre style="color: #f8f8f8;background-color: #141414;font-size: 10pt;font-family: '??'"><span style="color: #777777">01 </span><span style="color: #30a630">@HandlerSocket</span><span style="color: #f8f8f8">(</span>FIND<span style="color: #f8f8f8">)</span>
<span style="color: #777777">02 </span><span style="color: #30a630">@Index</span><span style="color: #f8f8f8">(</span><span style="color: #d9ff77">"AGE"</span><span style="color: #f8f8f8">)</span>
<span style="color: #777777">03 </span><span style="color: #30a630">@EntityClass</span><span style="color: #f8f8f8">(</span>User<span style="color: #f8f8f8">.</span><span style="color: #a56a30;font-weight: bold">class</span><span style="color: #f8f8f8">)</span>
<span style="color: #777777">04 </span>ResultIterator<span style="color: #f8f8f8">&lt;</span>User<span style="color: #f8f8f8">&gt;</span> <span style="color: #3b84cc">findUserAgeGreaterThan</span><span style="color: #f8f8f8">(</span><span style="color: #30a630">@Operator</span><span style="color: #f8f8f8">(</span>GT<span style="color: #f8f8f8">)</span> <span style="color: #c7ca87">int</span> age<span style="color: #f8f8f8">,</span> <span style="color: #30a630">@Offset</span> <span style="color: #c7ca87">int</span> offset<span style="color: #f8f8f8">,</span> <span style="color: #30a630">@Limit</span> <span style="color: #c7ca87">int</span> limit<span style="color: #f8f8f8">);</span></pre>
<pre><span style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;line-height: 19px">上面都是全字段写入或读取, 在部分修改的场景中如何注解呢?</span></pre>
<pre style="color: #f8f8f8;background-color: #141414;font-size: 10pt;font-family: '??'"><span style="color: #777777">01 </span><span style="color: #30a630">@HandlerSocket</span><span style="color: #f8f8f8">(</span>UPDATE<span style="color: #f8f8f8">)</span>
<span style="color: #777777">02 </span><span style="color: #30a630">@Columns</span><span style="color: #f8f8f8">(</span><span style="color: #d9ff77">"age"</span><span style="color: #f8f8f8">)</span>
<span style="color: #777777">03 </span><span style="color: #30a630">@Index</span><span style="color: #f8f8f8">(</span><span style="color: #d9ff77">"NAME"</span><span style="color: #f8f8f8">)</span>
<span style="color: #777777">04 </span><span style="color: #c7ca87">void</span> <span style="color: #3b84cc">updateUserAge</span><span style="color: #f8f8f8">(</span><span style="color: #30a630">@Operator</span><span style="color: #f8f8f8">(</span>EQ<span style="color: #f8f8f8">)</span> String name<span style="color: #f8f8f8">,</span> <span style="color: #c7ca87">int</span> age<span style="color: #f8f8f8">);</span></pre>
<pre><span style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;line-height: 19px">通过<tt>Colunms</tt>告诉<tt>HS4J</tt>打开的<tt>Index</tt>仅对<tt>age</tt>进行操作, 并以<tt>name</tt>为<tt>key</tt>, 是用<tt>age</tt>参数作为<tt>value</tt>.</span></pre>
<p>修改多值的情况下, 请保证参数声明的顺序和<tt>Columns</tt>定义的顺序一致:</p>
<pre style="color: #f8f8f8;background-color: #141414;font-size: 10pt;font-family: '??'"><span style="color: #777777">01 </span><span style="color: #30a630">@HandlerSocket</span><span style="color: #f8f8f8">(</span>UPDATE<span style="color: #f8f8f8">)</span>
<span style="color: #777777">02 </span><span style="color: #30a630">@Columns</span><span style="color: #f8f8f8">({</span><span style="color: #d9ff77">"name"</span><span style="color: #f8f8f8">,</span> <span style="color: #d9ff77">"age"</span><span style="color: #f8f8f8">})</span>
<span style="color: #777777">03 </span><span style="color: #c7ca87">void</span> <span style="color: #3b84cc">updateUserNameAndAge</span><span style="color: #f8f8f8">(</span><span style="color: #30a630">@Operator</span><span style="color: #f8f8f8">(</span>EQ<span style="color: #f8f8f8">)</span><span style="color: #c7ca87">long</span> id<span style="color: #f8f8f8">,</span> String name<span style="color: #f8f8f8">,</span> <span style="color: #c7ca87">int</span> age<span style="color: #f8f8f8">);</span></pre>
<pre><span style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;line-height: 19px">主要的用法介绍到此, 还其他的用法就不再累述了, 请参看<a rel="nofollow" href="https://github.com/killme2008/hs4j/tree/master/contributes/hs4j-kit/src/test/java/com/github/zhongl/hs4j/kit">单元测试代码</a>举一反三吧!</span></pre>
]]></content:encoded>
			<wfw:commentRss>http://www.tbdata.org/archives/1780/feed</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>jvm垃圾回收</title>
		<link>http://www.tbdata.org/archives/1773</link>
		<comments>http://www.tbdata.org/archives/1773#comments</comments>
		<pubDate>Thu, 12 May 2011 03:45:00 +0000</pubDate>
		<dc:creator>boqian</dc:creator>
				<category><![CDATA[java]]></category>

		<guid isPermaLink="false">http://www.tbdata.org/?p=1773</guid>
		<description><![CDATA[在jvm中堆空间划分为三个代：年轻代（Young Generation）、年老代（Old Generation）和永久代（Permanent Generation）。年轻代和年老代是存储动态产生的对象。永久带主要是存储的是java的类信息，包括解析得到的方法、属性、字段等等。永久带基本不参与垃圾回收。我们这里讨论的垃圾回收主要是针对年轻代和年老代。具体如下图。 年轻代又分成3个部分，一个eden区和两个相同的survior区。刚开始创建的对象都是放置在eden区的。分成这样3个部分，主要是为了生命周期短的对象尽量留在年轻带。当eden区申请不到空间的时候，进行minorGC，把存活的对象拷贝到survior。年老代主要存放生命周期比较长的对象，比如缓存对象。具体jvm内存回收过程描述如下（可以结合上图）： 1、对象在Eden区完成内存分配 2、当Eden区满了，再创建对象，会因为申请不到空间，触发minorGC，进行young(eden+1survivor)区的垃圾回收 3、minorGC时，Eden不能被回收的对象被放入到空的survivor（Eden肯定会被清空），另一个survivor里不能被GC回收的对象也会被放入这个survivor，始终保证一个survivor是空的 4、当做第3步的时候，如果发现survivor满了，则这些对象被copy到old区，或者survivor并没有满，但是有些对象已经足够Old，也被放入Old区 XX:MaxTenuringThreshold 5、当Old区被放满的之后，进行fullGC 在知道垃圾回收机制以后，大家可以在对jvm中堆的各个参数进行优化设置，来提高性能。]]></description>
			<content:encoded><![CDATA[<p>在jvm中堆空间划分为三个代：年轻代（Young Generation）、年老代（Old Generation）和永久代（Permanent Generation）。年轻代和年老代是存储动态产生的对象。永久带主要是存储的是java的类信息，包括解析得到的方法、属性、字段等等。永久带基本不参与垃圾回收。我们这里讨论的垃圾回收主要是针对年轻代和年老代。具体如下图。</p>
<p><a href="http://www.tbdata.org/wp-content/uploads/2011/05/1.bmp"><img class="alignnone size-full wp-image-1774" src="http://www.tbdata.org/wp-content/uploads/2011/05/1.bmp" alt="" /></a><img src="/DOCUME%7E1/boqian.zwq/LOCALS%7E1/Temp/moz-screenshot.png" alt="" /></p>
<p>年轻代又分成3个部分，一个eden区和两个相同的survior区。刚开始创建的对象都是放置在eden区的。分成这样3个部分，主要是为了生命周期短的对象尽量留在年轻带。当eden区申请不到空间的时候，进行minorGC，把存活的对象拷贝到survior。年老代主要存放生命周期比较长的对象，比如缓存对象。具体jvm内存回收过程描述如下（可以结合上图）：</p>
<p>1、对象在Eden区完成内存分配<br />
2、当Eden区满了，再创建对象，会因为申请不到空间，触发minorGC，进行young(eden+1survivor)区的垃圾回收<br />
3、minorGC时，Eden不能被回收的对象被放入到空的survivor（Eden肯定会被清空），另一个survivor里不能被GC回收的对象也会被放入这个survivor，始终保证一个survivor是空的<br />
4、当做第3步的时候，如果发现survivor满了，则这些对象被copy到old区，或者survivor并没有满，但是有些对象已经足够Old，也被放入Old区 XX:MaxTenuringThreshold<br />
5、当Old区被放满的之后，进行fullGC</p>
<p>在知道垃圾回收机制以后，大家可以在对jvm中堆的各个参数进行优化设置，来提高性能。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tbdata.org/archives/1773/feed</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>Hive-如何基于分区优化</title>
		<link>http://www.tbdata.org/archives/1765</link>
		<comments>http://www.tbdata.org/archives/1765#comments</comments>
		<pubDate>Thu, 05 May 2011 11:21:07 +0000</pubDate>
		<dc:creator>hongmen</dc:creator>
				<category><![CDATA[所有]]></category>

		<guid isPermaLink="false">http://www.tbdata.org/?p=1765</guid>
		<description><![CDATA[Hive优化 &#8211; 如何基于分区优化 最近一直做系统优化，但从建模的角度今天有个小优化，原理比较简单，效果可能不是很大，但很有意思。 这种优化的好处是不用改变sql代码，对用户是透明的。 所以分享下。 - 由于hive在文件基础上，而会全部扫一个分区里面的内容。 hive表的概念是基于hadoop的文件系统hdfs，表其实是分布式文件里面的一个文件目录。 再加上没有索引，如果要取的表里面的某些字段就必须全部扫描该表对应的文件目录 - 如：建表way1： create table if not exists t_hm_0501_test_01 ( uid string, nick string ) PARTITIONED BY (pt STRING , bc_seller string ) row format delimited fields terminated by &#8216;\t&#8217; lines terminated by &#8216;\n&#8217; stored as textfile; - 在hadoop的hdfs中其实是这样的目录 &#8211; t_hm_0501_test_01表对应hdfs里的如下文件目录。 /t_hm_0501_test_01 &#8212;- 一级分区 /t_hm_0501_test_01/pt=20110501000000 /t_hm_0501_test_01/pt=20110502000000<div class="more"><a href="http://www.tbdata.org/archives/1765" class="more">阅读全文 &#62;</a></div>]]></description>
			<content:encoded><![CDATA[<p><strong>Hive</strong><strong>优化</strong><strong> &#8211; </strong><strong>如何基于分区优化</strong><strong> </strong></p>
<p>最近一直做系统优化，但从建模的角度今天有个小优化，原理比较简单，效果可能不是很大，但很有意思。</p>
<p>这种优化的好处是不用改变sql代码，对用户是透明的。</p>
<p>所以分享下。</p>
<p>-</p>
<p>由于hive在文件基础上，而会全部扫一个分区里面的内容。</p>
<p>hive表的概念是基于hadoop的文件系统hdfs，表其实是分布式文件里面的一个文件目录。</p>
<p>再加上没有索引，如果要取的表里面的某些字段就必须全部扫描该表对应的文件目录</p>
<p>-</p>
<p>如：建表way1：</p>
<p>create table if not exists t_hm_0501_test_01</p>
<p>(</p>
<p><span style="text-decoration: underline">uid</span> string,</p>
<p>nick string</p>
<p>)</p>
<p>PARTITIONED BY (<span style="text-decoration: underline">pt</span> STRING , bc_seller string )</p>
<p>row format delimited</p>
<p>fields terminated by &#8216;\t&#8217;</p>
<p>lines terminated by &#8216;\n&#8217;</p>
<p>stored as <span style="text-decoration: underline">textfile</span>;</p>
<p>-</p>
<p>在hadoop的hdfs中其实是这样的目录</p>
<p>&#8211;</p>
<p>t_hm_0501_test_01表对应hdfs里的如下文件目录。</p>
<p>/t_hm_0501_test_01</p>
<p>&#8212;-</p>
<p>一级分区</p>
<p>/t_hm_0501_test_01/pt=20110501000000</p>
<p>/t_hm_0501_test_01/pt=20110502000000</p>
<p>&#8211;</p>
<p>二级分区</p>
<p>/t_hm_0501_test_01/pt=20110501000000/bc_seller=0</p>
<p>/t_hm_0501_test_01/pt=20110501000000/bc_seller=1</p>
<p>最后那个分区目录后面放的是真正的数据文件</p>
<p>&#8212;</p>
<p>如果有语句 select ,.. from t_hm_0501_test_01  where pt’=20110501000000’ and bc_seller=0</p>
<p>Hadoop只读取/t_hm_0501_test_01/pt=20110501000000/bc_seller=0 下面的数据，不用处理bc_seller = 1 的数据。</p>
<p>&#8211;</p>
<p>如果这个表where条件中的值不是分区字段，则会全部扫里面的内容。</p>
<p>如果我们把部分常用字段枚举成分区字段，则会减少扫的内容（条数）。</p>
<p>！！</p>
<p>Way2：</p>
<p>如果这样建表：</p>
<p>create table if not exists t_hm_0501_test_01</p>
<p>(</p>
<p><span style="text-decoration: underline">uid</span> string,</p>
<p>nick string</p>
<p>)</p>
<p>PARTITIONED BY (<span style="text-decoration: underline">pt</span> STRING )</p>
<p>row format delimited</p>
<p>fields terminated by &#8216;\t&#8217;</p>
<p>lines terminated by &#8216;\n&#8217;</p>
<p>stored as <span style="text-decoration: underline">textfile</span>;</p>
<p>-</p>
<p>一级分区</p>
<p>/t_hm_0501_test_01/pt=20110501000000</p>
<p>/t_hm_0501_test_01/pt=20110502000000</p>
<p>同样的sql 语句：</p>
<p>select ,.. from t_hm_0501_test_01  where pt’=20110501000000’ and bc_seller=0</p>
<p>-</p>
<p>其实是扫的是：</p>
<p>/t_hm_0501_test_01/pt=20110501000000 所有东西，包括下面bc_seller=1的数据，增加了脏数据。</p>
<p>浪费了一些map 及其他资源。</p>
<p>-</p>
<p>这其实是一个树形结构，如果做得好就是个tree算法，可以最少的读取文件。</p>
<p>而且这种优化的好处是不用改变sql代码，对用户是透明的。</p>
<p>那么如何设定partition 及如何确定其分区值</p>
<p>就成了关键。</p>
<ol>
<li>还可以凭借一些业务经验去确定，更科学的是通过系统自动的解决该问题。</li>
</ol>
<p>这里通过对hive sql 元数据解析，写一下算法进行分析，得到更好的提出更优的分区</p>
<p>具体如何选择需要，需要改字段满足一些特性。</p>
<ol>
<li>比较容易枚举</li>
<li>字段指相对固定</li>
<li>频率最高的过滤字段</li>
</ol>
<p>&#8212;&#8212;&#8212;&#8212;</p>
<p>如下例子：</p>
<p>如果你在数据分析的过程中，</p>
<p>你的用户表操作的性别过滤很多，可以以性别作为分区。</p>
<p>&#8212;&#8212;&#8212;-</p>
<p>如果你经常分析成交数据</p>
<p>大量分析计算30天的交易成交，其次是60天的成交。</p>
<p>你也可以时段进行分区，这样可以节省你很多成本。</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;</p>
<p>微博：<a href="http://www.weibo.com/xiaodongdata">www.weibo.com/xiaodongdata</a></p>
<p>QQ ： 448683559</p>
]]></content:encoded>
			<wfw:commentRss>http://www.tbdata.org/archives/1765/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Hive源码解析-之-语法解析器</title>
		<link>http://www.tbdata.org/archives/1753</link>
		<comments>http://www.tbdata.org/archives/1753#comments</comments>
		<pubDate>Tue, 03 May 2011 10:40:07 +0000</pubDate>
		<dc:creator>hongmen</dc:creator>
				<category><![CDATA[所有]]></category>

		<guid isPermaLink="false">http://www.tbdata.org/?p=1753</guid>
		<description><![CDATA[hive 源码 解析]]></description>
			<content:encoded><![CDATA[<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal"><span lang="EN-US">Hive</span><span style="font-family: 宋体;">语法解析器是根据</span><span lang="EN-US">&lt;上次分享的 </span><span style="font-family: 宋体;"><a href="http://www.tbdata.org/archives/1727">词法分析</a></span><span lang="EN-US"> &gt; </span><span style="font-family: 宋体;">生成的语法树为基础，进行语法解析。根据语法</span><span lang="EN-US">token</span><span style="font-family: 宋体;">的情况实现了五个具体的语法解析器。</span></p>
<p class="MsoNormal"><span style="font-family: 宋体;">+</span></p>
<p class="MsoListParagraph" style="margin-left: 18.0pt; text-indent: 0cm;"><span style="font-family: 宋体;"><span style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;"><span style="font-family: 宋体;">在你生成语法器的时候，</span><span lang="EN-US"> SemanticAnalyzerFactory</span><span style="font-family: 宋体;">分别针对不同的情况生成对应的某个语法器,如下</span></span></span></p>
<p class="MsoListParagraph" style="margin-left: 18.0pt; text-indent: 0cm;"><span style="font-family: 宋体;"><span style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;"><span style="font-family: 宋体;"><br />
</span></span></span></p>
<p class="MsoListParagraph" style="margin-left: 18.0pt; text-indent: 0cm;"><span lang="EN-US"><!--[endif]--></span></p>
<p class="MsoListParagraph" style="margin-left: 18.0pt; text-indent: 0cm;"><span lang="EN-US">SemanticAnalyzerFactory</span><span style="font-family: 宋体;">类：</span></p>
<p class="MsoListParagraph" style="margin-left: 18.0pt; text-indent: 0cm;"><span lang="EN-US"><a href="http://www.tbdata.org/wp-content/uploads/2011/05/factory1.png"><img class="alignnone size-full wp-image-1756" src="http://www.tbdata.org/wp-content/uploads/2011/05/factory1.png" alt="" width="494" height="788" /></a><!--[endif]--></span></p>
<p class="MsoListParagraph" style="margin-left: 18.0pt; text-indent: 0cm;"><span style="font-family: 宋体;"><br />
</span></p>
<p class="MsoListParagraph" style="margin-left: 18.0pt; text-indent: 0cm;"><span lang="EN-US">+ </span></p>
<p class="MsoListParagraph" style="margin-left: 18.0pt; text-indent: 0cm;"><span lang="EN-US"> </span></p>
<p class="MsoListParagraph" style="margin-left: 18.0pt; text-indent: 0cm;">
<p class="MsoListParagraph" style="margin-left: 18.0pt; text-indent: 0cm;"><span style="font-family: 宋体;">现在有五个语法解析器</span><span lang="EN-US"> analyzer</span><span style="font-family: 宋体;">继承了</span><span lang="EN-US">BaseSemanticAnalyzer</span><span style="font-family: 宋体;">。</span></p>
<p class="MsoListParagraph" style="margin-left: 18.0pt; text-indent: 0cm;"><span lang="EN-US"><a href="http://www.tbdata.org/wp-content/uploads/2011/05/factory2.png"><img class="alignnone size-full wp-image-1754" style="border: 0px initial initial;" src="http://www.tbdata.org/wp-content/uploads/2011/05/factory2.png" alt="" width="886" height="390" /></a></span></p>
<p class="MsoListParagraph" style="margin-left: 18.0pt; text-indent: 0cm;"><span lang="EN-US"> </span></p>
<p class="MsoListParagraph" style="margin-left: 18.0pt; text-indent: 0cm;"><span style="font-size: 12.0pt; font-family: 宋体; color: red;">五个</span><span style="font-size: 12.0pt; color: red;" lang="EN-US">SemanticAnalyzer</span><span style="font-size: 12.0pt; font-family: 宋体; color: red;">的简单介绍：</span><span style="font-size: 12.0pt; color: red;" lang="EN-US"> </span></p>
<p class="MsoListParagraph" style="margin-left: 18.0pt; text-indent: 0cm;"><span lang="EN-US"> </span></p>
<p class="MsoListParagraph" style="margin-left: 18.0pt; text-indent: 0cm;"><span style="color: red;" lang="EN-US"> ExplainSemanticAnalyzer </span></p>
<p class="MsoListParagraph" style="margin-left: 18.0pt; text-indent: 0cm;"><span style="font-family: 宋体;">对语法树、执行计划</span> <span style="font-family: 宋体;">做了一个打印操作，其他的基本上都是按照</span><span lang="EN-US">SemanticAnalyzer</span><span style="font-family: 宋体;">执行的，最重要的差别，就是在整个解析过程中它没有让</span><span lang="EN-US">context</span><span style="font-family: 宋体;">在构建文件真正的临时文件所需的文件及文件路径等。</span></p>
<p class="MsoListParagraph" style="margin-left: 18.0pt; text-indent: 0cm;"><span lang="EN-US"> </span></p>
<p class="MsoListParagraph" style="margin-left: 18.0pt; text-indent: 0cm;"><span lang="EN-US"> </span></p>
<p class="MsoListParagraph" style="margin-left: 18.0pt; text-indent: 0cm;"><span style="color: red;" lang="EN-US"> FunctionSemanticAnalyzer </span><span style="font-family: 宋体; color: red;">：</span><span style="color: red;" lang="EN-US"> </span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoListParagraph" style="margin-left: 18.0pt; text-indent: 0cm;"><span lang="EN-US"><br />
<!--[endif]--></span></p>
<p class="MsoListParagraph" style="margin-left: 18.0pt; text-indent: 0cm;"><span lang="EN-US"> </span></p>
<p class="MsoListParagraph" style="margin-left: 18.0pt; text-indent: 0cm;"><span style="font-family: 宋体;">主要操作是创建和消除一个的</span><span lang="EN-US">function</span><span style="font-family: 宋体;">的元信息。</span></p>
<p class="MsoListParagraph" style="margin-left: 18.0pt; text-indent: 0cm;"><span style="font-family: 宋体;">如：</span></p>
<p class="MsoListParagraph" style="margin-left: 18.0pt; text-indent: 0cm;"><span lang="EN-US">CREATE TEMPORARY FUNCTION str_to_date AS &#8216;<span style="text-decoration: underline;">com</span>.<span style="text-decoration: underline;">taobao</span>.hive.<span style="text-decoration: underline;">udf</span>.UDFStrToDate&#8217;;</span></p>
<p class="MsoListParagraph" style="margin-left: 18.0pt; text-indent: 0cm;"><span lang="EN-US"> </span></p>
<p class="MsoListParagraph" style="margin-left: 18.0pt; text-indent: 0cm;"><span lang="EN-US">sql</span><span style="font-family: 宋体;">可以调用该自定义的</span><span lang="EN-US">function</span><span style="font-family: 宋体;">。</span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal"><span style="color: red;" lang="EN-US"> DDL SemanticAnalyzer</span><span style="font-family: 宋体; color: red;">：</span><span style="color: red;" lang="EN-US"> </span></p>
<p class="MsoNormal"><span style="font-family: 宋体;">主要是对表、</span><span lang="EN-US">view</span><span style="font-family: 宋体;">、</span><span lang="EN-US">partition</span><span style="font-family: 宋体;">的级别增删改查的操作。</span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal"><span style="font-family: 宋体;">如：</span><span lang="EN-US">show tables;</span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal"><span style="color: red;" lang="EN-US"> Load SemanticAnalyzer:</span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal"><span lang="EN-US">Load</span><span style="font-family: 宋体;">操作。</span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal"><span style="color: red;" lang="EN-US"> SemanticAnalyzer</span><span style="font-family: 宋体; color: red;">：</span><span style="color: red;" lang="EN-US"> </span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoListParagraph" style="margin-left: 18.0pt; text-indent: 0cm;"><span style="font-family: 宋体;">对于我们最重要关注的是</span><span lang="EN-US">SemanticAnalyzer</span><span style="font-family: 宋体;">：对应</span><span lang="EN-US">sql</span><span style="font-family: 宋体;">语句进行解析，这也是最核心最复杂的组件。</span></p>
<p class="MsoListParagraph" style="margin-left: 18.0pt; text-indent: 0cm;">
<p class="MsoListParagraph" style="margin-left: 18.0pt; text-indent: 0cm;"><span style="font-family: 宋体;">=</span></p>
<p class="MsoListParagraph" style="margin-left: 18.0pt; text-indent: 0cm;"><span lang="EN-US"> </span></p>
<p class="MsoListParagraph" style="margin-left: 18.0pt; text-indent: 0cm;"><span lang="EN-US"> </span></p>
<p class="MsoNormal"><span style="font-size: 14.0pt;" lang="EN-US">BeseSemanticAnalyze </span><span style="font-family: 宋体;">中语法解析开始于下面：</span></p>
<p class="MsoListParagraph" style="margin-left: 18.0pt; text-indent: 0cm;"><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><strong><span lang="EN-US">public</span></strong><span lang="EN-US"> </span><strong><span lang="EN-US">void</span></strong><span lang="EN-US"> <span style="background: silver;">analyze</span>(ASTNode ast, Context ctx) </span><strong><span lang="EN-US">throws</span></strong><span lang="EN-US"> SemanticException {</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><strong><span lang="EN-US">this</span></strong><span lang="EN-US">.</span><span lang="EN-US">ctx</span><span lang="EN-US"> = ctx;</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span>analyzeInternal(ast);</span><span lang="EN-US"> </span></p>
<p class="MsoNormal"><span lang="EN-US"><span> </span>}</span></p>
<p class="MsoNormal">=</p>
<p class="MsoNormal"><span style="font-family: 宋体;">五个解析器都继承于它，并实现</span><span lang="EN-US">analyzeInternal</span><span>（），不同的</span><span lang="EN-US">analyzer</span><span>不同的实现过程，我们关注的是普通</span><span lang="EN-US">sql</span><span>（</span><span lang="EN-US">select<span> </span>from </span><span>）的解析，所以在这里直接看</span><span lang="EN-US">SemanticAnalyzer</span><span style="font-family: 宋体;">。</span><span lang="EN-US"> </span></p>
<p class="MsoNormal">= （注： ctx 是 context 类，很重要，在下面会提到）</p>
<p class="MsoNormal">+</p>
<p class="MsoNormal"><span>所以解析过程的就从这里开始。</span><span lang="EN-US"> 我们只说正常sql （select &#8230; from）的解析。</span></p>
<p class="MsoNormal">这就是hive源码里面的<span>SemanticAnalyzer类（超大的一个类）。</span></p>
<p class="MsoNormal"><span>因为很重要直接代码如下：</span></p>
<p class="MsoNormal"><span><br />
</span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal"><span style="font-size: 16.0pt;" lang="EN-US">SemanticAnalyzer</span><span style="font-size: 16.0pt; font-family: 宋体;">的</span><span lang="EN-US">analyzeInternal</span><span>（）</span><span lang="EN-US"> </span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><strong><span lang="EN-US">public</span></strong><span lang="EN-US"> </span><strong><span lang="EN-US">void</span></strong><span lang="EN-US"> analyzeInternal(ASTNode ast) </span><strong><span lang="EN-US">throws</span></strong><span lang="EN-US"> SemanticException {</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span>reset();</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span>QB qb = </span><strong><span lang="EN-US">new</span></strong><span lang="EN-US"> QB(</span><strong><span lang="EN-US">null</span></strong><span lang="EN-US">, </span><strong><span lang="EN-US">null</span></strong><span lang="EN-US">, </span><strong><span lang="EN-US">false</span></strong><span lang="EN-US">);</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><strong><span lang="EN-US">this</span></strong><span lang="EN-US">.</span><span lang="EN-US">qb</span><span lang="EN-US"> = qb;</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><strong><span lang="EN-US">this</span></strong><span lang="EN-US">.</span><span lang="EN-US">ast</span><span lang="EN-US"> = ast;</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span>ASTNode child = ast;</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">LOG</span><span lang="EN-US">.info(</span><span lang="EN-US">“Starting Semantic Analysis”</span><span lang="EN-US">);</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span>System.</span><em><span lang="EN-US">out</span></em><span lang="EN-US">.print(</span><span lang="EN-US">“Starting Semantic Analysis”</span><span lang="EN-US">);</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left; text-indent: 19.5pt;"><span lang="EN-US">// analyze create table command</span></p>
<p class="MsoNormal" style="text-align: left; text-indent: 19.5pt;"><span lang="EN-US">//<span> </span></span></p>
<p class="MsoNormal" style="text-align: left; text-indent: 19.5pt;"><span lang="EN-US">//</span><span>建表或</span><span lang="EN-US">view </span><span>前处理</span><span lang="EN-US"> ,</span><span>如：</span><span> <span lang="EN-US">create table<span> </span>.. <span> </span>as select .. from <span> </span></span></span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><strong><span lang="EN-US">if</span></strong><span lang="EN-US"> (ast.getToken().getType() == HiveParser.</span><em><span lang="EN-US">TOK_CREATETABLE</span></em><span lang="EN-US">) {</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">// if it is not CTAS, we don&#8217;t need to go further and just return</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><strong><span lang="EN-US">if</span></strong><span lang="EN-US"> ((child = <span style="background: silver;">analyzeCreateTable</span>(ast, qb)) == </span><strong><span lang="EN-US">null</span></strong><span lang="EN-US">) {</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><strong><span lang="EN-US">return</span></strong><span lang="EN-US">;</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span>}</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span>}</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">// analyze create view command</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><strong><span lang="EN-US">if</span></strong><span lang="EN-US"> (ast.getToken().getType() == HiveParser.</span><em><span lang="EN-US">TOK_CREATEVIEW</span></em><span lang="EN-US">) {</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span>child = <span style="background: silver;">analyzeCreateView</span>(ast, qb);</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><strong><span lang="EN-US">if</span></strong><span lang="EN-US"> (child == </span><strong><span lang="EN-US">null</span></strong><span lang="EN-US">) {</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><strong><span lang="EN-US">return</span></strong><span lang="EN-US">;</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span>}</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">viewSelect</span><span lang="EN-US"> = child;</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span>}</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left; text-indent: 5.0pt;"><span lang="EN-US">// </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">// continue analyzing from the child ASTNode.</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span><span style="background: silver;">doPhase1</span>(child, qb, initPhase1Ctx());//</span><span>获取</span><span lang="EN-US">subSql,table </span><span>等对应别名</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">LOG</span><span lang="EN-US">.info(</span><span lang="EN-US">“Completed phase 1 of Semantic Analysis”</span><span lang="EN-US">);</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span><span style="background: silver;">getMetaData</span>(qb);</span><span lang="EN-US">//get </span><span>元数据</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">LOG</span><span lang="EN-US">.info(</span><span lang="EN-US">“Completed getting MetaData in Semantic Analysis”</span><span lang="EN-US">);</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">// Save the result schema derived from the sink operator produced</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">// by genPlan.<span> </span>This has the correct column names, which clients</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">// such as JDBC would prefer instead of the c0, c1 we&#8217;ll end</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">// up with later.</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span>Operator sinkOp = <span style="background: silver;">genPlan</span>(qb);</span><span lang="EN-US">//</span><span>这个层次才开始</span><span lang="EN-US">column names</span><span>，生产</span><span lang="EN-US">operator</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">resultSchema</span><span lang="EN-US"> =</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span>convertRowSchemaToViewSchema(</span><span lang="EN-US">opParseCtx</span><span lang="EN-US">.get(sinkOp).getRR());</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><strong><span lang="EN-US">if</span></strong><span lang="EN-US"> (</span><span lang="EN-US">createVwDesc</span><span lang="EN-US"> != </span><strong><span lang="EN-US">null</span></strong><span lang="EN-US">) {</span><span lang="EN-US">//</span><span>四面</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span><span style="background: silver;">saveViewDefinition</span>();</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">// Since we&#8217;re only creating a view (not executing it), we</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">// don&#8217;t need to optimize or translate the plan (and in fact, those</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">// procedures can interfere with the view creation). So</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">// skip the rest of this method.</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">ctx</span><span lang="EN-US">.setResDir(</span><strong><span lang="EN-US">null</span></strong><span lang="EN-US">);</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">ctx</span><span lang="EN-US">.setResFile(</span><strong><span lang="EN-US">null</span></strong><span lang="EN-US">);</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><strong><span lang="EN-US">return</span></strong><span lang="EN-US">;</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span>}</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span>ParseContext pCtx = </span><strong><span lang="EN-US">new</span></strong><span lang="EN-US"> ParseContext(</span><span lang="EN-US">conf</span><span lang="EN-US">, qb, child, </span><span lang="EN-US">opToPartPruner</span><span lang="EN-US">,</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">topOps</span><span lang="EN-US">, </span><span lang="EN-US">topSelOps</span><span lang="EN-US">, </span><span lang="EN-US">opParseCtx</span><span lang="EN-US">, </span><span lang="EN-US">joinContext</span><span lang="EN-US">, </span><span lang="EN-US">topToTable</span><span lang="EN-US">,</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">loadTableWork</span><span lang="EN-US">, </span><span lang="EN-US">loadFileWork</span><span lang="EN-US">, </span><span lang="EN-US">ctx</span><span lang="EN-US">, </span><span lang="EN-US">idToTableNameMap</span><span lang="EN-US">, </span><span lang="EN-US">destTableId</span><span lang="EN-US">, </span><span lang="EN-US">uCtx</span><span lang="EN-US">,</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">listMapJoinOpsNoReducer</span><span lang="EN-US">, </span><span lang="EN-US">groupOpToInputTables</span><span lang="EN-US">, </span><span lang="EN-US">prunedPartitions</span><span lang="EN-US">,</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">opToSamplePruner</span><span lang="EN-US">);</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">//</span><span>进入优化器，生成更好的</span><span lang="EN-US">operator tree</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span>Optimizer optm = </span><strong><span lang="EN-US">new</span></strong><span lang="EN-US"> Optimizer();</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span>optm.setPctx(pCtx);</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span>optm.initialize(</span><span lang="EN-US">conf</span><span lang="EN-US">);</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span>pCtx = optm.<span style="background: silver;">optimize</span>();</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span>init(pCtx);</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span>qb = pCtx.getQB();</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">// At this point we have the complete operator tree</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">// from which we want to find the reduce operator</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span><span style="background: silver;">genMapRedTasks</span>(qb);</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">LOG</span><span lang="EN-US">.info(</span><span lang="EN-US">“Completed plan generation”</span><span lang="EN-US">);</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><strong><span lang="EN-US">return</span></strong><span lang="EN-US">;</span><span lang="EN-US"> </span></p>
<p class="MsoNormal"><span lang="EN-US"><span> </span><span style="background: silver;">}</span></span></p>
<p class="MsoNormal">
<p class="MsoNormal"><span lang="EN-US"><span style="background: silver;"><br />
</span></span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal"><span style="font-family: 宋体;">关键方法：</span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal"><span lang="EN-US">doPhase1</span><span>（）</span><span lang="EN-US"> </span></p>
<p class="MsoNormal"><span>这个方法相当于把</span><span lang="EN-US">tree</span><span>的大枝叶先过滤了一遍，</span><span>解决了一些别名问题和对应为问题，</span></p>
<p class="MsoNormal"><span>包括：表和</span><span lang="EN-US">subsql</span><span>的对应的别名，</span></p>
<p class="MsoNormal"><span lang="EN-US">Tree </span><span>的</span><span lang="EN-US">string </span><span>与</span><span lang="EN-US"> ast </span><span>对应等，只是没有涉及到字段级别。</span></p>
<p class="MsoNormal"><span lang="EN-US">1</span><span style="font-family: 宋体; background: silver;">次解析</span></p>
<p class="MsoNormal"><strong><span lang="EN-US">public</span></strong><span lang="EN-US"> </span><strong><span lang="EN-US">void</span></strong><span lang="EN-US"> doPhase1(ASTNode ast, QB qb, Phase1Ctx ctx_1)</span></p>
<p class="MsoNormal"><span style="font-family: 宋体;">以下是官方注释。</span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">/**</span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">*</span><span lang="EN-US"> </span><span lang="EN-US">Phase</span><span lang="EN-US"> </span><span lang="EN-US">1:</span><span lang="EN-US"> </span><span lang="EN-US">(including,</span><span lang="EN-US"> </span><span lang="EN-US">but</span><span lang="EN-US"> </span><span lang="EN-US">not</span><span lang="EN-US"> </span><span lang="EN-US">limited</span><span lang="EN-US"> </span><span lang="EN-US">to):</span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">*</span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">*</span><span lang="EN-US"> </span><span lang="EN-US">1.</span><span lang="EN-US"> </span><span lang="EN-US">Gets</span><span lang="EN-US"> </span><span lang="EN-US">all</span><span lang="EN-US"> </span><span lang="EN-US">the</span><span lang="EN-US"> </span><span lang="EN-US">aliases</span><span lang="EN-US"> </span><span lang="EN-US">for</span><span lang="EN-US"> </span><span lang="EN-US">all</span><span lang="EN-US"> </span><span lang="EN-US">the</span><span lang="EN-US"> </span><span lang="EN-US">tables</span><span lang="EN-US"> </span><span lang="EN-US">/</span><span lang="EN-US"> </span><span style="text-decoration: underline;"><span lang="EN-US">subqueries</span></span><span lang="EN-US"> </span><span lang="EN-US">and</span><span lang="EN-US"> </span><span lang="EN-US">makes</span><span lang="EN-US"> </span><span lang="EN-US">the</span><span lang="EN-US"> <span style="color: #3f5fbf;">appropriate</span><span style="color: black;"> </span><span style="color: #3f5fbf;">mapping</span><span style="color: black;"> </span><span style="color: #3f5fbf;">in</span><span style="color: black;"> </span><span style="color: #3f5fbf;">aliasToTabs,</span><span style="color: black;"> </span><span style="color: #3f5fbf;">aliasToSubq</span><span style="color: black;"> </span></span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">*</span><span lang="EN-US"> </span><span lang="EN-US">2.</span><span lang="EN-US"> </span><span lang="EN-US">Gets</span><span lang="EN-US"> </span><span lang="EN-US">the</span><span lang="EN-US"> </span><span lang="EN-US">location</span><span lang="EN-US"> </span><span lang="EN-US">of</span><span lang="EN-US"> </span><span lang="EN-US">the</span><span lang="EN-US"> <span style="color: #3f5fbf;">destination</span><span style="color: black;"> </span><span style="color: #3f5fbf;">and</span><span style="color: black;"> </span><span style="color: #3f5fbf;">names</span><span style="color: black;"> </span><span style="color: #3f5fbf;">the</span><span style="color: black;"> </span><span style="text-decoration: underline;"><span style="color: #3f5fbf;">clase</span></span><span style="color: black;"> </span><span style="color: #3f5fbf;">“<span style="text-decoration: underline;">inclause</span>“</span><span style="color: black;"> </span><span style="color: #3f5fbf;">+</span><span style="color: black;"> </span><span style="color: #3f5fbf;">i</span><span style="color: black;"> </span></span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">*</span><span lang="EN-US"> </span><span lang="EN-US">3.</span><span lang="EN-US"> </span><span lang="EN-US">Creates</span><span lang="EN-US"> </span><span lang="EN-US">a</span><span lang="EN-US"> </span><span lang="EN-US">map</span><span lang="EN-US"> </span><span lang="EN-US">from</span><span lang="EN-US"> </span><span lang="EN-US">a</span><span lang="EN-US"> <span style="color: #3f5fbf;">string</span><span style="color: black;"> </span><span style="color: #3f5fbf;">representation</span><span style="color: black;"> </span><span style="color: #3f5fbf;">of</span><span style="color: black;"> </span><span style="color: #3f5fbf;">an</span><span style="color: black;"> </span><span style="color: #3f5fbf;">aggregation</span><span style="color: black;"> </span><span style="color: #3f5fbf;">tree</span><span style="color: black;"> </span><span style="color: #3f5fbf;">to</span><span style="color: black;"> </span><span style="color: #3f5fbf;">the</span><span style="color: black;"> </span><span style="color: #3f5fbf;">actual</span><span style="color: black;"> </span><span style="color: #3f5fbf;">aggregation</span><span style="color: black;"> </span><span style="color: #3f5fbf;">AST<span> </span></span></span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">*</span><span lang="EN-US"> </span><span lang="EN-US">4.</span><span lang="EN-US"> </span><span lang="EN-US">Creates</span><span lang="EN-US"> </span><span lang="EN-US">a</span><span lang="EN-US"> </span><span lang="EN-US">mapping</span><span lang="EN-US"> </span><span lang="EN-US">from</span><span lang="EN-US"> </span><span lang="EN-US">the</span><span lang="EN-US"> </span><span lang="EN-US">clause</span><span lang="EN-US"> </span><span lang="EN-US">name</span><span lang="EN-US"> </span><span lang="EN-US">to</span><span lang="EN-US"> </span><span lang="EN-US">the</span><span lang="EN-US"> </span><span lang="EN-US">select</span><span lang="EN-US"> </span><span lang="EN-US">expression</span><span lang="EN-US"> </span><span lang="EN-US">AST</span><span lang="EN-US"> </span><span lang="EN-US">in</span><span lang="EN-US"> <span style="color: #3f5fbf;">destToSelExpr</span><span style="color: black;"> </span></span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">*</span><span lang="EN-US"> </span><span lang="EN-US">5.</span><span lang="EN-US"> </span><span lang="EN-US">Creates</span><span lang="EN-US"> </span><span lang="EN-US">a</span><span lang="EN-US"> </span><span lang="EN-US">mapping</span><span lang="EN-US"> </span><span lang="EN-US">from</span><span lang="EN-US"> </span><span lang="EN-US">a</span><span lang="EN-US"> </span><span lang="EN-US">table</span><span lang="EN-US"> </span><span lang="EN-US">alias</span><span lang="EN-US"> </span><span lang="EN-US">to</span><span lang="EN-US"> </span><span lang="EN-US">the</span><span lang="EN-US"> </span><span lang="EN-US">lateral</span><span lang="EN-US"> </span><span lang="EN-US">view</span></p>
<p class="MsoNormal"><span lang="EN-US"><span> </span></span><span lang="EN-US">*</span><span lang="EN-US"> </span><span lang="EN-US">AST&#8217;s</span><span lang="EN-US"> </span><span lang="EN-US">in</span><span lang="EN-US"> </span><span lang="EN-US">aliasToLateralViews</span></p>
<p class="MsoNormal">
<p class="MsoNormal"><span lang="EN-US"><span style="font-family: 宋体; color: #000000;">这里是递归的遍历这颗树，</span></span></p>
<p class="MsoNormal"><span style="font-size: 12.0pt;" lang="EN-US"> </span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal"><a href="http://www.tbdata.org/wp-content/uploads/2011/05/解析方法.png"><img class="alignnone size-full wp-image-1757" src="http://www.tbdata.org/wp-content/uploads/2011/05/解析方法.png" alt="" width="536" height="377" /></a></p>
<p class="MsoNormal">
<p class="MsoNormal" style="text-align: left;">代码示例，如面对 TOK_FROM</p>
<p class="MsoNormal" style="text-align: left;">
<p class="MsoNormal" style="text-align: left;"><strong><span lang="EN-US">case</span></strong><span lang="EN-US"> HiveParser.TOK_FROM:</span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><strong><span lang="EN-US">int</span></strong><span lang="EN-US"> child_count = ast.getChildCount();</span><span lang="EN-US">//</span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><strong><span lang="EN-US">if</span></strong><span lang="EN-US"> (child_count != 1) {</span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><strong><span lang="EN-US">throw</span></strong><span lang="EN-US"> </span><strong><span lang="EN-US">new</span></strong><span lang="EN-US"> SemanticException(</span><span lang="EN-US">“Multiple Children “</span><span lang="EN-US"> + child_count);</span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span>}</span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">// Check if this is a <span style="text-decoration: underline;">subquery</span> / lateral view</span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><span lang="EN-US">// </span><span>正对不同情况，给出不同解决方法</span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span>ASTNode frm = (ASTNode) ast.getChild(0);</span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><strong><span lang="EN-US">if</span></strong><span lang="EN-US"> (frm.getToken().getType() == HiveParser.TOK_TABREF) {</span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span>processTable(qb, frm);</span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span>} </span><strong><span lang="EN-US">else</span></strong><span lang="EN-US"> </span><strong><span lang="EN-US">if</span></strong><span lang="EN-US"> (frm.getToken().getType() == HiveParser.TOK_SUBQUERY) {</span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span>processSubQuery(qb, frm);</span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span>} </span><strong><span lang="EN-US">else</span></strong><span lang="EN-US"> </span><strong><span lang="EN-US">if</span></strong><span lang="EN-US"> (frm.getToken().getType() == HiveParser.TOK_LATERAL_VIEW) {</span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span>processLateralView(qb, frm);</span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span>} </span><strong><span lang="EN-US">else</span></strong><span lang="EN-US"> </span><strong><span lang="EN-US">if</span></strong><span lang="EN-US"> (isJoinToken(frm)) {</span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span>processJoin(qb, frm);</span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span>qbp.setJoinExpr(frm);</span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span>}</span></p>
<p class="MsoNormal"><strong><span lang="EN-US">break</span></strong><span lang="EN-US">;</span></p>
<p class="MsoNormal"><span lang="EN-US"><br />
</span></p>
<p class="MsoNormal"><strong><span style="font-family: 宋体;">把摘下来的信息放在</span><span lang="EN-US">QB</span><span style="font-family: 宋体;">、</span><span lang="EN-US">QBParseInfo</span></strong><span style="font-family: 宋体;"><strong>等几个容器里面</strong>。如：</span><span style="text-decoration: underline;"><span>如果是</span></span><span style="text-decoration: underline;"><span lang="EN-US">select </span></span><span style="text-decoration: underline;"><span>就把信息记录到</span></span><span style="text-decoration: underline;"><span lang="EN-US">QBParseInfo</span></span><span style="text-decoration: underline;"><span lang="EN-US"> </span></span><span style="text-decoration: underline;"><span>中。</span></span><span style="text-decoration: underline;"> </span></p>
<p class="MsoNormal"><span style="text-decoration: underline;"><span lang="EN-US">skipRecursion</span></span><span style="text-decoration: underline;"><span lang="EN-US"> </span></span><span style="text-decoration: underline;"><span>标示递归是否结束。</span></span><span style="text-decoration: underline;"><span> </span></span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal"><span style="font-family: 宋体;">其中涉及了几个容器：</span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal">
<p class="MsoNormal"><span lang="EN-US"><a href="http://www.tbdata.org/wp-content/uploads/2011/05/容器.png"><img class="alignnone size-full wp-image-1758" src="http://www.tbdata.org/wp-content/uploads/2011/05/容器.png" alt="" width="134" height="294" /></a></span></p>
<p class="MsoNormal"><span style="text-decoration: underline;"><span lang="EN-US">QBParseInfo</span></span><span style="text-decoration: underline;"><span lang="EN-US"> </span></span><span style="text-decoration: underline;"><span>是辅助</span></span><span style="text-decoration: underline;"><span lang="EN-US">analyzer</span></span><span style="text-decoration: underline;"><span>语法解析的一个容器，</span></span><span style="text-decoration: underline;"> </span></p>
<p class="MsoNormal"><span style="text-decoration: underline;"><span>而</span></span><span style="text-decoration: underline;"><span lang="EN-US">qb</span></span><span style="text-decoration: underline;"><span>放的是</span></span><span style="text-decoration: underline;"><span lang="EN-US">sql block</span></span><span style="text-decoration: underline;"><span>基本单元，包括表名别名问题。</span></span></p>
<p class="MsoNormal"><span style="font-family: 宋体; color: #0000ff;"><span style="text-decoration: underline;">这里我们可以拿到很多我们想要的东西。</span></span></p>
<p class="MsoNormal">
<p class="MsoNormal"><span style="text-decoration: underline;"><span><span style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; color: #000000;"><span style="text-decoration: underline;"><span lang="EN-US">QBParseInfo</span></span></span></span></span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal"><span style="text-decoration: underline;"><span lang="EN-US"><span style="text-decoration: none;"> </span></span></span></p>
<p class="MsoNormal"><span lang="EN-US">Implementation</span><span lang="EN-US"> </span><span lang="EN-US">of</span><span lang="EN-US"> </span><span lang="EN-US">the</span><span lang="EN-US"> </span><span lang="EN-US">parse</span><span lang="EN-US"> </span><span lang="EN-US">information</span><span lang="EN-US"> </span><span lang="EN-US">related</span><span lang="EN-US"> </span><span lang="EN-US">to</span><span lang="EN-US"> </span><span lang="EN-US">a</span><span lang="EN-US"> </span><span lang="EN-US">query</span><span lang="EN-US"> </span><span lang="EN-US">block.</span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal"><span>各种对应关系，如：</span><span lang="EN-US"> select , groupby , groupby </span><span>等的</span><span lang="EN-US">string –Map&#8211; astNode</span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><strong><span lang="EN-US">private</span></strong><span lang="EN-US"> </span><strong><span lang="EN-US">final</span></strong><span lang="EN-US"> </span><strong><span lang="EN-US">boolean</span></strong><span lang="EN-US"> </span><span lang="EN-US">isSubQ</span><span lang="EN-US">;</span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><strong><span lang="EN-US">private</span></strong><span lang="EN-US"> </span><strong><span lang="EN-US">final</span></strong><span lang="EN-US"> String </span><span lang="EN-US">alias</span><span lang="EN-US">;</span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><strong><span lang="EN-US">private</span></strong><span lang="EN-US"> <span style="background: silver;">ASTNode</span> </span><span lang="EN-US">joinExpr</span><span lang="EN-US">;</span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><strong><span lang="EN-US">private</span></strong><span lang="EN-US"> <span style="background: silver;">ASTNode</span> </span><span lang="EN-US">hints</span><span lang="EN-US">;</span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><strong><span lang="EN-US">private</span></strong><span lang="EN-US"> </span><strong><span lang="EN-US">final</span></strong><span lang="EN-US"> HashMap&lt;String, <span style="background: silver;">ASTNode</span>&gt; </span><span lang="EN-US">aliasToSrc</span><span lang="EN-US">;</span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><strong><span lang="EN-US">private</span></strong><span lang="EN-US"> </span><strong><span lang="EN-US">final</span></strong><span lang="EN-US"> HashMap&lt;String, <span style="background: silver;">ASTNode</span>&gt; </span><span lang="EN-US">nameToDest</span><span lang="EN-US">;</span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><strong><span lang="EN-US">private</span></strong><span lang="EN-US"> </span><strong><span lang="EN-US">final</span></strong><span lang="EN-US"> HashMap&lt;String, TableSample&gt; </span><span lang="EN-US">nameToSample</span><span lang="EN-US">;</span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><strong><span lang="EN-US">private</span></strong><span lang="EN-US"> </span><strong><span lang="EN-US">final</span></strong><span lang="EN-US"> Map&lt;String, <span style="background: silver;">ASTNode</span>&gt; </span><span lang="EN-US">destToSelExpr</span><span lang="EN-US">;</span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span><strong><span lang="EN-US">private</span></strong><span lang="EN-US"> </span><strong><span lang="EN-US">final</span></strong><span lang="EN-US"> HashMap&lt;String, <span style="background: silver;">ASTNode</span>&gt; </span><span lang="EN-US">destToWhereExpr</span><span lang="EN-US">;</span></p>
<p class="MsoNormal"><span lang="EN-US"><span> </span></span><strong><span lang="EN-US">private</span></strong><span lang="EN-US"> </span><strong><span lang="EN-US">final</span></strong><span lang="EN-US"> HashMap&lt;String, <span style="background: silver;">ASTNode</span>&gt; </span><span lang="EN-US">destToGroupby</span><span lang="EN-US">;</span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal"><span lang="EN-US"><span> </span></span></p>
<p class="MsoNormal">
<p class="MsoNormal"><span lang="EN-US"><br />
</span></p>
<p class="MsoNormal" style="text-align: left;"><span lang="EN-US"><span> </span></span></p>
<p class="MsoNormal" style="text-align: left;">==</p>
<p class="MsoNormal" style="text-align: left;">
<p class="MsoNormal"><span style="font-size: 16.0pt;" lang="EN-US">Context </span><span style="font-size: 16.0pt; font-family: 宋体;">类</span><span style="font-family: 宋体;">在这里很重要</span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal"><span lang="EN-US">Context : </span><span style="font-family: 宋体;">是</span><span lang="EN-US">query</span><span style="font-family: 宋体;">的一个</span><span lang="EN-US">context</span></p>
<p class="MsoNormal"><span style="font-family: 宋体;">主要功能：</span></p>
<p class="MsoNormal"><span lang="EN-US">1 </span><span style="font-family: 宋体;">标示</span><span lang="EN-US">explain:</span><span lang="EN-US"> </span><span>如果是</span><span lang="EN-US">explain</span><span>语句，</span><span lang="EN-US"> explain</span><span>为</span><span lang="EN-US">ture</span><span>都不会实际的建立这些文件。</span></p>
<p class="MsoNormal"><span lang="EN-US">2</span><span style="font-family: 宋体;">可以建立</span><span lang="EN-US">tmp-file </span><span style="font-family: 宋体;">（在</span><span lang="EN-US">query</span><span style="font-family: 宋体;">执行过程中所需要的</span><span lang="EN-US">tmp-file </span><span style="font-family: 宋体;">文件和路径</span><span lang="EN-US"><span> </span></span><span style="font-family: 宋体;">），生成和清除中间临时文件及路径。</span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal"><strong><span style="font-family: 宋体;">所以我们可以再这里获取整个过程中的临时文件，用于优化使用。</span><span lang="EN-US"> </span></strong></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal"><span lang="EN-US"><span> </span></span><strong><span lang="EN-US">private</span></strong><span lang="EN-US"> <span style="text-decoration: underline;">Path</span> makeMRScratchDir(<span style="text-decoration: underline;">HiveConf</span> conf, </span><strong><span lang="EN-US">boolean</span></strong><span lang="EN-US"> mkdir)</span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal"><span style="font-size: small;"><br />
</span></p>
<p class="MsoNormal" style="text-align: left;">
<p class="MsoNormal" style="text-align: left;">==</p>
<p class="MsoNormal" style="text-align: left;"><span style="color: #7f0055;"><strong><br />
</strong></span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal"><span style="font-family: 宋体;">未完待续！</span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal"><span style="font-family: 宋体;">微博：</span><span lang="EN-US"><a href="http://www.weibo.com/xiaodongdata">www.weibo.com/xiaodongdata</a><span> </span></span></p>
<p class="MsoNormal"><span lang="EN-US">QQ </span><span style="font-family: 宋体;">：</span><span lang="EN-US"> 448683559</span></p>
]]></content:encoded>
			<wfw:commentRss>http://www.tbdata.org/archives/1753/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

