<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>hyperxu</title>
  
  <subtitle>一个专注于运维领域的个人技术博客</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="http://www.hyperxu.com/"/>
  <updated>2025-06-30T09:58:15.370Z</updated>
  <id>http://www.hyperxu.com/</id>
  
  <author>
    <name>hyperxu</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>Clickhouse可观测实践：5 分钟本地部署 ClickStack</title>
    <link href="http://www.hyperxu.com/2025/06/25/clickstack-2/"/>
    <id>http://www.hyperxu.com/2025/06/25/clickstack-2/</id>
    <published>2025-06-25T06:23:28.000Z</published>
    <updated>2025-06-30T09:58:15.370Z</updated>
    
    <content type="html"><![CDATA[<p>在上一篇文章中，我们深入探讨了 <code>ClickStack</code> 的架构设计与核心价值。我们知道，它凭借 <code>ClickHouse</code> 与 <code>OpenTelemetry</code> 的精妙组合，为可观测性领域带来了成本与效率的双重革新。纸上得来终觉浅，跟随以下步骤，你可以快速在本地运行一个完整的 <code>ClickStack</code> 实例，并导入一些样例数据体验<code>ClickStack</code>在日志、追踪、指标全领域的、高性能且极具性价比的解决方案。</p><h2 id="1-搭建测试环境"><a href="#1-搭建测试环境" class="headerlink" title="1.搭建测试环境"></a>1.搭建测试环境</h2><p>工欲善其事，必先利其器。为了顺利完成本次实验，请确保你的本地开发环境已安装Docker和Git。</p><p>由于是测试环境，我们选择<code>All-in-One</code>的部署模式，单个 Docker 容器，捆绑了所有 <code>ClickStack</code> 组件，用于演示和局部全栈测试。</p><p>这个综合的 Docker 镜像捆绑了所有 ClickStack 组件：</p><ul><li><strong>ClickHouse</strong></li><li><strong>HyperDX</strong></li><li><strong>OpenTelemetry (OTel) 收集器</strong>（在端口 <code>4317</code> 和 <code>4318</code> 上暴露 OTLP）</li></ul><p>此选项包含身份验证，允许在会话和用户之间持久保存仪表板、警报和保存的搜索。</p><h3 id="1-1-使用Docker部署"><a href="#1-1-使用Docker部署" class="headerlink" title="1.1.使用Docker部署"></a>1.1.使用Docker部署</h3><p>以下命令将运行一个 <code>OpenTelemetry</code> 收集器（在端口 4317 和 4318 上）和 HyperDX 界面（在端口 8080 上）。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker run -p 8080:8080 -p 4317:4317 -p 4318:4318 docker.hyperdx.io/hyperdx/hyperdx-all-in-one</span><br></pre></td></tr></table></figure><p>部署成功后会打印环境信息</p><p><img src="https://uploads.hyperxu.com/clickhouse-2/1.png" alt=""></p><a id="more"></a><h3 id="1-2-访问HyperDX界面"><a href="#1-2-访问HyperDX界面" class="headerlink" title="1.2.访问HyperDX界面"></a>1.2.访问HyperDX界面</h3><p>访问 <a href="http://localhost:8080/" target="_blank" rel="noopener">http://localhost:8080</a> 以访问 HyperDX 界面。</p><p>创建一个用户，提供符合要求的用户名和密码。</p><p>点击 <strong><code>Create</code></strong> 后，将为集成的 ClickHouse 实例创建数据源。</p><p><img src="https://uploads.hyperxu.com/clickhouse-2/2.png" alt="image.png"></p><p>随后会进入到HyperDX的主界面，此时并没有数据</p><p><img src="https://uploads.hyperxu.com/clickhouse-2/3.png" alt="image.png"></p><h2 id="2-导入示例日志、trace和指标"><a href="#2-导入示例日志、trace和指标" class="headerlink" title="2.导入示例日志、trace和指标"></a>2.导入示例日志、trace和指标</h2><h3 id="2-1-配置数据源"><a href="#2-1-配置数据源" class="headerlink" title="2.1.配置数据源"></a>2.1.配置数据源</h3><p><code>All-in-One</code>部署模式中已经部署了Clickhouse，直接配置<code>connection</code>本地Clickhouse</p><p><img src="https://uploads.hyperxu.com/clickhouse-2/4.png" alt="image.png"></p><h3 id="2-2-复制摄取API-KEY"><a href="#2-2-复制摄取API-KEY" class="headerlink" title="2.2.复制摄取API KEY"></a>2.2.复制摄取API KEY</h3><p>导航到 <strong>团队设置</strong> 并从 <strong>API 密钥</strong> 部分复制 <strong>摄取 API 密钥</strong>。此 API 密钥确保通过 OpenTelemetry 收集器进行数据摄取的安全性。</p><p><img src="https://uploads.hyperxu.com/clickhouse-2/5.png" alt="image.png"></p><h3 id="2-3-准备并导入样例数据"><a href="#2-3-准备并导入样例数据" class="headerlink" title="2.3.准备并导入样例数据"></a>2.3.准备并导入样例数据</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># wget https://storage.googleapis.com/hyperdx/sample.tar.gz</span></span><br></pre></td></tr></table></figure><p>此文件包含来自我们公共 <code>OpenTelemetry</code> 演示 的示例日志、指标和跟踪——一个简单的微服务电子商务商店。将此文件复制到您选择的目录中。</p><p>上面API 密钥导入环境变量，并将数据发送到 OTel 收集器：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># export API key</span></span><br><span class="line"><span class="built_in">export</span> CLICKSTACK_API_KEY=&lt;YOUR_INGESTION_API_KEY&gt;</span><br><span class="line"></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="keyword">for</span> filename <span class="keyword">in</span> $(tar -tf sample.tar.gz); <span class="keyword">do</span></span><br><span class="line">  endpoint=<span class="string">"http://localhost:4318/v1/<span class="variable">$&#123;filename%.json&#125;</span>"</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">"loading <span class="variable">$&#123;filename%.json&#125;</span>"</span></span><br><span class="line">  tar -xOf sample.tar.gz <span class="string">"<span class="variable">$filename</span>"</span> | <span class="keyword">while</span> <span class="built_in">read</span> -r line; <span class="keyword">do</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"<span class="variable">$line</span>"</span> | curl -s -o /dev/null -X POST <span class="string">"<span class="variable">$endpoint</span>"</span> \</span><br><span class="line">    -H <span class="string">"Content-Type: application/json"</span> \</span><br><span class="line">    -H <span class="string">"authorization: <span class="variable">$&#123;CLICKSTACK_API_KEY&#125;</span>"</span> \</span><br><span class="line">    --data-binary @-</span><br><span class="line">  <span class="keyword">done</span></span><br><span class="line"><span class="keyword">done</span></span><br></pre></td></tr></table></figure><p>这模拟了 OLTP 日志、跟踪和指标源将数据发送到 OTel 收集器。在生产环境中，这些源可能是语言客户端，甚至是其他 OTel 收集器。</p><h2 id="3-观测日志、trace及指标"><a href="#3-观测日志、trace及指标" class="headerlink" title="3.观测日志、trace及指标"></a>3.观测日志、trace及指标</h2><h3 id="3-1-观测日志"><a href="#3-1-观测日志" class="headerlink" title="3.1.观测日志"></a>3.1.观测日志</h3><p>在Search视图，默认已经可以观测到日志记录</p><p><img src="https://uploads.hyperxu.com/clickhouse-2/6.png" alt="image.png"></p><p><code>HyperDX</code>允许您对事件（日志和跟踪）进行全文搜索。您可以通过输入与您的事件匹配的关键字来开始搜索。例如，如果您的日志包含“Error”，您只需在搜索框中输入“Error”即可找到它。</p><h3 id="3-2-诊断trace"><a href="#3-2-诊断trace" class="headerlink" title="3.2.诊断trace"></a>3.2.诊断trace</h3><p>在trace界面，可以查看完整的分布式跟踪，并诊断问题错误来源</p><p><img src="https://uploads.hyperxu.com/clickhouse-2/7.png" alt="image.png"></p><h3 id="3-3-观测指标"><a href="#3-3-观测指标" class="headerlink" title="3.3.观测指标"></a>3.3.观测指标</h3><p>有些时候，我们需要更直观的观测全局错误指标。可以选择相应的指标作为数据源，完成图表构建器以绘制 <strong>visa_validation_cache.size (Gauge)</strong> 的 <strong>最大值</strong> 并按播放按钮。缓存显然在达到最大大小之前不断增加，之后生成了错误。</p><p><img src="https://uploads.hyperxu.com/clickhouse-2/8.png" alt="image.png"></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;在上一篇文章中，我们深入探讨了 &lt;code&gt;ClickStack&lt;/code&gt; 的架构设计与核心价值。我们知道，它凭借 &lt;code&gt;ClickHouse&lt;/code&gt; 与 &lt;code&gt;OpenTelemetry&lt;/code&gt; 的精妙组合，为可观测性领域带来了成本与效率的双重革新。纸上得来终觉浅，跟随以下步骤，你可以快速在本地运行一个完整的 &lt;code&gt;ClickStack&lt;/code&gt; 实例，并导入一些样例数据体验&lt;code&gt;ClickStack&lt;/code&gt;在日志、追踪、指标全领域的、高性能且极具性价比的解决方案。&lt;/p&gt;
&lt;h2 id=&quot;1-搭建测试环境&quot;&gt;&lt;a href=&quot;#1-搭建测试环境&quot; class=&quot;headerlink&quot; title=&quot;1.搭建测试环境&quot;&gt;&lt;/a&gt;1.搭建测试环境&lt;/h2&gt;&lt;p&gt;工欲善其事，必先利其器。为了顺利完成本次实验，请确保你的本地开发环境已安装Docker和Git。&lt;/p&gt;
&lt;p&gt;由于是测试环境，我们选择&lt;code&gt;All-in-One&lt;/code&gt;的部署模式，单个 Docker 容器，捆绑了所有 &lt;code&gt;ClickStack&lt;/code&gt; 组件，用于演示和局部全栈测试。&lt;/p&gt;
&lt;p&gt;这个综合的 Docker 镜像捆绑了所有 ClickStack 组件：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ClickHouse&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HyperDX&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OpenTelemetry (OTel) 收集器&lt;/strong&gt;（在端口 &lt;code&gt;4317&lt;/code&gt; 和 &lt;code&gt;4318&lt;/code&gt; 上暴露 OTLP）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;此选项包含身份验证，允许在会话和用户之间持久保存仪表板、警报和保存的搜索。&lt;/p&gt;
&lt;h3 id=&quot;1-1-使用Docker部署&quot;&gt;&lt;a href=&quot;#1-1-使用Docker部署&quot; class=&quot;headerlink&quot; title=&quot;1.1.使用Docker部署&quot;&gt;&lt;/a&gt;1.1.使用Docker部署&lt;/h3&gt;&lt;p&gt;以下命令将运行一个 &lt;code&gt;OpenTelemetry&lt;/code&gt; 收集器（在端口 4317 和 4318 上）和 HyperDX 界面（在端口 8080 上）。&lt;/p&gt;
&lt;figure class=&quot;highlight bash&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;docker run -p 8080:8080 -p 4317:4317 -p 4318:4318 docker.hyperdx.io/hyperdx/hyperdx-all-in-one&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;部署成功后会打印环境信息&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://uploads.hyperxu.com/clickhouse-2/1.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="clickhouse" scheme="http://www.hyperxu.com/categories/clickhouse/"/>
    
    
      <category term="大数据" scheme="http://www.hyperxu.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="clickhouse" scheme="http://www.hyperxu.com/tags/clickhouse/"/>
    
      <category term="olap" scheme="http://www.hyperxu.com/tags/olap/"/>
    
      <category term="可观测" scheme="http://www.hyperxu.com/tags/%E5%8F%AF%E8%A7%82%E6%B5%8B/"/>
    
  </entry>
  
  <entry>
    <title>解构 ClickStack：不止是降本，更是可观测性架构的一次演进</title>
    <link href="http://www.hyperxu.com/2025/06/19/clickstack-1/"/>
    <id>http://www.hyperxu.com/2025/06/19/clickstack-1/</id>
    <published>2025-06-19T06:23:28.000Z</published>
    <updated>2025-06-27T16:31:12.007Z</updated>
    
    <content type="html"><![CDATA[<p><img src="https://uploads.hyperxu.com/clickhouse-1/1.png" alt=""></p><h3 id="1-我们的可观测性平台做对了吗"><a href="#1-我们的可观测性平台做对了吗" class="headerlink" title="1.我们的可观测性平台做对了吗"></a>1.我们的可观测性平台做对了吗</h3><p>作为工程师，我们每天都在与系统的复杂性搏斗。而可观测性平台，本应是我们在黑暗中探索的明灯。但现实往往是，这盏“灯”本身就价格不菲，还时常忽明忽暗。</p><p>你是否也面临着这些似曾相识的场景？</p><ul><li>高昂的账单： 每月审视商业 SaaS 服务的账单，感觉每一行日志、每一个指标都在燃烧经费。</li><li>运维的泥潭： 维护着一套由 ELK、Prometheus、Jaeger 等“攒”起来的系统，不同组件的升级、扩容和协调，消耗了大量精力。</li><li>低效的排障： 发现问题时，不得不在 Grafana、Kibana、Jaeger UI 之间来回跳转，复制粘贴 TraceID，宝贵的排障时间就在这无尽的“上下文切换”中流逝。<a id="more"></a></li></ul><p>这些挑战背后，指向了行业的一个核心痛点：我们长期在<code>“功能完备性”</code>与<code>“总拥有成本 (TCO)”</code>之间做着艰难的权衡。</p><p>有没有一种架构，能够打破这种两难困境？ClickStack 尝试给出它的答案。</p><h3 id="2-架构解析：ClickStack-为何选择-ClickHouse-OTel？"><a href="#2-架构解析：ClickStack-为何选择-ClickHouse-OTel？" class="headerlink" title="2.架构解析：ClickStack 为何选择 ClickHouse + OTel？"></a>2.架构解析：ClickStack 为何选择 ClickHouse + OTel？</h3><p>ClickStack 以 ClickHouse 为统一存储引擎，原生集成 OpenTelemetry 标准，旨在从根本上解决日志、追踪、指标三大数据的孤岛问题。</p><p>ClickStack 的架构设计非常清晰，它将宝押在了两项关键技术上：ClickHouse 作为统一存储，OpenTelemetry 作为标准入口。</p><p><img src="https://uploads.hyperxu.com/clickhouse-1/2.png" alt=""></p><p>这个选择并非偶然，而是深思熟虑的结果。</p><p><strong>OpenTelemetry Collector：拥抱标准，面向未来</strong></p><p>作为数据入口，OTel Collector 提供了无与伦比的开放性和兼容性。它意味着你的可观测性体系从第一天起就“不站队”，避免了被特定厂商锁定的风险。这是一个着眼于未来的战略选择。</p><p><strong>ClickHouse：性能与成本的最佳平衡点</strong></p><p>这才是 ClickStack 架构的真正王牌。为什么是 ClickHouse？因为它几乎是为可观测性这类海量数据分析场景量身定做的。</p><p>它采用列式存储 (Columnar Storage)。 简单来说，就是把同一列的数据（比如所有请求的 status_code）存在一起。这种存储方式带来了两大革命性优势：</p><ul><li>极高的压缩比： 数据类型单一，压缩算法能发挥到极致。反映在成本上，就是你的存储开销可能会轻松降低 50%-80%。</li><li>闪电般的查询速度： 当你只关心某几列数据时（这在分析场景中非常常见），数据库无需读取整行，查询性能得到指数级提升。</li></ul><p><strong>HyperDX 前端：终结“多屏协同”的割裂感</strong></p><p>统一的存储，自然需要统一的交互界面。为了提供现代化的用户体验，ClickStack 的前端 fork 并改进自优秀的开源项目 HyperDX。它将原本分散在不同工具中的查询和分析能力整合到了一处，让数据之间的关联变得自然而高效。</p><h3 id="3-核心优势剖析：ClickStack-究竟能带来什么？"><a href="#3-核心优势剖析：ClickStack-究竟能带来什么？" class="headerlink" title="3.核心优势剖析：ClickStack 究竟能带来什么？"></a>3.核心优势剖析：ClickStack 究竟能带来什么？</h3><p>那么，这个架构到底能给我们的日常工作带来什么实际好处？</p><p>核心在于，它将可观测性的关注点，从“如何采集和存储”，拉回到了“如何高效地使用”上。</p><ol><li><p>显著的成本效益: 这是最直接的价值。更低的存储占用和简化的运维架构，意味着更低的总拥有成本 (TCO)。对于预算敏感的团队，这足以成为选择它的决定性理由。</p></li><li><p>流畅的开发体验: 想象一下，在一个基于 HyperDX 的界面中，你看到某个服务 P99 延迟指标突然飙高，直接点击就能下钻到对应的分布式追踪列表；在最慢的一条 Trace 中，又能一键查看当时系统打印出的相关日志。这种无缝的分析体验，将极大缩短故障定位时间（MTTR）。</p></li><li><p>架构的自由与主动权: 建立在开放标准之上，意味着你的技术栈拥有了更高的灵活性。你可以随时引入其他兼容 OTel 的工具，而不用担心被平台“绑架”。</p></li></ol><h3 id="4-横向评估：ClickStack-在生态中的坐标"><a href="#4-横向评估：ClickStack-在生态中的坐标" class="headerlink" title="4.横向评估：ClickStack 在生态中的坐标"></a>4.横向评估：ClickStack 在生态中的坐标</h3><p>当然，没有完美的技术方案，只有最合适的选择。我们将 ClickStack 放置于当前主流方案的坐标系中，可以更清晰地看到它的位置。</p><p><img src="https://uploads.hyperxu.com/clickhouse-1/3.png" alt=""></p><p><strong>分析结论：</strong><br>ClickStack 并非要取代谁，而是提供了一种全新的、极具吸引力的“第三条道路”。它精准地切入了商业 SaaS 的“价格敏感区”和传统开源组合的“运维复杂区”，为市场提供了宝贵的差异化选择。</p><p>五、结论：谁应该认真考虑 ClickStack？<br>经过全面的分析，我们可以得出结论：ClickStack 是一个定位清晰、架构先进且极具潜力的开源可观测性解决方案。</p><p>如果你和你的团队符合以下画像，那么强烈建议你将其纳入技术雷达：</p><ul><li><p><strong>务实的成本控制者</strong>： 你正在积极寻求降低可观测性平台的总体拥有成本，希望将每一分钱都花在刀刃上。</p></li><li><p><strong>高效的系统建设者</strong>： 你希望构建一个技术栈统一、数据无缝关联的私有化平台，并愿意为此投入一定的运维精力。</p></li><li><p><strong>开放的架构拥抱者</strong>： 你坚信开放标准的力量，希望构建一个不被任何厂商锁定、面向未来的技术基础设施。</p></li></ul><p>诚然，作为一个发展中的项目，ClickStack 在某些高级功能（如 AIOps）和商业生态上尚需时日。但它已经用一种优雅而高效的方式，解决了可观测性领域最核心的成本与效率问题。</p><p>对于许多团队而言，这或许已经足够。</p><p>项目地址: <a href="https://github.com/clickvisual/clickstack" target="_blank" rel="noopener">https://github.com/clickvisual/clickstack</a></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;https://uploads.hyperxu.com/clickhouse-1/1.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;1-我们的可观测性平台做对了吗&quot;&gt;&lt;a href=&quot;#1-我们的可观测性平台做对了吗&quot; class=&quot;headerlink&quot; title=&quot;1.我们的可观测性平台做对了吗&quot;&gt;&lt;/a&gt;1.我们的可观测性平台做对了吗&lt;/h3&gt;&lt;p&gt;作为工程师，我们每天都在与系统的复杂性搏斗。而可观测性平台，本应是我们在黑暗中探索的明灯。但现实往往是，这盏“灯”本身就价格不菲，还时常忽明忽暗。&lt;/p&gt;
&lt;p&gt;你是否也面临着这些似曾相识的场景？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;高昂的账单： 每月审视商业 SaaS 服务的账单，感觉每一行日志、每一个指标都在燃烧经费。&lt;/li&gt;
&lt;li&gt;运维的泥潭： 维护着一套由 ELK、Prometheus、Jaeger 等“攒”起来的系统，不同组件的升级、扩容和协调，消耗了大量精力。&lt;/li&gt;
&lt;li&gt;低效的排障： 发现问题时，不得不在 Grafana、Kibana、Jaeger UI 之间来回跳转，复制粘贴 TraceID，宝贵的排障时间就在这无尽的“上下文切换”中流逝。
    
    </summary>
    
      <category term="clickhouse" scheme="http://www.hyperxu.com/categories/clickhouse/"/>
    
    
      <category term="大数据" scheme="http://www.hyperxu.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="clickhouse" scheme="http://www.hyperxu.com/tags/clickhouse/"/>
    
      <category term="olap" scheme="http://www.hyperxu.com/tags/olap/"/>
    
      <category term="可观测" scheme="http://www.hyperxu.com/tags/%E5%8F%AF%E8%A7%82%E6%B5%8B/"/>
    
  </entry>
  
  <entry>
    <title>一次CPU sys上涨引发对kafka PageCache的思考</title>
    <link href="http://www.hyperxu.com/2020/10/12/kafka-9/"/>
    <id>http://www.hyperxu.com/2020/10/12/kafka-9/</id>
    <published>2020-10-12T03:00:28.000Z</published>
    <updated>2020-10-12T03:12:20.530Z</updated>
    
    <content type="html"><![CDATA[<p><img src="https://uploads.hyperxu.com/WechatIMG150.jpg" alt=""></p><h2 id="1-CPU-sys-上涨背景"><a href="#1-CPU-sys-上涨背景" class="headerlink" title="1.CPU sys 上涨背景"></a>1.CPU sys 上涨背景</h2><table><thead><tr><th>配置</th><th>机型 A</th><th>机型 B</th></tr></thead><tbody><tr><td>CPU</td><td>48C</td><td>48C</td></tr><tr><td>MEM</td><td>8*32G</td><td>12*16G</td></tr><tr><td>DATA DISK</td><td>12*960G SSD</td><td>12*4T SSD</td></tr></tbody></table><p>线上某个<code>kafka</code>集群由于种种原因，从 24 * 机型 A 置换迁移为 12 * 机型 B。从集群总资源维度看，排除其他客观因素，置换后，<code>CPU</code>总核数少了一半，使用率上升其实也是预期之内的。事实上置换后，集群<code>CPU</code>使用率确实也由原有的 20%提升至 40%，上升了约 1 倍多。但置换后，<code>cpu sys</code>使用率均值约达到了 12%，较为抢眼，系统相关服务却并无异常，令人有些困惑。</p><a id="more"></a><p>这个问题其实并不难解释，先说结论，因为<code>kafka</code>数据操作会优先在<code>PageCache</code>中进行，导致读写磁盘数据时是系统内核线程去操作而非用户应用层面，所以单机数据读写压力上涨后，系统内核线程的繁忙就表现为<code>cpu sys</code>上涨，甚至比<code>cpu user</code>使用还来的明显。</p><p>今天就借此和大家探讨下，<code>kafka</code>高吞吐性能的核心之一—<code>PageCache</code>。</p><h2 id="2-kafka-消息存储"><a href="#2-kafka-消息存储" class="headerlink" title="2.kafka 消息存储"></a>2.kafka 消息存储</h2><p><code>kafka</code>的存储设计和一般的存储设计理念也差不多，都是分缓存，持久化层，缓存数据尽量放内存，持久化数据就会考虑多副本且落盘。一般的应用引擎设计都会考虑自己来实现缓存及写盘这一套逻辑，<code>kafka</code>的不同之处在于他并没有自己在内存中创建缓冲区，然后再实现向磁盘<code>write</code>的一系列方法，而是直接站在巨人们的肩膀上，使用了系统层面的<code>PageCache</code>。</p><p>基于<code>Linux</code>开源社区一众贡献者的多年打磨迭代，<code>Linux</code>的文件系统早已在<code>PageCache</code>做了大量的优化和填坑，且还会持续优化，这无异于为<code>kafka</code>的缓存模块提供的强大助力。</p><p>直接使用内核系统的<code>PageCache</code>:</p><ul><li>减少内存开销： <code>Java</code>对象的内存开销（overhead）非常大，往往是对象中存储的数据所占内存的两倍以上</li><li>规避 GC 问题：<code>JVM</code>中的内存垃圾回收已经是多年诟病的问题了，随着堆内数据不断增长而变得越来越不明确，回收所花费的代价也会越来越大</li><li>简单可靠：内核系统会调用所有的空闲的内存作为<code>PageCache</code>，并在其上做了大量的优化：预读，后写，<code>flush</code>管理等，这些都不再需要应用层操心，全部有系统接管完成</li></ul><h2 id="3-kafka-数据读写"><a href="#3-kafka-数据读写" class="headerlink" title="3.kafka 数据读写"></a>3.kafka 数据读写</h2><h3 id="3-1-读写接力"><a href="#3-1-读写接力" class="headerlink" title="3.1.读写接力"></a>3.1.读写接力</h3><p><code>Linux</code>系统会把还没应用程序申请走的内存挪给<code>PageCache</code>使用，此时，当写入数据时，会先写入<code>PageCache</code>中，并标记为<code>dirty</code>。读取数据时，会先再<code>PageCache</code>中查询，如果有就快速返回，没有才会去磁盘读取回写到<code>PageCache</code>中。</p><p>因此，一般情况，只要生产和消费速率相差不是很远，数据读写都会发生在<code>PageCache</code>中，没有磁盘操作。这比起自己在内存中再维护一份消息数据提供读写，既不会浪费内存，又不用考虑<code>GC</code>，即便<code>kafka</code>应用重启了，数据也还在<code>PageCache</code>中，可以快速读取恢复。</p><h3 id="3-2-异步-flush-数据落盘"><a href="#3-2-异步-flush-数据落盘" class="headerlink" title="3.2.异步 flush 数据落盘"></a>3.2.异步 flush 数据落盘</h3><p>由于<code>kafka</code>调用的是系统的<code>PageCache</code>，所以这里讲的<code>kafka</code>数据<code>flush</code>其实就是<code>Linux</code>内核的后台异步<code>flush</code>。</p><p>内核线程<code>pdflush</code>负责将有<code>dirty</code>标记的内存页，发送给 IO 调度层。内核会为每个磁盘起一条<code>pdflush</code>线程，每 5 秒（<code>/proc/sys/vm/dirty_writeback_centisecs</code>）唤醒一次，主要由以下面三个参数来调整：</p><ul><li><code>/proc/sys/vm/dirty_expire_centisecs</code>：默认值 30s，<code>page dirty</code>的时间超过这个值，就会刷盘，所以即使意外<code>OS crash</code>，理论最多也就丢这 30s 的数据</li><li><code>/proc/sys/vm/dirty_background_ratio</code>：默认值 10%，如果<code>dirty page</code>的总大小超过了可用内存的 10%(即/proc/meminfo 里 MemFree + Cached - Mapped)，则会在后台启动<code>pdflush</code>线程刷盘，这个值是个比较重要的调优参数。</li><li><code>/proc/sys/vm/dirty_ratio</code>：默认值 30%，如果写入数据过快，超过了<code>pdflush</code>的速率，此时<code>dirty page</code>会迅速积压，当超过可用内存的 30%，则此时所有应用的写操作都会被<code>block</code>，各自去执行<code>flush</code>，因为操作系统认为现在已经来不及写盘了，如果<code>crash</code>会丢过多的数据，会阻塞住不再接纳更多的数据。我们要尽量避免这种情况的发生，长时间的写入阻塞，很容易带来一系列的雪崩问题。在 Redis2.8 以前，Rewrite AOF 就经常导致这个大面积阻塞问题。</li></ul><h3 id="3-3-Page-Cache-清理策略"><a href="#3-3-Page-Cache-清理策略" class="headerlink" title="3.3.Page Cache 清理策略"></a>3.3.Page Cache 清理策略</h3><p>当写入的数据逐渐增多，直到内存满了，此时就需要考虑把应用占用的内存数据挪到<code>swap</code>区去，或者开始清理<code>PageCache</code>了。一般来说，我们会通过调整<code>/proc/sys/vm/swappiness</code>的值为 0，来尽量不使用<code>swap</code>。剩下的就来看看<code>PageCache</code>是如何清理的了。</p><p><code>Page Cache</code>的清理策略是改良版的<code>LRU</code>。如果直接用<code>LRU</code>，一些新读取但只用一次的冷数据会占满了<code>LRU</code>的头部。因此将原来一条<code>LRU</code>的队列拆成了两条，一条放新数据，一条放已经访问过好几次的热数据。刚访问的数据放在新<code>LRU</code>队列里，多次访问命中后会升级到旧<code>LRU</code>队列的热数据队列。清理时会从新<code>LRU</code>队列的尾部开始清理，直到清理出足够的内存。</p><p><code>Linux</code>通过配置<code>/proc/sys/vm/min_free_kbytes</code>的值，来优化系统开始回收内存的阈值。</p><h3 id="3-4-预读策略"><a href="#3-4-预读策略" class="headerlink" title="3.4.预读策略"></a>3.4.预读策略</h3><p>根据清理策略，当消费太慢，堆积的数据过多直到<code>Page Cache</code>被清理掉了，此时就需要读盘了。</p><p>系统内核针对这个问题，会有个预读策略，每次读取请求都会尝试预读更多的数据。</p><ul><li>首次预读：readahead*size = read_size * 2 or _ 4，首次预读窗口会是读大小的 2~4 倍，可以提升 IO 效率</li><li>后续预读：readahead_size *= 2 ，后续预读会逐渐倍增，直到达到最大预读大小</li></ul><p>这也是为什么有时候，我们会觉得应用有个”热身状态”，刚开始卡一下后，运行的越来越快，这其中预读策略就起到了一定的 IO 优化效果。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;https://uploads.hyperxu.com/WechatIMG150.jpg&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;1-CPU-sys-上涨背景&quot;&gt;&lt;a href=&quot;#1-CPU-sys-上涨背景&quot; class=&quot;headerlink&quot; title=&quot;1.CPU sys 上涨背景&quot;&gt;&lt;/a&gt;1.CPU sys 上涨背景&lt;/h2&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;配置&lt;/th&gt;
&lt;th&gt;机型 A&lt;/th&gt;
&lt;th&gt;机型 B&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;CPU&lt;/td&gt;
&lt;td&gt;48C&lt;/td&gt;
&lt;td&gt;48C&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MEM&lt;/td&gt;
&lt;td&gt;8*32G&lt;/td&gt;
&lt;td&gt;12*16G&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DATA DISK&lt;/td&gt;
&lt;td&gt;12*960G SSD&lt;/td&gt;
&lt;td&gt;12*4T SSD&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;线上某个&lt;code&gt;kafka&lt;/code&gt;集群由于种种原因，从 24 * 机型 A 置换迁移为 12 * 机型 B。从集群总资源维度看，排除其他客观因素，置换后，&lt;code&gt;CPU&lt;/code&gt;总核数少了一半，使用率上升其实也是预期之内的。事实上置换后，集群&lt;code&gt;CPU&lt;/code&gt;使用率确实也由原有的 20%提升至 40%，上升了约 1 倍多。但置换后，&lt;code&gt;cpu sys&lt;/code&gt;使用率均值约达到了 12%，较为抢眼，系统相关服务却并无异常，令人有些困惑。&lt;/p&gt;
    
    </summary>
    
      <category term="kafka" scheme="http://www.hyperxu.com/categories/kafka/"/>
    
    
      <category term="大数据" scheme="http://www.hyperxu.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="kafka" scheme="http://www.hyperxu.com/tags/kafka/"/>
    
  </entry>
  
  <entry>
    <title>kafka消费组及重平衡的影响</title>
    <link href="http://www.hyperxu.com/2020/06/14/kafka-8/"/>
    <id>http://www.hyperxu.com/2020/06/14/kafka-8/</id>
    <published>2020-06-14T07:14:28.000Z</published>
    <updated>2020-12-07T10:31:45.780Z</updated>
    
    <content type="html"><![CDATA[<p><img src="https://uploads.hyperxu.com/computer-keyboard-34153.jpg" alt=""></p><p>消费组应该算是<code>kafka</code>中一个比较有特色的设计模式了，而他的重平衡机制也是我们在实际生产使用中，无法避免的一个问题。</p><h2 id="消费组"><a href="#消费组" class="headerlink" title="消费组"></a>消费组</h2><p><code>Consumer Group</code>为<code>kafka</code>提供了可扩展、高容错特性的消费者机制。简单介绍下，大致有以下特点：</p><ul><li>一个<code>Consumer Group</code>内可以有多个<code>Consumer</code>实例，该实例可以是一个进程，也可以是进程下的多线程</li><li>每个<code>Consumer Group</code>有一个唯一标识的<code>Group ID</code></li><li>不同<code>Consumer Group</code>之间相互独立，互不影响</li><li><code>Consumer Group</code>内实例，与订阅的<code>topic</code>分区关系是一对一，或一对多的关系，<code>Consumer Group</code>会通过<code>Coordinator</code>尽量保持公平分配</li></ul><a id="more"></a><p><img src="https://uploads.hyperxu.com/5433D18B-CDF1-4418-AB6C-1F9CF5559A7E.png" alt=""></p><p>理想情况下，我们应该设置<code>Consumer</code>实例的数量等于该<code>Group</code>订阅<code>topic</code>的分区总数，可以最大发挥消费性能。若设置的<code>Consumer</code>实例数少于订阅的分区数，则会为每个<code>Consumer</code>实例分配多个分区，消费性能会有所下降。若设置的<code>Consumer</code>实例数大于订阅的分区数，则会为每个<code>Consumer</code>实例分配 1 个分区进行消费，多余的<code>Consumer</code>实例则会闲置，只会浪费资源。</p><h2 id="重平衡"><a href="#重平衡" class="headerlink" title="重平衡"></a>重平衡</h2><p>重平衡（<code>Rebalance</code>）就是让一个<code>Consumer Group</code>下所有的<code>Consumer</code>实例,合理分配消费订阅<code>topic</code>的所有分区的过程。有 3 种情况会触发<code>Consumer Group</code>的<code>Rebalance</code>：</p><ol><li><code>Group</code>下实例数发生变化。有新的<code>Consumer</code>实例加入或者离开组。</li><li>订阅的<code>topic</code>数发生变化。<code>Consumer Group</code>可以使用正则的方式订阅<code>topic</code>，比如 <code>consumer.subscribe(Pattern.compile(“public.*log”))</code>，该<code>Group</code>订阅所有以 public 开头，log 结尾的<code>topic</code>。这期间，新建了一个满足这样条件的<code>topic</code>，那么该<code>Group</code>也会发生<code>Rebalance</code>。</li><li><code>topic</code>分区数发生变化。比如<code>topic</code>扩分区的时候，也会触发<code>Rebalance</code>。</li></ol><p>单看上面任一触发条件，都没啥毛病。问题在于<code>Rebalance</code>过程中会出现以下问题：</p><ul><li><code>Rebalance</code>过程的表现有些类似<code>JVM FGC</code>的情况，期间整个应用都会夯住，所有<code>Consumer</code>实例都会停止消费，等待<code>Rebalance</code>完成。</li><li><code>Rebalance</code>过程中，所有<code>Consumer</code>实例都会参与重新分配。即便<code>Consumer Group</code>中部分<code>Consumer</code>实例分配合理，也需要打散重新分配，会导致<code>TCP</code>重新建立连接，是一个比较重的操作，较为浪费资源。</li><li><code>Rebalance</code>的耗时取决于<code>Consumer Group</code>下的实例数量，一旦实例数过多，耗时极长，会造成大量消费延迟。</li></ul><h2 id="避免重平衡"><a href="#避免重平衡" class="headerlink" title="避免重平衡"></a>避免重平衡</h2><p>对于上述<code>Rebalance</code>带来的一些弊端，从目前的社区版来看，暂时还没有很好的解决办法，我们只能尽量避免<code>Rebalance</code>的发生。<br>在生产业务场景中，很多<code>Rebalance</code>都是预期外或者不必要的。我们应用的<code>TPS</code>大多是被这类<code>Rebalance</code>拖慢的。</p><p>从上述的 3 个<code>Rebalance</code>触发条件抓手，后两条<code>topic</code>数量及分区数变化，一般都是主动运维的相关操作，这种操作带来的<code>Rebalance</code>一般是必然发生，难以避免的，我们组要来讨论下<code>Consumer Group</code>组成员变化引发的<code>Rebalance</code>。</p><p><code>Consumer Group</code>实例增加的情况比较单一，当新启动一个<code>Consumer</code>的<code>group.id</code>已经存在，<code>Coordinator</code>会接管这个新实例，将其加入<code>group.id</code>相同的组，并重分配分区。这种操作场景，一般都还是预期内的，可能是通过扩容来提高<code>TPS</code>的操作。<br><code>Consumer Group</code>实例数减少的情况就比较复杂了。除了正常停止下线某些<code>Consumer</code>实例，还会出现<code>Coordinator</code>误判实例为已停止状态，从而主动踢出<code>Group</code>。导致<code>Rebalance</code>发生。每个<code>Consumer</code>会定期向<code>Coordinator</code>发心跳包，保持<code>keepalive</code>。如果因为某些特殊原因，如网络抖动时，某个<code>Consumer</code>实例没有及时发送心跳请求，<code>Coordinator</code>会将其判定为离线，并从<code>Group</code>中移除，并开启新一轮<code>Rebalance</code>。针对这个问题，可以通过设置<code>Consumer</code>端一下几个参数来进行优化调整：</p><ul><li><strong>session.timeout.ms</strong><br>即<code>Consumer Group</code>内实例的心跳超时时间，默认值是 10s</li><li><strong>heartbeat.interval.ms</strong><br>即心跳请求频率，频繁发送心跳请求会额外消耗带宽资源，但是能够更及时的触发<code>Rebalance</code>，默认值为 3s</li><li><strong>max.poll.interval.ms</strong><br>调用<code>poll</code>方法的时间间隔，默认值为 5min。期间没消费完<code>poll</code>回的消息，<code>Coordinator</code>会开启新一轮<code>Rebalance</code></li></ul><p>根据平时的实践经验，建议:<br><code>session.timeout.ms=6s</code><br><code>heartbeat.interval.ms=2s</code><br>原则上最好是满足<code>session.timeout.ms &gt;= 3 * heartbeat.interval.ms</code>公式。</p><p><code>max.poll.interval.ms</code>则需要根据下游实际消费能力进行调整，尽量设置的大一点，需要大于下游的最大消息处理时间。</p><p>如果进行完上述的各种调整后，还是频发触发<code>Rebalance</code>，最好再去排查下<code>Consumer</code>端的 GC 情况，实际生产环境中我经常碰到因为 GC 设置问题导致的<code>Consumer</code>程序频发 FGC 的问题，从而导致非预期内的<code>Rebalance</code>。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;https://uploads.hyperxu.com/computer-keyboard-34153.jpg&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;消费组应该算是&lt;code&gt;kafka&lt;/code&gt;中一个比较有特色的设计模式了，而他的重平衡机制也是我们在实际生产使用中，无法避免的一个问题。&lt;/p&gt;
&lt;h2 id=&quot;消费组&quot;&gt;&lt;a href=&quot;#消费组&quot; class=&quot;headerlink&quot; title=&quot;消费组&quot;&gt;&lt;/a&gt;消费组&lt;/h2&gt;&lt;p&gt;&lt;code&gt;Consumer Group&lt;/code&gt;为&lt;code&gt;kafka&lt;/code&gt;提供了可扩展、高容错特性的消费者机制。简单介绍下，大致有以下特点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个&lt;code&gt;Consumer Group&lt;/code&gt;内可以有多个&lt;code&gt;Consumer&lt;/code&gt;实例，该实例可以是一个进程，也可以是进程下的多线程&lt;/li&gt;
&lt;li&gt;每个&lt;code&gt;Consumer Group&lt;/code&gt;有一个唯一标识的&lt;code&gt;Group ID&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;不同&lt;code&gt;Consumer Group&lt;/code&gt;之间相互独立，互不影响&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Consumer Group&lt;/code&gt;内实例，与订阅的&lt;code&gt;topic&lt;/code&gt;分区关系是一对一，或一对多的关系，&lt;code&gt;Consumer Group&lt;/code&gt;会通过&lt;code&gt;Coordinator&lt;/code&gt;尽量保持公平分配&lt;/li&gt;
&lt;/ul&gt;
    
    </summary>
    
      <category term="kafka" scheme="http://www.hyperxu.com/categories/kafka/"/>
    
    
      <category term="大数据" scheme="http://www.hyperxu.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="kafka" scheme="http://www.hyperxu.com/tags/kafka/"/>
    
  </entry>
  
  <entry>
    <title>kafka生产者的幂等和事务处理</title>
    <link href="http://www.hyperxu.com/2020/05/06/kafka-7/"/>
    <id>http://www.hyperxu.com/2020/05/06/kafka-7/</id>
    <published>2020-05-06T06:39:28.000Z</published>
    <updated>2020-10-12T03:11:58.298Z</updated>
    
    <content type="html"><![CDATA[<p>之前和大家聊过<code>kafka</code>是如何保证消息不丢失的，今天再讲讲在不丢消息的同时，如何实现精确一次处理的语义实现。</p><p>消息组件对消息的可靠性保障，常见的模式有3种：</p><ul><li>最多一次(at most once)：消息可能会丢失，但不会重复</li><li>至少一次(at least once)：消息不会丢失，但有可能重复</li><li>精确一次(<strong>exactly once</strong>)：消息不会丢失，且不会重复，精准一次发送</li></ul><p><code>kafka</code>默认情况下，提供的是<code>至少一次</code>的可靠性保障。即<code>broker</code>保障<code>已提交</code>的消息的发送，但是遇上某些意外情况，如：网络抖动，超时等问题，导致<code>Producer</code>没有收到<code>broker</code>返回的数据<code>ack</code>，则<code>Producer</code>会继续重试发送消息，从而导致消息重复发送。<br>相应的，如果我们禁止<code>Producer</code>的失败重试发送功能，消息要么写入成功，要么写入失败，但绝不会重复发送。这样就是<code>最多一次</code>的消息保障模式。<br>但对于消息组件，排除特殊业务场景，我们追求的一定是<code>精确一次</code>的消息保障模式。<code>kafka</code>通过幂等性（Idempotence）和事务（Transaction）的机制，提供了这种精确的消息保障。</p><a id="more"></a><h2 id="幂等"><a href="#幂等" class="headerlink" title="幂等"></a>幂等</h2><p>这里就不多说幂等的含义了，不清楚的自己查下资料。<code>Producer</code>默认不是幂等性的，向分区发送数据时，可能会出现同一条消息被发送多次导致消息重复的情况。但只需增加一些参数，即可开启幂等性。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">props.put(“enable.idempotence”, ture)</span><br><span class="line">或者</span><br><span class="line">props.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG， <span class="keyword">true</span>)</span><br></pre></td></tr></table></figure><p>开启<code>enable.idempotence</code>后，<code>kafka</code>就会自动帮你做好消息去重的一系列工作。底层具体实现原理很简单，就是用空间换时间的优化思路，即在<code>broker</code>端多存一些字段来标识数据的唯一性。当<code>Producer</code>发送了具有相同字段值的消息后，<code>broker</code>会进行匹配去重，丢弃重复的数据。实际的代码没这么简单，但大致是这么个处理逻辑。<br>官方的这个幂等实现看似简单高效，但也存在他的局限性。他只能保证单分区上的幂等性，即一个幂等性<code>Producer</code>只能够保证某个<code>topic</code>的一个分区上不出现重复消息，无法实现多分区的幂等。此外，如果<code>Producer</code>重启，也会导致幂等重置。</p><h2 id="事务"><a href="#事务" class="headerlink" title="事务"></a>事务</h2><p>对于多分区保证幂等的场景，则需要事务特性来处理了。<code>kafka</code>的事务跟我们常见数据库事务概念差不多，也是提供经典的<code>ACID</code>，即原子性（Atomicity）、一致性 (Consistency)、隔离性 (Isolation) 和持久性 (Durability)。<br><code>事务Producer</code>保证消息写入分区的原子性，即这批消息要么全部写入成功，要么全失败。此外，<code>Producer</code>重启回来后，<code>kafka</code>依然保证它们发送消息的精确一次处理。<br>事务特性的配置也很简单：</p><ul><li>和幂等<code>Producer</code>一样，开启<code>enable.idempotence = true</code></li><li>设置<code>Producer</code>端参数<code>transctional.id</code></li></ul><p><code>事务Producer</code>的代码稍微也有点不一样，需要调一些事务处理的<code>API</code>。数据的发送需要放在<code>beginTransaction</code>和<code>commitTransaction</code>之间。<code>Consumer</code>端的代码也需要加上<code>isolation.level</code>参数，用以处理事务提交的数据。示例代码:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">producer.initTransactions();</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">            producer.beginTransaction();</span><br><span class="line">            producer.send(record1);</span><br><span class="line">            producer.send(record2);</span><br><span class="line">            producer.commitTransaction();</span><br><span class="line">&#125; <span class="keyword">catch</span> (KafkaException e) &#123;</span><br><span class="line">            producer.abortTransaction();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>事务Producer</code>虽然在多分区的数据处理上保证了幂等，但是处理性能上相应的是会有一些下降的。</p><h2 id="依赖redis实现幂等"><a href="#依赖redis实现幂等" class="headerlink" title="依赖redis实现幂等"></a>依赖redis实现幂等</h2><p>这里为什么还要额外讲通过依赖<code>redis</code>来实现幂等呢？因为笔者在早期维护<code>kafka</code>相关应用时，那会0.8系列版本的<code>kafka</code>还没有这些自带的幂等事务特性，只能依靠开发者自己来实现。<br>常见的方式就是通过数据的业务属性来生成个<code>uniqueId</code>来维护到<code>redis</code>中，利用<code>redis</code>的高并发，高吞吐，分布式锁特性，让写入<code>kafka</code>多分区的数据前，先去<code>redis</code>中校验一下<code>uniqueId</code>等方式，来实现幂等。得益于<code>redis</code>的高性能，在保证幂等同时，还能不让消息数据吞吐性能下降太多。当然，因为<code>redis</code>的依赖引入，也增加了架构的复杂度，从运维上来说也增加了整体的故障点，其中取舍需要自己来全局判断。</p><blockquote><p>这次大概先介绍了下<code>kafka</code>的幂等各种实现方式，实际在事务，和依赖redis分布式锁来实现幂等的方式中，还要许多点值得我们深究来聊一下的，篇幅所限，后续再细讲</p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;之前和大家聊过&lt;code&gt;kafka&lt;/code&gt;是如何保证消息不丢失的，今天再讲讲在不丢消息的同时，如何实现精确一次处理的语义实现。&lt;/p&gt;
&lt;p&gt;消息组件对消息的可靠性保障，常见的模式有3种：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;最多一次(at most once)：消息可能会丢失，但不会重复&lt;/li&gt;
&lt;li&gt;至少一次(at least once)：消息不会丢失，但有可能重复&lt;/li&gt;
&lt;li&gt;精确一次(&lt;strong&gt;exactly once&lt;/strong&gt;)：消息不会丢失，且不会重复，精准一次发送&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;kafka&lt;/code&gt;默认情况下，提供的是&lt;code&gt;至少一次&lt;/code&gt;的可靠性保障。即&lt;code&gt;broker&lt;/code&gt;保障&lt;code&gt;已提交&lt;/code&gt;的消息的发送，但是遇上某些意外情况，如：网络抖动，超时等问题，导致&lt;code&gt;Producer&lt;/code&gt;没有收到&lt;code&gt;broker&lt;/code&gt;返回的数据&lt;code&gt;ack&lt;/code&gt;，则&lt;code&gt;Producer&lt;/code&gt;会继续重试发送消息，从而导致消息重复发送。&lt;br&gt;相应的，如果我们禁止&lt;code&gt;Producer&lt;/code&gt;的失败重试发送功能，消息要么写入成功，要么写入失败，但绝不会重复发送。这样就是&lt;code&gt;最多一次&lt;/code&gt;的消息保障模式。&lt;br&gt;但对于消息组件，排除特殊业务场景，我们追求的一定是&lt;code&gt;精确一次&lt;/code&gt;的消息保障模式。&lt;code&gt;kafka&lt;/code&gt;通过幂等性（Idempotence）和事务（Transaction）的机制，提供了这种精确的消息保障。&lt;/p&gt;
    
    </summary>
    
      <category term="kafka" scheme="http://www.hyperxu.com/categories/kafka/"/>
    
    
      <category term="大数据" scheme="http://www.hyperxu.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="kafka" scheme="http://www.hyperxu.com/tags/kafka/"/>
    
  </entry>
  
  <entry>
    <title>kafka是如何保证消息不丢失的</title>
    <link href="http://www.hyperxu.com/2020/01/16/kafka-6/"/>
    <id>http://www.hyperxu.com/2020/01/16/kafka-6/</id>
    <published>2020-01-16T06:39:28.000Z</published>
    <updated>2020-02-08T06:14:49.525Z</updated>
    
    <content type="html"><![CDATA[<p>今天和大家聊一下，<code>kafka</code>对于消息的可靠性保证。作为消息引擎组件，保证消息不丢失，是非常重要的。</p><p>那么<code>kafka</code>是如何保证消息不丢失的呢？</p><h2 id="前提条件"><a href="#前提条件" class="headerlink" title="前提条件"></a>前提条件</h2><p>任何消息组件不丢数据都是在特定场景下一定条件的，<code>kafka</code>要保证消息不丢，有两个核心条件。</p><p>第一，必须是<code>已提交的消息</code>，即<code>committed message</code>。<code>kafka</code>对于<code>committed message</code>的定义是，生产者提交消息到<code>broker</code>，并等到多个<code>broker</code>确认并返回给生产者已提交的确认信息。而这<code>多个broker</code>是由我们自己来定义的，可以选择只要有一个<code>broker</code>成功保存该消息就算是已提交，也可以是令所有<code>broker</code>都成功保存该消息才算是已提交。不论哪种情况，<code>kafka</code>只对已提交的消息做持久化保证。</p><p>第二，也就是最基本的条件，虽然<code>kafka</code>集群是分布式的，但也必须保证有足够<code>broker</code>正常工作，才能对消息做持久化做保证。也就是说 <code>kafka</code>不丢消息是有前提条件的，假如你的消息保存在 N 个<code>kafka broker</code>上，那么这个前提条件就是这 N 个<code>broker</code>中至少有 1 个存活。只要这个条件成立，<code>kafka</code>就能保证你的这条消息永远不会丢失。</p><a id="more"></a><h2 id="如何保证消息不丢"><a href="#如何保证消息不丢" class="headerlink" title="如何保证消息不丢"></a>如何保证消息不丢</h2><p>一条消息从产生，到发送到<code>kafka</code>保存，到被取出消费，会有多个场景和流程阶段，可能会出现丢失情况，我们聊一下<code>kafka</code>通过哪些手段来保障消息不丢。</p><h3 id="生产端"><a href="#生产端" class="headerlink" title="生产端"></a>生产端</h3><p><code>Producer</code>端可能会丢失消息。目前<code>Kafka Producer</code>是异步发送消息的，也就是说如果你调用的是<code>producer.send(msg)</code>这个<code>API</code>，那么它通常会立即返回，但此时你不保证消息发送已成功完成。可能会出现：网络抖动，导致消息压根就没有发送到<code>Broker</code>端；或者消息本身不合规导致<code>Broker</code>拒绝接收（比如消息太大了，超过了<code>Broker</code>的限制）。</p><p>实际上，使用<code>producer.send(msg, callback)</code>接口就能避免这个问题，根据回调，一旦出现消息提交失败的情况，就可以有针对性地进行处理。如果是因为那些瞬时错误，<code>Producer</code>重试就可以了；如果是消息不合规造成的，那么调整消息格式后再次发送。总之，处理发送失败的责任在<code>Producer</code>端而非<code>Broker</code>端。当然，如果此时<code>broker</code>宕机，那就另当别论，需要及时处理<code>broker</code>异常问题。</p><h3 id="消费端"><a href="#消费端" class="headerlink" title="消费端"></a>消费端</h3><p><code>Consumer</code>端丢数据的情况，稍微复杂点。<code>Consumer</code>有个”位移“(<code>offset</code>)的概念，表示<code>Consumer</code>当前消费到<code>topic</code>分区的哪个位置。如图：<br><img src="https://uploads.hyperxu.com/log_consumer.png" alt=""></p><p><code>kafka</code>通过先消费消息，后更新<code>offset</code>，来保证消息不丢失。但是这样可能会出现消息重复的情况，具体如何保证<code>only-once</code>，后续再单独分享。</p><p>当我们<code>consumer</code>端开启多线程异步去消费时，情况又会变得复杂一些。此时<code>consumer</code>自动地向前更新<code>offset</code>，假如其中某个线程运行失败了，它负责的消息没有被成功处理，但位移已经被更新了，因此这条消息对于<code>consumer</code>而言实际上是丢失了。这里的关键就在自动提交<code>offset</code>，如何真正地确认消息是否真的被消费，再进行更新<code>offset</code>。</p><p>这个问题的解决起来也简单：如果是多线程异步处理消费消息，<code>consumer</code>不要开启自动提交<code>offset</code>，<code>consumer</code>端程序自己来处理<code>offset</code>的提交更新。提醒你一下，单个<code>consumer</code>程序使用多线程来消费消息说起来容易，写成代码还是有点麻烦的，因为你很难正确地处理<code>offset</code>的更新，也就是说避免无消费消息丢失很简单，但极易出现消息被消费了多次的情况。</p><h2 id="实践配置"><a href="#实践配置" class="headerlink" title="实践配置"></a>实践配置</h2><p>最后分享下<code>kafka</code>无消息丢失配置：</p><ol><li><code>producer</code>端使用<code>producer.send(msg, callback)</code>带有回调的<code>send</code>方法。</li><li>设置<code>acks = all</code>。<code>acks</code>是<code>Producer</code>的一个参数，代表“已提交”消息的定义。如果设置成<code>all</code>，则表明所有<code>Broker</code>都要接收到消息，该消息才算是“已提交”。</li><li>设置<code>retries</code>为一个较大的值。同样是<code>Producer</code>的参数。当出现网络抖动时，消息发送可能会失败，此时配置了<code>retries</code>的<code>Producer</code>能够自动重试发送消息，尽量避免消息丢失。</li><li>设置<code>unclean.leader.election.enable = false</code>。这是<code>Broker</code>端的参数，在<code>kafka</code>版本迭代中社区也多次反复修改过他的默认值，之前比较具有争议。它控制哪些<code>Broker</code>有资格竞选分区的<code>Leader</code>。如果一个<code>Broker</code>落后原先的<code>Leader</code>太多，那么它一旦成为新的<code>Leader</code>，将会导致消息丢失。故一般都要将该参数设置成<code>false</code>。</li><li>设置<code>replication.factor &gt;= 3</code>。这也是<code>Broker</code>端的参数。保存多份消息冗余，不多解释了。</li><li>设置<code>min.insync.replicas &gt; 1</code>。<code>Broker</code>端参数，控制消息至少要被写入到多少个副本才算是“已提交”。设置成大于 1 可以提升消息持久性。在生产环境中不要使用默认值 1。确保<code>replication.factor &gt; min.insync.replicas</code>。如果两者相等，那么只要有一个副本离线，整个分区就无法正常工作了。推荐设置成<code>replication.factor = min.insync.replicas + 1</code>。</li><li>确保消息消费完成再提交。<code>Consumer</code>端有个参数<code>enable.auto.commit</code>，最好设置成<code>false</code>，并自己来处理<code>offset</code>的提交更新。</li></ol><blockquote><p>春节将至，希望大家春节期间，线上服务稳定运行不宕机。提前祝大家新年快乐。</p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;今天和大家聊一下，&lt;code&gt;kafka&lt;/code&gt;对于消息的可靠性保证。作为消息引擎组件，保证消息不丢失，是非常重要的。&lt;/p&gt;
&lt;p&gt;那么&lt;code&gt;kafka&lt;/code&gt;是如何保证消息不丢失的呢？&lt;/p&gt;
&lt;h2 id=&quot;前提条件&quot;&gt;&lt;a href=&quot;#前提条件&quot; class=&quot;headerlink&quot; title=&quot;前提条件&quot;&gt;&lt;/a&gt;前提条件&lt;/h2&gt;&lt;p&gt;任何消息组件不丢数据都是在特定场景下一定条件的，&lt;code&gt;kafka&lt;/code&gt;要保证消息不丢，有两个核心条件。&lt;/p&gt;
&lt;p&gt;第一，必须是&lt;code&gt;已提交的消息&lt;/code&gt;，即&lt;code&gt;committed message&lt;/code&gt;。&lt;code&gt;kafka&lt;/code&gt;对于&lt;code&gt;committed message&lt;/code&gt;的定义是，生产者提交消息到&lt;code&gt;broker&lt;/code&gt;，并等到多个&lt;code&gt;broker&lt;/code&gt;确认并返回给生产者已提交的确认信息。而这&lt;code&gt;多个broker&lt;/code&gt;是由我们自己来定义的，可以选择只要有一个&lt;code&gt;broker&lt;/code&gt;成功保存该消息就算是已提交，也可以是令所有&lt;code&gt;broker&lt;/code&gt;都成功保存该消息才算是已提交。不论哪种情况，&lt;code&gt;kafka&lt;/code&gt;只对已提交的消息做持久化保证。&lt;/p&gt;
&lt;p&gt;第二，也就是最基本的条件，虽然&lt;code&gt;kafka&lt;/code&gt;集群是分布式的，但也必须保证有足够&lt;code&gt;broker&lt;/code&gt;正常工作，才能对消息做持久化做保证。也就是说 &lt;code&gt;kafka&lt;/code&gt;不丢消息是有前提条件的，假如你的消息保存在 N 个&lt;code&gt;kafka broker&lt;/code&gt;上，那么这个前提条件就是这 N 个&lt;code&gt;broker&lt;/code&gt;中至少有 1 个存活。只要这个条件成立，&lt;code&gt;kafka&lt;/code&gt;就能保证你的这条消息永远不会丢失。&lt;/p&gt;
    
    </summary>
    
      <category term="kafka" scheme="http://www.hyperxu.com/categories/kafka/"/>
    
    
      <category term="大数据" scheme="http://www.hyperxu.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="kafka" scheme="http://www.hyperxu.com/tags/kafka/"/>
    
  </entry>
  
  <entry>
    <title>kafka的发行版选择</title>
    <link href="http://www.hyperxu.com/2020/01/15/kafka-5/"/>
    <id>http://www.hyperxu.com/2020/01/15/kafka-5/</id>
    <published>2020-01-15T05:13:28.000Z</published>
    <updated>2020-01-15T09:39:22.594Z</updated>
    
    <content type="html"><![CDATA[<p>今天继续和大家聊一下，<code>kafka</code>的各种发行版。<code>kafka</code>历经数年的发展，从最初纯粹的消息引擎，到近几年开始在流处理平台生态圈发力，衍生出了各种不同特性的版本。</p><h2 id="你了解几种kafka"><a href="#你了解几种kafka" class="headerlink" title="你了解几种kafka"></a>你了解几种kafka</h2><p><code>kafka</code>的确有好几种，这里我不是指他的版本，是指存在多个组织或公司发布不同特性的<code>kafka</code>。你应该听说过<code>Linux</code>发行版，比如我们熟知的<code>CentOS</code>、<code>RedHat</code>、<code>Ubuntu</code>等，它们都是<code>Linux</code>系统，其实就是因为它们是不同公司发布的<code>Linux</code>系统，即不同的发行版。<code>kafka</code>也同样有多个发行版。</p><h3 id="Apache-Kafka"><a href="#Apache-Kafka" class="headerlink" title="Apache Kafka"></a>Apache Kafka</h3><p><code>Apache Kafka</code>是最“正统”的<code>kafka</code>，也应该是你最熟悉的发行版了。自<code>kafka</code>开源之初，它便在<code>Apache</code>基金会孵化并最终毕业成为顶级项目，也被称为社区版<code>kafka</code>。重要的是，它是后面其他所有发行版的基础。也就是说，后面提到的其他发行版，要么是原封不动地继承了<code>Apache Kafka</code>，要么是在此之上扩展了新功能，总之<code>Apache Kafka</code>是我们学习和使用<code>kafka</code>的基础。</p><a id="more"></a><h3 id="Cloudera-Hortonworks-Kafka"><a href="#Cloudera-Hortonworks-Kafka" class="headerlink" title="Cloudera/Hortonworks Kafka"></a>Cloudera/Hortonworks Kafka</h3><p><code>Cloudera</code>提供的<code>CDH</code>和<code>Hortonworks</code>提供的<code>HDP</code>是最常见的大数据平台，里面集成了目前主流的大数据框架，能够帮助用户实现从分布式存储、集群调度、流处理到机器学习、实时数据库等全方位的数据处理。我了解到很多创业公司在搭建数据平台时首选就是这两个产品。不管是<code>CDH</code>还是<code>HDP</code>里面都集成了<code>Apache Kafka</code>，因此我把这两款产品中的<code>Kafka</code>称为<code>CDH Kafka</code>和<code>HDP Kafka</code>。</p><p>当然在2018年10月两家公司宣布合并，共同打造世界领先的数据平台，合并成<code>CDP</code>一款产品，但能肯定的是<code>Apache Kafka</code>依然会包含其中，并作为新数据平台的一部分对外提供服务。</p><h3 id="Confluent-Kafka"><a href="#Confluent-Kafka" class="headerlink" title="Confluent Kafka"></a>Confluent Kafka</h3><p><code>Confluent</code>公司，2014年，<code>Kafka</code>的3个创始人<code>Jay Kreps</code>、<code>Naha Narkhede</code>和饶军离开<code>LinkedIn</code>创办了<code>Confluent</code>公司，专注于提供基于<code>Kafka</code>的企业级流处理解决方案。2019年1月，<code>Confluent</code>公司成功融资D轮1.25亿美元，估值也到了25亿美元，足见资本市场的青睐。</p><p>这里说点题外话， 饶军是我们中国人，清华毕业的大神级人物。我们已经看到越来越多的<code>Apache</code>顶级项目创始人中出现了中国人的身影，另一个例子就是<code>Apache Pulsar</code>，它是一个以打败<code>Kafka</code>为目标的新一代消息引擎系统。至于在开源社区中活跃的国人更是数不胜数，这种现象实在令人振奋。</p><p><code>Confluent</code>公司，主要从事商业化<code>Kafka</code>工具开发，并在此基础上发布了<code>Confluent Kafka</code>。<code>Confluent Kafka</code>提供了一些<code>Apache Kafka</code>没有的高级特性，比如跨数据中心备份、<code>Schema</code>注册中心以及集群监控工具等。</p><h2 id="特性对比"><a href="#特性对比" class="headerlink" title="特性对比"></a>特性对比</h2><p>说完世面上常见的<code>kafka</code>，我们来比较一下他们的特性</p><h3 id="Apache-Kafka-1"><a href="#Apache-Kafka-1" class="headerlink" title="Apache Kafka"></a>Apache Kafka</h3><p><code>Apache Kafka</code>，它现在依然是开发人数最多、版本迭代速度最快的<code>kafka</code>。如果你使用<code>Apache Kafka</code>碰到任何问题并提交问题到社区，社区都会比较及时地响应你。这对于我们<code>kafka</code>普通使用者来说还是比较友好的。</p><p>但是<code>Apache Kafka</code>的劣势在于它仅提供最最基础的组件，对于像<code>Kafka Connect</code>额外的数据工具，社区版<code>kafka</code>只提供了一种连接器，即读写磁盘文件的连接器，而没有与其他外部系统交互的连接器，在实际使用过程中需要自行编写代码实现。另外<code>Apache Kafka</code>没有提供任何监控框架或工具，你需要借助第三方的监控框架实现对<code>kafka</code>的监控。目前有一些开源的监控框架可以帮助我们用于监控<code>Kafka</code>（比如<code>Kafka manager</code>，<code>Kafka Eagle</code>等）</p><p>总而言之，如果仅仅是需要一个消息引擎系统或是简单的流处理应用场景，同时需要对系统有较大把控，那么推荐你使用<code>Apache Kafka</code>。</p><h3 id="CDH-HDP-Kafka"><a href="#CDH-HDP-Kafka" class="headerlink" title="CDH/HDP Kafka"></a>CDH/HDP Kafka</h3><p>再说说大数据云平台公司发布的<code>Kafka</code>（CDH/HDP Kafka）。这些大数据平台已经集成了<code>Apache Kafka</code>，通过便捷化的界面操作将·Kafka·的安装、运维、管理、监控全部统一在控制台中。如果你是这些平台的用户一定觉得非常方便，因为所有的操作都可以在前端界面上完成，而不必去执行复杂的<code>Kafka</code>命令。另外这些平台提供的监控界面也非常友好，通常不需要进行任何配置就能有效地监控 <code>Kafka</code>。</p><p>这样做的结果是直接降低了你对<code>Kafka</code>集群的掌控程度。毕竟你对底层的<code>Kafka</code>集群一无所知，难以做到心中有数。这种<code>Kafka</code> 的另一个弊端在于它的滞后性。由于它有自己的发布周期，因此是否能及时地包含最新版本的<code>Kafka</code>就成为了一个问题。比如<code>CDH 6.1.0</code>版本发布时<code>Apache Kafka</code>已经演进到了 2.1.0 版本，但<code>CDH</code>中的<code>Kafka</code>依然是 2.0.0 版本，显然那些在<code>Kafka</code> 2.1.0 中修复的<code>Bug</code>只能等到<code>CDH</code>下次版本更新时才有可能被真正修复，而整个<code>CDH</code>升级还是略显繁重的。</p><p>简单来说，如果你需要快速地搭建消息引擎系统，或者你需要搭建的是多框架构成的数据平台且<code>Kafka</code>只是其中一个组件，那么我推荐你使用这些大数据云平台公司提供的<code>Kafka</code>。</p><h3 id="Confluent-Kafka-1"><a href="#Confluent-Kafka-1" class="headerlink" title="Confluent Kafka"></a>Confluent Kafka</h3><p><code>Confluent Kafka</code>目前分为免费版和企业版两种。前者和<code>Apache Kafka</code>非常相像，除了常规的组件之外，免费版还包含 <code>Schema</code>注册中心和<code>REST proxy</code>两大功能。前者是帮助你集中管理<code>Kafka</code>消息格式以实现数据前后兼容；后者用开放<code>HTTP</code>接口的方式允许你通过网络访问<code>Kafka</code>的各种功能，这两个都是<code>Apache Kafka</code>所没有的。</p><p>除此之外，免费版包含了更多的连接器，都是<code>Confluent</code>公司开发并认证过的，可以免费使用。至于企业版，则提供更多功能。最有用的当属跨数据中心备份和集群监控两大功能了。多个数据中心之间数据的同步以及对集群的监控历来是<code>Kafka</code>的痛点，<code>Confluent Kafka</code>企业版提供了强大的解决方案。</p><p>不过<code>Confluent</code>公司暂时没有发展国内业务的计划，相关的资料以及技术支持都很欠缺，很多国内<code>Confluent Kafka</code>使用者甚至无法找到对应的中文文档，因此目前<code>Confluent Kafka</code>在国内的普及率比较低。</p><p>如果你需要用到<code>Kafka</code>的一些高级特性，且没有预算成本压力，那么推荐使用<code>Confluent Kafka</code>。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;今天继续和大家聊一下，&lt;code&gt;kafka&lt;/code&gt;的各种发行版。&lt;code&gt;kafka&lt;/code&gt;历经数年的发展，从最初纯粹的消息引擎，到近几年开始在流处理平台生态圈发力，衍生出了各种不同特性的版本。&lt;/p&gt;
&lt;h2 id=&quot;你了解几种kafka&quot;&gt;&lt;a href=&quot;#你了解几种kafka&quot; class=&quot;headerlink&quot; title=&quot;你了解几种kafka&quot;&gt;&lt;/a&gt;你了解几种kafka&lt;/h2&gt;&lt;p&gt;&lt;code&gt;kafka&lt;/code&gt;的确有好几种，这里我不是指他的版本，是指存在多个组织或公司发布不同特性的&lt;code&gt;kafka&lt;/code&gt;。你应该听说过&lt;code&gt;Linux&lt;/code&gt;发行版，比如我们熟知的&lt;code&gt;CentOS&lt;/code&gt;、&lt;code&gt;RedHat&lt;/code&gt;、&lt;code&gt;Ubuntu&lt;/code&gt;等，它们都是&lt;code&gt;Linux&lt;/code&gt;系统，其实就是因为它们是不同公司发布的&lt;code&gt;Linux&lt;/code&gt;系统，即不同的发行版。&lt;code&gt;kafka&lt;/code&gt;也同样有多个发行版。&lt;/p&gt;
&lt;h3 id=&quot;Apache-Kafka&quot;&gt;&lt;a href=&quot;#Apache-Kafka&quot; class=&quot;headerlink&quot; title=&quot;Apache Kafka&quot;&gt;&lt;/a&gt;Apache Kafka&lt;/h3&gt;&lt;p&gt;&lt;code&gt;Apache Kafka&lt;/code&gt;是最“正统”的&lt;code&gt;kafka&lt;/code&gt;，也应该是你最熟悉的发行版了。自&lt;code&gt;kafka&lt;/code&gt;开源之初，它便在&lt;code&gt;Apache&lt;/code&gt;基金会孵化并最终毕业成为顶级项目，也被称为社区版&lt;code&gt;kafka&lt;/code&gt;。重要的是，它是后面其他所有发行版的基础。也就是说，后面提到的其他发行版，要么是原封不动地继承了&lt;code&gt;Apache Kafka&lt;/code&gt;，要么是在此之上扩展了新功能，总之&lt;code&gt;Apache Kafka&lt;/code&gt;是我们学习和使用&lt;code&gt;kafka&lt;/code&gt;的基础。&lt;/p&gt;
    
    </summary>
    
      <category term="kafka" scheme="http://www.hyperxu.com/categories/kafka/"/>
    
    
      <category term="大数据" scheme="http://www.hyperxu.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="kafka" scheme="http://www.hyperxu.com/tags/kafka/"/>
    
  </entry>
  
  <entry>
    <title>kafka分区数和吞吐量的关系</title>
    <link href="http://www.hyperxu.com/2020/01/01/kafka-3/"/>
    <id>http://www.hyperxu.com/2020/01/01/kafka-3/</id>
    <published>2020-01-01T03:13:28.000Z</published>
    <updated>2020-01-03T06:13:10.434Z</updated>
    
    <content type="html"><![CDATA[<p><img src="https://uploads.hyperxu.com/logo_20200103135730.png" alt=""></p><h2 id="分区-partition-概念"><a href="#分区-partition-概念" class="headerlink" title="分区(partition)概念"></a>分区(partition)概念</h2><p>要讲kafka分区数和吞吐量的关系，首先得理解什么是分区(partition)。<br><img src="https://uploads.hyperxu.com/E7D66E0B-E03B-4FAF-ADEE-22D86FC69211_20200103135829.png" alt="E7D66E0B-E03B-4FAF-ADEE-22D86FC69211"></p><p><code>Partition</code>是作用于具体的<code>Topic</code>而已的，而不是一个独立的概念。<code>Partition</code>能水平扩展客户端的读写性能，是高吞吐量的保障。通俗的讲，<code>Partition</code>就是一块保存具体数据的空间，本质就是磁盘上存放数据的文件夹，所以<code>Partition</code>是不能跨<code>Broker</code>存在的，也不能在同一个<code>Broker</code>上跨磁盘。对于一个<code>Topic</code>，可以根据需要设定<code>Partition</code>的个数。数据持久化时，每条消息都是根据一定的分区规则路由到对应的<code>Partition</code>中，并<code>append</code>在<code>log</code>文件的尾部(这一点类似于<code>HDFS</code>)，如上图；在同一个<code>Partition</code>中消息是顺序写入的且始终保持有序性；但是不同<code>Partition</code>之间不能保证消息的有序性(高吞吐量的保障)。<code>kafka</code>就是通过使用分区的设计将<code>topic</code>的消息打散到多个分区分布保存在不同的<code>broker</code>上，实现了<code>producer</code>和<code>consumer</code>消息处理的高吞吐量。</p><a id="more"></a><h2 id="吞吐量关系"><a href="#吞吐量关系" class="headerlink" title="吞吐量关系"></a>吞吐量关系</h2><p><img src="https://uploads.hyperxu.com/5433D18B-CDF1-4418-AB6C-1F9CF5559A7E_20200103135836.png" alt="5433D18B-CDF1-4418-AB6C-1F9CF5559A7E"></p><p>在<code>kafka</code>中，<code>Partition</code>并不是最小的数据存储单元。<code>Partition</code>下还可以细分成<code>Segment</code>，每个<code>Partition</code>是由一个或多个<code>Segment</code>组成。但<code>patition</code>是<code>kafka</code>并行操作的最小单元。在<code>producer</code>和<code>broker</code>端，向每一个分区写入数据是可以完全并行化的，此时，可以通过加大硬件资源的利用率来提升系统的吞吐量，例如对数据进行压缩。在<code>consumer</code>端，<code>kafka</code>只允许单个<code>partition</code>的数据同时被一个<code>consumer</code>线程消费。因此，在<code>consumer</code>端，每一个<code>Consumer Group</code>内部的<code>consumer</code>并行度完全依赖于被消费的分区数量。因此，通常情况下，在一个Kafka集群中，<code>partition</code>的数量越多，意味着可以到达的吞吐量越大。</p><p>　　我们可以粗略地通过吞吐量来计算<code>kafka</code>集群的分区数量。假设对于单个<code>partition</code>，<code>producer</code>端的可达吞吐量为p，<code>Consumer</code>端的可达吞吐量为c，期望的目标吞吐量为t，那么集群所需要的<code>partition</code>数量至少为<code>max(t/p,t/c)</code>。在<code>producer</code>端，单个分区的吞吐量大小会受到批量大小、数据压缩方法、确认类型（同步/异步）、复制因子等配置参数的影响。经过测试，在<code>producer</code>端，单个<code>partition</code>的吞吐量通常是在10MB/s左右。在<code>consumer</code>端，单个<code>partition</code>的吞吐量依赖于<code>consumer</code>端每个消息的应用逻辑处理速度。因此，我们需要对<code>consumer</code>端的吞吐量进行测量。</p><h2 id="分区扩展"><a href="#分区扩展" class="headerlink" title="分区扩展"></a>分区扩展</h2><p>　　虽然随着时间的推移，我们能够对分区的数量进行添加，但是对于基于<code>Key</code>来生成的这一类消息不太一样。当<code>producer</code>向<code>kafka</code>写入基于<code>key</code>的消息时，<code>kafka</code>通过<code>key</code>的<code>hash</code>值来确定消息需要写入哪个具体的分区。通过这样的方案，<code>kafka</code>能够确保相同<code>key</code>值的数据可以写入同一个<code>partition</code>。<code>kafka</code>的这一能力对于一部分顺序要求的业务应用是极为重要的，例如对于同一个<code>key</code>的所有消息，<code>consumer</code>需要按消息的顺序进行有序消费。如果<code>partition</code>的数量发生改变，那么上面的有序性保证将不复存在。为了避免上述情况发生，通常的解决办法是多分配一些分区，以满足未来的需求。</p><p>　　此外，我们还可以基于当前的业务吞吐量为<code>kafka</code>集群分配较小的<code>broker</code>数量，随着业务增长，再向集群中增加更多的<code>broker</code>，然后将适当比例的<code>partition</code>迁移到新增加的<code>broker</code>中去（迁移可以参考我之前的文章）。通过这样的方法，可以在满足各种应用场景（包括基于<code>key</code>消息的场景）的情况下，保持业务吞吐量的扩展性。</p><p>在规划分区数时，除了吞吐量，还有一些其他因素值得考虑，后续再聊。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;https://uploads.hyperxu.com/logo_20200103135730.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;分区-partition-概念&quot;&gt;&lt;a href=&quot;#分区-partition-概念&quot; class=&quot;headerlink&quot; title=&quot;分区(partition)概念&quot;&gt;&lt;/a&gt;分区(partition)概念&lt;/h2&gt;&lt;p&gt;要讲kafka分区数和吞吐量的关系，首先得理解什么是分区(partition)。&lt;br&gt;&lt;img src=&quot;https://uploads.hyperxu.com/E7D66E0B-E03B-4FAF-ADEE-22D86FC69211_20200103135829.png&quot; alt=&quot;E7D66E0B-E03B-4FAF-ADEE-22D86FC69211&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Partition&lt;/code&gt;是作用于具体的&lt;code&gt;Topic&lt;/code&gt;而已的，而不是一个独立的概念。&lt;code&gt;Partition&lt;/code&gt;能水平扩展客户端的读写性能，是高吞吐量的保障。通俗的讲，&lt;code&gt;Partition&lt;/code&gt;就是一块保存具体数据的空间，本质就是磁盘上存放数据的文件夹，所以&lt;code&gt;Partition&lt;/code&gt;是不能跨&lt;code&gt;Broker&lt;/code&gt;存在的，也不能在同一个&lt;code&gt;Broker&lt;/code&gt;上跨磁盘。对于一个&lt;code&gt;Topic&lt;/code&gt;，可以根据需要设定&lt;code&gt;Partition&lt;/code&gt;的个数。数据持久化时，每条消息都是根据一定的分区规则路由到对应的&lt;code&gt;Partition&lt;/code&gt;中，并&lt;code&gt;append&lt;/code&gt;在&lt;code&gt;log&lt;/code&gt;文件的尾部(这一点类似于&lt;code&gt;HDFS&lt;/code&gt;)，如上图；在同一个&lt;code&gt;Partition&lt;/code&gt;中消息是顺序写入的且始终保持有序性；但是不同&lt;code&gt;Partition&lt;/code&gt;之间不能保证消息的有序性(高吞吐量的保障)。&lt;code&gt;kafka&lt;/code&gt;就是通过使用分区的设计将&lt;code&gt;topic&lt;/code&gt;的消息打散到多个分区分布保存在不同的&lt;code&gt;broker&lt;/code&gt;上，实现了&lt;code&gt;producer&lt;/code&gt;和&lt;code&gt;consumer&lt;/code&gt;消息处理的高吞吐量。&lt;/p&gt;
    
    </summary>
    
      <category term="kafka" scheme="http://www.hyperxu.com/categories/kafka/"/>
    
    
      <category term="大数据" scheme="http://www.hyperxu.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="kafka" scheme="http://www.hyperxu.com/tags/kafka/"/>
    
  </entry>
  
  <entry>
    <title>kafka分区数过多的弊端</title>
    <link href="http://www.hyperxu.com/2020/01/01/kafka-4/"/>
    <id>http://www.hyperxu.com/2020/01/01/kafka-4/</id>
    <published>2020-01-01T03:13:28.000Z</published>
    <updated>2020-01-08T09:36:06.251Z</updated>
    
    <content type="html"><![CDATA[<p>上篇文章我们了解到，如果一个<code>topic</code>分区越多，理论上整个集群所能达到的吞吐量就越大。那么，分区数越多就越好吗？显然不是。今天我们来聊下<code>kafka</code>在分区数过多的情况下，会带来哪些弊端。</p><h2 id="内存开销"><a href="#内存开销" class="headerlink" title="内存开销"></a>内存开销</h2><p>客户端<code>producer</code>有个参数<code>batch.size</code>默认为16KB。它会为每个分区缓存消息，一旦批次数满了后，将消息批量发出。一般来说，这个设计是用于提升吞吐性能的。但是由于这个参数是<code>partition</code>级别的，如果分区数越多，这部分缓存所需的内存占用也会越多。假如有10000个分区，按照默认配置，这部分缓存就要占用约157MB的内存。而<code>consumer</code>端呢？抛开拉取数据所需的内存不说，单说线程的开销。如果还是10000个分区，同时<code>consumer</code>线程数要匹配分区数的话(大部分情况下是最佳的消费吞吐量配置)，那么在<code>consumer client</code>就要创建10000个线程，也需要创建大约10000个<code>Socket</code>去获取分区数据，这里面的线程切换的开销本身就已经不容小觑了。<br>服务器端的开销也不小，如果阅读<code>kafka</code>源码的话就会发现，服务器端的很多组件在内存中维护了<code>partition</code>级别的缓存，比如<code>controller</code>，<code>FetcherManager</code>等，因此分区数越多，这种缓存的成本就越大。</p><a id="more"></a><h2 id="文件句柄开销"><a href="#文件句柄开销" class="headerlink" title="文件句柄开销"></a>文件句柄开销</h2><p>每个分区在文件系统上会对应一个目录，用于存储维护<code>kafka</code>数据日志。该目录通常会有3个文件，<code>.log</code>，<code>.index</code>，<code>.timeindex</code>，对应<code>kafka</code>的日志数据文件和索引文件(老版本kafka没有<code>timeindex</code>文件)。<code>broker</code>会一直保持打开这3个文件句柄(<code>file handler</code>)。因此，如果分区数越多，所需要保持打开状态的文件句柄数也就越多，最终可能会突破单台<code>broker</code>的<code>ulimit -n</code>的上限。</p><h2 id="链路延迟"><a href="#链路延迟" class="headerlink" title="链路延迟"></a>链路延迟</h2><p><code>kafka</code>的链路延迟也就是<code>producer</code>端发布消息到<code>consumer</code>端接收消息所需要的时间。<code>kafka</code>只有在消息提交之后，才会将消息暴露给消费者，期间消息需要在<code>in-sync</code>副本列表中完成同步复制，这是耗时的主要部分。默认情况下，每个<code>broker</code>从其他<code>broker</code>节点进行数据副本同步时，该节点只会为此分配一个线程，该线程需要完成该<code>broker</code>上所有<code>partition</code>数据的复制。我查到数据显示，将1000个<code>partition</code>从一个<code>broker</code>到另一个<code>broker</code>所需时间延迟约为20ms，这意味着链路延迟至少是20ms。这样的延迟对于一些实时业务来说可能就有些长了。</p><h2 id="SLA"><a href="#SLA" class="headerlink" title="SLA"></a>SLA</h2><p><code>kafka</code>是通过副本机制(replica)提供高可用，来保障<code>SLA</code>的。每个<code>partition</code>都会有多个副本，每个副本分别存在于不同的<code>broker</code>。所有的数据副本中，有一个数据副本被选举为<code>leader</code>，负责处理<code>producer</code>和<code>consumer</code>请求。其他的数据副本为<code>follower</code>，由<code>Kafka controller</code>负责保证与<code>leader</code>的同步。当<code>leader</code>不可用时，会从<code>follower</code>中重新选出新的<code>leader</code>，这中间会有短暂的不可用时间，虽然大部分情况下可能只是几毫秒级别。但是假如，一个2节点的<code>kafka</code>集群中存在2000个<code>partition</code>，每个<code>partition</code>拥有2个副本。当其中一个<code>broker</code>意外宕机，所有1000个<code>partition</code>同时变得不可用。假设每一个<code>partition</code>恢复时间是5ms，那么1000个<code>partition</code>的恢复时间将会花费5秒钟，这可能对用户来说就会有一个明显的感知了。如果宕机的是<code>controller</code>节点，不可用时间将会更严重。</p><p>上述问题，通常情况下，都可以通过扩容集群来缓解，毕竟在不考虑成本的情况下，堆机器可以解决90%的问题。当然正常情况，还是得在合理的成本范围内，进行合理的规划和调优，上述弊端一般都是能在可控范围内的。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;上篇文章我们了解到，如果一个&lt;code&gt;topic&lt;/code&gt;分区越多，理论上整个集群所能达到的吞吐量就越大。那么，分区数越多就越好吗？显然不是。今天我们来聊下&lt;code&gt;kafka&lt;/code&gt;在分区数过多的情况下，会带来哪些弊端。&lt;/p&gt;
&lt;h2 id=&quot;内存开销&quot;&gt;&lt;a href=&quot;#内存开销&quot; class=&quot;headerlink&quot; title=&quot;内存开销&quot;&gt;&lt;/a&gt;内存开销&lt;/h2&gt;&lt;p&gt;客户端&lt;code&gt;producer&lt;/code&gt;有个参数&lt;code&gt;batch.size&lt;/code&gt;默认为16KB。它会为每个分区缓存消息，一旦批次数满了后，将消息批量发出。一般来说，这个设计是用于提升吞吐性能的。但是由于这个参数是&lt;code&gt;partition&lt;/code&gt;级别的，如果分区数越多，这部分缓存所需的内存占用也会越多。假如有10000个分区，按照默认配置，这部分缓存就要占用约157MB的内存。而&lt;code&gt;consumer&lt;/code&gt;端呢？抛开拉取数据所需的内存不说，单说线程的开销。如果还是10000个分区，同时&lt;code&gt;consumer&lt;/code&gt;线程数要匹配分区数的话(大部分情况下是最佳的消费吞吐量配置)，那么在&lt;code&gt;consumer client&lt;/code&gt;就要创建10000个线程，也需要创建大约10000个&lt;code&gt;Socket&lt;/code&gt;去获取分区数据，这里面的线程切换的开销本身就已经不容小觑了。&lt;br&gt;服务器端的开销也不小，如果阅读&lt;code&gt;kafka&lt;/code&gt;源码的话就会发现，服务器端的很多组件在内存中维护了&lt;code&gt;partition&lt;/code&gt;级别的缓存，比如&lt;code&gt;controller&lt;/code&gt;，&lt;code&gt;FetcherManager&lt;/code&gt;等，因此分区数越多，这种缓存的成本就越大。&lt;/p&gt;
    
    </summary>
    
      <category term="kafka" scheme="http://www.hyperxu.com/categories/kafka/"/>
    
    
      <category term="大数据" scheme="http://www.hyperxu.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="kafka" scheme="http://www.hyperxu.com/tags/kafka/"/>
    
  </entry>
  
  <entry>
    <title>kafka数据存储目录间迁移</title>
    <link href="http://www.hyperxu.com/2019/12/13/kafka-2/"/>
    <id>http://www.hyperxu.com/2019/12/13/kafka-2/</id>
    <published>2019-12-13T03:13:28.000Z</published>
    <updated>2019-12-14T11:09:36.883Z</updated>
    
    <content type="html"><![CDATA[<p>生产环境<code>kafka</code>集群，在数据量大的情况下，会出现单机各个磁盘间的占用不均匀情况，经常出现“一边倒”的情形。</p><h2 id="原因探究"><a href="#原因探究" class="headerlink" title="原因探究"></a>原因探究</h2><p>这是因为<code>kafka</code>只保证分区数量在各个磁盘上均匀分布，但它无法统计每个分区实际占用磁盘空间。因此很有可能出现某些分区消息数量巨大导致占用大量磁盘空间的情况。在1.1版本之前，用户对此基本没有优雅的处理方法，即便手动迁移日志文件和offset信息，也需要重启生效，风险极高。因为1.1之前<code>kafka</code>只支持分区数据在不同<code>broker</code>间的重分配，而无法做到在同一个<code>broker</code>下的不同磁盘间做重分配。1.1版本正式支持副本在不同路径间的迁移，具体的实现细节详见<code>kafka</code>官方<code>wiki</code><a href="https://cwiki.apache.org/confluence/display/KAFKA/KIP-113%3A+Support+replicas+movement+between+log+directories" target="_blank" rel="external"><code>KIP-113</code></a>。</p><h2 id="目录间迁移步骤"><a href="#目录间迁移步骤" class="headerlink" title="目录间迁移步骤"></a>目录间迁移步骤</h2><p>假设我在<code>server.properties</code>文件中配置了多个日志存储路径(代表多块磁盘)，如下所示：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="comment"># A comma seperated list of directories under which to store log files</span></div><div class="line"></div><div class="line">log.dirs=/data1/kafka-logs,/data2/kafka-logs,/data3/kafka-logs</div></pre></td></tr></table></figure></p><a id="more"></a><p>然后我创建了一个9分区的<code>topic</code>，并发送了900W条消息。查询这些目录发现<code>Kafka</code>均匀地将9个分区分布到这三个路径上，如下所示：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line">&gt; ll /data1/kafka-logs/ |grep <span class="built_in">test</span>-topic</div><div class="line">drwxr-xr-x   6 kafka  staff  192 Dec 14 17:21 <span class="built_in">test</span>-topic-3</div><div class="line">drwxr-xr-x   6 kafka  staff  192 Dec 14 17:21 <span class="built_in">test</span>-topic-4</div><div class="line">drwxr-xr-x   6 kafka  staff  192 Dec 14 17:21 <span class="built_in">test</span>-topic-5</div><div class="line"></div><div class="line">&gt; ll /data2/kafka-logs/ |grep <span class="built_in">test</span>-topic</div><div class="line">drwxr-xr-x   6 kafka  staff  192 Dec 14 17:21 <span class="built_in">test</span>-topic-0</div><div class="line">drwxr-xr-x   6 kafka  staff  192 Dec 14 17:21 <span class="built_in">test</span>-topic-1</div><div class="line">drwxr-xr-x   6 kafka  staff  192 Dec 14 17:21 <span class="built_in">test</span>-topic-2</div><div class="line"></div><div class="line">&gt; ll /data3/kafka-logs/ |grep <span class="built_in">test</span>-topic</div><div class="line">drwxr-xr-x   6 kafka  staff  192 Dec 14 17:21 <span class="built_in">test</span>-topic-6</div><div class="line">drwxr-xr-x   6 kafka  staff  192 Dec 14 17:21 <span class="built_in">test</span>-topic-7</div><div class="line">drwxr-xr-x   6 kafka  staff  192 Dec 14 17:21 <span class="built_in">test</span>-topic-8</div></pre></td></tr></table></figure></p><p>假设由于还有其他<code>topic</code>数据分布等原因，导致磁盘存储不均衡。需要将<code>test-topic</code>的<code>6</code>，<code>7</code>，<code>8</code>分区全部迁移到<code>/data2</code>路径下，并且把<code>test-topic</code>的<code>1</code>分区迁移到<code>/data1</code>下。若要实现这个需求，我们首先需要写一个<code>JSON</code>文件,<code>migrate-replica.json</code>:<br><figure class="highlight json"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">    <span class="attr">"partitions"</span>: [</div><div class="line">        &#123;</div><div class="line">            <span class="attr">"topic"</span>: <span class="string">"test-topic"</span>,</div><div class="line">            <span class="attr">"partition"</span>: <span class="number">1</span>,</div><div class="line">            <span class="attr">"replicas"</span>: [</div><div class="line">                <span class="number">0</span></div><div class="line">            ],</div><div class="line">            <span class="attr">"log_dirs"</span>: [</div><div class="line">                <span class="string">"/data1/kafka-logs"</span></div><div class="line">            ]</div><div class="line">        &#125;,</div><div class="line">        &#123;</div><div class="line">            <span class="attr">"topic"</span>: <span class="string">"test-topic"</span>,</div><div class="line">            <span class="attr">"partition"</span>: <span class="number">6</span>,</div><div class="line">            <span class="attr">"replicas"</span>: [</div><div class="line">                <span class="number">0</span></div><div class="line">            ],</div><div class="line">            <span class="attr">"log_dirs"</span>: [</div><div class="line">                <span class="string">"/data2/kafka-logs"</span></div><div class="line">            ]</div><div class="line">        &#125;,</div><div class="line">        &#123;</div><div class="line">            <span class="attr">"topic"</span>: <span class="string">"test-topic"</span>,</div><div class="line">            <span class="attr">"partition"</span>: <span class="number">7</span>,</div><div class="line">            <span class="attr">"replicas"</span>: [</div><div class="line">                <span class="number">0</span></div><div class="line">            ],</div><div class="line">            <span class="attr">"log_dirs"</span>: [</div><div class="line">                <span class="string">"/data2/kafka-logs"</span></div><div class="line">            ]</div><div class="line">        &#125;,</div><div class="line">        &#123;</div><div class="line">            <span class="attr">"topic"</span>: <span class="string">"test-topic"</span>,</div><div class="line">            <span class="attr">"partition"</span>: <span class="number">8</span>,</div><div class="line">            <span class="attr">"replicas"</span>: [</div><div class="line">                <span class="number">0</span></div><div class="line">            ],</div><div class="line">            <span class="attr">"log_dirs"</span>: [</div><div class="line">                <span class="string">"/data2/kafka-logs"</span></div><div class="line">            ]</div><div class="line">        &#125;</div><div class="line">    ],</div><div class="line">    <span class="attr">"version"</span>: <span class="number">1</span></div><div class="line">&#125;</div></pre></td></tr></table></figure></p><p>其中，<code>replicas</code>中的0表示<code>broker ID</code>，由于本文只启动了一个<code>broker</code>，且<code>broker.id = 0</code>，故这里只写0即可。实际上你可以指定多个<code>broker</code>实现为多个<code>broker</code>同时迁移副本的功能。另外当前的<code>version</code>固定是1。</p><p>保存好这个<code>JSON</code>后，我们执行以下命令执行副本迁移：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">&gt; bin/kafka-reassign-partitions.sh  --zookeeper localhost:2181 --bootstrap-server localhost:9092 --reassignment-json-file ../migrate-replica.json --execute</div><div class="line"></div><div class="line">Current partition replica assignment</div><div class="line">&#123;<span class="string">"version"</span>:1,<span class="string">"partitions"</span>:[&#123;<span class="string">"topic"</span>:<span class="string">"test-topic"</span>,<span class="string">"partition"</span>:8,<span class="string">"replicas"</span>:[0],<span class="string">"log_dirs"</span>:[<span class="string">"any"</span>]&#125;,&#123;<span class="string">"topic"</span>:<span class="string">"test-topic"</span>,<span class="string">"partition"</span>:4,<span class="string">"replicas"</span>:[0],<span class="string">"log_dirs"</span>:[<span class="string">"any"</span>]&#125;,&#123;<span class="string">"topic"</span>:<span class="string">"test-topic"</span>,<span class="string">"partition"</span>:5,<span class="string">"replicas"</span>:[0],<span class="string">"log_dirs"</span>:[<span class="string">"any"</span>]&#125;,&#123;<span class="string">"topic"</span>:<span class="string">"test-topic"</span>,<span class="string">"partition"</span>:2,<span class="string">"replicas"</span>:[0],<span class="string">"log_dirs"</span>:[<span class="string">"any"</span>]&#125;,&#123;<span class="string">"topic"</span>:<span class="string">"test-topic"</span>,<span class="string">"partition"</span>:6,<span class="string">"replicas"</span>:[0],<span class="string">"log_dirs"</span>:[<span class="string">"any"</span>]&#125;,&#123;<span class="string">"topic"</span>:<span class="string">"test-topic"</span>,<span class="string">"partition"</span>:3,<span class="string">"replicas"</span>:[0],<span class="string">"log_dirs"</span>:[<span class="string">"any"</span>]&#125;,&#123;<span class="string">"topic"</span>:<span class="string">"test-topic"</span>,<span class="string">"partition"</span>:1,<span class="string">"replicas"</span>:[0],<span class="string">"log_dirs"</span>:[<span class="string">"any"</span>]&#125;,&#123;<span class="string">"topic"</span>:<span class="string">"test-topic"</span>,<span class="string">"partition"</span>:7,<span class="string">"replicas"</span>:[0],<span class="string">"log_dirs"</span>:[<span class="string">"any"</span>]&#125;,&#123;<span class="string">"topic"</span>:<span class="string">"test-topic"</span>,<span class="string">"partition"</span>:0,<span class="string">"replicas"</span>:[0],<span class="string">"log_dirs"</span>:[<span class="string">"any"</span>]&#125;]&#125;</div><div class="line"></div><div class="line">Save this to use as the --reassignment-json-file option during rollback</div><div class="line"></div><div class="line">Successfully started reassignment of partitions.</div></pre></td></tr></table></figure></p><h2 id="迁移结果"><a href="#迁移结果" class="headerlink" title="迁移结果"></a>迁移结果</h2><p>执行完成后，我们再次查看存储目录副本分布：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line">&gt; ll /data1/kafka-logs/ |grep <span class="built_in">test</span>-topic</div><div class="line">drwxr-xr-x   6 kafka  staff  192 Dec 14 17:21 <span class="built_in">test</span>-topic-1</div><div class="line">drwxr-xr-x   6 kafka  staff  192 Dec 14 17:21 <span class="built_in">test</span>-topic-3</div><div class="line">drwxr-xr-x   6 kafka  staff  192 Dec 14 17:21 <span class="built_in">test</span>-topic-4</div><div class="line">drwxr-xr-x   6 kafka  staff  192 Dec 14 17:21 <span class="built_in">test</span>-topic-5</div><div class="line"></div><div class="line">&gt; ll /data2/kafka-logs/ |grep <span class="built_in">test</span>-topic</div><div class="line">drwxr-xr-x   6 kafka  staff  192 Dec 14 17:21 <span class="built_in">test</span>-topic-0</div><div class="line">drwxr-xr-x   6 kafka  staff  192 Dec 14 17:21 <span class="built_in">test</span>-topic-1</div><div class="line">drwxr-xr-x   6 kafka  staff  192 Dec 14 17:21 <span class="built_in">test</span>-topic-2</div><div class="line">drwxr-xr-x   6 kafka  staff  192 Dec 14 17:21 <span class="built_in">test</span>-topic-6</div><div class="line">drwxr-xr-x   6 kafka  staff  192 Dec 14 17:21 <span class="built_in">test</span>-topic-7</div><div class="line">drwxr-xr-x   6 kafka  staff  192 Dec 14 17:21 <span class="built_in">test</span>-topic-8</div><div class="line"></div><div class="line">&gt; ll /data3/kafka-logs/ |grep <span class="built_in">test</span>-topic</div></pre></td></tr></table></figure></p><p>可以看到，<code>6</code>，<code>7</code>，<code>8</code>已经被成功地迁移到<code>/data2</code>下，而分区<code>1</code>也迁移到了<code>/data1</code>下。值得一提的是，不仅所有的日志段、索引文件被迁移，实际上分区外层的<code>checkpoint</code>文件也会被更新。比如我们检查<code>/data2</code>下的<code>replication-offset-checkpoint</code>文件可以发现，现在该文件已经包含了<code>6</code>，<code>7</code>，<code>8</code>分区的位移数据，如下所示：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">&gt; cat replication-offset-checkpoint </div><div class="line">0</div><div class="line">7</div><div class="line"><span class="built_in">test</span>-topic 8 1000000</div><div class="line"><span class="built_in">test</span>-topic 2 1000000</div><div class="line"><span class="built_in">test</span> 0 1285714</div><div class="line"><span class="built_in">test</span>-topic 6 1000000</div><div class="line"><span class="built_in">test</span>-topic 7 1000000</div><div class="line"><span class="built_in">test</span>-topic 0 1000000</div><div class="line"><span class="built_in">test</span> 2 1285714</div></pre></td></tr></table></figure></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;生产环境&lt;code&gt;kafka&lt;/code&gt;集群，在数据量大的情况下，会出现单机各个磁盘间的占用不均匀情况，经常出现“一边倒”的情形。&lt;/p&gt;
&lt;h2 id=&quot;原因探究&quot;&gt;&lt;a href=&quot;#原因探究&quot; class=&quot;headerlink&quot; title=&quot;原因探究&quot;&gt;&lt;/a&gt;原因探究&lt;/h2&gt;&lt;p&gt;这是因为&lt;code&gt;kafka&lt;/code&gt;只保证分区数量在各个磁盘上均匀分布，但它无法统计每个分区实际占用磁盘空间。因此很有可能出现某些分区消息数量巨大导致占用大量磁盘空间的情况。在1.1版本之前，用户对此基本没有优雅的处理方法，即便手动迁移日志文件和offset信息，也需要重启生效，风险极高。因为1.1之前&lt;code&gt;kafka&lt;/code&gt;只支持分区数据在不同&lt;code&gt;broker&lt;/code&gt;间的重分配，而无法做到在同一个&lt;code&gt;broker&lt;/code&gt;下的不同磁盘间做重分配。1.1版本正式支持副本在不同路径间的迁移，具体的实现细节详见&lt;code&gt;kafka&lt;/code&gt;官方&lt;code&gt;wiki&lt;/code&gt;&lt;a href=&quot;https://cwiki.apache.org/confluence/display/KAFKA/KIP-113%3A+Support+replicas+movement+between+log+directories&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;&lt;code&gt;KIP-113&lt;/code&gt;&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&quot;目录间迁移步骤&quot;&gt;&lt;a href=&quot;#目录间迁移步骤&quot; class=&quot;headerlink&quot; title=&quot;目录间迁移步骤&quot;&gt;&lt;/a&gt;目录间迁移步骤&lt;/h2&gt;&lt;p&gt;假设我在&lt;code&gt;server.properties&lt;/code&gt;文件中配置了多个日志存储路径(代表多块磁盘)，如下所示：&lt;br&gt;&lt;figure class=&quot;highlight bash&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;1&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;2&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;3&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;&lt;span class=&quot;comment&quot;&gt;# A comma seperated list of directories under which to store log files&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;log.dirs=/data1/kafka-logs,/data2/kafka-logs,/data3/kafka-logs&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="kafka" scheme="http://www.hyperxu.com/categories/kafka/"/>
    
    
      <category term="大数据" scheme="http://www.hyperxu.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="kafka" scheme="http://www.hyperxu.com/tags/kafka/"/>
    
  </entry>
  
  <entry>
    <title>kafka集群扩容后的数据均衡</title>
    <link href="http://www.hyperxu.com/2019/12/01/kafka-1/"/>
    <id>http://www.hyperxu.com/2019/12/01/kafka-1/</id>
    <published>2019-12-01T03:13:28.000Z</published>
    <updated>2019-12-14T11:03:51.561Z</updated>
    
    <content type="html"><![CDATA[<p>生产环境的<code>kafka</code>集群扩容，是一个比较常见的需求和操作。然而<code>kafka</code>在新增节点后并不会像<code>elasticsearch</code>那样感知到新节点加入后，自动将数据<code>reblance</code>到整个新集群中，因此这个过程需要我们手动分配。</p><h2 id="分区重分配方案"><a href="#分区重分配方案" class="headerlink" title="分区重分配方案"></a>分区重分配方案</h2><p>扩容后的数据均衡，其本质就是对<code>topic</code>进行分区重分配，数据迁移的过程。<br>在执行分区重分配的过程中，对集群的影响主要有两点：</p><ol><li>分区重分配主要是对<code>topic</code>数据进行<code>Broker</code>间的迁移，因此会占用集群的带宽资源；</li><li>分区重分配会改变分区<code>Leader</code>所在的<code>Broker</code>，因此会影响客户端。</li></ol><a id="more"></a><p>针对以上两点，第一点可以在晚间业务低峰时操作，必要时还可以和业务沟通，临时缩短数据保存时间，加快迁移，减少带宽影响时间。<br>针对第二点，有两个方案：</p><ol><li>整个分配方案分成两个步骤：<br> 1）手动生成分配方案，原有分区<code>Leader</code>位置不改变，只对副本进行分区重分配；<br> 2）等待数据迁移完成后，再手动更改分区分配方案，目的是均衡<code>Leader</code>。</li><li>直接用<code>Kafka</code>官方提供的分区重新分配工具生成分区重分配方案，直接执行分区重分配。</li></ol><h2 id="重分配方案分析"><a href="#重分配方案分析" class="headerlink" title="重分配方案分析"></a>重分配方案分析</h2><h3 id="方案一"><a href="#方案一" class="headerlink" title="方案一"></a>方案一</h3><p>方案一理论对客户端影响最小，把整个分配方案分成了两个步骤，也就是将对集群的带宽资源与客户端的影响分开了，对过程可控性很高。但问题是，如果集群中的某些<code>topic</code>，比如有64个分区，3副本，共192个副本，就需要在保持原有分区<code>Leader</code>位置不变的情况下，手动均衡其余副本，这个人工步骤过度繁杂，稍微有一点偏差，就会造成副本不均衡。</p><h3 id="方案二"><a href="#方案二" class="headerlink" title="方案二"></a>方案二</h3><p>针对方案二我特意去看了分区重分配的源码，并对其过程进一步分析。发现分区重分配的步骤是，将分区原有的副本与新分配的副本合并成一个新的副本集合，新分配的副本努力追上<code>Leader</code>的<code>offset</code>，最终加入<code>ISR</code>。待全部副本都加入<code>ISR</code>之后，就会进行分区<code>Leader</code>选举，选举完后删除原有副本。这里注意，由于是最后选举完成才删除原副本，所以重分配的过程中，日志存储量是会大幅增加的。具体细节我后续单独写一篇文章叙述。</p><p>根据以上分析，意味着在数据进行重分配过程中，<code>Leader</code>并没有发生变动，所以客户端不会阻塞，数据迁移完成后进行<code>Leader</code>选举时发生变更，生产者会及时拉取最新的元数据，并重新进行消息发送，影响并不大。</p><h2 id="重分配步骤"><a href="#重分配步骤" class="headerlink" title="重分配步骤"></a>重分配步骤</h2><p>其实官方文档关于集群扩容讲解很详细：Expanding your cluster ,整个过程分为三个步骤：获取kafka给出的建议分配方案、按照给出的分配方案执行分配、查看分配的进度以及状态。这三个步骤对应了kafka脚本提供的三个<code>partition reassigment</code>工具。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">--generate: 在此模式下，给定一个 topic 列表和一个 broker 列表，该工具会生成一个候选重新分配，以将指定的 topic 的所有分区移动到新的broker。此选项仅提供了一种便捷的方式，可以根据 tpoc 和目标 broker 列表生成分区重新分配计划。</div><div class="line">--execute: 在此模式下，该工具基于用户提供的重新分配计划启动分区重新分配。（使用--reassignment-json-file选项）。这可以是由管理员制作的自定义重新分配计划，也可以是使用--generate选项提供的自定义重新分配计划。</div><div class="line">--verify: 在此模式下，该工具将验证最近用 --execute 模式执行间的所有分区的重新分配状态。状态可以是成功完成，失败或正在进行。</div></pre></td></tr></table></figure></p><ol><li><p>生成需要执行分区重分配的<code>topic</code>列表json文件：</p> <figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">&gt; cat topics-to-move.json</div><div class="line">&#123;<span class="string">"topics"</span>: [&#123;<span class="string">"topic"</span>: <span class="string">"foo1"</span>&#125;,</div><div class="line">           &#123;<span class="string">"topic"</span>: <span class="string">"foo2"</span>&#125;],</div><div class="line"><span class="string">"version"</span>:1</div><div class="line">&#125;</div></pre></td></tr></table></figure></li><li><p>使用<code>kafka-reassign-partitions.sh</code>脚本获取分配方案：</p> <figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line">&gt; bin/kafka-reassign-partitions.sh --zookeeper localhost:2181 --topics-to-move-json-file topics-to-move.json --broker-list <span class="string">"5,6"</span> --generate</div><div class="line">当前分区副本分配</div><div class="line"> &#123;<span class="string">"version"</span>:1,</div><div class="line"><span class="string">"partitions"</span>:[&#123;<span class="string">"topic"</span>:<span class="string">"foo1"</span>,<span class="string">"partition"</span>:2,<span class="string">"replicas"</span>:[1,2]&#125;,</div><div class="line">              &#123;<span class="string">"topic"</span>:<span class="string">"foo1"</span>,<span class="string">"partition"</span>:0,<span class="string">"replicas"</span>:[3,4]&#125;,</div><div class="line">              &#123;<span class="string">"topic"</span>:<span class="string">"foo2"</span>,<span class="string">"partition"</span>:2,<span class="string">"replicas"</span>:[1,2]&#125;,</div><div class="line">              &#123;<span class="string">"topic"</span>:<span class="string">"foo2"</span>,<span class="string">"partition"</span>:0,<span class="string">"replicas"</span>:[3,4]&#125;,</div><div class="line">              &#123;<span class="string">"topic"</span>:<span class="string">"foo1"</span>,<span class="string">"partition"</span>:1,<span class="string">"replicas"</span>:[2,3]&#125;,</div><div class="line">              &#123;<span class="string">"topic"</span>:<span class="string">"foo2"</span>,<span class="string">"partition"</span>:1,<span class="string">"replicas"</span>:[2,3]&#125;]</div><div class="line">&#125;</div><div class="line"></div><div class="line">建议的分区重新分配配置</div><div class="line">&#123;<span class="string">"version"</span>:1,</div><div class="line"><span class="string">"partitions"</span>:[&#123;<span class="string">"topic"</span>:<span class="string">"foo1"</span>,<span class="string">"partition"</span>:2,<span class="string">"replicas"</span>:[5,6]&#125;,</div><div class="line">              &#123;<span class="string">"topic"</span>:<span class="string">"foo1"</span>,<span class="string">"partition"</span>:0,<span class="string">"replicas"</span>:[5,6]&#125;,</div><div class="line">              &#123;<span class="string">"topic"</span>:<span class="string">"foo2"</span>,<span class="string">"partition"</span>:2,<span class="string">"replicas"</span>:[5,6]&#125;,</div><div class="line">              &#123;<span class="string">"topic"</span>:<span class="string">"foo2"</span>,<span class="string">"partition"</span>:0,<span class="string">"replicas"</span>:[5,6]&#125;,</div><div class="line">              &#123;<span class="string">"topic"</span>:<span class="string">"foo1"</span>,<span class="string">"partition"</span>:1,<span class="string">"replicas"</span>:[5,6]&#125;,</div><div class="line">              &#123;<span class="string">"topic"</span>:<span class="string">"foo2"</span>,<span class="string">"partition"</span>:1,<span class="string">"replicas"</span>:[5,6]&#125;]</div><div class="line">&#125;</div></pre></td></tr></table></figure><p> 保存当前分区副本分配情况，用作回滚操作用。保存建议的分区重新分配配置到<code>expand-cluster-reassignment.json</code>用于执行迁移。</p></li><li><p>执行重分配，并验证。</p> <figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">&gt; bin/kafka-reassign-partitions.sh --zookeeper localhost:2181 --reassignment-json-file expand-cluster-reassignment.json --execute</div><div class="line"></div><div class="line">&gt; bin/kafka-reassign-partitions.sh --zookeeper localhost:2181 --reassignment-json-file expand-cluster-reassignment.json --verify</div><div class="line">Status of partition reassignment:</div><div class="line">Reassignment of partition [foo1,0] completed successfully</div><div class="line">Reassignment of partition [foo1,1] is <span class="keyword">in</span> progress</div><div class="line">Reassignment of partition [foo1,2] is <span class="keyword">in</span> progress</div><div class="line">Reassignment of partition [foo2,0] completed successfully</div><div class="line">Reassignment of partition [foo2,1] completed successfully</div><div class="line">Reassignment of partition [foo2,2] completed successfully</div></pre></td></tr></table></figure><p><code>is still in progress</code>表示还在处理中，全部迁移成功后每个<code>partition</code>都会显示<code>completed successfully</code>。注意如果<code>topic</code>数据量大，这个过程可能会很长，在此期间不要进行敏感操作，可能会导致数据不一致。</p></li></ol><h2 id="自定义重分配"><a href="#自定义重分配" class="headerlink" title="自定义重分配"></a>自定义重分配</h2><p>分区重新分配工具还可以将分区的副本移动到指定的一组<code>broker</code>。只需自定义修改分配配置文件，后续步骤同上。</p><p>例如，以下示例将<code>topic foo1</code>的分区0移到<code>broker</code>5,6中和将<code>topic foo2</code>的分区1移到<code>broker</code>2,3中：<br><figure class="highlight bash"><figcaption><span>json</span></figcaption><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div></pre></td><td class="code"><pre><div class="line">&gt; cat custom-reassignment.json</div><div class="line">&#123;</div><div class="line">    <span class="string">"version"</span>: 1,</div><div class="line">    <span class="string">"partitions"</span>: [</div><div class="line">        &#123;</div><div class="line">            <span class="string">"topic"</span>: <span class="string">"foo1"</span>,</div><div class="line">            <span class="string">"partition"</span>: 0,</div><div class="line">            <span class="string">"replicas"</span>: [</div><div class="line">                5,</div><div class="line">                6</div><div class="line">            ]</div><div class="line">        &#125;,</div><div class="line">        &#123;</div><div class="line">            <span class="string">"topic"</span>: <span class="string">"foo2"</span>,</div><div class="line">            <span class="string">"partition"</span>: 1,</div><div class="line">            <span class="string">"replicas"</span>: [</div><div class="line">                2,</div><div class="line">                3</div><div class="line">            ]</div><div class="line">        &#125;</div><div class="line">    ]</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;生产环境的&lt;code&gt;kafka&lt;/code&gt;集群扩容，是一个比较常见的需求和操作。然而&lt;code&gt;kafka&lt;/code&gt;在新增节点后并不会像&lt;code&gt;elasticsearch&lt;/code&gt;那样感知到新节点加入后，自动将数据&lt;code&gt;reblance&lt;/code&gt;到整个新集群中，因此这个过程需要我们手动分配。&lt;/p&gt;
&lt;h2 id=&quot;分区重分配方案&quot;&gt;&lt;a href=&quot;#分区重分配方案&quot; class=&quot;headerlink&quot; title=&quot;分区重分配方案&quot;&gt;&lt;/a&gt;分区重分配方案&lt;/h2&gt;&lt;p&gt;扩容后的数据均衡，其本质就是对&lt;code&gt;topic&lt;/code&gt;进行分区重分配，数据迁移的过程。&lt;br&gt;在执行分区重分配的过程中，对集群的影响主要有两点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;分区重分配主要是对&lt;code&gt;topic&lt;/code&gt;数据进行&lt;code&gt;Broker&lt;/code&gt;间的迁移，因此会占用集群的带宽资源；&lt;/li&gt;
&lt;li&gt;分区重分配会改变分区&lt;code&gt;Leader&lt;/code&gt;所在的&lt;code&gt;Broker&lt;/code&gt;，因此会影响客户端。&lt;/li&gt;
&lt;/ol&gt;
    
    </summary>
    
      <category term="kafka" scheme="http://www.hyperxu.com/categories/kafka/"/>
    
    
      <category term="大数据" scheme="http://www.hyperxu.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="kafka" scheme="http://www.hyperxu.com/tags/kafka/"/>
    
  </entry>
  
  <entry>
    <title>Dr.Elephant实战常见问题及解决方法</title>
    <link href="http://www.hyperxu.com/2019/11/12/dr-elephant-resolve-1/"/>
    <id>http://www.hyperxu.com/2019/11/12/dr-elephant-resolve-1/</id>
    <published>2019-11-12T03:13:28.000Z</published>
    <updated>2019-11-12T09:01:11.139Z</updated>
    
    <content type="html"><![CDATA[<p>通过之前一系列的文章叙述，想必大家都对<code>dr.elephant</code>有了一个较为清晰的了解。通过自己线上经验的积累，以及和一些读者的交流，我汇总了一些大家在实战中遇到的问题和解决方案。</p><h2 id="常规问题"><a href="#常规问题" class="headerlink" title="常规问题"></a>常规问题</h2><p>由于在和读者交流的过程中，发现大家技术水平参差不齐，本着科普性文章的初衷，这里先讲一些比较基础的要点，大佬们可以忽略，直接跳过。</p><ol><li>在打包时，需要对照自己的<code>Hadoop</code>或者<code>Spark</code>版本，修改<code>compile.conf</code>文件中的版本号。否则有可能出现采集不到集群作业信息的情况。</li><li>最好将自己<code>Hadoop</code>集群的相关配置文件都拷贝到<code>dr.elephant</code>的<code>app-conf</code>目录下。</li><li>统一自己<code>Hadoop</code>集群的环境变量。</li></ol><h2 id="数据库问题"><a href="#数据库问题" class="headerlink" title="数据库问题"></a>数据库问题</h2><h3 id="Database-‘default’-is-in-an-inconsistent-state"><a href="#Database-‘default’-is-in-an-inconsistent-state" class="headerlink" title="Database ‘default’ is in an inconsistent state!"></a>Database ‘default’ is in an inconsistent state!</h3><p>启动失败并出现这个报错，一般是<code>play</code>框架的<code>evolution</code>问题，解决方法如下：</p><ol><li>停止<code>dr.elephant</code>并确保进程已<code>kill</code></li><li>删除原来的数据库并重新建库</li><li>配置<code>app-conf/elephant.conf</code>中<code>jvm_props=&quot;-Devolutionplugin=enabled -DapplyEvolutions.default=true&quot;</code>，开启<code>evolution</code>，使其能够自动初始化表结构。</li></ol><a id="more"></a><h3 id="Specified-key-was-too-long-max-key-length-is-767-bytes-ERROR-1071-SQLSTATE-42000"><a href="#Specified-key-was-too-long-max-key-length-is-767-bytes-ERROR-1071-SQLSTATE-42000" class="headerlink" title="Specified key was too long; max key length is 767 bytes [ERROR:1071, SQLSTATE:42000]"></a>Specified key was too long; max key length is 767 bytes [ERROR:1071, SQLSTATE:42000]</h3><p>这是一个较为常见的错误了，官方的历史遗留问题导致，根据报错可以看出是由于索引长度超过<code>mysql</code>允许的最大长度导致。解决方法如下：</p><ol><li><p><code>conf/evolutions/default</code>目录下的<code>1.sql</code>和<code>5.sql</code>中，增加索引长度的截取为100。</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line">evolutions/default/1.sql</div><div class="line"><span class="keyword">create</span> <span class="keyword">index</span> yarn_app_result_i4 <span class="keyword">on</span> yarn_app_result (flow_exec_id(<span class="number">100</span>));</div><div class="line"><span class="keyword">create</span> <span class="keyword">index</span> yarn_app_result_i5 <span class="keyword">on</span> yarn_app_result (job_def_id(<span class="number">100</span>));</div><div class="line"><span class="keyword">create</span> <span class="keyword">index</span> yarn_app_result_i6 <span class="keyword">on</span> yarn_app_result (flow_def_id(<span class="number">100</span>));</div><div class="line"></div><div class="line">evolutions/default/5.sql</div><div class="line"><span class="comment">-- flow_definition table</span></div><div class="line"><span class="keyword">change</span> <span class="keyword">to</span> <span class="keyword">UNIQUE</span> <span class="keyword">KEY</span> flow_def_id (flow_def_id(<span class="number">100</span>))</div><div class="line"><span class="comment">-- job_definition table</span></div><div class="line"><span class="keyword">change</span> <span class="keyword">to</span> <span class="keyword">UNIQUE</span> <span class="keyword">KEY</span> job_def_id (job_def_id(<span class="number">100</span>))</div><div class="line"><span class="comment">-- job_execution table</span></div><div class="line"><span class="keyword">change</span> the <span class="keyword">index</span> <span class="keyword">length</span> <span class="keyword">like</span> below:</div><div class="line"><span class="keyword">create</span> <span class="keyword">index</span> index_je_job_exec_id <span class="keyword">on</span> job_execution (job_exec_id(<span class="number">100</span>));</div><div class="line"><span class="keyword">create</span> <span class="keyword">index</span> index_je_job_exec_url <span class="keyword">on</span> job_execution (job_exec_url(<span class="number">100</span>));</div></pre></td></tr></table></figure></li><li><p>或者修改<code>mysql</code>的<code>my.cnf</code>配置文件，添加<code>innodb_large_prefix=1</code>，然后重启<code>MySQL</code>，使其自身支持较大索引</p></li><li>此外，建议<code>mysql</code>直接使用5.6及以上的版本，避免一些不必要的问题</li></ol><h2 id="作业信息采集问题"><a href="#作业信息采集问题" class="headerlink" title="作业信息采集问题"></a>作业信息采集问题</h2><p><code>dr.elephant</code>的核心原理就是通过采集作业信息日志，来进行一系列的分析，算法推荐等功能。<br>主要分为<code>hadoop</code>的<code>MapReduce</code>，和<code>spark</code>作业信息采集。</p><h3 id="hadoop"><a href="#hadoop" class="headerlink" title="hadoop"></a>hadoop</h3><h4 id="采集原理"><a href="#采集原理" class="headerlink" title="采集原理"></a>采集原理</h4><p><code>MapReduce</code>作业信息有两种拉取方式可选，在<code>app-conf/FetcherConf.xml</code>进行配置。<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">fetcher</span>&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">applicationtype</span>&gt;</span>mapreduce<span class="tag">&lt;/<span class="name">applicationtype</span>&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">classname</span>&gt;</span>com.linkedin.drelephant.mapreduce.fetchers.MapReduceFetcherHadoop2<span class="tag">&lt;/<span class="name">classname</span>&gt;</span></div><div class="line">   <span class="tag">&lt;<span class="name">params</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">sampling_enabled</span>&gt;</span>false<span class="tag">&lt;/<span class="name">sampling_enabled</span>&gt;</span></div><div class="line">   <span class="tag">&lt;/<span class="name">params</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">fetcher</span>&gt;</span></div><div class="line"> </div><div class="line"><span class="tag">&lt;<span class="name">fetcher</span>&gt;</span>   </div><div class="line"><span class="tag">&lt;<span class="name">applicationtype</span>&gt;</span>mapreduce<span class="tag">&lt;/<span class="name">applicationtype</span>&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">classname</span>&gt;</span>com.linkedin.drelephant.mapreduce.fetchers.MapReduceFSFetcherHadoop2<span class="tag">&lt;/<span class="name">classname</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">params</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">sampling_enabled</span>&gt;</span>false<span class="tag">&lt;/<span class="name">sampling_enabled</span>&gt;</span>            </div><div class="line">     <span class="tag">&lt;<span class="name">history_log_size_limit_in_mb</span>&gt;</span>500<span class="tag">&lt;/<span class="name">history_log_size_limit_in_mb</span>&gt;</span></div><div class="line">     <span class="tag">&lt;<span class="name">history_server_time_zone</span>&gt;</span>PST<span class="tag">&lt;/<span class="name">history_server_time_zone</span>&gt;</span></div><div class="line">    <span class="tag">&lt;/<span class="name">params</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">fetcher</span>&gt;</span></div></pre></td></tr></table></figure></p><p>通过源码分析，由于源码过长，这里就不贴出来了，直接讲源码逻辑，发现两个<code>Fetcher</code>类分别是：</p><ul><li><strong>MapReduceFetcherHadoop2</strong>：通过<code>API</code>从<code>yarn history server</code>获取作业信息日志</li><li><strong>MapReduceFSFetcherHadoop2</strong>：通过读取<code>HDFS</code>和<code>YARN</code>的配置文件，读取<code>mapreduce.jobhistory.done-dir</code>等相关配置，直接读取<code>HDFS</code>上<code>YARN</code>的历史作业信息日志。每个作业对应<code>.jhist</code>和<code>.xml</code>两个文件<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="comment"># *.xml文件里面记录的是相应作业运行时候的完整参数配置</span></div><div class="line">hdfs dfs -cat /mr-history/<span class="keyword">done</span>/2019/11/01/000000/job_1477464172237_0052_conf.xml</div><div class="line"></div><div class="line"><span class="comment"># *.jhist文件里存放的是具体Hadoop作业运行的详细信息</span></div><div class="line">hdfs dfs -cat /mr-history/<span class="keyword">done</span>/2019/11/01/000000/job_1477464172237_0052-1477984365827-ocdp-QuasiMonteCarlo-1477984395977-4-1-SUCCEEDED-default-1477984370174.jhist</div></pre></td></tr></table></figure></li></ul><h4 id="问题点"><a href="#问题点" class="headerlink" title="问题点"></a>问题点</h4><ol><li><p>为什么采集不到作业信息，界面上没有任何显示？</p><ul><li>注意<code>dr.elephant</code>打包前<code>Hadoop version</code>配置和被采集集群的版本信息是否对应上了，否则会出现采集不到的情况。</li><li>查看<code>history_log_size_limit_in_mb</code>配置大小是否小于实际单个日志文件大小，导致无法拉取日志。</li><li>检查<code>drelephant.analysis.fetch.initial.windowMillis</code>配置时间，这个配置为初始化时间拉取时间窗口，即拉取当前时间之前多久的历史作业。如果当前时间到时间窗口之前没有历史作业，则会出现无作业信息的情况。</li><li><code>drelephant.analysis.retry.interval</code>配置为拉取间隔时间，这个配置过大，也会导致长时间不拉取作业，而无作业信息。</li></ul></li><li><p>运行一段时间后，为什么作业信息延迟严重？</p><ul><li><code>drelephant.analysis.thread.count</code>作业分析线程数影响着分析效率，设置的过小很容易延迟</li><li>以上采集不到作业信息问题的几个排查点，也比较容易造成延迟情况，需要自己根据作业数量，进行一个评估设置</li></ul></li></ol><h3 id="spark"><a href="#spark" class="headerlink" title="spark"></a>spark</h3><h4 id="采集原理-1"><a href="#采集原理-1" class="headerlink" title="采集原理"></a>采集原理</h4><p><code>Spark</code>作业信息同样有两种拉取方式可选，在<code>app-conf/FetcherConf.xml</code>进行配置。</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">fetcher</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">applicationtype</span>&gt;</span>spark<span class="tag">&lt;/<span class="name">applicationtype</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">classname</span>&gt;</span>com.linkedin.drelephant.spark.fetchers.FSFetcher<span class="tag">&lt;/<span class="name">classname</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">params</span>&gt;</span></div><div class="line">      <span class="tag">&lt;<span class="name">event_log_size_limit_in_mb</span>&gt;</span>500<span class="tag">&lt;/<span class="name">event_log_size_limit_in_mb</span>&gt;</span></div><div class="line">      <span class="tag">&lt;<span class="name">event_log_location_uri</span>&gt;</span>webhdfs://localhost:50070/system/spark-history<span class="tag">&lt;/<span class="name">event_log_location_uri</span>&gt;</span></div><div class="line">    <span class="tag">&lt;/<span class="name">params</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">fetcher</span>&gt;</span></div><div class="line"></div><div class="line"><span class="tag">&lt;<span class="name">fetcher</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">applicationtype</span>&gt;</span>spark<span class="tag">&lt;/<span class="name">applicationtype</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">classname</span>&gt;</span>com.linkedin.drelephant.spark.fetchers.SparkFetcher<span class="tag">&lt;/<span class="name">classname</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">params</span>&gt;</span></div><div class="line">      <span class="tag">&lt;<span class="name">use_rest_for_eventlogs</span>&gt;</span>true<span class="tag">&lt;/<span class="name">use_rest_for_eventlogs</span>&gt;</span></div><div class="line">      <span class="tag">&lt;<span class="name">should_process_logs_locally</span>&gt;</span>true<span class="tag">&lt;/<span class="name">should_process_logs_locally</span>&gt;</span></div><div class="line">    <span class="tag">&lt;/<span class="name">params</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">fetcher</span>&gt;</span></div></pre></td></tr></table></figure><p>通过源码分析，由于源码过长，这里就不贴出来了，直接讲源码逻辑，发现两个Fetcher类分别是：</p><ul><li><strong>FSFetcher</strong>：直接通过<code>hdfs</code>拉取<code>spark</code>的历史日志</li><li><strong>SparkFetcher</strong>：通过<code>SHS REST API</code>拉取<code>spark</code>的<code>eventlogs</code>，需要<code>spark</code>版本在1.5.0以上。此外还可以支持<code>backfill</code>功能，但仅适用于2.3.0以上版本。</li></ul><h4 id="问题点-1"><a href="#问题点-1" class="headerlink" title="问题点"></a>问题点</h4><ol><li><p><code>MapReduce</code>作业正常采集并分析，为什么<code>spark</code>作业没有分析数据？</p><ul><li>首先参照上面<code>hadoop</code>版本打包问题检查，打包前是否同样在配置文件中修改为正确的<code>spark</code>版本</li><li>检查<code>hdfs</code>上<code>spark eventlogs</code>存放目录是否产生了日志文件，以及程序是否有相应的操作权限</li><li>如果使用了老版本的<code>dr.elephant</code>，则还需要注意<code>spark</code>是否开启了<code>spark.eventLog.compress</code>，导致产生的<code>spark</code>日志为<code>snappy</code>格式，使得<code>dr.elephant</code>无法识别。老版本可以通过增加配置进行识别<figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">event_log_dir</span>&gt;</span>/spark- history<span class="tag">&lt;/<span class="name">event_log_dir</span>&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">spark_log_ext</span>&gt;</span>.snappy<span class="tag">&lt;/<span class="name">spark_log_ext</span>&gt;</span></div></pre></td></tr></table></figure></li></ul></li><li><p>为什么部分<code>spark</code>作业缺失，<code>dr.elephant</code>没有显示所有作业？</p><ul><li>同上<code>Hadoop</code>问题点，可能出现了延迟问题</li><li><code>SHS</code>可能没有配好<code>spark</code>日志聚合，解决办法另行找<code>SHS</code>日志聚合资料，这里不再多说</li></ul></li></ol><blockquote><p>以上是个人在实战中遇到的一些问题及解决方法，后续如果还有其他问题我也会及时更新，或者大家还遇上啥坑了也可以和我交流讨论。</p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;通过之前一系列的文章叙述，想必大家都对&lt;code&gt;dr.elephant&lt;/code&gt;有了一个较为清晰的了解。通过自己线上经验的积累，以及和一些读者的交流，我汇总了一些大家在实战中遇到的问题和解决方案。&lt;/p&gt;
&lt;h2 id=&quot;常规问题&quot;&gt;&lt;a href=&quot;#常规问题&quot; class=&quot;headerlink&quot; title=&quot;常规问题&quot;&gt;&lt;/a&gt;常规问题&lt;/h2&gt;&lt;p&gt;由于在和读者交流的过程中，发现大家技术水平参差不齐，本着科普性文章的初衷，这里先讲一些比较基础的要点，大佬们可以忽略，直接跳过。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在打包时，需要对照自己的&lt;code&gt;Hadoop&lt;/code&gt;或者&lt;code&gt;Spark&lt;/code&gt;版本，修改&lt;code&gt;compile.conf&lt;/code&gt;文件中的版本号。否则有可能出现采集不到集群作业信息的情况。&lt;/li&gt;
&lt;li&gt;最好将自己&lt;code&gt;Hadoop&lt;/code&gt;集群的相关配置文件都拷贝到&lt;code&gt;dr.elephant&lt;/code&gt;的&lt;code&gt;app-conf&lt;/code&gt;目录下。&lt;/li&gt;
&lt;li&gt;统一自己&lt;code&gt;Hadoop&lt;/code&gt;集群的环境变量。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;数据库问题&quot;&gt;&lt;a href=&quot;#数据库问题&quot; class=&quot;headerlink&quot; title=&quot;数据库问题&quot;&gt;&lt;/a&gt;数据库问题&lt;/h2&gt;&lt;h3 id=&quot;Database-‘default’-is-in-an-inconsistent-state&quot;&gt;&lt;a href=&quot;#Database-‘default’-is-in-an-inconsistent-state&quot; class=&quot;headerlink&quot; title=&quot;Database ‘default’ is in an inconsistent state!&quot;&gt;&lt;/a&gt;Database ‘default’ is in an inconsistent state!&lt;/h3&gt;&lt;p&gt;启动失败并出现这个报错，一般是&lt;code&gt;play&lt;/code&gt;框架的&lt;code&gt;evolution&lt;/code&gt;问题，解决方法如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;停止&lt;code&gt;dr.elephant&lt;/code&gt;并确保进程已&lt;code&gt;kill&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;删除原来的数据库并重新建库&lt;/li&gt;
&lt;li&gt;配置&lt;code&gt;app-conf/elephant.conf&lt;/code&gt;中&lt;code&gt;jvm_props=&amp;quot;-Devolutionplugin=enabled -DapplyEvolutions.default=true&amp;quot;&lt;/code&gt;，开启&lt;code&gt;evolution&lt;/code&gt;，使其能够自动初始化表结构。&lt;/li&gt;
&lt;/ol&gt;
    
    </summary>
    
      <category term="Dr.Elephant" scheme="http://www.hyperxu.com/categories/Dr-Elephant/"/>
    
    
      <category term="Dr.Elephant" scheme="http://www.hyperxu.com/tags/Dr-Elephant/"/>
    
      <category term="大数据" scheme="http://www.hyperxu.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="hadoop" scheme="http://www.hyperxu.com/tags/hadoop/"/>
    
      <category term="hive" scheme="http://www.hyperxu.com/tags/hive/"/>
    
  </entry>
  
  <entry>
    <title>【Dr.Elephant中文文档-8】调优建议</title>
    <link href="http://www.hyperxu.com/2019/08/27/dr-elephant-8/"/>
    <id>http://www.hyperxu.com/2019/08/27/dr-elephant-8/</id>
    <published>2019-08-27T03:13:28.000Z</published>
    <updated>2019-08-27T03:28:18.851Z</updated>
    
    <content type="html"><![CDATA[<p>你可以使用<code>Dr. Elephant</code>来分析你的作业（只需在搜索页贴入你的作业ID），就可以知道你的作业有哪些地方需要优化。</p><h2 id="加速你的作业流"><a href="#加速你的作业流" class="headerlink" title="加速你的作业流"></a>加速你的作业流</h2><p>一般对于特定的作业，最好有自己的配置。大多数情况下，作业的默认配置无法提供最佳性能。尽管作业调优比较费劲，但一些简单的调整往往也能带来不错的效果。</p><p>需要特别注意的是<code>mapper</code>和<code>reducer</code>的数量，<code>io</code>和内存使用的配置，以及生成的文件数量。对这几个参数进行调整，让参数更适合当前的任务，可以极大的提升任务的执行性能。</p><p>Apache的官网中<a href="http://hadoop.apache.org/docs/current/hadoop-mapreduce-client/hadoop-mapreduce-client-core/MapReduceTutorial.html#Task_Execution__Environment" target="_blank" rel="external">Hadoop Map/Reduce Tutorial</a>这篇文章提供很多详细且有用的调试建议，有兴趣的可以仔细看看。</p><h2 id="常规建议"><a href="#常规建议" class="headerlink" title="常规建议"></a>常规建议</h2><h3 id="逐步调优很重要"><a href="#逐步调优很重要" class="headerlink" title="逐步调优很重要"></a>逐步调优很重要</h3><p>对于<code>Pig</code>作业来说，如果使用默认参数来设置<code>reducer</code>的数量，这对作业的性能可能是致命的。一般来说，对每个<code>Pig</code>作业，都花一些时间来调优参数<code>PARALLEL</code>是非常值得做的。例如：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">memberFeaturesGrouped = GROUP memberFeatures BY memberId PARALLEL 90;</div></pre></td></tr></table></figure><a id="more"></a><h3 id="文件数vs块数量"><a href="#文件数vs块数量" class="headerlink" title="文件数vs块数量"></a>文件数vs块数量</h3><p>为了防止<code>NameNode</code>崩溃，存大文件比小文件更合理。<code>NameNode</code>每存储一个文件大概消耗<code>70 bytes</code>，每存储一个块大概消耗<code>60 byte</code>。一般情况下，对于任务来说，使用一个较大的文件要比使用十个小文件的效率高一些。在大规模集群下，这<code>10 byte</code>的差距会越来越大。此外在许多情况下，1个大文件比10个小文件操作起来更高效。</p><h3 id="Java任务内存管理"><a href="#Java任务内存管理" class="headerlink" title="Java任务内存管理"></a>Java任务内存管理</h3><p>默认情况下，每个<code>map/reduce</code>作业可以分配最大2G的内存。对于java任务，这2G的空间既包括1G的堆内存，又包括0.5-1G的非堆内存。对于有些任务来说，默认的空间分配可能是不够用的。下面列举了一些能够减少内存使用的技巧：</p><h4 id="UseCompressedOops"><a href="#UseCompressedOops" class="headerlink" title="UseCompressedOops"></a>UseCompressedOops</h4><p>32位<code>JVM</code>使用32bit无符号整型来定位内存区域，最大和定义的堆内存为<code>(2^32 -1) = 4GB</code>。64位的<code>JVM</code>虚拟机使用64bit的无符号Long整型来定位内存区域，最大可定义的堆内存大小为<code>(2^64 - 1) = 16</code>艾字节。虽然定义的堆内存增加了，但是用<code>Long</code>代替<code>int</code>型，所需内存空间也增加了。大约为原来的1.5倍。这使得你可以突破1G堆空间的限制，对此你可以做些什么呢？现在所有的<code>JVM</code>都支持<code>UseCompressedOops</code>选型，在某些情况下，他使用32bit的空间代替64bit空间来保存内存定位信息。这将可以减少内存的占用而不用回到32bit的情况。你可以在<code>azkaban</code>的作业文件中增加以下选项来实现：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">hadoop-inject.mapreduce.(map|reduce).java.opts=-Xmx1G -XX:+UseCompressedOops</div></pre></td></tr></table></figure><p>注意<code>azkaban</code>默认会使用自定义的属性覆盖掉默认配置属性，而不是将自定义的部分添加到<code>mapred-site.xml</code>默认文件中。你需要确认<code>CompressedOops</code>选项和其他默认的配置都是有效的。需要确认的是：”-Xmx1G”是配置文件<code>mapred-site.xml</code>中的，而其他的配置文件是我们自定义的。</p><h4 id="UseCompressedStrings"><a href="#UseCompressedStrings" class="headerlink" title="UseCompressedStrings"></a>UseCompressedStrings</h4><p>这个选项会将<code>String</code>类型的变量转化为<code>byte[]</code>类型来保存。如果一个任务中使用了大量的<code>String</code>类型变量，那么这个选项将会极大的节约内存使用。在参数<code>mapreduce.(map|reduce).java.opts</code>配置中添加<code>-XX:+UseCompressedString</code>就会激活这个选项。每个作业分配的虚拟内存空间是需要的物理内存空间的2.1倍。如果我们程序抛出以下错误：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">Container [pid=PID,containerID=container_ID]</div><div class="line"> is running beyond virtual memory limits. Current usage: 365.1 MB of 1</div><div class="line">GB physical memory used; 3.2 GB of 2.1 GB virtual memory used. Killing</div><div class="line">container</div></pre></td></tr></table></figure><p>你就可以尝试使用这个选项来对程序进行优化。</p><h3 id="关键调优参数"><a href="#关键调优参数" class="headerlink" title="关键调优参数"></a>关键调优参数</h3><h4 id="Mappers"><a href="#Mappers" class="headerlink" title="Mappers"></a>Mappers</h4><h5 id="mapreduce-input-fileinputformat-split-minsize"><a href="#mapreduce-input-fileinputformat-split-minsize" class="headerlink" title="mapreduce.input.fileinputformat.split.minsize"></a>mapreduce.input.fileinputformat.split.minsize</h5><p>这个参数表示输入到<code>map</code>中的每个文件块切分的大小的最小值。通过增加<code>dfs.blocksize</code>的块大小，可以增加每个<code>map</code>中输入文件块的大小，从而减少<code>map</code>的数量。这是因为如果说你设置<code>mapreduce.input.fileinputformat.split.minsize</code>的大小为HDFS块大小（dfs.blocksize）的4倍时，那么输入到每个<code>map</code>的数量就是4倍的<code>dfs.blocksize</code>，这样就减少了<code>map</code>的数量。如果把这个值设置为<code>256</code>MB，那么输入的文件大小就是<code>268435456</code>bit。</p><h5 id="mapreduce-input-fileinputformat-split-maxsize"><a href="#mapreduce-input-fileinputformat-split-maxsize" class="headerlink" title="mapreduce.input.fileinputformat.split.maxsize"></a>mapreduce.input.fileinputformat.split.maxsize</h5><p>这个参数表示当使用<code>CombineFileInputFormat</code>和<code>MultiFileInputFormat</code>时，输入到<code>map</code>的每个文件的最大值。当这个值小于<code>dfs.blocksize</code>时，会增加作业的<code>mapper</code>的数量。这是因为如果说你设置<code>mapreduce.input.fileinputformat.split.minsize</code>的大小为HDFS块大小（dfs.blocksize）的1/4时，这样就将输入到每个<code>map</code>的文件大小限制为<code>dfs.blocksize</code>的1/4，就增加了<code>map</code>的数量。这个值是输入文件切分的值。因此要设置为256MB，你将指定值为268435456。需要注意的是，如果在使用<code>CombineFileInputFormat</code>时未设置最大分割大小，则作业将仅使用1个<code>mapper</code>来处理作业。（这可能是你不希望看到的）</p><h4 id="Reducers"><a href="#Reducers" class="headerlink" title="Reducers"></a>Reducers</h4><h5 id="mapreduce-job-reduces"><a href="#mapreduce-job-reduces" class="headerlink" title="mapreduce.job.reduces"></a>mapreduce.job.reduces</h5><p>影响作业流性能的最大杀手之一是<code>reducers</code>的数量。<code>reducers</code>数量过少，可能会使任务时间超过15分钟，而数量过多也同样会有问题。针对每个特定的任务因地制宜的调整<code>reducer</code>数量是一项艺术。下面列了一些方法来帮助我们来设置合适的<code>reducer</code>数量：</p><ul><li><code>reducers</code>越多意味着<code>Namenode</code>上文件越多，过多的小文件可能会导致<code>Namenode</code>宕机。因此如果<code>reduce</code>输出不大（小于512M），可以减少<code>reducers</code>的数量</li><li><code>reducers</code>越多意味着每个<code>reducer</code>处理数据的时间越短，如果<code>reducers</code>数量过少。那么每个<code>reducer</code>的消耗时间就会增加，<code>reducer</code>运行越快，就能处理更多的作业。</li></ul><p>在大型任务中，清洗（Shuffling）操作的代价是比较高的。我们通过<code>HDFS</code>文件系统的各个计数器可以看到有大量的数据需要在不同的节点间进行交换。我们用20个<code>reducers</code>的作业来做个试验，文件系统的计数器如下：<br><strong>FileSystemCounter：</strong></p><ul><li>FILE_BYTES_READ | 2950482442768</li><li>HDFS_BYTES_READ | 1223524334581</li><li>FILE_BYTES_WRITTEN | 5967256875163</li></ul><p>我们可以看到有超过1K个<code>map</code>产生了约5TB的中间数据。再看下清洗时间：<br><strong>Shuffle Finished:</strong><br>17-Aug-2010 | 13:32:05 | ( 1hrs, 29mins, 56 sec)</p><p><strong>Sort Finished:</strong><br>17-Aug-2010 | 14:18:35 | (46mins, 29sec)</p><p>可以看出，大约有5TB的数据花费了1个半小时来清洗，然后有花了46分钟来进行排序。这个时间成本是巨大的。我们希望任务可以在5-15分钟内完成。我们现在已经解决了这个问题。让我们来算一下：使用20个<code>reducers</code>需要消耗360分钟，200个<code>reducers</code>则需要36分钟，400个<code>reducers</code>则需要18分钟。因此围绕这个逻辑来进行改进，将<code>reducers</code>控制在500个以下则出现以下结果：<br><strong>Shuffle Finished:</strong><br>17-Aug-2010 | 16:32:32 | ( 12 mins, 46 sec)</p><p><strong>Sort Finished:</strong><br>17-Aug-2010 | 16:32:37 | (4sec)<br>效果看着还不错，通过一些调优，我们可以缩短一些任务时间。正如你猜的那样，反过来也一样。如果清洗时间很短，CPU使用也很少，那么说明<code>reducer</code>数量过少，合适的配置需要通过不断的试验来确定。</p><h5 id="mapreduce-job-reduce-slowstart-completedmaps"><a href="#mapreduce-job-reduce-slowstart-completedmaps" class="headerlink" title="mapreduce.job.reduce.slowstart.completedmaps"></a>mapreduce.job.reduce.slowstart.completedmaps</h5><p>这个参数决定了在<code>reducer</code>开始执行之前，至少有多少比例的<code>mapper</code>必须执行结束。默认值是80%。对于大多数任务，调整这个数字的大小可能会带来性能提升。决定这个数字的因素是：</p><ul><li>每个<code>reducer</code>接收到多少数据</li><li>剩下的<code>map</code>每一个<code>map</code>作业需要花费的时间<br>如果<code>map</code>的输出数据量比较大，一般会建议让<code>reducer</code>提前开始执行去处理这些数据。如果<code>map</code>任务产生的数量不是很大，一般建议让<code>reducer</code>的执行时间开始的晚一些。对于清洗过程的一个粗略的时间估计是：当所有的<code>mapper</code>结束后开始，到第一个<code>reducer</code>开始执行的时间为止。这是<code>reducer</code>获得所有的输入所需要的时间。一般认为，<code>reducers</code>开始执行时间是：最后一个<code>map</code>结束时间+清洗时间。</li></ul><h4 id="压缩"><a href="#压缩" class="headerlink" title="压缩"></a>压缩</h4><h5 id="mapreduce-map-output-compress"><a href="#mapreduce-map-output-compress" class="headerlink" title="mapreduce.map.output.compress"></a>mapreduce.map.output.compress</h5><p>将这个参数设置为<code>True</code>可以将<code>map</code>输出的数据进行压缩。这可以减少节点间的数据交互，但我们必须确保解压缩的时间要比传输时间短，否则会起反作用。对于大型或可以高度压缩的<code>map</code>输出数据，这个参数选型非常有必要，将大量减少清洗时间。对于小型<code>map</code>输出数据集，关闭这个参数，将降低解压缩带来的CPU消耗时间。注意与<code>mapreduce.output.fileoutputformat.compress</code>选项不同，那个参数决定了任务的输出回写到<code>HDFS</code>时是否需要压缩。</p><h4 id="内存"><a href="#内存" class="headerlink" title="内存"></a>内存</h4><h5 id="mapreduce-map-reduce-memory-mb"><a href="#mapreduce-map-reduce-memory-mb" class="headerlink" title="mapreduce.(map|reduce).memory.mb"></a>mapreduce.(map|reduce).memory.mb</h5><p>新版<code>Hadoop</code>中增加了堆内存的限制特性。这使得系统在繁忙情况下更好的管理资源分配。默认情况下，系统会分配给<code>Java</code>任务1GB的堆内存，以及0.5-1GB的非堆内存空间。因此，<code>mapreduce.(map|reduce).memory.mb</code>的默认值为2GB。在某些情况下，这个内存值是不够用的。如果只是设置<code>Java</code>的参数<code>-Xmx</code>，任务会被杀死，需要同时设置参数<code>mapreduce.(map|reduce).momory.mb</code>才能有效的提升或者限制内存使用。</p><h4 id="进阶"><a href="#进阶" class="headerlink" title="进阶"></a>进阶</h4><h4 id="控制io-sort-record-percent的值"><a href="#控制io-sort-record-percent的值" class="headerlink" title="控制io.sort.record.percent的值"></a>控制<code>io.sort.record.percent</code>的值</h4><p>参数<code>io.sort.record.percent</code>决定使用多少的间接缓存空间来保存每条和每条记录的元信息。一般来说，这个参数的设置是不合理的。</p><p>假设在<code>map</code>作业中使用日志的<code>xml</code>的配置文件：</p><table><thead><tr><th>property</th><th>value</th></tr></thead><tbody><tr><td>bufstart</td><td>45633950</td></tr><tr><td>bufend</td><td>68450908</td></tr><tr><td>kvstart</td><td>503315</td></tr><tr><td>kvend</td><td>838860</td></tr><tr><td>length</td><td>838860</td></tr><tr><td>io.sort.mb</td><td>256</td></tr><tr><td>io.sort.record.percent</td><td>0.05</td></tr></tbody></table><p>使用上面的值：<br>| property | value |<br>| — | — |<br>| io.sort.spill.percent (length-kvstart+kvend) / length) | 0.8 |<br>| Size of meta data is (bufend-bufstat) | 22816958 (MB) |<br>| Records in memory (length-(kvstart+kvend)) | 671087 |<br>| Average record size (size/records) | 34 Bytes |<br>| Record+Metadata | 50 Bytes |<br>| Records per io.sort.mb (io.sort.mb/(record+metadata)) | 5.12 million |<br>| Metadata % in io.sort.mb ((records per io.sort.mb)*metadata/io.sort.mb) | 0.32 |</p><p>我们可以在256MB的缓存中保存很多数据。但是<code>io.sort.record.percent</code>应该设置为0.32，而不是0.5。当设置为0.5时，记录的元信息的缓存会比记录的缓存更大。</p><p>调整这个参数会使<code>map</code>运行的更快，磁盘溢出问题更少，因为<code>io.sort.mb</code>的效率更高了；不会再很快就使用完元数据缓冲区的80%空间。</p><p>调整<code>io.sort.record.percent</code>后会使得许多<code>map</code>数据不再溢出到磁盘，减少了55%的磁盘溢。最终，系统节省了30%的CPU资源消耗，和30分钟运行时间。</p><h5 id="mapreduce-map-reduce-speculative"><a href="#mapreduce-map-reduce-speculative" class="headerlink" title="mapreduce.(map|reduce).speculative"></a>mapreduce.(map|reduce).speculative</h5><p>这个参数决定了是否允许相同的<code>map/reduce</code>并行执行。你大概知道当发生数据倾斜时，有些<code>mapper</code>或<code>reducer</code>会运行很长时间。在这种情况下，你可能希望通过一些预判来防止数据倾斜。</p><h4 id="Pig"><a href="#Pig" class="headerlink" title="Pig"></a>Pig</h4><p>在<code>Pig</code>中你可以通过增加以下命令来设置<code>Hadoop</code>和<code>Pig</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">SET &lt;property_name&gt; &lt;property_value&gt;;</div></pre></td></tr></table></figure><p>例如，如果你的<code>map</code>内存不足，可以通过以下命令增加内存</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">SET mapreduce.map.memory.mb 4096;</div></pre></td></tr></table></figure><p>在<code>Azkaban</code>中，可以通过以下命令实现</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">jvm.args=-Dmapreduce.map.memory.mb=4096</div><div class="line">to your job properties.</div></pre></td></tr></table></figure><h5 id="pig-maxCombinedSplitSize-增加或减少mapper数量"><a href="#pig-maxCombinedSplitSize-增加或减少mapper数量" class="headerlink" title="pig.maxCombinedSplitSize / 增加或减少mapper数量"></a>pig.maxCombinedSplitSize / 增加或减少<code>mapper</code>数量</h5><p>默认情况下，<code>Pig</code>合并了小文件（<code>pig.splitCombination</code>默认为<code>true</code>），直到需要切分的<code>HDFS</code>块大小超过512MB。要进一步增加这个值，需要调高<code>pig.maxCombinedSplitSize</code>。相关详情可以看<a href="https://pig.apache.org/docs/r0.11.1/perf.html#combine-files" target="_blank" rel="external">这里</a>。你可以在你的<code>Pig</code>脚本中添加以下命令</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="built_in">set</span> pig.maxCombinedSplitSize &lt;size-in-bytes&gt;;</div></pre></td></tr></table></figure><p>在你的<code>Pig</code>脚本的开头。如果您通过<code>Azkaban</code>执行此<code>Pig</code>脚本，您也可以通过添加以下命令来设置他</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">jvm.args=-Dpig.maxCombinedSplitSize=&lt;size-in-bytes&gt;</div></pre></td></tr></table></figure><p>在你的作业属性中。如果你的<code>mapper</code>耗时太长，想增加<code>mapper</code>数量，你必须同时设置</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="built_in">set</span> pig.maxCombinedSplitSize &lt;size-in-bytes&gt;;</div><div class="line"><span class="built_in">set</span> mapreduce.input.fileinputformat.split.maxsize &lt;size-in-bytes&gt;;</div></pre></td></tr></table></figure><p>如果值小于512MB。是因为<code>Pig</code>拆分块的值超过了<code>pig.maxCombinedSplitSize</code>，拆分大小由以下配置决定</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">max(mapreduce.input.fileinputformat.split.minsize, min(mapreduce.input.fileinputformat.split.maxsize, dfs.blocksize))</div></pre></td></tr></table></figure><p>提供一些集群设置：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">mapreduce.input.fileinputformat.split.minsize=0</div><div class="line">mapreduce.input.fileinputformat.split.maxsize is <span class="built_in">unset</span></div><div class="line">dfs.blocksize=536870912 // 512 MB</div><div class="line">will evaluate to 512 MB.</div></pre></td></tr></table></figure><h5 id="Reducers数量"><a href="#Reducers数量" class="headerlink" title="Reducers数量"></a>Reducers数量</h5><p>在<code>Pig</code>中，你可以基于每个作业控制<code>Reducer</code>的数量，还可以选择为整个脚本设置默认的<code>reducers</code>数量。浏览<a href="https://pig.apache.org/docs/r0.11.1/perf.html#parallel" target="_blank" rel="external">此处</a>获取更多信息。</p><h4 id="Hive"><a href="#Hive" class="headerlink" title="Hive"></a>Hive</h4><ul><li>mapreduce.input.fileinputformat.split.minsize</li><li>mapreduce.input.fileinputformat.split.maxsize</li><li>mapreduce.input.fileinputformat.split.minsize.per.node</li><li>mapreduce.input.fileinputforomat.split.minsize.per.rack</li></ul><p>对应<code>Hive</code>来说，你可能需要设置以上4个参数来调整切分大小。<br>例如：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">-- The following are the default configurations on our Hadoop clusters.</div><div class="line"><span class="built_in">set</span> mapreduce.input.fileinputformat.split.maxsize                 = 2147483648;</div><div class="line"><span class="built_in">set</span> mapreduce.input.fileinputformat.split.minsize                 = 1073741824;</div><div class="line"><span class="built_in">set</span> mapreduce.input.fileinputformat.split.minsize.per.node        = 1073741824;</div><div class="line"><span class="built_in">set</span> mapreduce.input.fileinputformat.split.minsize.per.rack        = 1073741824;</div></pre></td></tr></table></figure><p>如果你想增加<code>mapper</code>的数量，就减少这些配置的值；反之，则增加这些配置的值。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;你可以使用&lt;code&gt;Dr. Elephant&lt;/code&gt;来分析你的作业（只需在搜索页贴入你的作业ID），就可以知道你的作业有哪些地方需要优化。&lt;/p&gt;
&lt;h2 id=&quot;加速你的作业流&quot;&gt;&lt;a href=&quot;#加速你的作业流&quot; class=&quot;headerlink&quot; title=&quot;加速你的作业流&quot;&gt;&lt;/a&gt;加速你的作业流&lt;/h2&gt;&lt;p&gt;一般对于特定的作业，最好有自己的配置。大多数情况下，作业的默认配置无法提供最佳性能。尽管作业调优比较费劲，但一些简单的调整往往也能带来不错的效果。&lt;/p&gt;
&lt;p&gt;需要特别注意的是&lt;code&gt;mapper&lt;/code&gt;和&lt;code&gt;reducer&lt;/code&gt;的数量，&lt;code&gt;io&lt;/code&gt;和内存使用的配置，以及生成的文件数量。对这几个参数进行调整，让参数更适合当前的任务，可以极大的提升任务的执行性能。&lt;/p&gt;
&lt;p&gt;Apache的官网中&lt;a href=&quot;http://hadoop.apache.org/docs/current/hadoop-mapreduce-client/hadoop-mapreduce-client-core/MapReduceTutorial.html#Task_Execution__Environment&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;Hadoop Map/Reduce Tutorial&lt;/a&gt;这篇文章提供很多详细且有用的调试建议，有兴趣的可以仔细看看。&lt;/p&gt;
&lt;h2 id=&quot;常规建议&quot;&gt;&lt;a href=&quot;#常规建议&quot; class=&quot;headerlink&quot; title=&quot;常规建议&quot;&gt;&lt;/a&gt;常规建议&lt;/h2&gt;&lt;h3 id=&quot;逐步调优很重要&quot;&gt;&lt;a href=&quot;#逐步调优很重要&quot; class=&quot;headerlink&quot; title=&quot;逐步调优很重要&quot;&gt;&lt;/a&gt;逐步调优很重要&lt;/h3&gt;&lt;p&gt;对于&lt;code&gt;Pig&lt;/code&gt;作业来说，如果使用默认参数来设置&lt;code&gt;reducer&lt;/code&gt;的数量，这对作业的性能可能是致命的。一般来说，对每个&lt;code&gt;Pig&lt;/code&gt;作业，都花一些时间来调优参数&lt;code&gt;PARALLEL&lt;/code&gt;是非常值得做的。例如：&lt;/p&gt;
&lt;figure class=&quot;highlight bash&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;1&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;memberFeaturesGrouped = GROUP memberFeatures BY memberId PARALLEL 90;&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
    
    </summary>
    
      <category term="Dr.Elephant" scheme="http://www.hyperxu.com/categories/Dr-Elephant/"/>
    
    
      <category term="Dr.Elephant" scheme="http://www.hyperxu.com/tags/Dr-Elephant/"/>
    
      <category term="大数据" scheme="http://www.hyperxu.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="hadoop" scheme="http://www.hyperxu.com/tags/hadoop/"/>
    
      <category term="hive" scheme="http://www.hyperxu.com/tags/hive/"/>
    
  </entry>
  
  <entry>
    <title>【Dr.Elephant中文文档-7】自动调优</title>
    <link href="http://www.hyperxu.com/2019/08/22/dr-elephant-7/"/>
    <id>http://www.hyperxu.com/2019/08/22/dr-elephant-7/</id>
    <published>2019-08-22T14:12:24.000Z</published>
    <updated>2019-08-22T14:21:29.213Z</updated>
    
    <content type="html"><![CDATA[<h2 id="目标"><a href="#目标" class="headerlink" title="目标"></a>目标</h2><p><code>Dr.Elephant</code>这个项目是希望构建一个可以自动优化<code>hadoop mapreduce</code>相关函数的调优框架。在这种情况下，是为了函数消耗最少的资源来完成作业。我们还希望在未来的版本将作业时间也作为函数资源消耗的参考指标。我们使用迭代算法和粒子群优化算法进行自动调优。这些迭代通过分析作业的多次运行结果来完成，我们已经在15-20次的作业中优化了20-30%的资源。</p><p>自动优化从作业的默认参数开始，并且在每次运行之后计算判断当前参数是否适合，并且根据算法建议新的参数。为了与<code>Dr.Elephant</code>交互，开发了<code>getCurrentRunParameters</code>新<code>API</code>，它返回给定作业当前运行的参数。</p><a id="more"></a><h2 id="组件"><a href="#组件" class="headerlink" title="组件"></a>组件</h2><h3 id="守护进程"><a href="#守护进程" class="headerlink" title="守护进程"></a>守护进程</h3><p>自动调优模块中有以下4个守护进程：</p><ul><li>基线计算：该<code>Daemon</code>通过<code>Dr.Elephant</code>的历史数据平均值来计算新作业应当消耗的资源和时间</li><li>作业完成检查器：一旦修改了新的调优参数来执行作业，该<code>Daemon</code>将继续轮询作业直到完成。对于<code>Azkaban</code>调度器则使用<code>Azkaban rest API</code></li><li>打分器（<code>Fitness Computation Daemon</code>）：一旦作业完成（成功/失败），该<code>Daemon</code>将根据作业消耗的资源和数据大小来评判参数集是否合适</li><li>参数生成器（<code>Param Generator</code>）：一旦设置好当前的参数集，该<code>Daemon</code>就会生成新的参数建议。目前我们使用<code>PSO</code>算法进行新的参数建议。</li></ul><h3 id="Rest-API"><a href="#Rest-API" class="headerlink" title="Rest API"></a>Rest API</h3><p>有个<code>getCurrentRunParameters</code>的新<code>API</code>，它从数据库获取建议的参数并将其返回。目前，这是外部系统和<code>Dr Elephant</code>之间唯一的自动调优交互。</p><p>自动调优的测试版本将支持以下特性：</p><ul><li>目前支持<code>Pig Script</code>优化</li><li>支持<code>Azkaban</code>调度器</li><li>支持全局开启或关闭自动调优</li><li>约束应用参数确保不会因为自动调优而导致作业失败</li><li>如果参数超出允许资源使用/执行时间的限制，则对其进行处罚</li></ul><p>我们计划将在未来的版本支持以下特性：</p><ul><li>支持<code>Hive</code>和<code>Spark</code></li><li>执行时间优化</li><li>改进可视化报告</li></ul><h3 id="自动调优架构调整"><a href="#自动调优架构调整" class="headerlink" title="自动调优架构调整"></a>自动调优架构调整</h3><p><strong>Table 1: tuning_algorithm</strong><br>这张表用于记录优化度量信息（资源，时间）和作业类型（Pig,Hive）的算法。通常情况下，一种作业类型应该有一种算法，但框架也支持一行有多种算法。</p><p><strong>Table 2: tuning_parameter</strong><br>此表记录<code>tuning_algorithm</code>中的每个算法优化的Hadoop参数。<br>例如<code>mapreduce.map.memory.mb</code>，<code>mapreduce.task.io.sort.mb</code>等。</p><p><strong>Table 3: flow_definition</strong><br>此表记录作业流，可以来自任何调度程序，如<code>Azkaban</code>，<code>Oozie</code>，<code>Appworx</code>等。</p><p><strong>Table 4: job_definition</strong><br>此表记录需要优化的作业。还包含除了自动优化信息之外的一般信息。作业信息放在2张表里，因为并不是所有作业都需要开启自动调优。</p><p><strong>Table 5: tuning_job_definition</strong><br>此表记录需要优化的作业和仅需要自动调整的信息。</p><p><strong>Table 6: flow_execution</strong><br>此表记录作业流的执行步骤。</p><p><strong>Table 7: job_execution</strong><br>此表记录作业流中的一次作业。包含自动优化之外的作业执行信息。作业信息放在2张表里，因为并不是所有作业都需要开启自动调优。</p><p><strong>Table 8: tuning_job_execution</strong><br>此表记录一次作业流的作业，并包含自动调优相关信息。这次执行对应一组参数。</p><p><strong>Table 9: job_saved_state</strong><br>由于优化算法的内部表。存储需要优化的作业的当前状态。</p><p><strong>Table 10: job_suggested_param_value</strong><br>记录一次作业的建议参数值。</p>]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;目标&quot;&gt;&lt;a href=&quot;#目标&quot; class=&quot;headerlink&quot; title=&quot;目标&quot;&gt;&lt;/a&gt;目标&lt;/h2&gt;&lt;p&gt;&lt;code&gt;Dr.Elephant&lt;/code&gt;这个项目是希望构建一个可以自动优化&lt;code&gt;hadoop mapreduce&lt;/code&gt;相关函数的调优框架。在这种情况下，是为了函数消耗最少的资源来完成作业。我们还希望在未来的版本将作业时间也作为函数资源消耗的参考指标。我们使用迭代算法和粒子群优化算法进行自动调优。这些迭代通过分析作业的多次运行结果来完成，我们已经在15-20次的作业中优化了20-30%的资源。&lt;/p&gt;
&lt;p&gt;自动优化从作业的默认参数开始，并且在每次运行之后计算判断当前参数是否适合，并且根据算法建议新的参数。为了与&lt;code&gt;Dr.Elephant&lt;/code&gt;交互，开发了&lt;code&gt;getCurrentRunParameters&lt;/code&gt;新&lt;code&gt;API&lt;/code&gt;，它返回给定作业当前运行的参数。&lt;/p&gt;
    
    </summary>
    
      <category term="Dr.Elephant" scheme="http://www.hyperxu.com/categories/Dr-Elephant/"/>
    
    
      <category term="Dr.Elephant" scheme="http://www.hyperxu.com/tags/Dr-Elephant/"/>
    
      <category term="大数据" scheme="http://www.hyperxu.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="hadoop" scheme="http://www.hyperxu.com/tags/hadoop/"/>
    
      <category term="hive" scheme="http://www.hyperxu.com/tags/hive/"/>
    
  </entry>
  
  <entry>
    <title>【Dr.Elephant中文文档-6】度量指标和启发式算法</title>
    <link href="http://www.hyperxu.com/2019/06/13/dr-elephant-6/"/>
    <id>http://www.hyperxu.com/2019/06/13/dr-elephant-6/</id>
    <published>2019-06-13T03:02:24.000Z</published>
    <updated>2019-06-13T03:14:22.117Z</updated>
    
    <content type="html"><![CDATA[<h1 id="度量指标"><a href="#度量指标" class="headerlink" title="度量指标"></a>度量指标</h1><h2 id="资源用量"><a href="#资源用量" class="headerlink" title="资源用量"></a>资源用量</h2><p>资源使用情况是你作业在GB小时内使用的资源量。</p><h3 id="计量统计"><a href="#计量统计" class="headerlink" title="计量统计"></a>计量统计</h3><p>我们将作业的资源使用量定义为任务容器大小和任务运行时间的乘积。因此，作业的资源使用量可以定义为<code>mapper</code>和<code>reducer</code>任务的资源使用量总和。</p><h3 id="范例"><a href="#范例" class="headerlink" title="范例"></a>范例</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">Consider a job with: </div><div class="line">4 mappers with runtime &#123;12, 15, 20, 30&#125; mins. </div><div class="line">4 reducers with runtime &#123;10 , 12, 15, 18&#125; mins. </div><div class="line">Container size of 4 GB </div><div class="line">Then, </div><div class="line">Resource used by all mappers: 4 * (( 12 + 15 + 20 + 30 ) / 60 ) GB Hours = 5.133 GB Hours </div><div class="line">Resource used by all reducers: 4 * (( 10 + 12 + 15 + 18 ) / 60 ) GB Hours = 3.666 GB Hours </div><div class="line">Total resource used by the job = 5.133 + 3.6666 = 8.799 GB Hours</div></pre></td></tr></table></figure><h2 id="浪费的资源量"><a href="#浪费的资源量" class="headerlink" title="浪费的资源量"></a>浪费的资源量</h2><p>这显示了作业以GB小时浪费的资源量或以浪费的资源百分比。</p><a id="more"></a><h3 id="计量统计-1"><a href="#计量统计-1" class="headerlink" title="计量统计"></a>计量统计</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line">To calculate the resources wasted, we calculate the following: </div><div class="line">The minimum memory wasted by the tasks (Map and Reduce)</div><div class="line">The runtime of the tasks (Map and Reduce)</div><div class="line">The minimum memory wasted by a task is equal to the difference between the container size and maximum task memory(peak memory) among all tasks. The resources wasted by the task is <span class="keyword">then</span> the minimum memory wasted by the task multiplied by the duration of the task. The total resource wasted by the job <span class="keyword">then</span> will be equal to the sum of wasted resources of all the tasks. </div><div class="line"> </div><div class="line">Let us define the following <span class="keyword">for</span> each task: </div><div class="line"></div><div class="line">peak_memory_used := The upper bound on the memory used by the task. </div><div class="line">runtime := The run time of the task. </div><div class="line"></div><div class="line">The peak_memory_used <span class="keyword">for</span> any task is calculated by finding out the maximum of physical memory(max_physical_memory) used by all the tasks and the virtual memory(virtual_memory) used by the task. </div><div class="line">Since peak_memory_used <span class="keyword">for</span> each task is upper bounded by max_physical_memory, we can say <span class="keyword">for</span> each task: </div><div class="line"></div><div class="line">peak_memory_used = Max(max_physical_memory, virtual_memory/2.1)</div><div class="line">Where 2.1 is the cluster memory factor. </div><div class="line"></div><div class="line">The minimum memory wasted by each task can <span class="keyword">then</span> be calculated as: </div><div class="line"></div><div class="line">wasted_memory = Container_size - peak_memory_used </div><div class="line"></div><div class="line">The minimum resource wasted by each task can <span class="keyword">then</span> be calculated as: </div><div class="line"></div><div class="line">wasted_resource = wasted_memory * runtime</div></pre></td></tr></table></figure><h2 id="运行时间"><a href="#运行时间" class="headerlink" title="运行时间"></a>运行时间</h2><p>运行时间指标显示了作业运行的总时间。</p><h3 id="计量统计-2"><a href="#计量统计-2" class="headerlink" title="计量统计"></a>计量统计</h3><p>作业运行时间是作业提交到资源管理器和作业完成时的时间差。</p><h3 id="范例-1"><a href="#范例-1" class="headerlink" title="范例"></a>范例</h3><p>作业的提交时间为<code>1461837302868 ms</code>，结束时间为<code>1461840952182 ms</code>，作业的<code>runtime</code>时间是<code>1461840952182 - 1461837302868 = 3649314 ms</code>，即<code>1.01</code>小时。</p><h2 id="等待时间"><a href="#等待时间" class="headerlink" title="等待时间"></a>等待时间</h2><p>等待时间是作业处于等待状态消耗的时间</p><h3 id="计量统计-3"><a href="#计量统计-3" class="headerlink" title="计量统计"></a>计量统计</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div></pre></td><td class="code"><pre><div class="line">For each task, <span class="built_in">let</span> us define the following: </div><div class="line"></div><div class="line">ideal_start_time := The ideal time when all the tasks should have started </div><div class="line">finish_time := The time when the task finished </div><div class="line">task_runtime := The runtime of the task </div><div class="line"></div><div class="line">- Map tasks</div><div class="line">For map tasks, we have </div><div class="line"></div><div class="line">ideal_start_time := The job submission time </div><div class="line"></div><div class="line">We will find the mapper task with the longest runtime ( task_runtime_max) and the task <span class="built_in">which</span> finished last ( finish_time_last ) </div><div class="line">The total <span class="built_in">wait</span> time of the job due to mapper tasks would be: </div><div class="line"></div><div class="line">mapper_wait_time = finish_time_last - ( ideal_start_time + task_runtime_max) </div><div class="line"></div><div class="line">- Reduce tasks</div><div class="line">For reducer tasks, we have </div><div class="line"></div><div class="line">ideal_start_time := This is computed by looking at the reducer slow start percentage (mapreduce.job.reduce.slowstart.completedmaps) and finding the finish time of the map task after <span class="built_in">which</span> first reducer should have started</div><div class="line">We will find the reducer task with the longest runtime ( task_runtime_max) and the task <span class="built_in">which</span> finished last ( finish_time_last ) </div><div class="line"></div><div class="line">The total <span class="built_in">wait</span> time of the job due to reducer tasks would be: </div><div class="line">reducer_wait_time = finish_time_last - ( ideal_start_time + task_runtime_max)</div></pre></td></tr></table></figure><h1 id="启发式算法"><a href="#启发式算法" class="headerlink" title="启发式算法"></a>启发式算法</h1><h2 id="Map-Reduce"><a href="#Map-Reduce" class="headerlink" title="Map-Reduce"></a>Map-Reduce</h2><h3 id="Mapper数据倾斜"><a href="#Mapper数据倾斜" class="headerlink" title="Mapper数据倾斜"></a>Mapper数据倾斜</h3><p>Mapper数据倾斜启发式算法能够显示作业是否发生数据倾斜。启发式算法会将所有Mapper分成两组，第一组的平均值会小于第二组。<br>例如，第一组有900个Mapper作业，每个Mapper作业平均数据量为7MB，而另一份包含1200个Mapper作业，且每个Mapper作业的平均数据量是500MB。</p><h4 id="计算"><a href="#计算" class="headerlink" title="计算"></a>计算</h4><p>首先通过递归算法计算两组平均内存消耗，来评估作业的等级。其误差为两组平均内存消耗的差除以这俩组最小的平均内存消耗的差的值。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div></pre></td><td class="code"><pre><div class="line">Let us define the following variables,</div><div class="line"></div><div class="line">    deviation: the deviation <span class="keyword">in</span> input bytes between two groups</div><div class="line">    num_of_tasks: the number of map tasks</div><div class="line">    file_size: the average input size of the larger group</div><div class="line"></div><div class="line">    num_tasks_severity: List of severity thresholds <span class="keyword">for</span> the number of tasks. e.g., num_tasks_severity = &#123;10, 20, 50, 100&#125;</div><div class="line">    deviation_severity: List of severity threshold values <span class="keyword">for</span> the deviation of input bytes between two groups. e.g., deviation_severity: &#123;2, 4, 8, 16&#125;</div><div class="line">    files_severity: The severity threshold values <span class="keyword">for</span> the fraction of HDFS block size. e.g. files_severity = &#123; ⅛, ¼, ½, 1&#125;</div><div class="line"></div><div class="line">Let us define the following <span class="built_in">functions</span>,</div><div class="line"></div><div class="line">    func avg(x): returns the average of a list x</div><div class="line">    func len(x): returns the length of a list x</div><div class="line">    func min(x,y): returns minimum of x and y</div><div class="line">    func getSeverity(x,y): Compares value x with severity threshold values <span class="keyword">in</span> y and returns the severity.</div><div class="line"></div><div class="line">We’ll compute two groups recursively based on average memory consumed by them. </div><div class="line"></div><div class="line">Let us call the two groups: group_1 and group_2</div><div class="line"></div><div class="line">Without loss of generality, <span class="built_in">let</span> us assume that,</div><div class="line">    avg(group_1) &gt; avg(group_2) and len(group_1)&lt; len(group_2) <span class="keyword">then</span>,</div><div class="line"></div><div class="line">    deviation = avg(group_1) - avg(group_2) / min(avg(group_1)) - avg(group_2))</div><div class="line">    file_size = avg(group_1)</div><div class="line">    num_of_tasks = len(group_0)</div><div class="line"></div><div class="line">The overall severity of the heuristic can be computed as,</div><div class="line">    severity = min(</div><div class="line">        getSeverity(deviation, deviation_severity)</div><div class="line">        , getSeverity(file_size,files_severity)</div><div class="line">        , getSeverity(num_of_tasks,num_tasks_severity)</div><div class="line">    )</div><div class="line">    </div><div class="line"></div><div class="line">---</div><div class="line"></div><div class="line"></div><div class="line">误差（deviation）：分成两部分后输入数据量的误差</div><div class="line">作业数量（num_of_tasks）：map作业的数量</div><div class="line">文件大小（file_size）：较大的那部分的平均输入数据量的大小</div><div class="line">作业数量的严重度（num_tasks_severity）：一个List包含了作业数量的严重度阈值，例如num_tasks_severity = &#123;10, 20, 50, 100&#125;</div><div class="line">误差严重度（deviation severity）：一个List包含了两部分Mapper作业输入数据差值的严重度阈值，例如deviation_severity: &#123;2, 4, 8, 16&#125;</div><div class="line">文件严重度（files_severity）：一个List包含了文件大小占HDFS块大小比例的严重度阈值，例如files_severity = &#123; ⅛, ¼, ½, 1&#125;</div><div class="line"></div><div class="line">然后定义如下的方法，</div><div class="line">方法 avg(x)：返回List x的平均值</div><div class="line">方法 len(x)：返回List x的长度大小</div><div class="line">方法 min(x,y)：返回x和y中较小的一个</div><div class="line">方法 getSeverity(x,y)：比较x和y中的严重度阈值，返回严重度的值</div><div class="line"></div><div class="line">接下来，根据两个部分的平均内存消耗，进行递归计算。</div><div class="line">假设分成的两部分分别为group_1和group_2</div><div class="line">为了不失一般性，假设 </div><div class="line">avg(group_1) &gt; ave(group_2) and len(group_1) &lt; len(group_2)</div><div class="line">以及</div><div class="line">deviation = avg(group_1) - avg(group_2) / min(avg(group_1) - avg(group_2))</div><div class="line">file_size = avg(group_1)</div><div class="line">num_of_tasks = len(group_0)</div><div class="line"></div><div class="line">启发式算法的严重度可以通过下面的方法来计算：</div><div class="line">severity = min(getSeverity(deviation, deviation_severity),getSeverity(file_size,files_severity),getSeverity(num_of_tasks,num_tasks_severity))</div></pre></td></tr></table></figure><h4 id="参数配置"><a href="#参数配置" class="headerlink" title="参数配置"></a>参数配置</h4><p>阈值参数<code>deviation_severity</code>、<code>num_tasks_severity</code>和<code>files_severity</code>能够简单的进行配置。如果想进一步了解如何配置这些参数，可以点击<a href="https://www.hyperxu.com/2019/02/18/dr-elephant-4/">这里</a>进行查看。</p><h3 id="Mapper-GC"><a href="#Mapper-GC" class="headerlink" title="Mapper GC"></a>Mapper GC</h3><p>Mapper GC会分析任务的GC效率。它会计算出GC时间占所有CPU时间的百分比。</p><h4 id="计算-1"><a href="#计算-1" class="headerlink" title="计算"></a>计算</h4><p>启发式算法对<code>Mapper GC</code>严重度的计算按照如下过程进行。首先，计算出所有作业的平均的CPU使用时间、平均运行时间以及平均垃圾回收消耗的时间。我们要计算<code>Mapper GC</code>严重度的最小值，这个值可以通过平均运行时间和平均垃圾回收时间占平均CPU总消耗时间的比例来计算。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line">Let us define the following variables:</div><div class="line"></div><div class="line">    avg_gc_time: average time spent garbage collecting</div><div class="line">    avg_cpu_time: average cpu time of all the tasks</div><div class="line">    avg_runtime: average runtime of all the tasks</div><div class="line">    gc_cpu_ratio: avg_gc_time/ avg_cpu_time</div><div class="line"></div><div class="line">    gc_ratio_severity: List of severity threshold values <span class="keyword">for</span> the ratio of  avg_gc_time to avg_cpu_time.</div><div class="line">    runtime_severity: List of severity threshold values <span class="keyword">for</span> the avg_runtime.</div><div class="line"></div><div class="line">Let us define the following <span class="built_in">functions</span>,</div><div class="line"></div><div class="line">    func min(x,y): returns minimum of x and y</div><div class="line">    func getSeverity(x,y): Compares value x with severity threshold values <span class="keyword">in</span> y and returns the severity.</div><div class="line"></div><div class="line">The overall severity of the heuristic can <span class="keyword">then</span> be computed as,</div><div class="line"></div><div class="line">    severity = min(getSeverity(avg_runtime, runtime_severity), getSeverity(gc_cpu_ratio, gc_ratio_severity)</div></pre></td></tr></table></figure><h4 id="参数配置-1"><a href="#参数配置-1" class="headerlink" title="参数配置"></a>参数配置</h4><p>阈值参数<code>gc_ratio_severity</code>和<code>runtime_severity</code>也是可以简单配置的。如果想进一步了解如何配置这些参数，可以参考<a href="https://www.hyperxu.com/2019/02/18/dr-elephant-4/">这里</a>。</p><h3 id="Mapper内存消耗"><a href="#Mapper内存消耗" class="headerlink" title="Mapper内存消耗"></a>Mapper内存消耗</h3><p>此部分指标用来检查<code>mapper</code>的内存消耗。他会检查任务的消耗内存与容器请求到的内存比例。消耗的内存指任务最大消耗物理内存快照的平均值。容器请求的内存是作业<code>mapreduce.map/reduce.memory.mb</code>的配置值，是作业能请求到的最大物理内存。</p><h4 id="计算-2"><a href="#计算-2" class="headerlink" title="计算"></a>计算</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line">Let us define the following variables,</div><div class="line"></div><div class="line">    avg_physical_memory: Average of the physical memories of all tasks.</div><div class="line">    container_memory: Container memory</div><div class="line"></div><div class="line">    container_memory_severity: List of threshold values <span class="keyword">for</span> the average container memory of the tasks.</div><div class="line">    memory_ratio_severity: List of threshold values <span class="keyword">for</span> the ratio of avg_plysical_memory to container_memory</div><div class="line"></div><div class="line">Let us define the following <span class="built_in">functions</span>,</div><div class="line"></div><div class="line">    func min(x,y): returns minimum of x and y</div><div class="line">    func getSeverity(x,y): Compares value x with severity threshold values <span class="keyword">in</span> y and returns the severity.</div><div class="line"></div><div class="line">The overall severity can <span class="keyword">then</span> be computed as,</div><div class="line"></div><div class="line">    severity = min(getSeverity(avg_physical_memory/container_memory, memory_ratio_severity)</div><div class="line">               , getSeverity(container_memory,container_memory_severity)</div><div class="line">              )</div></pre></td></tr></table></figure><h4 id="参数配置-2"><a href="#参数配置-2" class="headerlink" title="参数配置"></a>参数配置</h4><p>阈值参数<code>container_memory_severity</code>和<code>memory_ratio_severity</code>也是可以简单配置的。如果想进一步了解如何配置这些参数，可以参考<a href="https://www.hyperxu.com/2019/02/18/dr-elephant-4/">这里</a>。</p><h3 id="Mapper的运行速度"><a href="#Mapper的运行速度" class="headerlink" title="Mapper的运行速度"></a>Mapper的运行速度</h3><p>这部分分析<code>Mapper</code>代码的运行效率。通过这些分析可以知道<code>mapper</code>是否受限于CPU，或者处理的数据量过大。这个分析能够分析<code>mapper</code>运行速度快慢和处理的数据量大小之间的关系。</p><h4 id="计算-3"><a href="#计算-3" class="headerlink" title="计算"></a>计算</h4><p>这个启发式算法的严重度值，是<code>mapper</code>作业的运行速度的严重度和<code>mapper</code>作业的运行时间严重度中较小的一个。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line">Let us define the following variables,</div><div class="line"></div><div class="line">    median_speed: median of speeds of all the mappers. The speeds of mappers are found by taking the ratio of input bytes to runtime.</div><div class="line">    median_size: median of size of all the mappers</div><div class="line">    median_runtime: median of runtime of all the mappers.</div><div class="line"></div><div class="line">    disk_speed_severity: List of threshold values <span class="keyword">for</span> the median_speed.</div><div class="line">    runtime_severity: List of severity threshold values <span class="keyword">for</span> median_runtime.</div><div class="line"></div><div class="line">Let us define the following <span class="built_in">functions</span>,</div><div class="line"></div><div class="line">    func min(x,y): returns minimum of x and y</div><div class="line">    func getSeverity(x,y): Compares value x with severity threshold values <span class="keyword">in</span> y and returns the severity.</div><div class="line"></div><div class="line">The overall severity of the heuristic can <span class="keyword">then</span> be computed as,</div><div class="line"></div><div class="line">    severity = min(getSeverity(median_speed, disk_speed_severity), getSeverity(median_runtime, median_runtime_severity)</div></pre></td></tr></table></figure><h4 id="参数配置-3"><a href="#参数配置-3" class="headerlink" title="参数配置"></a>参数配置</h4><p>阈值参数<code>disk_speed_severity</code>和<code>runtime_severity</code>可以很简单的配置。如果想进一步的了解这些参数配置，可以点击<a href="https://www.hyperxu.com/2019/02/18/dr-elephant-4/">这里</a>查看。</p><h3 id="Mapper溢出"><a href="#Mapper溢出" class="headerlink" title="Mapper溢出"></a>Mapper溢出</h3><p>这个启发式算法通过分析磁盘<code>I/O</code>来评判<code>mapper</code>的性能。<code>mapper</code>溢出比例（溢出的记录数/总输出的记录数）是衡量<code>mapper</code>性能的一个重要指标：如果这个值接近2，表示几乎每个记录都溢出了，并临时写到磁盘两次（其中一次发生在内存排序缓存溢出时，另一次发生在合并所有溢出的块时）。当这些发生时表明<code>mapper</code>输入输出的数据量过大了。</p><h4 id="计算-4"><a href="#计算-4" class="headerlink" title="计算"></a>计算</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line">Let us define the following parameters,</div><div class="line"></div><div class="line">    total_spills: The sum of spills from all the map tasks.</div><div class="line">    total_output_records: The sum of output records from all the map tasks.</div><div class="line">    num_tasks: Total number of tasks.</div><div class="line">    ratio_spills: total_spills/ total_output_records</div><div class="line"></div><div class="line">    spill_severity: List of the threshold values <span class="keyword">for</span> ratio_spills</div><div class="line">    num_tasks_severity: List of threshold values <span class="keyword">for</span> total number of tasks.</div><div class="line"></div><div class="line">Let us define the following <span class="built_in">functions</span>,</div><div class="line"></div><div class="line">    func min(x,y): returns minimum of x and y</div><div class="line">    func getSeverity(x,y): Compares value x with severity threshold values <span class="keyword">in</span> y and returns the severity.</div><div class="line"></div><div class="line">The overall severity of the heuristic can <span class="keyword">then</span> be computed as,</div><div class="line"></div><div class="line">severity = min(getSeverity(ratio_spills, spill_severity), getSeverity(num_tasks, num_tasks_severity)</div></pre></td></tr></table></figure><h4 id="参数配置-4"><a href="#参数配置-4" class="headerlink" title="参数配置"></a>参数配置</h4><p>阈值<code>spill_severity</code>和<code>num_tasks_severity</code>可以简单的进行配置。如果想进一步了解配置参数的详细信息，可以点击这里查看。 <a href="https://www.hyperxu.com/2019/02/18/dr-elephant-4/">here</a>.</p><h3 id="Mapper运行时间"><a href="#Mapper运行时间" class="headerlink" title="Mapper运行时间"></a>Mapper运行时间</h3><p>这部分分析<code>mapper</code>的数量是否合适。通过分析结果，我们可以更好的优化任务中<code>mapper</code>的数量这个参数设置。有以下两种情况发生时，这个参数就需要优化了：</p><ul><li><code>Mapper</code>的运行时间很短。通常作业在以下情况下出现：<ul><li><code>mapper</code>数量过多</li><li><code>mapper</code>的平均运行时间很短</li><li>文件太小 </li></ul></li><li>大文件或不可分割文件块，通常作业在以下情况下出现：<ul><li><code>mapper</code>数量太少</li><li><code>mapper</code>的平均运行时间太长</li><li>文件过大 (个别达到 <strong>GB</strong> 级别)</li></ul></li></ul><h4 id="计算-5"><a href="#计算-5" class="headerlink" title="计算"></a>计算</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line">Let us define the following variables,</div><div class="line">    avg_size: average size of input data <span class="keyword">for</span> all the mappers</div><div class="line">    avg_time: average of runtime of all the tasks.</div><div class="line">    num_tasks: total number of tasks.</div><div class="line"></div><div class="line">    short_runtime_severity: The list of threshold values <span class="keyword">for</span> tasks with short runtime</div><div class="line">    long_runtime_severity: The list of threshold values <span class="keyword">for</span> tasks with long runtime.</div><div class="line">    num_tasks_severity: The list of threshold values <span class="keyword">for</span> number of tasks.</div><div class="line"></div><div class="line">Let us define the following <span class="built_in">functions</span>,</div><div class="line">    func min(x,y): returns minimum of x and y</div><div class="line">    func getSeverity(x,y): Compares value x with severity threshold values <span class="keyword">in</span> y and returns the severity.</div><div class="line"></div><div class="line">The overall severity of the heuristic can <span class="keyword">then</span> be computed as,</div><div class="line">    short_task_severity = min(getSeverity(avg_time,short_runtime_severity), getSeverity(num_tasks, num_tasks_severity))</div><div class="line">    severity = max(getSeverity(avg_size, long_runtime_severity), short_task_severity)</div></pre></td></tr></table></figure><h4 id="参数配置-5"><a href="#参数配置-5" class="headerlink" title="参数配置"></a>参数配置</h4><p>阈值<code>short_runtime_severity</code> 、<code>long_runtime_severity</code>以及<code>num_tasks_severity</code>可以很简单的配置。如果想进一步了解参数配置的详细信息，可以点击<a href="https://www.hyperxu.com/2019/02/18/dr-elephant-4/">这里</a>查看。</p><h3 id="Reducer数据倾斜"><a href="#Reducer数据倾斜" class="headerlink" title="Reducer数据倾斜"></a>Reducer数据倾斜</h3><p>这部分分析每个<code>Reduce</code>中的数据是否存在倾斜情况。这部分分析能够发现<code>Reducer</code>中是否存在这种情况，将<code>Reduce</code>分为两部分，其中一部分的输入数据量是否明显大于另一部分的输入数据量。</p><h4 id="计算-6"><a href="#计算-6" class="headerlink" title="计算"></a>计算</h4><p>首先通过递归算法计算均值并基于每个组消耗的平均内存消耗将任务划分为两组来评估该算法的等级。误差表示为两个部分<code>Reducer</code>的平均内存消耗之差除以两个部分最小内存消耗之差得到的比例。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div></pre></td><td class="code"><pre><div class="line">Let us define the following variables:</div><div class="line">  deviation: deviation <span class="keyword">in</span> input bytes between two groups</div><div class="line">  num_of_tasks: number of reduce tasks</div><div class="line">  file_size: average of larger group</div><div class="line">  num_tasks_severity: List of severity threshold values <span class="keyword">for</span> the number of tasks.</div><div class="line">  e.g. num_tasks_severity = &#123;10,20,50,100&#125;</div><div class="line">  deviation_severity: List of severity threshold values <span class="keyword">for</span> the deviation of input bytes between two groups.</div><div class="line">  e.g. deviation_severity = &#123;2,4,8,16&#125;</div><div class="line">  files_severity: The severity threshold values <span class="keyword">for</span> the fraction of HDFS block size</div><div class="line">  e.g. files_severity = &#123; ⅛, ¼, ½, 1&#125;</div><div class="line"></div><div class="line">Let us define the following <span class="built_in">functions</span>:</div><div class="line">  func avg(x): returns the average of a list x</div><div class="line">  func len(x): returns the length of a list x</div><div class="line">  func min(x,y): returns minimum of x and y</div><div class="line">  func getSeverity(x,y): Compares value x with severity threshold values <span class="keyword">in</span> y and returns the severity.</div><div class="line"></div><div class="line">We’ll compute two groups recursively based on average memory consumed by them. </div><div class="line">Let us call the two groups: group_1 and group_2</div><div class="line">Without loss of generality, <span class="built_in">let</span> us assume that:</div><div class="line">  avg(group_1) &gt; avg(group_2) and len(group_1)&lt; len(group_2) <span class="keyword">then</span>,</div><div class="line">  deviation = avg(group_1) - avg(group_2) / min(avg(group_1)) - avg(group_2))</div><div class="line">  file_size = avg(group_1)</div><div class="line">  num_of_tasks = len(group_0)</div><div class="line"></div><div class="line">The overall severity of the heuristic can be computed as, </div><div class="line">  severity = min(getSeverity(deviation,deviation_severity),getSeverity(file_size,files_severity),getSeverity(num_of_tasks,num_tasks_severity))</div></pre></td></tr></table></figure><h4 id="参数配置-6"><a href="#参数配置-6" class="headerlink" title="参数配置"></a>参数配置</h4><p>阈值<code>deviation_severity</code>、<code>num_tasks_severity</code>和<code>files_severity</code>，可以很简单的进行配置。如果想进一步了解这些参数的配置，可以点击<a href="https://www.hyperxu.com/2019/02/18/dr-elephant-4/">这里</a>查看。</p><h3 id="Reducer-GC"><a href="#Reducer-GC" class="headerlink" title="Reducer GC"></a>Reducer GC</h3><p>这部分分析任务的GC效率，能够计算并告诉我们GC时间占所用CPU时间的比例。</p><h4 id="计算-7"><a href="#计算-7" class="headerlink" title="计算"></a>计算</h4><p>首先，会计算出所有任务的平均CPU消耗时间、平均运行时间以及平均垃圾回收所消耗的时间。然后，算法会根据平均运行时间以及垃圾回收时间占平均CPU时间的比值来计算出最低的严重等级。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line">Let us define the following variables:</div><div class="line"></div><div class="line">    avg_gc_time: average time spent garbage collecting</div><div class="line">    avg_cpu_time: average cpu time of all the tasks</div><div class="line">    avg_runtime: average runtime of all the tasks</div><div class="line">    gc_cpu_ratio: avg_gc_time/ avg_cpu_time</div><div class="line"></div><div class="line">    gc_ratio_severity: List of severity threshold values <span class="keyword">for</span> the ratio of  avg_gc_time to avg_cpu_time.</div><div class="line">    runtime_severity: List of severity threshold values <span class="keyword">for</span> the avg_runtime.</div><div class="line"></div><div class="line">Let us define the following <span class="built_in">functions</span>,</div><div class="line"></div><div class="line">    func min(x,y): returns minimum of x and y</div><div class="line">    func getSeverity(x,y): Compares value x with severity threshold values <span class="keyword">in</span> y and returns the severity.</div><div class="line"></div><div class="line">The overall severity of the heuristic can <span class="keyword">then</span> be computed as,</div><div class="line">    severity = min(getSeverity(avg_runtime, runtime_severity), getSeverity(gc_cpu_ratio, gc_ratio_severity)</div></pre></td></tr></table></figure><h4 id="参数配置-7"><a href="#参数配置-7" class="headerlink" title="参数配置"></a>参数配置</h4><p>阈值<code>gc_ratio_severity</code>、<code>runtime_severity</code>可以很简单的配置，如果想进一步了解参数配置的详细过程，可以点击<a href="https://www.hyperxu.com/2019/02/18/dr-elephant-4/">这里</a>查看。</p><h3 id="Reducer内存消耗"><a href="#Reducer内存消耗" class="headerlink" title="Reducer内存消耗"></a>Reducer内存消耗</h3><p>这部分分析显示了任务的内存利用率。算法会比较作业消耗的内存以及容器要求的内存分配。消耗的内存是指每个作业消耗的最大内存的平均值。容器需求的内存是指任务配置的<code>mapreduce.map/reduce.memory.mb</code>，也就是任务能够使用最大物理内存。</p><h4 id="计算-8"><a href="#计算-8" class="headerlink" title="计算"></a>计算</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line">Let us define the following variables,</div><div class="line"></div><div class="line">    avg_physical_memory: Average of the physical memories of all tasks.</div><div class="line">    container_memory: Container memory</div><div class="line"></div><div class="line">    container_memory_severity: List of threshold values <span class="keyword">for</span> the average container memory of the tasks.</div><div class="line">    memory_ratio_severity: List of threshold values <span class="keyword">for</span> the ratio of avg_physical_memory to container_memory</div><div class="line"></div><div class="line">Let us define the following <span class="built_in">functions</span>,</div><div class="line"></div><div class="line">    func min(x,y): returns minimum of x and y</div><div class="line">    func getSeverity(x,y): Compares value x with severity threshold values <span class="keyword">in</span> y and returns the severity.</div><div class="line"></div><div class="line">The overall severity can <span class="keyword">then</span> be computed as,</div><div class="line"></div><div class="line">    severity = min(getSeverity(avg_physical_memory/container_memory, memory_ratio_severity)</div><div class="line">               , getSeverity(container_memory,container_memory_severity)</div><div class="line">              )</div></pre></td></tr></table></figure><h4 id="参数配置-8"><a href="#参数配置-8" class="headerlink" title="参数配置"></a>参数配置</h4><p>阈值<code>container_memory_severity</code>和<code>memory_ratio_severity</code>可以简单的进行配置。如果想进一步了解配置参数的详细信息，可以点击<a href="https://www.hyperxu.com/2019/02/18/dr-elephant-4/">这里</a>查看。</p><h3 id="Reducer运行时间"><a href="#Reducer运行时间" class="headerlink" title="Reducer运行时间"></a>Reducer运行时间</h3><p>这部分分析<code>Reducer</code>的运行效率，可以帮助我们更好的配置任务中<code>reducer</code>的数量。当出现以下两种情况时，说明<code>Reducer</code>的数量需要进行调优：</p><ul><li><code>Reducer</code>过多，hadoop任务可能的表现是：<ul><li><code>Reducer</code>数量过多</li><li><code>Reducer</code>的运行时间很短</li></ul></li><li><code>Reducer</code>过少，hadoop任务可能的表现是：<ul><li><code>Reducer</code>数量过少</li><li><code>Reducer</code>运行时间很长</li></ul></li></ul><h4 id="计算-9"><a href="#计算-9" class="headerlink" title="计算"></a>计算</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line">Let us define the following variables,</div><div class="line"></div><div class="line">    avg_size: average size of input data <span class="keyword">for</span> all the mappers</div><div class="line">    avg_time: average of runtime of all the tasks.</div><div class="line">    num_tasks: total number of tasks.</div><div class="line"></div><div class="line">    short_runtime_severity: The list of threshold values <span class="keyword">for</span> tasks with short runtime</div><div class="line">    long_runtime_severity: The list of threshold values <span class="keyword">for</span> tasks with long runtime.</div><div class="line">    num_tasks_severity: The number of tasks.</div><div class="line"></div><div class="line">Let us define the following <span class="built_in">functions</span>,</div><div class="line"></div><div class="line">    func min(x,y): returns minimum of x and y</div><div class="line">    func getSeverity(x,y): Compares value x with severity threshold values <span class="keyword">in</span> y and returns the severity.</div><div class="line"></div><div class="line">The overall severity of the heuristic can <span class="keyword">then</span> be computed as,</div><div class="line"></div><div class="line">    short_task_severity = min(getSeverity(avg_time,short_runtime_severity), getSeverity(num_tasks, num_tasks_severity))</div><div class="line">    severity = max(getSeverity(avg_size, long_runtime_severity), short_task_severity)</div></pre></td></tr></table></figure><h4 id="参数配置-9"><a href="#参数配置-9" class="headerlink" title="参数配置"></a>参数配置</h4><p>阈值参数<code>short_runtime_severity</code>、<code>long_runtime_severity</code>以及<code>num_tasks_severity</code>可以很简单的配置，如果想进一步了解参数配置的详细过程，可以点击<a href="https://www.hyperxu.com/2019/02/18/dr-elephant-4/">这里</a>查看。</p><h3 id="清洗-amp-排序"><a href="#清洗-amp-排序" class="headerlink" title="清洗&amp;排序"></a>清洗&amp;排序</h3><p>这部分分析<code>reducer</code>消耗的总时间以及<code>reducer</code>在进行清洗和排序时消耗的时间，通过这些分析，可以评估<code>reducer</code>的执行效率。</p><h4 id="计算-10"><a href="#计算-10" class="headerlink" title="计算"></a>计算</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line">Let’s define following variables,</div><div class="line"></div><div class="line">    avg_exec_time: average time spent <span class="keyword">in</span> execution by all the tasks.</div><div class="line">    avg_shuffle_time: average time spent <span class="keyword">in</span> shuffling.</div><div class="line">    avg_sort_time: average time spent <span class="keyword">in</span> sorting.</div><div class="line"></div><div class="line">    runtime_ratio_severity: List of threshold values <span class="keyword">for</span> the ratio of twice of average shuffle or sort time to average execution time.</div><div class="line">    runtime_severity: List of threshold values <span class="keyword">for</span> the runtime <span class="keyword">for</span> shuffle or sort stages. </div><div class="line"></div><div class="line">The overall severity can <span class="keyword">then</span> be found as,</div><div class="line"></div><div class="line">severity = max(shuffle_severity, sort_severity)</div><div class="line"></div><div class="line"><span class="built_in">where</span> shuffle_severity and sort_severity can be found as: </div><div class="line"></div><div class="line">shuffle_severity = min(getSeverity(avg_shuffle_time, runtime_severity), getSeverity(avg_shuffle_time*2/avg_exec_time, runtime_ratio_severity))</div><div class="line"></div><div class="line">sort_severity = min(getSeverity(avg_sort_time, runtime_severity), getSeverity(avg_sort_time*2/avg_exec_time, runtime_ratio_severity))</div></pre></td></tr></table></figure><h4 id="参数配置-10"><a href="#参数配置-10" class="headerlink" title="参数配置"></a>参数配置</h4><p>阈值参数<code>avg_exec_time</code>、<code>avg_shuffle_time</code>和<code>avg_sort_time</code>可以很简单的进行配置。更多关于参数配置的相信信息可以点击<a href="https://www.hyperxu.com/2019/02/18/dr-elephant-4/">这里</a>查看。</p><h2 id="Spark"><a href="#Spark" class="headerlink" title="Spark"></a>Spark</h2><h3 id="Spark的事件日志限制"><a href="#Spark的事件日志限制" class="headerlink" title="Spark的事件日志限制"></a>Spark的事件日志限制</h3><p><code>Spark</code>事件日志处理器当前无法处理很大的日志文件。<code>Dr-Elephant</code>需要花很长的时间去处理一个很大的<code>Spark</code>时间日志文件，期间很可能会影响<code>Dr-Elephant</code>本身的稳定运行。因此，目前我们设置了一个日志大小限制（100MB），如果超过这个大小，会新起一个进程去处理这个日志。</p><h4 id="计算-11"><a href="#计算-11" class="headerlink" title="计算"></a>计算</h4><p>如果数据被限流了，那么启发式算法将评估为最严重等级<code>CRITICAL</code>，否则，就没有评估等级。</p><h3 id="Spark负载均衡处理器"><a href="#Spark负载均衡处理器" class="headerlink" title="Spark负载均衡处理器"></a>Spark负载均衡处理器</h3><p>和<code>Map/Reduce</code>任务的执行机制不同，<code>Spark</code>应用在启动后会一次性分配它所需要的所有资源，直到整个任务结束才会释放这些资源。根据这个机制，对<code>Spark</code>的处理器的负载均衡就显得非常重要，可以避免集群中个别节点压力过大。</p><h4 id="计算-12"><a href="#计算-12" class="headerlink" title="计算"></a>计算</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div></pre></td><td class="code"><pre><div class="line">Let us define the following variables:</div><div class="line">    </div><div class="line">    peak_memory: List of peak memories <span class="keyword">for</span> all executors</div><div class="line">    durations: List of durations of all executors</div><div class="line">    inputBytes: List of input bytes of all executors</div><div class="line">    outputBytes: List of output bytes of all executors.</div><div class="line"></div><div class="line">    looser_metric_deviation_severity: List of threshold values <span class="keyword">for</span> deviation severity, loose bounds.</div><div class="line">    metric_deviation_severity: List of threshold values <span class="keyword">for</span> deviation severity, tight bounds. </div><div class="line"></div><div class="line">Let us define the following <span class="built_in">functions</span>:</div><div class="line"></div><div class="line">    func getDeviation(x): returns max(|maximum-avg|, |minimum-avg|)/avg, <span class="built_in">where</span></div><div class="line">        x = list of values</div><div class="line">        maximum = maximum of values <span class="keyword">in</span> x</div><div class="line">        minimum = minimum of values <span class="keyword">in</span> x</div><div class="line">        avg = average of values <span class="keyword">in</span> x</div><div class="line"></div><div class="line">    func getSeverity(x,y): Compares value x with severity threshold values <span class="keyword">in</span> y and returns the severity.</div><div class="line">    func max(x,y): returns the maximum value of x and y.</div><div class="line">    func M<span class="keyword">in</span>(l): returns the minimum of a list l.</div><div class="line"></div><div class="line">The overall severity can be found as,</div><div class="line"></div><div class="line">    severity = M<span class="keyword">in</span>( getSeverity(getDeviation(peak_memory), looser_metric_deviation_severity), </div><div class="line">               getSeverity(getDeviation(durations),  metric_deviation_severity),</div><div class="line">               getSeverity(getDeviation(inputBytes), metric_deviation_severity),</div><div class="line">               getSeverity(getDeviation(outputBytes), looser_metric_deviation_severity). </div><div class="line">               )</div></pre></td></tr></table></figure><h4 id="参数配置-11"><a href="#参数配置-11" class="headerlink" title="参数配置"></a>参数配置</h4><p>阈值参数<code>looser_metric_deviation_severity</code>和<code>metric_deviation_severity</code>可以简单的进行配置。如果想进一步了解参数配置的详细过程，可以点击<a href="https://www.hyperxu.com/2019/02/18/dr-elephant-4/">这里</a>查看。</p><h3 id="Spark任务运行时间"><a href="#Spark任务运行时间" class="headerlink" title="Spark任务运行时间"></a>Spark任务运行时间</h3><p>这部分启发式算法对<code>Spark</code>任务的运行时间进行调优分析。每个<code>Spark</code>应用程序可以拆分成多个任务，每个任务又可以拆分成多个运行阶段。</p><h4 id="计算-13"><a href="#计算-13" class="headerlink" title="计算"></a>计算</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line">Let us define the following variables,</div><div class="line"></div><div class="line">    avg_job_failure_rate: Average job failure rate</div><div class="line">    avg_job_failure_rate_severity: List of threshold values <span class="keyword">for</span> average job failure rate</div><div class="line"></div><div class="line">Let us define the following variables <span class="keyword">for</span> each job,</div><div class="line"></div><div class="line">    single_job_failure_rate: Failure rate of a single job</div><div class="line">    single_job_failure_rate_severity: List of threshold values <span class="keyword">for</span> single job failure rate.</div><div class="line"></div><div class="line">The severity of the job can be found as maximum of single_job_failure_rate_severity <span class="keyword">for</span> all <span class="built_in">jobs</span> and avg_job_failure_rate_severity.</div><div class="line"></div><div class="line">i.e. severity = max(getSeverity(single_job_failure_rate, single_job_failure_rate_severity),</div><div class="line">                    getSeverity(avg_job_failure_rate, avg_job_failure_rate_severity)</div><div class="line">                )</div><div class="line"></div><div class="line"><span class="built_in">where</span> single_job_failure_rate is computed <span class="keyword">for</span> all the jobs.</div></pre></td></tr></table></figure><h4 id="参数配置-12"><a href="#参数配置-12" class="headerlink" title="参数配置"></a>参数配置</h4><p>阈值参数<code>single_job_failure_rate_severity</code>和<code>avg_job_failure_rate_severity</code>可以很简单的进行配置。更多详细信息，可以点击<a href="https://www.hyperxu.com/2019/02/18/dr-elephant-4/">这里</a>查看。</p><h3 id="Spark内存限制"><a href="#Spark内存限制" class="headerlink" title="Spark内存限制"></a>Spark内存限制</h3><p>目前，<code>Spark</code>应用程序缺少动态资源分配的功能。与<code>Map/Reduce</code>任务不同，能够为每个<code>map/reduce</code>进程分配所需要的资源，并且在执行过程中逐步释放占用的资源。而<code>Spark</code>在应用程序执行时，会一次性的申请所需要的所有资源，直到任务结束才释放这些资源。过多的内存使用会对集群节点的稳定性产生影响。所以，我们需要限制<code>Spark</code>应用程序能使用的最大内存比例。</p><h4 id="计算-14"><a href="#计算-14" class="headerlink" title="计算"></a>计算</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line">Let us define the following variables,</div><div class="line"></div><div class="line">    total_executor_memory: total memory of all the executors</div><div class="line">    total_storage_memory: total memory allocated <span class="keyword">for</span> storage by all the executors</div><div class="line">    total_driver_memory: total driver memory allocated</div><div class="line">    peak_memory: total memory used at peak</div><div class="line"></div><div class="line">    mem_utilization_severity: The list of threshold values <span class="keyword">for</span> the memory utilization.</div><div class="line">    total_memory_severity_in_tb: The list of threshold values <span class="keyword">for</span> total memory.</div><div class="line"></div><div class="line">Let us define the following <span class="built_in">functions</span>,</div><div class="line"></div><div class="line">    func max(x,y): Returns maximum of x and y.</div><div class="line">    func getSeverity(x,y): Compares value x with severity threshold values <span class="keyword">in</span> y and returns the severity.</div><div class="line"></div><div class="line">The overall severity can <span class="keyword">then</span> be computed as,</div><div class="line"></div><div class="line">    severity = max(getSeverity(total_executor_memory,total_memory_severity_in_tb),</div><div class="line">                   getSeverity(peak_memory/total_storage_memory, mem_utilization_severity)</div><div class="line">               )</div></pre></td></tr></table></figure><h4 id="参数配置-13"><a href="#参数配置-13" class="headerlink" title="参数配置"></a>参数配置</h4><p>阈值参数<code>total_memory_severity_in_tb</code>和<code>mem_utilization_severity</code>可以很简单的配置。进一步了解，可以点击<a href="https://www.hyperxu.com/2019/02/18/dr-elephant-4/">这里</a>查看。</p><h3 id="Spark阶段运行时间"><a href="#Spark阶段运行时间" class="headerlink" title="Spark阶段运行时间"></a>Spark阶段运行时间</h3><p>与<code>Spark</code>任务运行时间一样，<code>Spark</code>应用程序可以分为多个任务，每个任务又可以分为多个运行阶段。</p><h4 id="计算-15"><a href="#计算-15" class="headerlink" title="计算"></a>计算</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div></pre></td><td class="code"><pre><div class="line">Let us define the following variable <span class="keyword">for</span> each spark job,</div><div class="line"></div><div class="line">    stage_failure_rate: The stage failure rate of the job</div><div class="line">    stagge_failure_rate_severity: The list of threshold values <span class="keyword">for</span> stage failure rate.</div><div class="line"></div><div class="line">Let us define the following variables <span class="keyword">for</span> each stage of a spark job,</div><div class="line"></div><div class="line">    task_failure_rate: The task failure rate of the stage</div><div class="line">    runtime: The runtime of a single stage</div><div class="line"></div><div class="line">    single_stage_tasks_failure_rate_severity: The list of threshold values <span class="keyword">for</span> task failure of a stage</div><div class="line">    stage_runtime_severity_in_min: The list of threshold values <span class="keyword">for</span> stage runtime.</div><div class="line"></div><div class="line">Let us define the following <span class="built_in">functions</span>,</div><div class="line"></div><div class="line">    func max(x,y): returns the maximum value of x and y.</div><div class="line">    func getSeverity(x,y): Compares value x with severity threshold values <span class="keyword">in</span> y and returns the severity.</div><div class="line"></div><div class="line">The overall severity can be found as:</div><div class="line"></div><div class="line">    severity_stage = max(getSeverity(task_failure_rate, single_stage_tasks_faioure_rate_severity),</div><div class="line">                   getSeverity(runtime, stage_runtime_severity_in_min)</div><div class="line">               )</div><div class="line">    severity_job = getSeverity(stage_failure_rate,stage_failure_rate_severity)</div><div class="line"></div><div class="line">    severity = max(severity_stage, severity_job)</div><div class="line"></div><div class="line"><span class="built_in">where</span> task_failure_rate is computed <span class="keyword">for</span> all the tasks.</div></pre></td></tr></table></figure><h4 id="参数配置-14"><a href="#参数配置-14" class="headerlink" title="参数配置"></a>参数配置</h4><p>阈值参数<code>single_stage_tasks_failure_rate_severity</code>、<code>stage_runtime_severity_in_min</code>和<code>stage_failure_rate_severity</code>可以很简单的配置。进一步了解，请点击<a href="https://www.hyperxu.com/2019/02/18/dr-elephant-4/">这里</a>。</p><blockquote><p>本章篇幅较长，一些专有名词及参数功能，可以在<code>Dr-Elephant</code>的<code>Dashboard</code>中查。</p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;度量指标&quot;&gt;&lt;a href=&quot;#度量指标&quot; class=&quot;headerlink&quot; title=&quot;度量指标&quot;&gt;&lt;/a&gt;度量指标&lt;/h1&gt;&lt;h2 id=&quot;资源用量&quot;&gt;&lt;a href=&quot;#资源用量&quot; class=&quot;headerlink&quot; title=&quot;资源用量&quot;&gt;&lt;/a&gt;资源用量&lt;/h2&gt;&lt;p&gt;资源使用情况是你作业在GB小时内使用的资源量。&lt;/p&gt;
&lt;h3 id=&quot;计量统计&quot;&gt;&lt;a href=&quot;#计量统计&quot; class=&quot;headerlink&quot; title=&quot;计量统计&quot;&gt;&lt;/a&gt;计量统计&lt;/h3&gt;&lt;p&gt;我们将作业的资源使用量定义为任务容器大小和任务运行时间的乘积。因此，作业的资源使用量可以定义为&lt;code&gt;mapper&lt;/code&gt;和&lt;code&gt;reducer&lt;/code&gt;任务的资源使用量总和。&lt;/p&gt;
&lt;h3 id=&quot;范例&quot;&gt;&lt;a href=&quot;#范例&quot; class=&quot;headerlink&quot; title=&quot;范例&quot;&gt;&lt;/a&gt;范例&lt;/h3&gt;&lt;figure class=&quot;highlight bash&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;1&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;2&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;3&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;4&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;5&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;6&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;7&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;8&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;Consider a job with: &lt;/div&gt;&lt;div class=&quot;line&quot;&gt;4 mappers with runtime &amp;#123;12, 15, 20, 30&amp;#125; mins. &lt;/div&gt;&lt;div class=&quot;line&quot;&gt;4 reducers with runtime &amp;#123;10 , 12, 15, 18&amp;#125; mins. &lt;/div&gt;&lt;div class=&quot;line&quot;&gt;Container size of 4 GB &lt;/div&gt;&lt;div class=&quot;line&quot;&gt;Then, &lt;/div&gt;&lt;div class=&quot;line&quot;&gt;Resource used by all mappers: 4 * (( 12 + 15 + 20 + 30 ) / 60 ) GB Hours = 5.133 GB Hours &lt;/div&gt;&lt;div class=&quot;line&quot;&gt;Resource used by all reducers: 4 * (( 10 + 12 + 15 + 18 ) / 60 ) GB Hours = 3.666 GB Hours &lt;/div&gt;&lt;div class=&quot;line&quot;&gt;Total resource used by the job = 5.133 + 3.6666 = 8.799 GB Hours&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;h2 id=&quot;浪费的资源量&quot;&gt;&lt;a href=&quot;#浪费的资源量&quot; class=&quot;headerlink&quot; title=&quot;浪费的资源量&quot;&gt;&lt;/a&gt;浪费的资源量&lt;/h2&gt;&lt;p&gt;这显示了作业以GB小时浪费的资源量或以浪费的资源百分比。&lt;/p&gt;
    
    </summary>
    
      <category term="Dr.Elephant" scheme="http://www.hyperxu.com/categories/Dr-Elephant/"/>
    
    
      <category term="Dr.Elephant" scheme="http://www.hyperxu.com/tags/Dr-Elephant/"/>
    
      <category term="大数据" scheme="http://www.hyperxu.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="hadoop" scheme="http://www.hyperxu.com/tags/hadoop/"/>
    
      <category term="hive" scheme="http://www.hyperxu.com/tags/hive/"/>
    
  </entry>
  
  <entry>
    <title>【Dr.Elephant中文文档-5】用户指南</title>
    <link href="http://www.hyperxu.com/2019/04/09/dr-elephant-5/"/>
    <id>http://www.hyperxu.com/2019/04/09/dr-elephant-5/</id>
    <published>2019-04-09T09:20:24.000Z</published>
    <updated>2019-04-22T03:49:47.533Z</updated>
    
    <content type="html"><![CDATA[<p>本节介绍如何使用<code>Dr.Elephant</code>来进行任务查看，分析，搜索，比较等。</p><h1 id="仪表盘"><a href="#仪表盘" class="headerlink" title="仪表盘"></a>仪表盘</h1><p><code>Dr.Elephant</code>的启动首页正如你所见<br><img src="https://uploads.hyperxu.com/15530473247720.jpg" alt=""></p><h1 id="组件"><a href="#组件" class="headerlink" title="组件"></a>组件</h1><h2 id="集群统计信息"><a href="#集群统计信息" class="headerlink" title="集群统计信息"></a>集群统计信息</h2><p>这个页面包含了集群最近的统计信息。列出了最近24小时分析过的作业数量，可进行优化的作业数量和待优化的作业数量。</p><h2 id="最新分析"><a href="#最新分析" class="headerlink" title="最新分析"></a>最新分析</h2><p>这一部分列出了最近一段时间分析的任务</p><a id="more"></a><h1 id="搜索页"><a href="#搜索页" class="headerlink" title="搜索页"></a>搜索页</h1><p><img src="https://uploads.hyperxu.com/15530490427340.jpg" alt=""><br>搜索页允许你通过一下选项过滤搜索作业和工作流：</p><ul><li><strong>作业ID</strong>：输入作业的ID，可以搜索一个特定的任务或者任务流。返回作业详情页面。</li><li><strong>工作流执行ID/URL</strong>：使用作业流的执行ID或者URL（例如Azkaban的作业流），可以搜索到被这个作业流触发的所有任务。</li><li><strong>用户名</strong>：提交任务的用户名</li><li><strong>任务类型</strong>：搜索特定类型的所有任务</li><li><strong>待优化等级</strong>：每个任务被<code>Dr.Elephant</code>诊断完以后，就会生成一份详细的诊断报告，其中就包括该任务的待优化等级。我们可以基于待优化等级搜索任务。例如，我们在待优化等级输入框中输入”severe（严重)”，搜索结果就会包含至少被一个启发式算法诊断为”severe”的所有任务。在该例子中，搜索时还可以同时指定启发式算法，那么搜索结果就只包含被该启发式算法诊断为”severe”的所有任务。</li><li><strong>任务结束日期</strong>：我们也可以使用任务结束时间作为搜索条件。在”from”和”to”这两个输入框中，可以分别设定起始时间和结束时间。这个时间段是一个左闭右开的区间 （[from, to)），包含from这个时间点，但不包含to这个时间点。<br>这些所有的搜索字段都可以组合使用。例如，我们可以指定”用户名”为”user1”，同时，指定”待优化等级”为”critical（危急）”，点击搜索，会返回所有的user1提交的任务中待优化等级为”critical”的任务。</li></ul><h1 id="作业详情"><a href="#作业详情" class="headerlink" title="作业详情"></a>作业详情</h1><p>点击控制台或者搜索页中的任意一个作业，可以看到作业详情。<br><img src="https://uploads.hyperxu.com/15547992077849.jpg" alt=""></p><h2 id="组件-1"><a href="#组件-1" class="headerlink" title="组件"></a>组件</h2><ol><li><strong>任务追踪链接（Jobtracker）</strong>：这个链接指向任务的追踪页面。在这个页面上，可以看到任务的详细信息、日志、<code>map</code>和<code>reduce</code>的<code>task</code>信息。</li><li><strong>任务执行链接（Job execution）</strong>：这个链接指向任务在调度器中的执行页面。例如，在<code>Azkaban调度器上</code>，指向这个任务的执行链接。</li><li><strong>任务定义（Job definition）</strong>：这个链接指向任务在调度器中的定义页面。例如，在Azkaban调度器上，指向这个任务的属性页面。</li><li><strong>任务流执行链接（Flow execution）</strong>：这个链接指向整个任务流的执行页面。例如，在Azkaban调度器上，指向这个任务流的执行页面。</li><li><strong>任务流定义（Flow definition）</strong>：同前面的任务定义（Job definition）。</li><li><strong>任务历史（Job history）</strong>：这个链接指向任务历史页面。</li><li><strong>任务流历史（Flow history）</strong>：这个链接指向任务流历史页。</li><li><strong>度量信息（Metrics）</strong>：这个链接显示<code>Dr.Elephant</code>每项作业的计算指标，目前显示使用的资源，浪费的资源，运行时间和等待时间信息。</li></ol><h2 id="启发式算法结果报告"><a href="#启发式算法结果报告" class="headerlink" title="启发式算法结果报告"></a>启发式算法结果报告</h2><p>当一个作业被<code>Dr.Elephant</code>分析时，<code>Dr.Elephant</code>会运行所有的启发式算法来分析这个任务。每个启发式算法都会对该任务计算出一个待优化等级，这个等级可能会是”无（none）”、”中等（moderate）”、”严重（severe）”或者”危急（critical）”。在每个任务的详细分析页面，都会展示它的待优化等级以及其他分析结果。如果任务的待优化等级不是”无（none）”时，表明某些启发式算法的诊断结果认为这个任务需要优化，同时也会提供相应的链接（帮助页面）来阐述该启发式算法提出的优化建议。开发者可以通过这个链接来帮助自己优化任务。</p><h1 id="任务比较"><a href="#任务比较" class="headerlink" title="任务比较"></a>任务比较</h1><p>通过这个比较页面，你可以比较两个不同的作业流执行情况。当我们比较两次作业流的执行时，相同的作业会做出比较并在顶部展示。其他的不同的作业，会按照作业流的顺序依次在下面展示。<br><img src="https://uploads.hyperxu.com/15547997883735.jpg" alt=""></p><h1 id="历史作业页面"><a href="#历史作业页面" class="headerlink" title="历史作业页面"></a>历史作业页面</h1><p>历史作业页面展示了每个特定任务近期所有执行情况的比较图</p><h2 id="启发式视图"><a href="#启发式视图" class="headerlink" title="启发式视图"></a>启发式视图</h2><p><img src="https://uploads.hyperxu.com/15547998975728.jpg" alt=""></p><h2 id="度量视图"><a href="#度量视图" class="headerlink" title="度量视图"></a>度量视图</h2><p><img src="https://uploads.hyperxu.com/15547999216964.jpg" alt=""></p><h2 id="组件-2"><a href="#组件-2" class="headerlink" title="组件"></a>组件</h2><h3 id="搜索框"><a href="#搜索框" class="headerlink" title="搜索框"></a>搜索框</h3><p>我们可以在历史任务页面的搜索框中输入任务的ID或者<code>Azkaban</code>的URL来搜索特定的任务。点击搜索，就会得到该任务的历史执行情况的展示。在前面提到的任务详情页面中，也有链接可以跳转到该任务的历史执行情况页面。这个页面中展示的折线图代表了该任务在历史上每次执行性能的一个打分。</p><h3 id="启发式图表（性能得分图）"><a href="#启发式图表（性能得分图）" class="headerlink" title="启发式图表（性能得分图）"></a>启发式图表（性能得分图）</h3><p>执行性能打分图是一个折线图。X轴代表时间，Y轴代表分数。当我们将鼠标停留在折线图中的某个点上时，会看到有弹框弹出。弹框中列出了该任务在本次执行中造成性能问题的Top 3的阶段。执行性能的分数是通过一个简单的公式计算出来的，越低的分数表明该任务执行性能越好。</p><h3 id="度量图（耗时和资源）"><a href="#度量图（耗时和资源）" class="headerlink" title="度量图（耗时和资源）"></a>度量图（耗时和资源）</h3><p>耗时和资源图表，X轴代表时间，Y轴代表资源。当鼠标悬停在其中一个数据点上时，该特定执行的指标会显示为弹出窗口。</p><h3 id="表格展示-启发式算法"><a href="#表格展示-启发式算法" class="headerlink" title="表格展示-启发式算法"></a>表格展示-启发式算法</h3><p>在性能打分折线图的下方，可以看到该任务流在近期每次执行的表格展示。第一列是每次执行的时间，点击每个时间，都能跳转到任务流在调度器中的执行详情页面。接下来的每一列都代表了任务流执行中的一个任务。在图表中的每个任务阶段，都包含了若干种颜色的圆点。当我们将鼠标停留在某个任意颜色的圆点上时，会弹出一个弹框展示所有的启发式算法，以及这些算法对该任务的待优化等级的分析结果。</p><h3 id="表格展示-度量标准"><a href="#表格展示-度量标准" class="headerlink" title="表格展示-度量标准"></a>表格展示-度量标准</h3><p>在图表下方，您可以看到图表数据点数据的表格展示。每行代表作业的特定执行，列代表作业的<code>mapreduce</code>阶段。每个<code>mapreduce</code>列都分为更多列，每列代表一个度量。</p><h1 id="历史任务流页面"><a href="#历史任务流页面" class="headerlink" title="历史任务流页面"></a>历史任务流页面</h1><p>在历史任务流页面，展示了每个特定任务流近期所有执行情况的比较。</p><h2 id="启发式算法视图"><a href="#启发式算法视图" class="headerlink" title="启发式算法视图"></a>启发式算法视图</h2><p><img src="https://uploads.hyperxu.com/15548004890185.jpg" alt=""></p><h2 id="度量视图-1"><a href="#度量视图-1" class="headerlink" title="度量视图"></a>度量视图</h2><p><img src="https://uploads.hyperxu.com/15548005174962.jpg" alt=""></p><h2 id="组件-3"><a href="#组件-3" class="headerlink" title="组件"></a>组件</h2><h3 id="搜索框-1"><a href="#搜索框-1" class="headerlink" title="搜索框"></a>搜索框</h3><p>我们可以在历史任务页面的搜索框中输入任务的ID或者<code>Azkaban</code>的URL来搜索特定的任务。点击搜索，就会得到该任务的历史执行情况的展示。在前面提到的任务详情页面中，也有链接可以跳转到该任务的历史执行情况页面。这个页面中展示的折线图代表了该任务在历史上每次执行性能的一个打分。</p><h3 id="启发式图表（性能得分图）-1"><a href="#启发式图表（性能得分图）-1" class="headerlink" title="启发式图表（性能得分图）"></a>启发式图表（性能得分图）</h3><p>执行性能打分图是一个折线图。X轴代表时间，Y轴代表分数。当我们将鼠标停留在折线图中的某个点上时，会看到有弹框弹出。弹框中列出了该任务在本次执行中造成性能问题的Top 3的阶段。执行性能的分数是通过一个简单的公式计算出来的，越低的分数表明该任务执行性能越好。</p><h3 id="度量图（耗时和资源）-1"><a href="#度量图（耗时和资源）-1" class="headerlink" title="度量图（耗时和资源）"></a>度量图（耗时和资源）</h3><p>耗时和资源图表，X轴代表时间，Y轴代表资源。当鼠标悬停在其中一个数据点上时，该特定执行的指标会显示为弹出窗口。</p><h3 id="表格展示-启发式算法-1"><a href="#表格展示-启发式算法-1" class="headerlink" title="表格展示-启发式算法"></a>表格展示-启发式算法</h3><p>在性能打分折线图的下方，可以看到该任务流在近期每次执行的表格展示。第一列是每次执行的时间，点击每个时间，都能跳转到任务流在调度器中的执行详情页面。接下来的每一列都代表了任务流执行中的一个任务。在图表中的每个任务阶段，都包含了若干种颜色的圆点。当我们将鼠标停留在某个任意颜色的圆点上时，会弹出一个弹框展示所有的启发式算法，以及这些算法对该任务的待优化等级的分析结果。</p><h1 id="帮助"><a href="#帮助" class="headerlink" title="帮助"></a>帮助</h1><p><img src="https://uploads.hyperxu.com/15548006776572.jpg" alt=""><br>在<code>Dr.Elephant</code> UI首页点击<code>Help</code>可以跳转到帮助页面。还可以通过其他方式跳转到帮助页面，比如通过点击UI中任务详情页的<code>explain</code>链接（当启发式算法诊断结果为<code>moderate</code>、<code>severe</code>或者<code>critical</code>时出现这个链接）。在帮助页面，可以看到所有的启发式算法的介绍，以及这些启发式算法给出的优化建议。点击某个特定的启发式算法，可以看到该启发式算法得出的详细优化建议。上面图片中，展示了<code>Mapper memory</code>启发式算法给出的优化建议。</p><h1 id="待优化等级"><a href="#待优化等级" class="headerlink" title="待优化等级"></a>待优化等级</h1><p>待优化等级代表了该任务的性能，表明了该任务在性能上需要优化的迫切程度。我们通过参数可以配置每个启发式算法的一些阈值，启发式算法给每个任务的诊断分析，都会得出一个待优化等级。待优化等级共有5个，下面按照待优化的迫切性降序排序给出：CRITICAL &gt; SEVERE &gt; MODERATE &gt; LOW &gt; NONE</p><table><thead><tr><th>Severity</th><th>Color</th><th>Description</th></tr></thead><tbody><tr><td>CRITICAL</td><td></td><td>The job is in critical state and must be tuned</td></tr><tr><td>SEVERE</td><td></td><td>There is scope for improvement</td></tr><tr><td>MODERATE</td><td></td><td>There is scope for further improvement</td></tr><tr><td>LOW</td><td></td><td>There is scope for few minor improvements</td></tr><tr><td>NONE</td><td></td><td>The job is safe. No tuning necessary</td></tr></tbody></table>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;本节介绍如何使用&lt;code&gt;Dr.Elephant&lt;/code&gt;来进行任务查看，分析，搜索，比较等。&lt;/p&gt;
&lt;h1 id=&quot;仪表盘&quot;&gt;&lt;a href=&quot;#仪表盘&quot; class=&quot;headerlink&quot; title=&quot;仪表盘&quot;&gt;&lt;/a&gt;仪表盘&lt;/h1&gt;&lt;p&gt;&lt;code&gt;Dr.Elephant&lt;/code&gt;的启动首页正如你所见&lt;br&gt;&lt;img src=&quot;https://uploads.hyperxu.com/15530473247720.jpg&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h1 id=&quot;组件&quot;&gt;&lt;a href=&quot;#组件&quot; class=&quot;headerlink&quot; title=&quot;组件&quot;&gt;&lt;/a&gt;组件&lt;/h1&gt;&lt;h2 id=&quot;集群统计信息&quot;&gt;&lt;a href=&quot;#集群统计信息&quot; class=&quot;headerlink&quot; title=&quot;集群统计信息&quot;&gt;&lt;/a&gt;集群统计信息&lt;/h2&gt;&lt;p&gt;这个页面包含了集群最近的统计信息。列出了最近24小时分析过的作业数量，可进行优化的作业数量和待优化的作业数量。&lt;/p&gt;
&lt;h2 id=&quot;最新分析&quot;&gt;&lt;a href=&quot;#最新分析&quot; class=&quot;headerlink&quot; title=&quot;最新分析&quot;&gt;&lt;/a&gt;最新分析&lt;/h2&gt;&lt;p&gt;这一部分列出了最近一段时间分析的任务&lt;/p&gt;
    
    </summary>
    
      <category term="Dr.Elephant" scheme="http://www.hyperxu.com/categories/Dr-Elephant/"/>
    
    
      <category term="Dr.Elephant" scheme="http://www.hyperxu.com/tags/Dr-Elephant/"/>
    
      <category term="大数据" scheme="http://www.hyperxu.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="hadoop" scheme="http://www.hyperxu.com/tags/hadoop/"/>
    
      <category term="hive" scheme="http://www.hyperxu.com/tags/hive/"/>
    
  </entry>
  
  <entry>
    <title>【Dr.Elephant中文文档-4】开发者指南</title>
    <link href="http://www.hyperxu.com/2019/02/18/dr-elephant-4/"/>
    <id>http://www.hyperxu.com/2019/02/18/dr-elephant-4/</id>
    <published>2019-02-18T14:06:24.000Z</published>
    <updated>2019-02-18T14:09:40.017Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Dr-Elephant设置"><a href="#Dr-Elephant设置" class="headerlink" title="Dr.Elephant设置"></a><code>Dr.Elephant</code>设置</h1><p>请按照快速安装说明操作<a href="https://www.hyperxu.com/2018/11/05/dr-elephant-3/">here</a>.</p><h1 id="先决条件"><a href="#先决条件" class="headerlink" title="先决条件"></a>先决条件</h1><h2 id="Play-Activator"><a href="#Play-Activator" class="headerlink" title="Play/Activator"></a>Play/Activator</h2><h2 id="Hadoop-Spark-on-Yarn"><a href="#Hadoop-Spark-on-Yarn" class="headerlink" title="Hadoop/Spark on Yarn"></a>Hadoop/Spark on Yarn</h2><p>为了在本地部署<code>Dr.Elephant</code>测试，你需要安装<code>Hadoop(version 2.x)</code>或者<code>Spark(Yarn mode, version &gt; 1.4.0)</code>，以及资源管理服务和历史作业服务（可以用伪分布式）。关于伪分布式模式在YARN上运行MapReduce作业相关说明可以在<a href="https://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-common/SingleCluster.html" target="_blank" rel="external">这里</a>找到。</p><p>如果还没设置环境变量，可以导入<code>HADOOP_HOME</code>变量</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">$&gt; <span class="built_in">export</span> HADOOP_HOME=/path/to/hadoop/home</div><div class="line">$&gt; <span class="built_in">export</span> HADOOP_CONF_DIR=<span class="variable">$HADOOP_HOME</span>/etc/hadoop</div></pre></td></tr></table></figure><a id="more"></a><p>将hadoop的home目录添加到系统变量下，因为<code>Dr.Elephant</code>会调用到hadoop的某些类库</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$&gt; <span class="built_in">export</span> PATH=<span class="variable">$HADOOP_HOME</span>/bin:<span class="variable">$PATH</span></div></pre></td></tr></table></figure><p>确保历史作业服务器正常运行，因为<code>Dr.Elephant</code>需要依赖他运行</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$&gt; <span class="variable">$HADOOP_HOME</span>/sbin/mr-jobhistory-daemon.sh start historyserver</div></pre></td></tr></table></figure><h2 id="数据库"><a href="#数据库" class="headerlink" title="数据库"></a>数据库</h2><p><code>Dr.Elephant</code>需要一个数据库来存储相关祖业信息和分析结果数据</p><p>本地配置并启动一个<code>mysql</code>。可以从以下链接获取最新版的<code>mysql</code>：<a href="https://www.mysql.com/downloads/。`Dr.Elephant`支持`mysql" target="_blank" rel="external">https://www.mysql.com/downloads/。`Dr.Elephant`支持`mysql</a> 5.5+<code>以上的版本，有啥问题可以去Alex (wget.null@gmail.com) 的Google小组讨论。创建一个名为</code>drelephant`的库。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">$&gt; mysql -u root -p</div><div class="line">mysql&gt; create database drelephant</div></pre></td></tr></table></figure><p>可以在<code>Dr.Elephant</code>的配置文件<code>app-conf/elephant.conf</code>中配置数据库的url、数据库名称、用户名和密码。</p><p><em>使用其他数据库</em><br>目前，<code>Dr.Elephant</code>默认是支持<code>MySQL</code>数据库。但我们可以在<code>evolution files</code>中看到DDL声明。如果想配置其他的数据库，可以参考<a href="https://www.playframework.com/documentation/2.6.x/ScalaDatabase" target="_blank" rel="external">这里</a>进行配置。</p><h1 id="测试Dr-Elephant"><a href="#测试Dr-Elephant" class="headerlink" title="测试Dr.Elephant"></a>测试<code>Dr.Elephant</code></h1><p>你可以通过调用编译脚本来测试，脚本会进行所有单元测试。</p><h1 id="项目结构"><a href="#项目结构" class="headerlink" title="项目结构"></a>项目结构</h1><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div></pre></td><td class="code"><pre><div class="line">app                             → Contains all the source files</div><div class="line"> └ com.linkedin.drelepahnt      → Application Daemons</div><div class="line"> └ org.apache.spark             → Spark Support</div><div class="line"> └ controllers                  → Controller logic</div><div class="line"> └ models                       → Includes models that Map to DB</div><div class="line"> └ views                        → Page templates</div><div class="line"></div><div class="line">app-conf                        → Application Configurations</div><div class="line"> └ elephant.conf                → Port, DB, Keytab and other JVM Configurations (Overrides application.conf)</div><div class="line"> └ FetcherConf.xml              → Fetcher Configurations</div><div class="line"> └ HeuristicConf.xml            → Heuristic Configurations</div><div class="line"> └ JobTypeConf.xml              → JobType Configurations</div><div class="line"></div><div class="line">conf                            → Configurations files</div><div class="line"> └ evolutions                   → DB Schema</div><div class="line"> └ application.conf             → Main configuration file</div><div class="line"> └ log4j.properties             → log configuration file</div><div class="line"> └ routes                       → Routes definition</div><div class="line"></div><div class="line">images</div><div class="line"> └ wiki                         → Contains the images used in the wiki documentation</div><div class="line"></div><div class="line">public                          → Public assets</div><div class="line"> └ assets                       → Library files</div><div class="line"> └ css                          → CSS files</div><div class="line"> └ images                       → Image files</div><div class="line"> └ js                           → Javascript files</div><div class="line"></div><div class="line">scripts</div><div class="line"> └ start.sh                     → Starts Dr. Elephant</div><div class="line"> └ stop.sh                      → Stops Dr. Elephant</div><div class="line"></div><div class="line">test                            → Source folder for unit tests</div><div class="line"></div><div class="line">compile.sh                      → Compiles the application</div></pre></td></tr></table></figure><h1 id="启发式算法"><a href="#启发式算法" class="headerlink" title="启发式算法"></a>启发式算法</h1><p><code>Dr.Elephant</code>已经为MapReduce和Spark集成了一系列的启发式算法。有关这些算法的详细信息，请参阅启发式算法指南。这些算法都是可插拔式的模块，可以很简单的配置好。</p><h2 id="添加新的启发式算法"><a href="#添加新的启发式算法" class="headerlink" title="添加新的启发式算法"></a>添加新的启发式算法</h2><ol><li>你可以添加自定义的算法到<code>Dr.Elephant</code>中。</li><li>创建新的启发式算法，并完成测试</li><li>为自定义的启发式算法创建一个新的<code>view</code>页，例如<code>helpMapperSpill.scala.html</code></li><li>在<code>HeuristicConf.xml</code>文件中添加该启发式算法的详情</li><li><code>HeuristicConf.xml</code>文件应该包含下列内容：<ul><li><em>applicationtype</em>：应用程序类型，是MapReduce还是spark</li><li><em>heuristicname</em>：算法名称</li><li><em>classname</em>：类名全称</li><li><em>viewname</em>：view页全称</li><li><em>hadoopversions</em>：该算法匹配的hadoop版本号</li></ul></li><li>运行<code>Dr.Elephant</code>，他应该包含你新添加的算法了</li></ol><p><code>HeuristicConf.xml</code>文件示例</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">&lt;heuristic&gt;</div><div class="line">&lt;applicationtype&gt;mapreduce&lt;/applicationtype&gt;</div><div class="line">&lt;heuristicname&gt;Mapper GC&lt;/heuristicname&gt;</div><div class="line">&lt;classname&gt;com.linkedin.drelephant.mapreduce.heuristics.MapperGCHeuristic&lt;/classname&gt;</div><div class="line">&lt;viewname&gt;views.html.help.mapreduce.helpGC&lt;/viewname&gt;</div><div class="line">&lt;/heuristic&gt;</div></pre></td></tr></table></figure><h2 id="配置启发式算法"><a href="#配置启发式算法" class="headerlink" title="配置启发式算法"></a>配置启发式算法</h2><p>如果你想要覆盖启发式算法中用到的关于严重性指标的的阈值，你可以在<code>HeuristicConf.xml</code>文件中指定其值，例子如下。<br>配置严重性阈值</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">&lt;heuristic&gt;</div><div class="line">&lt;applicationtype&gt;mapreduce&lt;/applicationtype&gt;</div><div class="line">&lt;heuristicname&gt;Mapper Data Skew&lt;/heuristicname&gt;</div><div class="line">&lt;classname&gt;com.linkedin.drelephant.mapreduce.heuristics.MapperDataSkewHeuristic&lt;/classname&gt;</div><div class="line">&lt;viewname&gt;views.html.help.mapreduce.helpMapperDataSkew&lt;/viewname&gt;</div><div class="line">&lt;params&gt;</div><div class="line">  &lt;num_tasks_severity&gt;10, 50, 100, 200&lt;/num_tasks_severity&gt;</div><div class="line">  &lt;deviation_severity&gt;2, 4, 8, 16&lt;/deviation_severity&gt;</div><div class="line">  &lt;files_severity&gt;1/8, 1/4, 1/2, 1&lt;/files_severity&gt;</div><div class="line">&lt;/params&gt;</div><div class="line">&lt;/heuristic&gt;</div></pre></td></tr></table></figure><h1 id="调度器"><a href="#调度器" class="headerlink" title="调度器"></a>调度器</h1><p>如今，<code>Dr.Elephant</code>支持3种工作流调度器。他们是<code>Azkaban</code>，<code>Airflow</code>和<code>Oozie</code>。默认情况下，这些调度器都是可用的，除了<code>Airflow</code>和<code>Oozie</code>需要一些配置外，一般都是开箱即用。</p><h2 id="调度器配置"><a href="#调度器配置" class="headerlink" title="调度器配置"></a>调度器配置</h2><p>调度器和他们所有的参数都在<code>app-conf</code>目录下的<code>SchedulerConf.xml</code>文件中配置。<br>通过下面的示例<code>SchedulerConf.xml</code>文件，了解调度器相应的配置和属性。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div></pre></td><td class="code"><pre><div class="line">&lt;!-- Scheduler configurations --&gt;</div><div class="line">&lt;schedulers&gt;</div><div class="line"></div><div class="line">    &lt;scheduler&gt;</div><div class="line">        &lt;name&gt;azkaban&lt;/name&gt;</div><div class="line">        &lt;classname&gt;com.linkedin.drelephant.schedulers.AzkabanScheduler&lt;/classname&gt;</div><div class="line">    &lt;/scheduler&gt;</div><div class="line"></div><div class="line">    &lt;scheduler&gt;</div><div class="line">        &lt;name&gt;airflow&lt;/name&gt;</div><div class="line">        &lt;classname&gt;com.linkedin.drelephant.schedulers.AirflowScheduler&lt;/classname&gt;</div><div class="line">        &lt;params&gt;</div><div class="line">            &lt;airflowbaseurl&gt;http://localhost:8000&lt;/airflowbaseurl&gt;</div><div class="line">        &lt;/params&gt;</div><div class="line">    &lt;/scheduler&gt;</div><div class="line"></div><div class="line">    &lt;scheduler&gt;</div><div class="line">        &lt;name&gt;oozie&lt;/name&gt;</div><div class="line">        &lt;classname&gt;com.linkedin.drelephant.schedulers.OozieScheduler&lt;/classname&gt;</div><div class="line">        &lt;params&gt;</div><div class="line">            &lt;!-- URL of oozie host --&gt;</div><div class="line">            &lt;oozie_api_url&gt;http://localhost:11000/oozie&lt;/oozie_api_url&gt;</div><div class="line"></div><div class="line">            &lt;!-- <span class="comment">### Non mandatory properties ###</span></div><div class="line">            <span class="comment">### choose authentication method</span></div><div class="line">            &lt;oozie_auth_option&gt;KERBEROS/SIMPLE&lt;/oozie_auth_option&gt;</div><div class="line">            <span class="comment">### override oozie console url with a template (only parameter will be the id)</span></div><div class="line">            &lt;oozie_job_url_template&gt;&lt;/oozie_job_url_template&gt;</div><div class="line">            &lt;oozie_job_exec_url_template&gt;&lt;/oozie_job_exec_url_template&gt;</div><div class="line">            <span class="comment">### (if scheduled jobs are expected make sure to add following templates since oozie doesn't provide their URLS on server v4.1.0)</span></div><div class="line">            &lt;oozie_workflow_url_template&gt;http://localhost:11000/oozie/?job=%s&lt;/oozie_workflow_url_template&gt;</div><div class="line">            &lt;oozie_workflow_exec_url_template&gt;http://localhost:11000/oozie/?job=%s&lt;/oozie_workflow_exec_url_template&gt;</div><div class="line">            <span class="comment">### Use true if you can assure all app names are unique.</span></div><div class="line">            <span class="comment">### When true dr-elephant will unit all coordinator runs (in case of coordinator killed and then run again)</span></div><div class="line">            &lt;oozie_app_name_uniqueness&gt;<span class="literal">false</span>&lt;/oozie_app_name_uniqueness&gt;</div><div class="line">            --&gt;</div><div class="line">        &lt;/params&gt;</div><div class="line">    &lt;/scheduler&gt;</div><div class="line">&lt;/schedulers&gt;</div></pre></td></tr></table></figure><h2 id="贡献新的调度器"><a href="#贡献新的调度器" class="headerlink" title="贡献新的调度器"></a>贡献新的调度器</h2><p>为了充分利用<code>Dr. Elephant</code>的全部功能，需要提供以下4个<code>ID</code></p><ol><li><strong>作业定义ID：</strong>整个作业流程中定义的唯一ID。通过过滤这个ID可以查询所有历史作业</li><li><strong>作业执行ID：</strong>作业执行的唯一ID</li><li><strong>工作流定义ID：</strong>独立于任何执行的对整个流程的唯一ID</li><li><strong>工作流执行ID：</strong>特定流程执行的唯一ID</li></ol><p><code>Dr. Elephant</code>希望通过上述ID能与任何调度器对接。没有这些ID，<code>Dr. Elephant</code>无法为<code>Azkaban</code>提供集成。例如，如果没有提供作业定义Id，那么<code>Dr. Elephant</code>将无法捕获作业的历史数据。同样，如果没有提供Flow定义Id，则无法捕获工作流的历史记录。如果没有上述所有链接，<code>Dr. Elephant</code>只能在执行过程中（Mapreduce作业级别）显示作业的性能数据。</p><p>除了上述的4个ID之外，<code>Dr. Elephant</code>还需要一个可选的工作名称和4个可选链接，这些链接将帮助用户轻松的从<code>Dr. Elephant</code>跳转到相应的作业应用程序。<br>请注意，这不会影响<code>Dr. Elephant</code>的功能。</p><ol><li>Flow Definition Url</li><li>Flow Execution Url</li><li>Job Definition Url</li><li>Job Execution Url</li></ol><h1 id="打分器"><a href="#打分器" class="headerlink" title="打分器"></a>打分器</h1><p>在<code>Dr.Elephant</code>中，通过启发式算法来分析运行完成的任务，会得到一个打分。这个分数的计算方法比较简单，可以通过将待优化等级的值乘以作业(task)数量。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">int score = 0;</div><div class="line"><span class="keyword">if</span> (severity != Severity.NONE &amp;&amp; severity != Severity.LOW) &#123;</div><div class="line">    score = severity.getValue() * tasks;</div><div class="line">&#125;</div><div class="line"><span class="built_in">return</span> score;</div></pre></td></tr></table></figure><p>我们定义下列打分类型：</p><ul><li>作业得分：所有作业的待优化等级数值之和</li><li>任务得分：该任务中所有的作业分数之和</li><li>任务流得分：该任务流中所有的任务分数之和</li></ul>]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;Dr-Elephant设置&quot;&gt;&lt;a href=&quot;#Dr-Elephant设置&quot; class=&quot;headerlink&quot; title=&quot;Dr.Elephant设置&quot;&gt;&lt;/a&gt;&lt;code&gt;Dr.Elephant&lt;/code&gt;设置&lt;/h1&gt;&lt;p&gt;请按照快速安装说明操作&lt;a href=&quot;https://www.hyperxu.com/2018/11/05/dr-elephant-3/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&quot;先决条件&quot;&gt;&lt;a href=&quot;#先决条件&quot; class=&quot;headerlink&quot; title=&quot;先决条件&quot;&gt;&lt;/a&gt;先决条件&lt;/h1&gt;&lt;h2 id=&quot;Play-Activator&quot;&gt;&lt;a href=&quot;#Play-Activator&quot; class=&quot;headerlink&quot; title=&quot;Play/Activator&quot;&gt;&lt;/a&gt;Play/Activator&lt;/h2&gt;&lt;h2 id=&quot;Hadoop-Spark-on-Yarn&quot;&gt;&lt;a href=&quot;#Hadoop-Spark-on-Yarn&quot; class=&quot;headerlink&quot; title=&quot;Hadoop/Spark on Yarn&quot;&gt;&lt;/a&gt;Hadoop/Spark on Yarn&lt;/h2&gt;&lt;p&gt;为了在本地部署&lt;code&gt;Dr.Elephant&lt;/code&gt;测试，你需要安装&lt;code&gt;Hadoop(version 2.x)&lt;/code&gt;或者&lt;code&gt;Spark(Yarn mode, version &amp;gt; 1.4.0)&lt;/code&gt;，以及资源管理服务和历史作业服务（可以用伪分布式）。关于伪分布式模式在YARN上运行MapReduce作业相关说明可以在&lt;a href=&quot;https://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-common/SingleCluster.html&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;这里&lt;/a&gt;找到。&lt;/p&gt;
&lt;p&gt;如果还没设置环境变量，可以导入&lt;code&gt;HADOOP_HOME&lt;/code&gt;变量&lt;/p&gt;
&lt;figure class=&quot;highlight bash&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;1&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;2&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;$&amp;gt; &lt;span class=&quot;built_in&quot;&gt;export&lt;/span&gt; HADOOP_HOME=/path/to/hadoop/home&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;$&amp;gt; &lt;span class=&quot;built_in&quot;&gt;export&lt;/span&gt; HADOOP_CONF_DIR=&lt;span class=&quot;variable&quot;&gt;$HADOOP_HOME&lt;/span&gt;/etc/hadoop&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
    
    </summary>
    
      <category term="Dr.Elephant" scheme="http://www.hyperxu.com/categories/Dr-Elephant/"/>
    
    
      <category term="Dr.Elephant" scheme="http://www.hyperxu.com/tags/Dr-Elephant/"/>
    
      <category term="大数据" scheme="http://www.hyperxu.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="hadoop" scheme="http://www.hyperxu.com/tags/hadoop/"/>
    
      <category term="hive" scheme="http://www.hyperxu.com/tags/hive/"/>
    
  </entry>
  
  <entry>
    <title>【Dr.Elephant中文文档-3】快速安装说明</title>
    <link href="http://www.hyperxu.com/2018/11/05/dr-elephant-3/"/>
    <id>http://www.hyperxu.com/2018/11/05/dr-elephant-3/</id>
    <published>2018-11-05T10:58:46.000Z</published>
    <updated>2018-11-05T11:00:57.139Z</updated>
    
    <content type="html"><![CDATA[<h1 id="快速安装说明"><a href="#快速安装说明" class="headerlink" title="快速安装说明"></a>快速安装说明</h1><p><strong>Step 1：</strong>在GitHub上注册一个账号，并fork一份<code>Dr. Elephant</code>项目代码。</p><p><strong>Step 2：</strong>检出代码。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">$&gt; git <span class="built_in">clone</span> https://github.com/&lt;username&gt;/dr-elephant</div><div class="line">$&gt; <span class="built_in">cd</span> dr-elephant*</div></pre></td></tr></table></figure><p><strong>Step 3：</strong>先决条件：</p><ul><li>你必须先安装<code>play</code>或者<code>activator</code>命令行。下载<code>activator</code>zip包<a href="https://downloads.typesafe.com/typesafe-activator/1.3.12/typesafe-activator-1.3.12.zip" target="_blank" rel="external">https://downloads.typesafe.com/typesafe-activator/1.3.12/typesafe-activator-1.3.12.zip</a> ，解压并添加<code>activator</code>命令到你自己的环境变量<code>$PATH</code>。对于老版的<code>play</code>，你需要添加<code>paly</code>命令并替换<code>activator</code>。</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="built_in">export</span> ACTIVATOR_HOME=/path/to/unzipped/activator</div><div class="line"><span class="built_in">export</span> PATH=<span class="variable">$ACTIVATOR_HOME</span>/bin:<span class="variable">$PATH</span></div></pre></td></tr></table></figure><ul><li><code>Dr.Elephant</code>将分析数据结果存储在MySQL数据中。如果你还没装，请先安装好MySQL。（推荐5.5以上版本）</li><li><p>为了正常使用<code>Dr. Elephant</code>UI界面，需要安装<code>npm</code>及其依赖</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">sudo yum install npm</div><div class="line">sudo npm install -g bower</div><div class="line"><span class="built_in">cd</span> web; bower install; <span class="built_in">cd</span> ..</div></pre></td></tr></table></figure></li><li><p>最后，你还需要安装好Hadoop或者Spark。</p></li></ul><a id="more"></a><p><strong>Step 4：</strong>（可选，Beta阶段）如果你想尝试自动优化的新功能，请按照以下步骤来操作。（更多详情：<a href="https://github.com/linkedin/dr-elephant/wiki/Auto-Tuning" target="_blank" rel="external">https://github.com/linkedin/dr-elephant/wiki/Auto-Tuning</a> ）</p><ul><li>修改<code>app-conf/AutoTuningConf.xml</code>中<code>autotuning.enabled</code>的选项为<font color="red"><code>true</code></font>来启用自动优化功能</li><li>安装python2.6以上的版本</li><li><p>如果你想使用自定义安装的python版本：</p><ul><li><p>将<code>PYTHON_PATH</code>设置为所需要的python版本的可执行文件路径即可：</p> <figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$&gt; <span class="built_in">export</span> PYTHON_PATH=/path/to/python/executable</div></pre></td></tr></table></figure></li><li><p>或者注释<code>app-conf/AutoTuningConf.xml</code>配置文件中的<code>python.path</code>选项。</p></li></ul></li><li><p>安装<code>inspyred</code>包：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">  sudo pip install inspyred</div><div class="line">  ``` </div><div class="line">  </div><div class="line">* 如果pip安装失败，可以从https://pip.pypa.io/en/stable/installing/ 处安装。</div><div class="line"></div><div class="line">**Step 5：**编译`Dr. Elephant`代码并打包生产`zip`包。`Compile.sh`脚本可以带一个配置文件路径参数，其中包含要编译的Hadoop和Spark的版本信息。具体信息请参阅开发者指南。</div><div class="line"></div><div class="line">``` bash</div><div class="line">$&gt; ./compile.sh [./compile.conf]</div></pre></td></tr></table></figure></li></ul><p>编译完成后，打包文件在<code>dist</code>目录下。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">$&gt; ls dist</div><div class="line">dr-elephant*.zip</div></pre></td></tr></table></figure><p><strong>Step 6：</strong>复制打包后的程序到你打算安装<code>Dr. Elephant</code>的服务器上。</p><p><strong>Step 7：</strong>在你安装<code>Dr. Elephant</code>的服务器上，确认以下环境变量配置好了。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">$&gt; <span class="built_in">export</span> HADOOP_HOME=/path/to/hadoop/home</div><div class="line">$&gt; <span class="built_in">export</span> HADOOP_CONF_DIR=<span class="variable">$HADOOP_HOME</span>/etc/hadoop</div><div class="line">$&gt; <span class="built_in">export</span> SPARK_HOME=/path/to/spark/home</div><div class="line">$&gt; <span class="built_in">export</span> SPARK_CONF_DIR=/path/to/conf</div></pre></td></tr></table></figure><p><strong>Step 8：</strong>你同样需要一个存储数据的后端数据库。在<code>elephant.conf</code>配置文件中配置MySQL数据库的相关连接信息。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="comment"># Database configuration</span></div><div class="line">db_url=localhost</div><div class="line">db_name=drelephant</div><div class="line">db_user=root</div><div class="line">db_password=<span class="string">""</span></div></pre></td></tr></table></figure><p><strong>Step 9：</strong>如果你的群集是<code>kerberised</code>，则更新<code>keytab</code>用户和<code>elephant.conf</code>文件中的<code>keytab</code>文件位置。</p><p><strong>Step 10：</strong>如果你是第一次运行<code>Dr. Elephant</code>，你需要打开<code>evolutions</code>功能，为此，请在<code>elephant.conf</code>配置文件中添加（或取消注释），<code>-Devolutionplugin=enabled</code> 和 <code>-DapplyEvolutions.default=true</code>。这将会让<code>Dr. Elephant</code>自动创建相关的MySQL表，下次重启程序时记得关闭这个选项。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">$&gt; vim ./app-conf/elephant.conf</div><div class="line">jvm_props=<span class="string">" -Devolutionplugin=enabled -DapplyEvolutions.default=true"</span></div></pre></td></tr></table></figure><p><strong>Step 11：</strong>要启动<code>Dr. Elephant</code>，需要在运行启动脚本时指定配置文件目录。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$&gt; /bin/start.sh /path/to/app-conf/directory</div></pre></td></tr></table></figure><p>要验证<code>Dr. Elephant</code>是否启动成功，请检查<code>dr.log</code>文件。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">$&gt; less <span class="variable">$DR_RELEASE</span>/dr.log</div><div class="line">...</div><div class="line">play - database [default] connected at jdbc:mysql://localhost/drelephant?characterEncoding=UTF-8</div><div class="line">application - Starting Application...</div><div class="line">play - Application started (Prod)</div><div class="line">play - Listening <span class="keyword">for</span> HTTP on /0:0:0:0:0:0:0:0:8080</div></pre></td></tr></table></figure><p>要分析<code>Dr. Elephant</code>是否正确的分析作业，请检查<code>dr.log</code>文件。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$&gt; less <span class="variable">$DR_RELEASE</span>/../logs/elephant/dr_elephant.log</div></pre></td></tr></table></figure><p><strong>Step 12：</strong>一旦应用启动，你可以打开ip:port (localhost:8080)，查看UI界面。</p><p><strong>Step 13：</strong>要停止应用，只需执行</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$&gt; bin/stop.sh</div></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;快速安装说明&quot;&gt;&lt;a href=&quot;#快速安装说明&quot; class=&quot;headerlink&quot; title=&quot;快速安装说明&quot;&gt;&lt;/a&gt;快速安装说明&lt;/h1&gt;&lt;p&gt;&lt;strong&gt;Step 1：&lt;/strong&gt;在GitHub上注册一个账号，并fork一份&lt;code&gt;Dr. Elephant&lt;/code&gt;项目代码。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 2：&lt;/strong&gt;检出代码。&lt;/p&gt;
&lt;figure class=&quot;highlight bash&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;1&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;2&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;$&amp;gt; git &lt;span class=&quot;built_in&quot;&gt;clone&lt;/span&gt; https://github.com/&amp;lt;username&amp;gt;/dr-elephant&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;$&amp;gt; &lt;span class=&quot;built_in&quot;&gt;cd&lt;/span&gt; dr-elephant*&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;Step 3：&lt;/strong&gt;先决条件：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;你必须先安装&lt;code&gt;play&lt;/code&gt;或者&lt;code&gt;activator&lt;/code&gt;命令行。下载&lt;code&gt;activator&lt;/code&gt;zip包&lt;a href=&quot;https://downloads.typesafe.com/typesafe-activator/1.3.12/typesafe-activator-1.3.12.zip&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;https://downloads.typesafe.com/typesafe-activator/1.3.12/typesafe-activator-1.3.12.zip&lt;/a&gt; ，解压并添加&lt;code&gt;activator&lt;/code&gt;命令到你自己的环境变量&lt;code&gt;$PATH&lt;/code&gt;。对于老版的&lt;code&gt;play&lt;/code&gt;，你需要添加&lt;code&gt;paly&lt;/code&gt;命令并替换&lt;code&gt;activator&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&quot;highlight bash&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;1&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;2&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;&lt;span class=&quot;built_in&quot;&gt;export&lt;/span&gt; ACTIVATOR_HOME=/path/to/unzipped/activator&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;&lt;span class=&quot;built_in&quot;&gt;export&lt;/span&gt; PATH=&lt;span class=&quot;variable&quot;&gt;$ACTIVATOR_HOME&lt;/span&gt;/bin:&lt;span class=&quot;variable&quot;&gt;$PATH&lt;/span&gt;&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Dr.Elephant&lt;/code&gt;将分析数据结果存储在MySQL数据中。如果你还没装，请先安装好MySQL。（推荐5.5以上版本）&lt;/li&gt;
&lt;li&gt;&lt;p&gt;为了正常使用&lt;code&gt;Dr. Elephant&lt;/code&gt;UI界面，需要安装&lt;code&gt;npm&lt;/code&gt;及其依赖&lt;/p&gt;
&lt;figure class=&quot;highlight bash&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;1&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;2&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;3&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;sudo yum install npm&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;sudo npm install -g bower&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;&lt;span class=&quot;built_in&quot;&gt;cd&lt;/span&gt; web; bower install; &lt;span class=&quot;built_in&quot;&gt;cd&lt;/span&gt; ..&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;最后，你还需要安装好Hadoop或者Spark。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
    
    </summary>
    
      <category term="Dr.Elephant" scheme="http://www.hyperxu.com/categories/Dr-Elephant/"/>
    
    
      <category term="Dr.Elephant" scheme="http://www.hyperxu.com/tags/Dr-Elephant/"/>
    
      <category term="大数据" scheme="http://www.hyperxu.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="hadoop" scheme="http://www.hyperxu.com/tags/hadoop/"/>
    
      <category term="hive" scheme="http://www.hyperxu.com/tags/hive/"/>
    
  </entry>
  
  <entry>
    <title>【Dr.Elephant中文文档-2】管理员指南</title>
    <link href="http://www.hyperxu.com/2018/10/30/dr-elephant-2/"/>
    <id>http://www.hyperxu.com/2018/10/30/dr-elephant-2/</id>
    <published>2018-10-30T09:05:46.000Z</published>
    <updated>2018-10-30T09:07:49.544Z</updated>
    
    <content type="html"><![CDATA[<h1 id="系统环境要求"><a href="#系统环境要求" class="headerlink" title="系统环境要求"></a>系统环境要求</h1><p><code>Dr. Elephant</code>依赖于YARN的资源管理服务器和历史作业记录服务器，来获取作业详细信息和记录。YARN作业及其分析的详细信息将存储在当前配置的后端mysql中。因此在运行<code>Dr. Elephant</code>前，必须安装好MySQL和hadoop 2。<br>从<a href="https://github.com/linkedin/dr-elephant/commit/28f4025bbade1be0fc93111ee439859c530a8747" target="_blank" rel="external">#162</a>开始，将不再支持<code>JAVA 6</code>。</p><a id="more"></a><h1 id="集群部署Dr-Elephant"><a href="#集群部署Dr-Elephant" class="headerlink" title="集群部署Dr. Elephant"></a>集群部署Dr. Elephant</h1><h2 id="部署配置"><a href="#部署配置" class="headerlink" title="部署配置"></a>部署配置</h2><ul><li>将配置文件的目录复制到集群的每台机器上</li><li><p>配置环境变量<code>$ELEPHANT_CONF_DIR</code>指向到你的配置文件目录</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$&gt; <span class="built_in">export</span> ELEPHANT_CONF_DIR=/path/to/conf/dir</div></pre></td></tr></table></figure></li></ul><h3 id="Airflow和Oozie配置"><a href="#Airflow和Oozie配置" class="headerlink" title="Airflow和Oozie配置"></a>Airflow和Oozie配置</h3><p>如果你使用Airflow或Oozie调度系统，则需要编辑你<code>$ELEPHANT_CONF_DIR</code>目录下的<code>SchedulerConf.xml</code>的配置文件：</p><ul><li>Airflow，设置<code>airflowbaseurl</code>配置属性指向你的Airflow服务</li><li>Oozie，设置<code>oozie_api_url</code>配置属性指向你的Oozie调度服务的API地址<ul><li>对于Oozie可以额外设置其他跟多可选属性，有关    更多信息，请参阅<code>SchedulerConf.xml</code>相关文档</li></ul></li></ul><h2 id="二进制部署"><a href="#二进制部署" class="headerlink" title="二进制部署"></a>二进制部署</h2><ul><li>SSH连接到集群机器</li><li><p>切换到合适的部署用户</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">sudo -iu &lt;user&gt;</div></pre></td></tr></table></figure></li><li><p>解压<code>dr-elephant</code>二进制包</p></li></ul><h2 id="启动-停止dr-elephant"><a href="#启动-停止dr-elephant" class="headerlink" title="启动/停止dr-elephant"></a>启动/停止<code>dr-elephant</code></h2><ul><li>进入<code>dr-elephant</code>根目录</li><li><p>想启动<code>dr-elephant</code>，请允许启动脚本。启动脚本提供了一个配置文件目录位置的可选参数。如果你已经设置好了<code>$ELEPHANT_CONF_DIR</code>环境变量，只需要直接启动就要，不用带任何参数。否则，需要在运行时带上配置文件目录位置的参数。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">./bin/start.sh [/path/to/app-conf]</div></pre></td></tr></table></figure></li><li><p>想停止运行，</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">./bin/stop.sh</div></pre></td></tr></table></figure></li><li><p>要部署新版本，请务必先停止正在运行的进程</p></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;系统环境要求&quot;&gt;&lt;a href=&quot;#系统环境要求&quot; class=&quot;headerlink&quot; title=&quot;系统环境要求&quot;&gt;&lt;/a&gt;系统环境要求&lt;/h1&gt;&lt;p&gt;&lt;code&gt;Dr. Elephant&lt;/code&gt;依赖于YARN的资源管理服务器和历史作业记录服务器，来获取作业详细信息和记录。YARN作业及其分析的详细信息将存储在当前配置的后端mysql中。因此在运行&lt;code&gt;Dr. Elephant&lt;/code&gt;前，必须安装好MySQL和hadoop 2。&lt;br&gt;从&lt;a href=&quot;https://github.com/linkedin/dr-elephant/commit/28f4025bbade1be0fc93111ee439859c530a8747&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;#162&lt;/a&gt;开始，将不再支持&lt;code&gt;JAVA 6&lt;/code&gt;。&lt;/p&gt;
    
    </summary>
    
      <category term="Dr.Elephant" scheme="http://www.hyperxu.com/categories/Dr-Elephant/"/>
    
    
      <category term="Dr.Elephant" scheme="http://www.hyperxu.com/tags/Dr-Elephant/"/>
    
      <category term="大数据" scheme="http://www.hyperxu.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="hadoop" scheme="http://www.hyperxu.com/tags/hadoop/"/>
    
      <category term="hive" scheme="http://www.hyperxu.com/tags/hive/"/>
    
  </entry>
  
  <entry>
    <title>【Dr.Elephant中文文档-1】Dr.Elephant简介</title>
    <link href="http://www.hyperxu.com/2018/10/24/dr-elephant-1/"/>
    <id>http://www.hyperxu.com/2018/10/24/dr-elephant-1/</id>
    <published>2018-10-24T08:56:04.000Z</published>
    <updated>2018-10-29T03:58:35.651Z</updated>
    
    <content type="html"><![CDATA[<h1 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h1><p><img src="https://uploads.hyperxu.com/15389919609117.jpg" alt=""><br><code>Dr. Elephant</code>是一个Hadoop和Spark的性能监控和调优工具。它能自动采集作业的度量指标并分析他，然后以简单明了的方式展现出来。<code>Dr. Elephant</code>的设计思想是通过作业分析结果来指导开发者进行作业调优，从而提升开发者效率和集群资源的利用率。<code>Dr. Elephant</code>使用了一组可配置的插件式启发算法来分析hadoop和spark作业并提供优化建议。然后针对结果数据来建议如何调整作业。这个算法还计算了作业的许多其他度量标准，用来为集群作业优化提供了有价值的参考信息。<br><a id="more"></a></p><h1 id="为什么要使用Dr-Elephant"><a href="#为什么要使用Dr-Elephant" class="headerlink" title="为什么要使用Dr.Elephant?"></a>为什么要使用Dr.Elephant?</h1><p>大多数Hadoop优化工具，不管是开源还是商业的，都被设计用来采集系统资源指标和监控集群资源信息。他们大多数专注于简化Hadoop集群的部署和管理。很少有工具来帮助用户优化Hadoop作业流的。少数的几个可用工具要么扩展性差，要么不支持快速发展的Hadoop框架。<code>Dr. Elephant</code>能很好支持Hadoop生态框架以及后续的新框架，同时对Spark的支持也很友好。你同时也可以通过插件的方式配置各种你喜欢的启发式算法。旨在帮助Hadoop和Spark的用户了解他们的内部工作流，并帮助他们轻松优化他们的作业。</p><h1 id="核心功能点"><a href="#核心功能点" class="headerlink" title="核心功能点"></a>核心功能点</h1><ul><li>基于自定义规则的可配置启发式插件，用于诊断作业任务</li><li>和Azkaban集成，并支持任何Hadoop调度框架，比如：Oozie</li><li>统计历史作业和工作流的性能指标</li><li>Job级别的工作流对比</li><li>针对MapReduce和Spark的性能诊断</li><li>具有良好的扩展性，能支持各种新的任务、应用和调度器</li><li>提供REST API，用户能够通过API获取所有信息</li></ul><h1 id="快速入门"><a href="#快速入门" class="headerlink" title="快速入门"></a>快速入门</h1><p><a href="">用户指南</a><br><a href="">开发者指南</a><br><a href="">管理员指南</a><br><a href="">任务优化技巧</a></p><h1 id="工作原理"><a href="#工作原理" class="headerlink" title="工作原理"></a>工作原理</h1><p><code>Dr. Elephant</code>会定期从YARN资源管理中心拉取近期成功和失败的作业列表。每个任务的元数据信息，计数器，配置及任务信息都可以从历史作业服务器获取到。一旦获取到所有的元数据信息，<code>Dr. Elephant</code>就基于这些元数据运行启发式算法，并生成一份该作业的性能诊断报告。该报告会多作业进行标记并评级，分为五个级别来评定改作业存在的性能问题严重程度。</p><h1 id="用例"><a href="#用例" class="headerlink" title="用例"></a>用例</h1><p>在LinkedIn，开发者们用<code>Dr. Elephant</code>来处理许多不同的用例，包括监控他们的工作流在集群上的运行情况，通过监控分析了解为什么作业运行较慢，比较作业每次运行的区别，<code>Dr. Elephant</code>的绿色性能指示灯已成为作业符合运行标准的先决条件。</p><h1 id="作业性能分析案例"><a href="#作业性能分析案例" class="headerlink" title="作业性能分析案例"></a>作业性能分析案例</h1><p><code>Dr. Elephant</code>的主页或者仪表盘，包含了近期所有作业分析的数据。<br><img src="https://uploads.hyperxu.com/15402611723816.png" alt=""><br>一旦一个作业完成运行，我们将能在仪表盘中找到他，或者通过搜索页找到他。作业搜索，可以通过作业id，作业执行的url（如果是通过调度器调度的作业，是有url的），作业的执行者，结束时间，作业类型，甚至通过作业等级来过滤搜索。<br><img src="https://uploads.hyperxu.com/15402613888790.png" alt=""></p><p>搜索结果提供了一份高级的作业分析报告，通过不同的颜色来标识不同的严重性等级用以体现作业的综合性能情况。红色表示作业有严重问题需要调优，绿色表示作业能够高效运行。</p><p>通过过滤并找到一个人的作业后，可以获取每个作业的完整报告。该报告包含每个算法的详细信息和链接，并针对改作业提供了相应的优化建议。<br><img src="https://uploads.hyperxu.com/15402620373072.png" alt=""></p><p><img src="https://uploads.hyperxu.com/15402620417876.png" alt=""></p>]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;简介&quot;&gt;&lt;a href=&quot;#简介&quot; class=&quot;headerlink&quot; title=&quot;简介&quot;&gt;&lt;/a&gt;简介&lt;/h1&gt;&lt;p&gt;&lt;img src=&quot;https://uploads.hyperxu.com/15389919609117.jpg&quot; alt=&quot;&quot;&gt;&lt;br&gt;&lt;code&gt;Dr. Elephant&lt;/code&gt;是一个Hadoop和Spark的性能监控和调优工具。它能自动采集作业的度量指标并分析他，然后以简单明了的方式展现出来。&lt;code&gt;Dr. Elephant&lt;/code&gt;的设计思想是通过作业分析结果来指导开发者进行作业调优，从而提升开发者效率和集群资源的利用率。&lt;code&gt;Dr. Elephant&lt;/code&gt;使用了一组可配置的插件式启发算法来分析hadoop和spark作业并提供优化建议。然后针对结果数据来建议如何调整作业。这个算法还计算了作业的许多其他度量标准，用来为集群作业优化提供了有价值的参考信息。&lt;br&gt;
    
    </summary>
    
      <category term="Dr.Elephant" scheme="http://www.hyperxu.com/categories/Dr-Elephant/"/>
    
    
      <category term="Dr.Elephant" scheme="http://www.hyperxu.com/tags/Dr-Elephant/"/>
    
      <category term="大数据" scheme="http://www.hyperxu.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="hadoop" scheme="http://www.hyperxu.com/tags/hadoop/"/>
    
      <category term="hive" scheme="http://www.hyperxu.com/tags/hive/"/>
    
  </entry>
  
  <entry>
    <title>kafka中文文档</title>
    <link href="http://www.hyperxu.com/2018/10/08/kafkacn/"/>
    <id>http://www.hyperxu.com/2018/10/08/kafkacn/</id>
    <published>2018-10-08T06:59:39.000Z</published>
    <updated>2018-10-08T07:03:20.388Z</updated>
    
    <content type="html"><![CDATA[<p>kafka中文文档：<a href="http://kafka.apachecn.org" target="_blank" rel="external">http://kafka.apachecn.org</a><br>github：<a href="https://github.com/apachecn/kafka-doc-zh" target="_blank" rel="external">https://github.com/apachecn/kafka-doc-zh</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;kafka中文文档：&lt;a href=&quot;http://kafka.apachecn.org&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;http://kafka.apachecn.org&lt;/a&gt;&lt;br&gt;github：&lt;a href=&quot;https://git
      
    
    </summary>
    
      <category term="kafka" scheme="http://www.hyperxu.com/categories/kafka/"/>
    
    
      <category term="大数据" scheme="http://www.hyperxu.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="kafka" scheme="http://www.hyperxu.com/tags/kafka/"/>
    
  </entry>
  
  <entry>
    <title>pyenv实现多个python版本并存</title>
    <link href="http://www.hyperxu.com/2018/09/20/pyenv-install/"/>
    <id>http://www.hyperxu.com/2018/09/20/pyenv-install/</id>
    <published>2018-09-20T08:08:42.000Z</published>
    <updated>2018-09-20T08:11:08.004Z</updated>
    
    <content type="html"><![CDATA[<h1 id="pyenv实现多个python版本并存"><a href="#pyenv实现多个python版本并存" class="headerlink" title="pyenv实现多个python版本并存"></a>pyenv实现多个python版本并存</h1><p>由于python 2和3存在不小的区别，尤其个别第三方库并不是都兼容，所以有时候我们需要在同一台服务器上运行多个不同的python版本</p><h3 id="1-安装pyenv"><a href="#1-安装pyenv" class="headerlink" title="1.安装pyenv"></a>1.安装pyenv</h3><p>(1)安装到$HOME/.pyenv目录:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">git <span class="built_in">clone</span> https://github.com/yyuu/pyenv.git ~/.pyenv</div></pre></td></tr></table></figure><p>(2)配置环境变量</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="built_in">echo</span> <span class="string">'export PYENV_ROOT="$HOME/.pyenv"'</span> &gt;&gt; ~/.bash_profile</div><div class="line"><span class="built_in">echo</span> <span class="string">'export PATH="$PYENV_ROOT/bin:$PATH"'</span> &gt;&gt; ~/.bash_profile</div></pre></td></tr></table></figure><p>(3)添加pyenv初始化到你的shell并生效</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="built_in">echo</span> <span class="string">'eval "$(pyenv init -)"'</span> &gt;&gt; ~/.bash_profile</div><div class="line"><span class="built_in">echo</span> <span class="string">'eval "$(pyenv virtualenv-init -)"'</span> &gt;&gt; ~/.bash_profile</div><div class="line"><span class="built_in">source</span> ~/.bash_profile</div></pre></td></tr></table></figure><a id="more"></a><h3 id="2-安装需要的python版本"><a href="#2-安装需要的python版本" class="headerlink" title="2.安装需要的python版本"></a>2.安装需要的python版本</h3><p>查看可安装版本：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div></pre></td><td class="code"><pre><div class="line">[db_dlp@prd-bigdata20 ~]$ pyenv install --list</div><div class="line">Available versions:</div><div class="line">  2.1.3</div><div class="line">  2.2.3</div><div class="line">  2.3.7</div><div class="line">  2.4</div><div class="line">  2.4.1</div><div class="line">  2.4.2</div><div class="line">  2.4.3</div><div class="line">  2.4.4</div><div class="line">  2.4.5</div><div class="line">  2.4.6</div><div class="line">  2.5</div><div class="line">  2.5.1</div><div class="line">  2.5.2</div><div class="line">  2.5.3</div><div class="line">  2.5.4</div><div class="line">  2.5.5</div><div class="line">  2.5.6</div><div class="line">  2.6.6</div><div class="line">  2.6.7</div><div class="line">  2.6.8</div><div class="line">  2.6.9</div><div class="line">  2.7-dev</div><div class="line">  2.7</div><div class="line">  2.7.1</div><div class="line">  2.7.2</div><div class="line">  2.7.3</div><div class="line">  2.7.4</div><div class="line">  2.7.5</div><div class="line">  2.7.6</div><div class="line">  ...</div></pre></td></tr></table></figure><p>安装：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">pyenv install 3.6.3</div></pre></td></tr></table></figure><p>卸载：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">pyenv uninstall 3.6.3</div></pre></td></tr></table></figure><p>查看已安装版本：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">[db_dlp@prd-bigdata20 ~]$ pyenv versions</div><div class="line">* system (<span class="built_in">set</span> by /home/db_dlp/.pyenv/version)</div><div class="line">  3.6.3</div></pre></td></tr></table></figure><h3 id="3-切换python版本"><a href="#3-切换python版本" class="headerlink" title="3.切换python版本"></a>3.切换python版本</h3><p>局部切换<br>python版本仅作用于指定的目录环境，切换到指定的目录下执行：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">pyenv <span class="built_in">local</span> 3.6.3</div></pre></td></tr></table></figure><p>全局切换</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">pyenv global 3.6.3</div></pre></td></tr></table></figure><h3 id="4-pyenv安装pyhton下载安装慢的问题"><a href="#4-pyenv安装pyhton下载安装慢的问题" class="headerlink" title="4.pyenv安装pyhton下载安装慢的问题"></a>4.pyenv安装pyhton下载安装慢的问题</h3><p>使用pyenv在安装python时，由于是去python官方下载，由于总所周知的原因，经常会遇到下载缓慢的问题。<br>对此，可以事先下载好python包，放到~/.pyenv/cache目录即可。修改~/.pyenv/plugins/python-build/share/python-build/3.6.3文件：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">cat ~/.pyenv/plugins/python-build/share/python-build/3.6.2</div><div class="line"><span class="comment">#require_gcc</span></div><div class="line">install_package <span class="string">"openssl-1.0.2g"</span> <span class="string">"https://www.openssl.org/source/openssl-1.0.2g.tar.gz#b784b1b3907ce39abf4098702dade6365522a253ad1552e267a9a0e89594aa33"</span> mac_openssl --if has_broken_mac_openssl</div><div class="line">install_package <span class="string">"readline-6.3"</span> <span class="string">"http://ftpmirror.gnu.org/readline/readline-6.3.tar.gz#56ba6071b9462f980c5a72ab0023893b65ba6debb4eeb475d7a563dc65cafd43"</span> standard --if has_broken_mac_readline</div><div class="line"><span class="keyword">if</span> has_tar_xz_support; <span class="keyword">then</span></div><div class="line">  install_package <span class="string">"Python-3.6.3"</span> <span class="string">"~/.pyenv/cache/Python-3.6.3.tar.gz"</span> ldflags_dirs standard verify_py35 ensurepip</div><div class="line"><span class="keyword">else</span></div><div class="line">  install_package <span class="string">"Python-3.6.3"</span> <span class="string">"~/.pyenv/cache/Python-3.6.3.tar.gz"</span> ldflags_dirs standard verify_py35 ensurepip</div><div class="line"><span class="keyword">fi</span></div></pre></td></tr></table></figure><p>可以提前安装好一些依赖包：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">yum install -y gcc make patch gdbm-devel openssl-devel sqlite-devel zlib-devel bzip2-devel readline-devel</div></pre></td></tr></table></figure><p>这样再次执行<code>pyenv install 3.6.3</code>时，就不会出现安装慢的问题。</p><h3 id="5-切换版本后python版本未变更"><a href="#5-切换版本后python版本未变更" class="headerlink" title="5.切换版本后python版本未变更"></a>5.切换版本后python版本未变更</h3><p>添加环境变量</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="built_in">export</span> PYENV_ROOT=<span class="string">"<span class="variable">$HOME</span>/.pyenv"</span></div><div class="line"><span class="built_in">export</span> PATH=<span class="string">"<span class="variable">$PYENV_ROOT</span>/bin:<span class="variable">$PATH</span>"</span></div><div class="line"><span class="built_in">eval</span> <span class="string">"<span class="variable">$(pyenv init -)</span>"</span></div><div class="line"><span class="built_in">eval</span> <span class="string">"<span class="variable">$(pyenv virtualenv-init -)</span>"</span></div></pre></td></tr></table></figure><p>如果变量生效报错，可能是需要安装<code>pyenv-virtualenv</code>插件：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">git <span class="built_in">clone</span> https://github.com/yyuu/pyenv-virtualenv.git ~/.pyenv/plugins/pyenv-virtualenv</div></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;pyenv实现多个python版本并存&quot;&gt;&lt;a href=&quot;#pyenv实现多个python版本并存&quot; class=&quot;headerlink&quot; title=&quot;pyenv实现多个python版本并存&quot;&gt;&lt;/a&gt;pyenv实现多个python版本并存&lt;/h1&gt;&lt;p&gt;由于python 2和3存在不小的区别，尤其个别第三方库并不是都兼容，所以有时候我们需要在同一台服务器上运行多个不同的python版本&lt;/p&gt;
&lt;h3 id=&quot;1-安装pyenv&quot;&gt;&lt;a href=&quot;#1-安装pyenv&quot; class=&quot;headerlink&quot; title=&quot;1.安装pyenv&quot;&gt;&lt;/a&gt;1.安装pyenv&lt;/h3&gt;&lt;p&gt;(1)安装到$HOME/.pyenv目录:&lt;/p&gt;
&lt;figure class=&quot;highlight bash&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;1&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;git &lt;span class=&quot;built_in&quot;&gt;clone&lt;/span&gt; https://github.com/yyuu/pyenv.git ~/.pyenv&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;(2)配置环境变量&lt;/p&gt;
&lt;figure class=&quot;highlight bash&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;1&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;2&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;&lt;span class=&quot;built_in&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&#39;export PYENV_ROOT=&quot;$HOME/.pyenv&quot;&#39;&lt;/span&gt; &amp;gt;&amp;gt; ~/.bash_profile&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;&lt;span class=&quot;built_in&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&#39;export PATH=&quot;$PYENV_ROOT/bin:$PATH&quot;&#39;&lt;/span&gt; &amp;gt;&amp;gt; ~/.bash_profile&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;(3)添加pyenv初始化到你的shell并生效&lt;/p&gt;
&lt;figure class=&quot;highlight bash&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;1&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;2&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;3&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;&lt;span class=&quot;built_in&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&#39;eval &quot;$(pyenv init -)&quot;&#39;&lt;/span&gt; &amp;gt;&amp;gt; ~/.bash_profile&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;&lt;span class=&quot;built_in&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&#39;eval &quot;$(pyenv virtualenv-init -)&quot;&#39;&lt;/span&gt; &amp;gt;&amp;gt; ~/.bash_profile&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;&lt;span class=&quot;built_in&quot;&gt;source&lt;/span&gt; ~/.bash_profile&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
    
    </summary>
    
      <category term="python" scheme="http://www.hyperxu.com/categories/python/"/>
    
    
      <category term="python" scheme="http://www.hyperxu.com/tags/python/"/>
    
      <category term="pyenv" scheme="http://www.hyperxu.com/tags/pyenv/"/>
    
  </entry>
  
  <entry>
    <title>Centos 6.9 安装CDH 5.14教程</title>
    <link href="http://www.hyperxu.com/2018/09/05/install-cdh5-14/"/>
    <id>http://www.hyperxu.com/2018/09/05/install-cdh5-14/</id>
    <published>2018-09-05T11:48:52.000Z</published>
    <updated>2018-09-05T11:54:41.748Z</updated>
    
    <content type="html"><![CDATA[<h2 id="1-前置准备"><a href="#1-前置准备" class="headerlink" title="1.前置准备"></a>1.前置准备</h2><p>上篇文章我讲了安装CDH的一些前置准备，可以参考《<a href="http://mp.weixin.qq.com/s?__biz=MzA4NTc5MTgyOQ==&amp;mid=2451987817&amp;idx=1&amp;sn=08a65ee7be0b6c43864823607b957a42&amp;chksm=88007853bf77f14582e2b9fc9233394f1beea36f72f6a6dde1f2f367e00c55660878e5a9e3a3&amp;scene=21#wechat_redirect" target="_blank" rel="external">CDH安装前置基础准备条件</a>》一文，这里就不再多说。</p><p>到此已完成：</p><ol><li>集群服务器配置，包括安装操作系统、关闭防火墙、同步服务器时钟等；</li><li>外部数据库安装</li><li>CDH和CM版本均为5.14且已配置本地yum源</li></ol><h2 id="2-Cloudera-Manager安装"><a href="#2-Cloudera-Manager安装" class="headerlink" title="2.Cloudera Manager安装"></a>2.Cloudera Manager安装</h2><h3 id="2-1-安装Cloudera-Manager-Server"><a href="#2-1-安装Cloudera-Manager-Server" class="headerlink" title="2.1.安装Cloudera Manager Server"></a>2.1.安装Cloudera Manager Server</h3><p>1.yum安装Cloudera Manager Server</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">yum -y install cloudera-manager-server</div></pre></td></tr></table></figure><a id="more"></a><p>2.安装完成后，初始化数据库：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">[root@bigdata02 yum.repos.d]# /usr/share/cmf/schema/scm_prepare_database.sh mysql cm cm passwordJAVA_HOME=/usr/java/jdk1.8.0_67-cloudera</div><div class="line">Verifying that we can write to /etc/cloudera-scm-server</div><div class="line">Creating SCM configuration file in /etc/cloudera-scm-server</div><div class="line">Executing:  /usr/java/jdk1.8.0_67-cloudera/bin/java -cp /usr/share/java/mysql-connector-java.jar:/usr/share/java/oracle-connector-java.jar:/usr/share/java/postgresql-connector-java.jar:/usr/share/cmf/schema/../lib/* com.cloudera.enterprise.dbutil.DbCommandExecutor /etc/cloudera-scm-server/db.properties com.cloudera.cmf.db.[main] DbCommandExecutor              INFO  Successfully connected to database.All done, your SCM database is configured correctly!</div></pre></td></tr></table></figure><h3 id="2-2-启动Cloudera-Manager-Server"><a href="#2-2-启动Cloudera-Manager-Server" class="headerlink" title="2.2.启动Cloudera Manager Server"></a>2.2.启动Cloudera Manager Server</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">[root@bigdata02 ~]# systemctl start cloudera-scm-server</div></pre></td></tr></table></figure><p>通过<a href="http://172.16.1.21:7180/cmf/login" target="_blank" rel="external">http://172.16.1.21:7180/cmf/login</a> 访问CM<br><img src="https://uploads.hyperxu.com/640.jpg" alt="640"></p><h2 id="3-CDH安装"><a href="#3-CDH安装" class="headerlink" title="3.CDH安装"></a>3.CDH安装</h2><h3 id="3-1-CDH集群安装向导"><a href="#3-1-CDH集群安装向导" class="headerlink" title="3.1.CDH集群安装向导"></a>3.1.CDH集群安装向导</h3><p>1.admin/admin登录到CM</p><p>2.一路下一步开始安装创建集群<br><img src="https://uploads.hyperxu.com/641.jpg" alt="641"></p><p>3.输入ip段搜索主机<br><img src="https://uploads.hyperxu.com/642.jpg" alt="642"></p><p>4.修改Parcels为之前创建的http本地源：<br><img src="https://uploads.hyperxu.com/643.jpg" alt="643"></p><p>5.一路下一步安装完成即可。</p><h2 id="4-集群设置安装"><a href="#4-集群设置安装" class="headerlink" title="4.集群设置安装"></a>4.集群设置安装</h2><p>1.按需选择自己需要服务进行安装<br><img src="https://uploads.hyperxu.com/644.jpg" alt="644"></p><p>2.进行角色分配，可参考下图：<br><img src="https://uploads.hyperxu.com/645.jpg" alt="645"></p><p>3.分配完后，默认配置，进行安装完成后，启动即可。<br><img src="https://uploads.hyperxu.com/6.jpg" alt="6"></p><blockquote><p>本篇讲的较为简单，后续的配置，会针对常用的几个应用一个个来讲，所以这里不做过多叙述了。</p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;1-前置准备&quot;&gt;&lt;a href=&quot;#1-前置准备&quot; class=&quot;headerlink&quot; title=&quot;1.前置准备&quot;&gt;&lt;/a&gt;1.前置准备&lt;/h2&gt;&lt;p&gt;上篇文章我讲了安装CDH的一些前置准备，可以参考《&lt;a href=&quot;http://mp.weixin.qq.com/s?__biz=MzA4NTc5MTgyOQ==&amp;amp;mid=2451987817&amp;amp;idx=1&amp;amp;sn=08a65ee7be0b6c43864823607b957a42&amp;amp;chksm=88007853bf77f14582e2b9fc9233394f1beea36f72f6a6dde1f2f367e00c55660878e5a9e3a3&amp;amp;scene=21#wechat_redirect&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;CDH安装前置基础准备条件&lt;/a&gt;》一文，这里就不再多说。&lt;/p&gt;
&lt;p&gt;到此已完成：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;集群服务器配置，包括安装操作系统、关闭防火墙、同步服务器时钟等；&lt;/li&gt;
&lt;li&gt;外部数据库安装&lt;/li&gt;
&lt;li&gt;CDH和CM版本均为5.14且已配置本地yum源&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;2-Cloudera-Manager安装&quot;&gt;&lt;a href=&quot;#2-Cloudera-Manager安装&quot; class=&quot;headerlink&quot; title=&quot;2.Cloudera Manager安装&quot;&gt;&lt;/a&gt;2.Cloudera Manager安装&lt;/h2&gt;&lt;h3 id=&quot;2-1-安装Cloudera-Manager-Server&quot;&gt;&lt;a href=&quot;#2-1-安装Cloudera-Manager-Server&quot; class=&quot;headerlink&quot; title=&quot;2.1.安装Cloudera Manager Server&quot;&gt;&lt;/a&gt;2.1.安装Cloudera Manager Server&lt;/h3&gt;&lt;p&gt;1.yum安装Cloudera Manager Server&lt;/p&gt;
&lt;figure class=&quot;highlight bash&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;1&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;yum -y install cloudera-manager-server&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
    
    </summary>
    
      <category term="CDH" scheme="http://www.hyperxu.com/categories/CDH/"/>
    
    
      <category term="大数据" scheme="http://www.hyperxu.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="cloudera manager" scheme="http://www.hyperxu.com/tags/cloudera-manager/"/>
    
  </entry>
  
  <entry>
    <title>CDH安装前置基础准备条件</title>
    <link href="http://www.hyperxu.com/2018/07/21/prepare-cdh/"/>
    <id>http://www.hyperxu.com/2018/07/21/prepare-cdh/</id>
    <published>2018-07-21T08:29:04.000Z</published>
    <updated>2018-07-21T08:41:31.640Z</updated>
    
    <content type="html"><![CDATA[<h1 id="CDH安装前置基础准备条件"><a href="#CDH安装前置基础准备条件" class="headerlink" title="CDH安装前置基础准备条件"></a>CDH安装前置基础准备条件</h1><h2 id="1-基础环境"><a href="#1-基础环境" class="headerlink" title="1.基础环境"></a>1.基础环境</h2><h3 id="1-1-节点规模"><a href="#1-1-节点规模" class="headerlink" title="1.1.节点规模"></a>1.1.节点规模</h3><p>测试环境，最小规模，最少4台服务器。一台做管理节点Cloudera Manager和NameNode等，另外三台用作worker，DATANODE节点，这种最小规模一般仅用于开发和测试。</p><p>如果是生产环境，最少6台，3台管理节点包括1个Cloudera Manager，2个NameNode做高可用，3个工作节点。</p><p>常见的较小规模的生产系统一般为10-20台。</p><p>###1.2.操作系统<br>CDH支持大部分主流的64位操作系统，我这里会以centos 6.9部署CDH 5.14版本为例子。其他CDH版本及其对应的操作系统版本可参考：<a href="https://www.cloudera.com/documentation/enterprise/release-notes/topics/rn_consolidated_pcm.html#concept_ap1_q2g_4cb" target="_blank" rel="external">CDH版本及其支持的操作系统版本</a></p><a id="more"></a> <h3 id="1-3-安装用户"><a href="#1-3-安装用户" class="headerlink" title="1.3.安装用户"></a>1.3.安装用户</h3><p>可以用root，或具有免密sudo权限的用户</p><p>###1.4.硬件要求<br>要评估群集的硬件和资源分配，其实需要分析要在群集上运行业务的负载情况，以及将要部署的CDH组件。<br>还应该考虑存储和处理的数据大小，工作负载的频率，需要运行的作业并发数量以及应用程序所需的资源。<br>所以硬件配置需要视具体情况而定。</p><p>测试集群最低要求：</p><ul><li>CPU：最少4 cores</li><li>内存：最少16GB</li><li>网络：千兆及以上</li><li>磁盘：视情况而定</li></ul><p>这里我使用的服务器配置是：</p><ul><li>CPU：56 cores</li><li>内存：14*16GB</li><li>网络：双万兆网卡绑定</li><li>磁盘：24*1.2T SAS(2.5 10K)</li></ul><h4 id="1-4-1-磁盘要求"><a href="#1-4-1-磁盘要求" class="headerlink" title="1.4.1.磁盘要求"></a>1.4.1.磁盘要求</h4><p>所有节点服务器系统盘可以使用raid1或raid10，数据盘不要使用raid，应该用JBOD模式。hdfs存储系统本身就是分布式高可用的，使用raid就失去使用hdfs的初衷，且会有性能损失。</p><p>如果集群的规模不大，有多个应用服务复用的话，NN，ZK，JN等管理服务存放的数据目录也可以放在使用raid的磁盘上。</p><p>DataNode数据盘建议选择ext4或xfs，并配置noatime：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">UUID=4df04bc1-c94b-45d6<span class="_">-a</span>80c-4b2269211fa0 /data1 ext4 defaults,noatime  1 2</div><div class="line">UUID=0ec154be-9923-4f05-ae0f-72fa98067d23 /data2 ext4 defaults,noatime  1 2</div><div class="line">UUID=a87a9192-3e75-40c6<span class="_">-a</span>58a<span class="_">-f</span>851e5f888e3 /data3 ext4 defaults,noatime  1 2</div><div class="line">UUID=283926d8-dc64-4a99-aa17-23e4f325897c /data4 ext4 defaults,noatime  1 2</div><div class="line">UUID=b547c6d3-5898-4053-8a15<span class="_">-e</span>38c7be3f9ba /data5 ext4 defaults,noatime  1 2</div><div class="line">UUID=8a332303-6bcb-47cb-9def-546b70b75bcf /data6 ext4 defaults,noatime  1 2</div><div class="line">UUID=2574f003-b84a-458b-8063<span class="_">-f</span>503066b1101 /data7 ext4 defaults,noatime  1 2</div></pre></td></tr></table></figure><p>目前常见的SATA读写速度大概在150MB/S-200MB/S，SAS或者SSD会更快，如果磁盘读写速度小于80MB/S，最好检查下磁盘，或者更换更好的磁盘，不然后期IO隐患很大。</p><h4 id="1-4-2-网络要求"><a href="#1-4-2-网络要求" class="headerlink" title="1.4.2.网络要求"></a>1.4.2.网络要求</h4><p>由于大数据应用，集群内部网络吞吐一般较大，稳健的高性能网络支撑十分重要。前期最好规划好，等到后期业务吞吐上去，网络撑不住再去升级底层网络设施是非常痛苦的。<br>最起码千兆网卡，根据实际情况，必要时需要考虑万兆网卡，以及配套的光纤交换机，并留有网卡绑定，交换机堆叠的扩展余地。</p><p>如果是使用云上的虚拟机，最好确认下网卡的多队列支持，笔者就被XX云网卡多队列支持数过少而坑过，导致集群性能利用率上不去，且CPU负载偏移，网络丢包等现象。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">[root@prd-bigdata06 ~]# ethtool -l eth0</div><div class="line">Channel parameters for eth0:</div><div class="line">Pre-set maximums:</div><div class="line">RX:             0</div><div class="line">TX:             0</div><div class="line">Other:          0</div><div class="line">Combined:       14</div><div class="line">Current hardware settings:</div><div class="line">RX:             0</div><div class="line">TX:             0</div><div class="line">Other:          0</div><div class="line">Combined:       14</div></pre></td></tr></table></figure><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">[root@bigdata17 ~]# ethtool -l p6p1</div><div class="line">Channel parameters for p6p1:</div><div class="line">Pre-set maximums:</div><div class="line">RX:             0</div><div class="line">TX:             0</div><div class="line">Other:          1</div><div class="line">Combined:       63</div><div class="line">Current hardware settings:</div><div class="line">RX:             0</div><div class="line">TX:             0</div><div class="line">Other:          1</div><div class="line">Combined:       56</div></pre></td></tr></table></figure><h2 id="2-系统及应用环境"><a href="#2-系统及应用环境" class="headerlink" title="2.系统及应用环境"></a>2.系统及应用环境</h2><h3 id="2-1-JDK"><a href="#2-1-JDK" class="headerlink" title="2.1.JDK"></a>2.1.JDK</h3><p>CDH发行版中自带JDK为1.7.0_67的版本，CDH5.3以后开始支持JDK1.8。可以实现自己安装好，或者后续安装CDH时，勾选CDH自带的JDK安装。</p><h3 id="2-2-外部数据库"><a href="#2-2-外部数据库" class="headerlink" title="2.2.外部数据库"></a>2.2.外部数据库</h3><p>CM自动部署安装时会自带数据库进行系统配置、schema等并进行相应管理。<br>也可是自行部署，不用自带的，具体支持的数据库包括：</p><ul><li><p>MySQL：5.1、5.5、5.6、5.7</p></li><li><p>PostgreSQL：8.1、8.3、8.4、9.1、9.2、9.3、9.4</p></li><li><p>Oracle：11gR2、12c</p></li></ul><p>这里我是自己部署的mysql，方便管理。 确保以下配置：</p><ul><li>增加数据库的最大连接数</li><li>确保数据库支持UTF-8编码</li><li>配置为主备模式，参考如何实现CDH元数据库MySQL的主备</li></ul><p>自己部署的话，就需要自己预先创建好CDH各项服务对应的元数据库。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div></pre></td><td class="code"><pre><div class="line">create database metastore default character set utf8;</div><div class="line">CREATE USER &apos;hive&apos;@&apos;%&apos;IDENTIFIED BY &apos;password&apos;;</div><div class="line">GRANT ALL PRIVILEGES ON metastore.* TO &apos;hive&apos;@&apos;%&apos;;</div><div class="line">FLUSH PRIVILEGES;</div><div class="line"></div><div class="line"></div><div class="line">create database cm default character set utf8;</div><div class="line">CREATE USER &apos;cm&apos;@&apos;%&apos;IDENTIFIED BY &apos;password&apos;;</div><div class="line">GRANT ALL PRIVILEGES ON cm. * TO &apos;cm&apos;@&apos;%&apos;;</div><div class="line">FLUSH PRIVILEGES;</div><div class="line"></div><div class="line">create database am default character set utf8;</div><div class="line">CREATE USER &apos;am&apos;@&apos;%&apos;IDENTIFIED BY &apos;password&apos;;</div><div class="line">GRANT ALL PRIVILEGES ON am. * TO &apos;am&apos;@&apos;%&apos;;</div><div class="line">FLUSH PRIVILEGES;</div><div class="line"></div><div class="line">create database rm default character set utf8;  </div><div class="line">CREATE USER &apos;rm&apos;@&apos;%&apos;IDENTIFIED BY &apos;password&apos;;</div><div class="line">GRANT ALL PRIVILEGES ON rm. * TO &apos;rm&apos;@&apos;%&apos;;</div><div class="line">FLUSH PRIVILEGES;</div><div class="line"></div><div class="line">create database hue default character set utf8;</div><div class="line">CREATE USER &apos;hue&apos;@&apos;%&apos;IDENTIFIED BY &apos;password&apos;;</div><div class="line">GRANT ALL PRIVILEGES ON hue. * TO &apos;hue&apos;@&apos;%&apos;;</div><div class="line">FLUSH PRIVILEGES;</div><div class="line"></div><div class="line">create database oozie default character set utf8;</div><div class="line">CREATE USER &apos;oozie&apos;@&apos;%&apos; IDENTIFIED BY &apos;password&apos;;</div><div class="line">GRANT ALL PRIVILEGES ON oozie. * TO &apos;oozie&apos;@&apos;%&apos;;</div><div class="line">FLUSH PRIVILEGES;</div><div class="line"></div><div class="line">create database sentry default character set utf8;</div><div class="line">CREATE USER &apos;sentry&apos;@&apos;%&apos; IDENTIFIED BY &apos;password&apos;;</div><div class="line">GRANT ALL PRIVILEGES ON sentry.* TO &apos;sentry&apos;@&apos;%&apos;;</div><div class="line">FLUSH PRIVILEGES;</div></pre></td></tr></table></figure><h3 id="2-3-开放端口"><a href="#2-3-开放端口" class="headerlink" title="2.3.开放端口"></a>2.3.开放端口</h3><p>以下常用服务端口，根据实际情况，需要在防火墙上放行。</p><table><thead><tr><th>Service</th><th>Port</th><th>Hosts</th></tr></thead><tbody><tr><td>Cloudera Manager</td><td>7180</td><td>CM所在主机</td></tr><tr><td>Cloudera Navigator Metadata</td><td>7187</td><td>Navigator所在主机</td></tr><tr><td>HDFS</td><td>50070,8020</td><td>Namenode所在主机</td></tr><tr><td>ResourceManager</td><td>8088,19888</td><td>RM, JobHistory所在主机</td></tr><tr><td>HBase</td><td>60010, 60030</td><td>HMaster, RegionServer所在主机</td></tr><tr><td>Hive</td><td>10002</td><td>HiveServer2所在主机</td></tr><tr><td>Hue</td><td>8888</td><td>Hue所在主机</td></tr><tr><td>Impala</td><td>25010, 25020, 25000</td><td></td></tr><tr><td>spark</td><td>18088</td><td>Spark HistoryServer所在机器</td></tr><tr><td>ssh</td><td>22</td><td></td></tr><tr><td>http</td><td>80</td><td>httpd服务所在机器，一般是CM那台主机</td></tr></tbody></table><h3 id="2-4-http服务"><a href="#2-4-http服务" class="headerlink" title="2.4.http服务"></a>2.4.http服务</h3><p>安装httpd服务主要是提供CDH和cm的本地源，进行离线安装。由于总所周知的原因，在线安装一般不会太顺利，最好是离线安装。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">[root@bigdata02~]<span class="comment"># yum -y install httpd</span></div><div class="line">[root@bigdata02~]<span class="comment"># chkconfig --add httpd </span></div><div class="line">[root@bigdata02~]<span class="comment"># chkconfig httpd on</span></div><div class="line">[root@bigdata02~]<span class="comment"># service httpd start</span></div><div class="line">Starting httpd:     [OK]</div><div class="line">[root@bigdata02~]<span class="comment">#</span></div></pre></td></tr></table></figure><h4 id="2-4-1-配置本地yum源"><a href="#2-4-1-配置本地yum源" class="headerlink" title="2.4.1.配置本地yum源"></a>2.4.1.配置本地yum源</h4><ol><li>在/var/www/html下新增cm5.14目录</li></ol><pre><code><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">[root@bigdata02~]# mkdir -p /var/www/html/cm5.14</div></pre></td></tr></table></figure></code></pre><ol><li>下载CM5.14版本的RPM安装包放在cm5.14目录下，并执行<code>createrepo</code>:</li></ol><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">[root@bigdata02]# createrepo .</div><div class="line">Spawning worker 0 with 7pkgs</div><div class="line">Workers Finished</div><div class="line">Gathering worker results</div><div class="line">Saving Primary metadata</div><div class="line">Saving file lists metadata</div><div class="line">Saving other metadata</div><div class="line">Generating sqlite DBs</div><div class="line">Sqlite DBs complete</div><div class="line">[root@bigdata02]# ll</div></pre></td></tr></table></figure><p><img src="https://uploads.hyperxu.com/15321540732204.jpg" alt=""></p><p><img src="https://uploads.hyperxu.com/15320804603462.jpg" alt=""></p><ol><li>在Cloudera Manager所在服务器的/etc/yum.repo.d目录下创建cm.repo文件，内容如下:</li></ol><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">[root@bigdata04 yum.repos.d]# vim cm.repo</div><div class="line"></div><div class="line">[cmrepo]</div><div class="line">name=Cloudera Manager 5.14</div><div class="line">baseurl=http://10.50.10.12/cm5.14</div><div class="line">gpgcheck=false</div><div class="line">enable=true</div></pre></td></tr></table></figure><ol><li>CDH Parcels部署同上。</li></ol><h3 id="2-5-hosts配置"><a href="#2-5-hosts配置" class="headerlink" title="2.5.hosts配置"></a>2.5.hosts配置</h3><p>将集群所有服务器的IP和HOSTNAME配置到hosts文件，并同步至集群的所有服务器。<br><img src="https://uploads.hyperxu.com/15321473133704.jpg" alt=""></p><h3 id="2-6-系统相关设置"><a href="#2-6-系统相关设置" class="headerlink" title="2.6.系统相关设置"></a>2.6.系统相关设置</h3><ul><li>禁用selinux</li><li>关闭iptables防火墙</li><li><p>swap相关设置</p><p>  swappiness表示如何使用swap分区。</p><p>  swappiness=0的时候表示最大限度使用物理内存，然后才是 swap空间，swappiness＝100的时候表示积极的使用swap分区，并且把内存上的数据及时的搬运到swap空间里面。linux的基本默认设置为60，这里我是设为1：</p>  <figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">/etc/sysctl.conf</div><div class="line">vm.swappiness=1</div></pre></td></tr></table></figure></li><li><p>关闭透明大页面</p>  <figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">    [root@bigdata02~]<span class="comment"># echo never &gt; /sys/kernel/mm/redhat_transparent_hugepage/defrag</span></div><div class="line">[root@bigdata02~]<span class="comment"># echo never &gt;/sys/kernel/mm/redhat_transparent_hugepage/enabled</span></div></pre></td></tr></table></figure></li></ul><h3 id="2-7-NTP时钟同步"><a href="#2-7-NTP时钟同步" class="headerlink" title="2.7.NTP时钟同步"></a>2.7.NTP时钟同步</h3><p>如果公司有自己的NTP Server则可以集群中所有节点可配置企业NTP Server，如果没有则在集群中选用一台服务器作为NTP Server，其它服务器与其保持同步，配置如下：</p><ol><li><p>所有节点安装<code>NTP</code></p> <figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">[root@bigdata02~]<span class="comment"># yum -y install ntp</span></div></pre></td></tr></table></figure></li><li><p>选一台做<code>ntp server</code></p> <figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">[root@bigdata02~]<span class="comment"># vim /etc/ntp.conf</span></div><div class="line"><span class="comment">#server 0.centos.pool.ntp.org iburst</span></div><div class="line"><span class="comment">#server 1.centos.pool.ntp.org iburst</span></div><div class="line"><span class="comment">#server 2.centos.pool.ntp.org iburst</span></div><div class="line"><span class="comment">#server 3.centos.pool.ntp.org iburst</span></div><div class="line">server  127.127.1.0     <span class="comment">#local clock</span></div><div class="line">fudge   127.127.1.0 stratum 10</div></pre></td></tr></table></figure></li><li><p>集群其它节点与其同步，配置如下：</p></li><li> <figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">    [root@bigdata04~]<span class="comment"># vim /etc/ntp.conf</span></div><div class="line"><span class="comment"># Use public servers from thepool.ntp.org project.</span></div><div class="line"><span class="comment">#server 0.centos.pool.ntp.org iburst</span></div><div class="line"><span class="comment">#server 1.centos.pool.ntp.org iburst</span></div><div class="line"><span class="comment">#server 2.centos.pool.ntp.org iburst</span></div><div class="line"><span class="comment">#server 3.centos.pool.ntp.org iburst</span></div><div class="line">server 172.16.1.22</div></pre></td></tr></table></figure></li><li><p>所有节点启动<code>ntp</code>:</p> <figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">    [root@bigdata04~]<span class="comment"># chkconfig --add ntpd</span></div><div class="line">[root@bigdata04~]<span class="comment"># chkconfig ntpd on</span></div><div class="line">[root@bigdata04~]<span class="comment"># service ntpd restart</span></div><div class="line">Shutting down ntpd:                                        [  OK  ]</div><div class="line">Starting ntpd:                                             [  OK  ]</div><div class="line">[root@bigdata04~]<span class="comment">#</span></div></pre></td></tr></table></figure></li></ol><h2 id="3-外部数据库"><a href="#3-外部数据库" class="headerlink" title="3.外部数据库"></a>3.外部数据库</h2><ol><li><p>集群中CM节点安装MySQL服务</p> <figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">    [root@bigdata02~]<span class="comment"># yum -y install mysql mysql-server</span></div><div class="line">    [root@bigdata02~]<span class="comment"># chkconfig --add mysqld</span></div><div class="line">[root@bigdata02~]<span class="comment"># chkconfig mysqld on</span></div><div class="line">[root@bigdata02~]<span class="comment"># service mysqld start</span></div><div class="line">Starting mysqld:                                           [  OK  ]</div></pre></td></tr></table></figure></li><li><p>初始化脚本</p> <figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div></pre></td><td class="code"><pre><div class="line">    [root@bigdata02~]<span class="comment"># mysql_secure_installation </span></div><div class="line">NOTE: RUNNING ALL PARTS OF THIS SCRIPT IS RECOMMENDED FORALL MySQL</div><div class="line">      SERVERS IN PRODUCTION USE!  PLEASE READEACH STEP CAREFULLY!</div><div class="line">In order to <span class="built_in">log</span> into MySQL to secure it, we<span class="string">'ll needthe current</span></div><div class="line">password for the root user.  If you'vejust installed MySQL, and</div><div class="line">you haven<span class="string">'t set the root password yet, the passwordwill be blank,</span></div><div class="line">so you should just press enter here.</div><div class="line">Enter current password for root (enter for none): </div><div class="line">OK, successfully used password, moving on...</div><div class="line">Setting the root password ensures that nobody can log into the MySQL</div><div class="line">root user without the proper authorisation.</div><div class="line">Set root password? [Y/n] y</div><div class="line">New password: </div><div class="line">Re-enter new password: </div><div class="line">Password updated successfully!</div><div class="line">Reloading privilege tables..</div><div class="line"> ... Success!</div><div class="line">By default, a MySQL installation has an anonymous user, allowing anyone</div><div class="line">to log into MySQL without having to have a user account created for</div><div class="line">them.  This is intended only for testing,and to make the installation</div><div class="line">go a bit smoother.  You should removethem before moving into a</div><div class="line">production environment.</div><div class="line">Remove anonymous users? [Y/n] y</div><div class="line"> ... Success!</div><div class="line">Normally, root should only be allowed to connect from 'localhost<span class="string">'. This</span></div><div class="line">ensures that someone cannot guess at the root password from the network.</div><div class="line">Disallow root login remotely? [Y/n] n</div><div class="line"> ... skipping.</div><div class="line">By default, MySQL comes with a database named '<span class="built_in">test</span><span class="string">' that anyone can</span></div><div class="line">access.  This is also intended only fortesting, and should be removed</div><div class="line">before moving into a production environment.</div><div class="line">Remove test database and access to it? [Y/n] y</div><div class="line"> - Dropping test database...</div><div class="line"> ... Success!</div><div class="line"> - Removing privileges on testdatabase...</div><div class="line"> ... Success!</div><div class="line">Reloading the privilege tables will ensure that all changes made so far</div><div class="line">will take effect immediately.</div><div class="line">Reload privilege tables now? [Y/n] y</div><div class="line"> ... Success!</div><div class="line">Cleaning up...</div><div class="line">All done!  If you've completed all of the above steps, your MySQL</div><div class="line">installationshould now be secure.</div><div class="line">Thanks <span class="keyword">for</span> usingMySQL!</div><div class="line">[root@bigdata02~]<span class="comment">#</span></div></pre></td></tr></table></figure></li><li><p>创建CDH所需要的库</p></li></ol><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div></pre></td><td class="code"><pre><div class="line">create database metastore default character set utf8;</div><div class="line">CREATE USER &apos;hive&apos;@&apos;%&apos;IDENTIFIED BY &apos;password&apos;;</div><div class="line">GRANT ALL PRIVILEGES ON metastore.* TO &apos;hive&apos;@&apos;%&apos;;</div><div class="line">FLUSH PRIVILEGES;</div><div class="line"></div><div class="line"></div><div class="line">create database cm default character set utf8;</div><div class="line">CREATE USER &apos;cm&apos;@&apos;%&apos;IDENTIFIED BY &apos;password&apos;;</div><div class="line">GRANT ALL PRIVILEGES ON cm. * TO &apos;cm&apos;@&apos;%&apos;;</div><div class="line">FLUSH PRIVILEGES;</div><div class="line"></div><div class="line">create database am default character set utf8;</div><div class="line">CREATE USER &apos;am&apos;@&apos;%&apos;IDENTIFIED BY &apos;password&apos;;</div><div class="line">GRANT ALL PRIVILEGES ON am. * TO &apos;am&apos;@&apos;%&apos;;</div><div class="line">FLUSH PRIVILEGES;</div><div class="line"></div><div class="line">create database rm default character set utf8;  </div><div class="line">CREATE USER &apos;rm&apos;@&apos;%&apos;IDENTIFIED BY &apos;password&apos;;</div><div class="line">GRANT ALL PRIVILEGES ON rm. * TO &apos;rm&apos;@&apos;%&apos;;</div><div class="line">FLUSH PRIVILEGES;</div><div class="line"></div><div class="line">create database hue default character set utf8;</div><div class="line">CREATE USER &apos;hue&apos;@&apos;%&apos;IDENTIFIED BY &apos;password&apos;;</div><div class="line">GRANT ALL PRIVILEGES ON hue. * TO &apos;hue&apos;@&apos;%&apos;;</div><div class="line">FLUSH PRIVILEGES;</div><div class="line"></div><div class="line">create database oozie default character set utf8;</div><div class="line">CREATE USER &apos;oozie&apos;@&apos;%&apos; IDENTIFIED BY &apos;password&apos;;</div><div class="line">GRANT ALL PRIVILEGES ON oozie. * TO &apos;oozie&apos;@&apos;%&apos;;</div><div class="line">FLUSH PRIVILEGES;</div><div class="line"></div><div class="line">create database sentry default character set utf8;</div><div class="line">CREATE USER &apos;sentry&apos;@&apos;%&apos; IDENTIFIED BY &apos;password&apos;;</div><div class="line">GRANT ALL PRIVILEGES ON sentry.* TO &apos;sentry&apos;@&apos;%&apos;;</div><div class="line">FLUSH PRIVILEGES;</div></pre></td></tr></table></figure><p>4.安装MySQL驱动，将mysql-connector-java-5.1.34.jar拷贝至/usr/share/java目录，并创建软链接。</p><blockquote><p>CDH的安装前置要求大致就这些，后续会讲下CDH部署的具体步骤。以及由浅入深的讲下CDH的一些基本组件。</p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;CDH安装前置基础准备条件&quot;&gt;&lt;a href=&quot;#CDH安装前置基础准备条件&quot; class=&quot;headerlink&quot; title=&quot;CDH安装前置基础准备条件&quot;&gt;&lt;/a&gt;CDH安装前置基础准备条件&lt;/h1&gt;&lt;h2 id=&quot;1-基础环境&quot;&gt;&lt;a href=&quot;#1-基础环境&quot; class=&quot;headerlink&quot; title=&quot;1.基础环境&quot;&gt;&lt;/a&gt;1.基础环境&lt;/h2&gt;&lt;h3 id=&quot;1-1-节点规模&quot;&gt;&lt;a href=&quot;#1-1-节点规模&quot; class=&quot;headerlink&quot; title=&quot;1.1.节点规模&quot;&gt;&lt;/a&gt;1.1.节点规模&lt;/h3&gt;&lt;p&gt;测试环境，最小规模，最少4台服务器。一台做管理节点Cloudera Manager和NameNode等，另外三台用作worker，DATANODE节点，这种最小规模一般仅用于开发和测试。&lt;/p&gt;
&lt;p&gt;如果是生产环境，最少6台，3台管理节点包括1个Cloudera Manager，2个NameNode做高可用，3个工作节点。&lt;/p&gt;
&lt;p&gt;常见的较小规模的生产系统一般为10-20台。&lt;/p&gt;
&lt;p&gt;###1.2.操作系统&lt;br&gt;CDH支持大部分主流的64位操作系统，我这里会以centos 6.9部署CDH 5.14版本为例子。其他CDH版本及其对应的操作系统版本可参考：&lt;a href=&quot;https://www.cloudera.com/documentation/enterprise/release-notes/topics/rn_consolidated_pcm.html#concept_ap1_q2g_4cb&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;CDH版本及其支持的操作系统版本&lt;/a&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="CDH" scheme="http://www.hyperxu.com/categories/CDH/"/>
    
    
      <category term="大数据" scheme="http://www.hyperxu.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
      <category term="cloudera manager" scheme="http://www.hyperxu.com/tags/cloudera-manager/"/>
    
  </entry>
  
  <entry>
    <title>【Scikit-Learn 中文文档】二：使用 scikit-learn 介绍机器学习 | ApacheCN</title>
    <link href="http://www.hyperxu.com/2017/12/07/scikit-learn-2/"/>
    <id>http://www.hyperxu.com/2017/12/07/scikit-learn-2/</id>
    <published>2017-12-07T09:31:45.000Z</published>
    <updated>2017-12-07T09:34:48.840Z</updated>
    
    <content type="html"><![CDATA[<p>中文文档: <a href="http://sklearn.apachecn.org/cn/0.19.0/tutorial/basic/tutorial.html" target="_blank" rel="external">http://sklearn.apachecn.org/cn/0.19.0/tutorial/basic/tutorial.html</a><br>英文文档: <a href="http://sklearn.apachecn.org/en/0.19.0/tutorial/basic/tutorial.html" target="_blank" rel="external">http://sklearn.apachecn.org/en/0.19.0/tutorial/basic/tutorial.html</a><br>GitHub: <a href="https://github.com/apachecn/scikit-learn-doc-zh（觉得不错麻烦给个" target="_blank" rel="external">https://github.com/apachecn/scikit-learn-doc-zh（觉得不错麻烦给个</a> Star，我们一直在努力）<br>贡献者: <a href="https://github.com/apachecn/scikit-learn-doc-zh#贡献者" target="_blank" rel="external">https://github.com/apachecn/scikit-learn-doc-zh#贡献者</a></p><blockquote><p><strong>内容提要</strong><br>在本节中，我们介绍一些在使用 scikit-learn 过程中用到的 机器学习 词汇，并且给出一些例子阐释它们。</p></blockquote><h3 id="机器学习：问题设置"><a href="#机器学习：问题设置" class="headerlink" title="机器学习：问题设置"></a>机器学习：问题设置</h3><p>一般来说，一个学习问题通常会考虑一系列 n 个 样本 数据，然后尝试预测未知数据的属性。 如果每个样本是 多个属性的数据 （比如说是一个多维记录），就说它有许多“属性”，或称 features(特征) 。<br>我们可以将学习问题分为几大类:<br><a id="more"></a></p><ul><li><code>监督学习</code> , 其中数据带有一个附加属性，即我们想要预测的结果值（ 点击此处 转到 scikit-learn 监督学习页面）。这个问题可以是:<ul><li><code>分类</code> : 样本属于两个或更多个类，我们想从已经标记的数据中学习如何预测未标记数据的类别。 分类问题的一个例子是手写数字识别，其目的是将每个输入向量分配给有限数目的离散类别之一。 我们通常把分类视作监督学习的一个离散形式（区别于连续形式），从有限的类别中，给每个样本贴上正确的标签。</li><li><code>回归</code>: 如果期望的输出由一个或多个连续变量组成，则该任务称为 回归. 回归问题的一个例子是预测鲑鱼的长度是其年龄和体重的函数。</li></ul></li><li><code>无监督学习</code>, 其中训练数据由没有任何相应目标值的一组输入向量x组成。这种问题的目标可能是在数据中发现彼此类似的示例所聚成的组，这种问题称为 聚类 , 或者，确定输入空间内的数据分布，称为 密度估计 ，又或从高维数据投影数据空间缩小到二维或三维以进行 可视化 （点击此处 转到 scikit-learn 无监督学习页面）。</li></ul><blockquote><p><strong>训练集和测试集</strong><br>机器学习是从数据的属性中学习，并将它们应用到新数据的过程。 这就是为什么机器学习中评估算法的普遍实践是把数据分割成 训练集 （我们从中学习数据的属性）和 测试集 （我们测试这些性质）。</p></blockquote><h3 id="加载示例数据集"><a href="#加载示例数据集" class="headerlink" title="加载示例数据集"></a>加载示例数据集</h3><p>scikit-learn 提供了一些标准数据集，例如 用于分类的 <code>iris</code> 和 <code>digits</code> 数据集 和 波士顿房价回归数据集。<br>在下文中，我们从我们的 shell 启动一个 Python 解释器，然后加载 iris 和 digits 数据集。我们的符号约定是 <code>$</code> 表示 shell 提示符，而 <code>&gt;&gt;&gt;</code> 表示 Python 解释器提示符:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">$ python</div><div class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> sklearn <span class="keyword">import</span> datasets</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>iris = datasets.load_iris()</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>digits = datasets.load_digits()</div></pre></td></tr></table></figure></p><p>数据集是一个类似字典的对象，它保存有关数据的所有数据和一些元数据。 该数据存储在<code>.data</code> 成员中，它是 <code>n_samples, n_features</code> 数组。 在监督问题的情况下，一个或多个响应变量存储在 .target 成员中。 有关不同数据集的更多详细信息，请参见 专用数据集部分 .<br>例如，在数字数据集的情况下，<code>digits.data</code> 使我们能够得到一些用于分类的样本特征:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="meta">&gt;&gt;&gt; </span>print(digits.data)  </div><div class="line">[[  <span class="number">0.</span>   <span class="number">0.</span>   <span class="number">5.</span> ...,   <span class="number">0.</span>   <span class="number">0.</span>   <span class="number">0.</span>]</div><div class="line"> [  <span class="number">0.</span>   <span class="number">0.</span>   <span class="number">0.</span> ...,  <span class="number">10.</span>   <span class="number">0.</span>   <span class="number">0.</span>]</div><div class="line"> [  <span class="number">0.</span>   <span class="number">0.</span>   <span class="number">0.</span> ...,  <span class="number">16.</span>   <span class="number">9.</span>   <span class="number">0.</span>]</div><div class="line"> ...,</div><div class="line"> [  <span class="number">0.</span>   <span class="number">0.</span>   <span class="number">1.</span> ...,   <span class="number">6.</span>   <span class="number">0.</span>   <span class="number">0.</span>]</div><div class="line"> [  <span class="number">0.</span>   <span class="number">0.</span>   <span class="number">2.</span> ...,  <span class="number">12.</span>   <span class="number">0.</span>   <span class="number">0.</span>]</div><div class="line"> [  <span class="number">0.</span>   <span class="number">0.</span>  <span class="number">10.</span> ...,  <span class="number">12.</span>   <span class="number">1.</span>   <span class="number">0.</span>]]</div></pre></td></tr></table></figure><p>并且 <code>digits.target</code> 表示了数据集内每个数字的真实类别，也就是我们期望从每个手写数字图像中学得的相应的数字标记:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="meta">&gt;&gt;&gt; </span>digits.target</div><div class="line">array([<span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>, ..., <span class="number">8</span>, <span class="number">9</span>, <span class="number">8</span>])</div></pre></td></tr></table></figure></p><p>数据数组的形状<br>数据总是 2D 数组，形状 (n_samples, n_features) ，尽管原始数据可能具有不同的形状。 在数字的情况下，每个原始样本是形状 (8, 8) 的图像，可以使用以下方式访问:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="meta">&gt;&gt;&gt; </span>digits.images[<span class="number">0</span>]</div><div class="line">array([[  <span class="number">0.</span>,   <span class="number">0.</span>,   <span class="number">5.</span>,  <span class="number">13.</span>,   <span class="number">9.</span>,   <span class="number">1.</span>,   <span class="number">0.</span>,   <span class="number">0.</span>],</div><div class="line">       [  <span class="number">0.</span>,   <span class="number">0.</span>,  <span class="number">13.</span>,  <span class="number">15.</span>,  <span class="number">10.</span>,  <span class="number">15.</span>,   <span class="number">5.</span>,   <span class="number">0.</span>],</div><div class="line">       [  <span class="number">0.</span>,   <span class="number">3.</span>,  <span class="number">15.</span>,   <span class="number">2.</span>,   <span class="number">0.</span>,  <span class="number">11.</span>,   <span class="number">8.</span>,   <span class="number">0.</span>],</div><div class="line">       [  <span class="number">0.</span>,   <span class="number">4.</span>,  <span class="number">12.</span>,   <span class="number">0.</span>,   <span class="number">0.</span>,   <span class="number">8.</span>,   <span class="number">8.</span>,   <span class="number">0.</span>],</div><div class="line">       [  <span class="number">0.</span>,   <span class="number">5.</span>,   <span class="number">8.</span>,   <span class="number">0.</span>,   <span class="number">0.</span>,   <span class="number">9.</span>,   <span class="number">8.</span>,   <span class="number">0.</span>],</div><div class="line">       [  <span class="number">0.</span>,   <span class="number">4.</span>,  <span class="number">11.</span>,   <span class="number">0.</span>,   <span class="number">1.</span>,  <span class="number">12.</span>,   <span class="number">7.</span>,   <span class="number">0.</span>],</div><div class="line">       [  <span class="number">0.</span>,   <span class="number">2.</span>,  <span class="number">14.</span>,   <span class="number">5.</span>,  <span class="number">10.</span>,  <span class="number">12.</span>,   <span class="number">0.</span>,   <span class="number">0.</span>],</div><div class="line">       [  <span class="number">0.</span>,   <span class="number">0.</span>,   <span class="number">6.</span>,  <span class="number">13.</span>,  <span class="number">10.</span>,   <span class="number">0.</span>,   <span class="number">0.</span>,   <span class="number">0.</span>]])</div></pre></td></tr></table></figure></p><p>该 数据集上的简单示例 说明了如何从原始数据开始调整，形成可以在 scikit-learn 中使用的数据。</p><p>从外部数据集加载<br>要从外部数据集加载，请参阅 加载外部数据集.</p><h3 id="学习和预测"><a href="#学习和预测" class="headerlink" title="学习和预测"></a>学习和预测</h3><p>在数字数据集的情况下，任务是给出图像来预测其表示的数字。 我们给出了 10 个可能类（数字 0 到 9）中的每一个的样本，我们在这些类上 拟合 一个 估计器 ，以便能够 预测 未知的样本所属的类。<br>在 scikit-learn 中，分类的估计器是一个 Python 对象，它实现了 <code>fit(X, y)</code> 和 <code>predict(T)</code>等方法。<br>估计器的一个例子类 <code>sklearn.svm.SVC</code> ，实现了 支持向量分类 。 估计器的构造函数以相应模型的参数为参数，但目前我们将把估计器视为即可:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> sklearn <span class="keyword">import</span> svm</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>clf = svm.SVC(gamma=<span class="number">0.001</span>, C=<span class="number">100.</span>)</div></pre></td></tr></table></figure></p><blockquote><p><strong>选择模型的参数</strong><br>在这个例子中，我们手动设置 gamma 值。不过，通过使用 网格搜索 及 交叉验证 等工具，可以自动找到参数的良好值。</p></blockquote><p>我们把我们的估计器实例命名为 <code>clf</code> ，因为它是一个分类器(classifier)。我们需要它适应模型，也就是说，要它从模型中<em>学习</em>。 这是通过将我们的训练集传递给 <code>fit</code> 方法来完成的。作为一个训练集，让我们使用数据集中除最后一张以外的所有图像。 我们用 <code>[:-1]</code> Python 语法选择这个训练集，它产生一个包含 <code>digits.data</code> 中除最后一个条目(entry)之外的所有条目的新数组<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="meta">&gt;&gt;&gt; </span>clf.fit(digits.data[:<span class="number">-1</span>], digits.target[:<span class="number">-1</span>])  </div><div class="line">SVC(C=<span class="number">100.0</span>, cache_size=<span class="number">200</span>, class_weight=<span class="keyword">None</span>, coef0=<span class="number">0.0</span>,</div><div class="line">  decision_function_shape=<span class="string">'ovr'</span>, degree=<span class="number">3</span>, gamma=<span class="number">0.001</span>, kernel=<span class="string">'rbf'</span>,</div><div class="line">  max_iter=<span class="number">-1</span>, probability=<span class="keyword">False</span>, random_state=<span class="keyword">None</span>, shrinking=<span class="keyword">True</span>,</div><div class="line">  tol=<span class="number">0.001</span>, verbose=<span class="keyword">False</span>)</div></pre></td></tr></table></figure></p><p>现在你可以预测新的值，特别是我们可以向分类器询问 <code>digits</code> 数据集中最后一个图像（没有用来训练的一条实例)的数字是什么:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="meta">&gt;&gt;&gt; </span>clf.predict(digits.data[<span class="number">-1</span>:])</div><div class="line">array([<span class="number">8</span>])</div></pre></td></tr></table></figure></p><p>相应的图像如下:<br><img src="http://sklearn.apachecn.org/cn/0.19.0/_images/sphx_glr_plot_digits_last_image_001.png" alt="|center"><br>正如你所看到的，这是一项具有挑战性的任务：图像分辨率差。你是否认同这个分类？<br>这个分类问题的一个完整例子可以作为一个例子来运行和学习： 识别手写数字。 <code>Recognizing hand-written digits.</code></p><h3 id="模型持久化"><a href="#模型持久化" class="headerlink" title="模型持久化"></a>模型持久化</h3><p>可以通过使用 Python 的内置持久化模块（即 pickle ）将模型保存:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> sklearn <span class="keyword">import</span> svm</div><div class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> sklearn <span class="keyword">import</span> datasets</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>clf = svm.SVC()</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>iris = datasets.load_iris()</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>X, y = iris.data, iris.target</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>clf.fit(X, y)  </div><div class="line"></div><div class="line">SVC(C=<span class="number">1.0</span>, cache_size=<span class="number">200</span>, class_weight=<span class="keyword">None</span>, coef0=<span class="number">0.0</span>,</div><div class="line">  decision_function_shape=<span class="string">'ovr'</span>, degree=<span class="number">3</span>, gamma=<span class="string">'auto'</span>, kernel=<span class="string">'rbf'</span>,</div><div class="line">  max_iter=<span class="number">-1</span>, probability=<span class="keyword">False</span>, random_state=<span class="keyword">None</span>, shrinking=<span class="keyword">True</span>,</div><div class="line">  tol=<span class="number">0.001</span>, verbose=<span class="keyword">False</span>)</div><div class="line"></div><div class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">import</span> pickle</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>s = pickle.dumps(clf)</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>clf2 = pickle.loads(s)</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>clf2.predict(X[<span class="number">0</span>:<span class="number">1</span>])</div><div class="line">array([<span class="number">0</span>])</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>y[<span class="number">0</span>]</div><div class="line"><span class="number">0</span></div></pre></td></tr></table></figure></p><p>在scikit的具体情况下，使用 joblib 替换 pickle（ <code>joblib.dump &amp; joblib.load</code>）可能会更有趣，这对大数据更有效，但只能序列化 (pickle) 到磁盘而不是字符串:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> sklearn.externals <span class="keyword">import</span> joblib</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>joblib.dump(clf, <span class="string">'filename.pkl'</span>) </div><div class="line">之后，您可以加载已保存的模型（可能在另一个 Python 进程中）:</div><div class="line">&gt;&gt;&gt;</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>clf = joblib.load(<span class="string">'filename.pkl'</span>)</div></pre></td></tr></table></figure></p><blockquote><p><strong><code>Warning</code></strong>    joblib.dump 以及 joblib.load 函数也接受 file-like（类文件） 对象而不是文件名。有关 Joblib 的数据持久化的更多信息，请 点击此处 。<br>请注意，pickle 有一些安全性和维护性问题。有关使用 scikit-learn 的模型持久化的更多详细信息，请参阅 模型持久化 部分。</p></blockquote><h3 id="规定"><a href="#规定" class="headerlink" title="规定"></a>规定</h3><p>scikit-learn 估计器遵循某些规则，使其行为更可预测。</p><h4 id="类型转换"><a href="#类型转换" class="headerlink" title="类型转换"></a>类型转换</h4><p>除非特别指定，输入将被转换为 float64<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">import</span> numpy <span class="keyword">as</span> np</div><div class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> sklearn <span class="keyword">import</span> random_projection</div><div class="line"></div><div class="line"><span class="meta">&gt;&gt;&gt; </span>rng = np.random.RandomState(<span class="number">0</span>)</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>X = rng.rand(<span class="number">10</span>, <span class="number">2000</span>)</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>X = np.array(X, dtype=<span class="string">'float32'</span>)</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>X.dtype</div><div class="line">dtype(<span class="string">'float32'</span>)</div><div class="line"></div><div class="line"><span class="meta">&gt;&gt;&gt; </span>transformer = random_projection.GaussianRandomProjection()</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>X_new = transformer.fit_transform(X)</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>X_new.dtype</div><div class="line">dtype(<span class="string">'float64'</span>)</div></pre></td></tr></table></figure></p><p>在这个例子中，X 原本是 float32 ，被 fit_transform(X) 被转换成 float64 。<br>回归目标被转换为 float64 ，但分类目标维持不变:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> sklearn <span class="keyword">import</span> datasets</div><div class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> sklearn.svm <span class="keyword">import</span> SVC</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>iris = datasets.load_iris()</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>clf = SVC()</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>clf.fit(iris.data, iris.target)  </div><div class="line">SVC(C=<span class="number">1.0</span>, cache_size=<span class="number">200</span>, class_weight=<span class="keyword">None</span>, coef0=<span class="number">0.0</span>,</div><div class="line">  decision_function_shape=<span class="string">'ovr'</span>, degree=<span class="number">3</span>, gamma=<span class="string">'auto'</span>, kernel=<span class="string">'rbf'</span>,</div><div class="line">  max_iter=<span class="number">-1</span>, probability=<span class="keyword">False</span>, random_state=<span class="keyword">None</span>, shrinking=<span class="keyword">True</span>,</div><div class="line">  tol=<span class="number">0.001</span>, verbose=<span class="keyword">False</span>)</div><div class="line"></div><div class="line"><span class="meta">&gt;&gt;&gt; </span>list(clf.predict(iris.data[:<span class="number">3</span>]))</div><div class="line">[<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>]</div><div class="line"></div><div class="line"><span class="meta">&gt;&gt;&gt; </span>clf.fit(iris.data, iris.target_names[iris.target])  </div><div class="line">SVC(C=<span class="number">1.0</span>, cache_size=<span class="number">200</span>, class_weight=<span class="keyword">None</span>, coef0=<span class="number">0.0</span>,</div><div class="line">  decision_function_shape=<span class="string">'ovr'</span>, degree=<span class="number">3</span>, gamma=<span class="string">'auto'</span>, kernel=<span class="string">'rbf'</span>,</div><div class="line">  max_iter=<span class="number">-1</span>, probability=<span class="keyword">False</span>, random_state=<span class="keyword">None</span>, shrinking=<span class="keyword">True</span>,</div><div class="line">  tol=<span class="number">0.001</span>, verbose=<span class="keyword">False</span>)</div><div class="line"></div><div class="line"><span class="meta">&gt;&gt;&gt; </span>list(clf.predict(iris.data[:<span class="number">3</span>]))  </div><div class="line">[<span class="string">'setosa'</span>, <span class="string">'setosa'</span>, <span class="string">'setosa'</span>]</div></pre></td></tr></table></figure></p><p>这里，第一个 predict() 返回一个整数数组，因为在 fit 中使用了 iris.target （一个整数数组）。 第二个 predict() 返回一个字符串数组，因为 iris.target_names 是一个字符串数组。</p><h4 id="再次训练和更新参数"><a href="#再次训练和更新参数" class="headerlink" title="再次训练和更新参数"></a>再次训练和更新参数</h4><p>估计器的超参数可以通过 sklearn.pipeline.Pipeline.set_params 方法在实例化之后进行更新。 调用 fit() 多次将覆盖以前的 fit() 所学到的参数:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div></pre></td><td class="code"><pre><div class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">import</span> numpy <span class="keyword">as</span> np</div><div class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> sklearn.svm <span class="keyword">import</span> SVC</div><div class="line"></div><div class="line"><span class="meta">&gt;&gt;&gt; </span>rng = np.random.RandomState(<span class="number">0</span>)</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>X = rng.rand(<span class="number">100</span>, <span class="number">10</span>)</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>y = rng.binomial(<span class="number">1</span>, <span class="number">0.5</span>, <span class="number">100</span>)</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>X_test = rng.rand(<span class="number">5</span>, <span class="number">10</span>)</div><div class="line"></div><div class="line"><span class="meta">&gt;&gt;&gt; </span>clf = SVC()</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>clf.set_params(kernel=<span class="string">'linear'</span>).fit(X, y)  </div><div class="line">SVC(C=<span class="number">1.0</span>, cache_size=<span class="number">200</span>, class_weight=<span class="keyword">None</span>, coef0=<span class="number">0.0</span>,</div><div class="line">  decision_function_shape=<span class="string">'ovr'</span>, degree=<span class="number">3</span>, gamma=<span class="string">'auto'</span>, kernel=<span class="string">'linear'</span>,</div><div class="line">  max_iter=<span class="number">-1</span>, probability=<span class="keyword">False</span>, random_state=<span class="keyword">None</span>, shrinking=<span class="keyword">True</span>,</div><div class="line">  tol=<span class="number">0.001</span>, verbose=<span class="keyword">False</span>)</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>clf.predict(X_test)</div><div class="line">array([<span class="number">1</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">1</span>, <span class="number">0</span>])</div><div class="line"></div><div class="line"><span class="meta">&gt;&gt;&gt; </span>clf.set_params(kernel=<span class="string">'rbf'</span>).fit(X, y)  </div><div class="line">SVC(C=<span class="number">1.0</span>, cache_size=<span class="number">200</span>, class_weight=<span class="keyword">None</span>, coef0=<span class="number">0.0</span>,</div><div class="line">  decision_function_shape=<span class="string">'ovr'</span>, degree=<span class="number">3</span>, gamma=<span class="string">'auto'</span>, kernel=<span class="string">'rbf'</span>,</div><div class="line">  max_iter=<span class="number">-1</span>, probability=<span class="keyword">False</span>, random_state=<span class="keyword">None</span>, shrinking=<span class="keyword">True</span>,</div><div class="line">  tol=<span class="number">0.001</span>, verbose=<span class="keyword">False</span>)</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>clf.predict(X_test)</div><div class="line">array([<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>])</div></pre></td></tr></table></figure></p><p>在这里，估计器被 SVC() 构造之后，默认内核 rbf 首先被改变到 linear ，然后改回到 rbf 重新训练估计器并进行第二次预测。</p><h4 id="多分类与多标签拟合"><a href="#多分类与多标签拟合" class="headerlink" title="多分类与多标签拟合"></a>多分类与多标签拟合</h4><p>当使用 多类分类器 时，执行的学习和预测任务取决于参与训练的目标数据的格式:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> sklearn.svm <span class="keyword">import</span> SVC</div><div class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> sklearn.multiclass <span class="keyword">import</span> OneVsRestClassifier</div><div class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> sklearn.preprocessing <span class="keyword">import</span> LabelBinarizer</div><div class="line"></div><div class="line"><span class="meta">&gt;&gt;&gt; </span>X = [[<span class="number">1</span>, <span class="number">2</span>], [<span class="number">2</span>, <span class="number">4</span>], [<span class="number">4</span>, <span class="number">5</span>], [<span class="number">3</span>, <span class="number">2</span>], [<span class="number">3</span>, <span class="number">1</span>]]</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>y = [<span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">1</span>, <span class="number">2</span>]</div><div class="line"></div><div class="line"><span class="meta">&gt;&gt;&gt; </span>classif = OneVsRestClassifier(estimator=SVC(random_state=<span class="number">0</span>))</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>classif.fit(X, y).predict(X)</div><div class="line">array([<span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">1</span>, <span class="number">2</span>])</div></pre></td></tr></table></figure></p><p>在上述情况下，分类器被使用一个含有多个标签的一维数组训练，因此 predict() 方法提供相应的多类别预测。分类器也可以通过二进制表示的的标签的二维数组来训练:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="meta">&gt;&gt;&gt; </span>y = LabelBinarizer().fit_transform(y)</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>classif.fit(X, y).predict(X)</div><div class="line">array([[<span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>],</div><div class="line">       [<span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>],</div><div class="line">       [<span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>],</div><div class="line">       [<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>],</div><div class="line">       [<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>]])</div></pre></td></tr></table></figure></p><p>这里，使用 LabelBinarizer 使目标向量 y 被转化成二维数组的标签表示。在这种情况下， predict() 返回一个表示相应多重标签预测的 2d 矩阵。<br>请注意，第四个和第五个实例返回全零向量，表明它们不能匹配用来训练中的目标标签中的任意一个。使用多分类输出，类似地可以为一个实例分配多个标签:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">&gt;&gt; <span class="keyword">from</span> sklearn.preprocessing <span class="keyword">import</span> MultiLabelBinarizer</div><div class="line">&gt;&gt; y = [[<span class="number">0</span>, <span class="number">1</span>], [<span class="number">0</span>, <span class="number">2</span>], [<span class="number">1</span>, <span class="number">3</span>], [<span class="number">0</span>, <span class="number">2</span>, <span class="number">3</span>], [<span class="number">2</span>, <span class="number">4</span>]]</div><div class="line">&gt;&gt; y = MultiLabelBinarizer().fit_transform(y)</div><div class="line">&gt;&gt; classif.fit(X, y).predict(X)</div><div class="line">array([[<span class="number">1</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>],</div><div class="line">       [<span class="number">1</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>],</div><div class="line">       [<span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>],</div><div class="line">       [<span class="number">1</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">1</span>, <span class="number">0</span>],</div><div class="line">       [<span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">1</span>]])</div></pre></td></tr></table></figure></p><p>在这种情况下，用来训练分类器的多个向量被赋予多个标记， MultiLabelBinarizer 被用来二进制化多个标签的二维数组，使之用来训练。 predict() 函数返回带有多个标记的二维数组作为每个实例的结果。</p><p>中文文档: <a href="http://sklearn.apachecn.org/cn/0.19.0/tutorial/basic/tutorial.html" target="_blank" rel="external">http://sklearn.apachecn.org/cn/0.19.0/tutorial/basic/tutorial.html</a><br>英文文档: <a href="http://sklearn.apachecn.org/en/0.19.0/tutorial/basic/tutorial.html" target="_blank" rel="external">http://sklearn.apachecn.org/en/0.19.0/tutorial/basic/tutorial.html</a><br>GitHub: <a href="https://github.com/apachecn/scikit-learn-doc-zh（觉得不错麻烦给个" target="_blank" rel="external">https://github.com/apachecn/scikit-learn-doc-zh（觉得不错麻烦给个</a> Star，我们一直在努力）<br>贡献者: <a href="https://github.com/apachecn/scikit-learn-doc-zh#贡献者" target="_blank" rel="external">https://github.com/apachecn/scikit-learn-doc-zh#贡献者</a><br>有兴趣的大佬们也可以和我们一起来维护，持续更新中 。。。<br>机器学习交流群: 629470233</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;中文文档: &lt;a href=&quot;http://sklearn.apachecn.org/cn/0.19.0/tutorial/basic/tutorial.html&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;http://sklearn.apachecn.org/cn/0.19.0/tutorial/basic/tutorial.html&lt;/a&gt;&lt;br&gt;英文文档: &lt;a href=&quot;http://sklearn.apachecn.org/en/0.19.0/tutorial/basic/tutorial.html&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;http://sklearn.apachecn.org/en/0.19.0/tutorial/basic/tutorial.html&lt;/a&gt;&lt;br&gt;GitHub: &lt;a href=&quot;https://github.com/apachecn/scikit-learn-doc-zh（觉得不错麻烦给个&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;https://github.com/apachecn/scikit-learn-doc-zh（觉得不错麻烦给个&lt;/a&gt; Star，我们一直在努力）&lt;br&gt;贡献者: &lt;a href=&quot;https://github.com/apachecn/scikit-learn-doc-zh#贡献者&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;https://github.com/apachecn/scikit-learn-doc-zh#贡献者&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;内容提要&lt;/strong&gt;&lt;br&gt;在本节中，我们介绍一些在使用 scikit-learn 过程中用到的 机器学习 词汇，并且给出一些例子阐释它们。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;机器学习：问题设置&quot;&gt;&lt;a href=&quot;#机器学习：问题设置&quot; class=&quot;headerlink&quot; title=&quot;机器学习：问题设置&quot;&gt;&lt;/a&gt;机器学习：问题设置&lt;/h3&gt;&lt;p&gt;一般来说，一个学习问题通常会考虑一系列 n 个 样本 数据，然后尝试预测未知数据的属性。 如果每个样本是 多个属性的数据 （比如说是一个多维记录），就说它有许多“属性”，或称 features(特征) 。&lt;br&gt;我们可以将学习问题分为几大类:&lt;br&gt;
    
    </summary>
    
      <category term="Scikit-Learn中文文档" scheme="http://www.hyperxu.com/categories/Scikit-Learn%E4%B8%AD%E6%96%87%E6%96%87%E6%A1%A3/"/>
    
    
      <category term="Scikit-Learn" scheme="http://www.hyperxu.com/tags/Scikit-Learn/"/>
    
      <category term="ApacheCN" scheme="http://www.hyperxu.com/tags/ApacheCN/"/>
    
      <category term="机器学习" scheme="http://www.hyperxu.com/tags/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    
  </entry>
  
  <entry>
    <title>【Scikit-Learn 中文文档】一：安装 sciki-learn| ApacheCN</title>
    <link href="http://www.hyperxu.com/2017/11/29/scikit-learn-1/"/>
    <id>http://www.hyperxu.com/2017/11/29/scikit-learn-1/</id>
    <published>2017-11-29T08:52:16.000Z</published>
    <updated>2017-11-29T09:01:36.868Z</updated>
    
    <content type="html"><![CDATA[<p>中文文档: <a href="http://sklearn.apachecn.org/cn/0.19.0/tutorial/basic/tutorial.html" target="_blank" rel="external">http://sklearn.apachecn.org/cn/0.19.0/tutorial/basic/tutorial.html</a><br>英文文档: <a href="http://sklearn.apachecn.org/en/0.19.0/tutorial/basic/tutorial.html" target="_blank" rel="external">http://sklearn.apachecn.org/en/0.19.0/tutorial/basic/tutorial.html</a><br>GitHub: <a href="https://github.com/apachecn/scikit-learn-doc-zh（觉得不错麻烦给个" target="_blank" rel="external">https://github.com/apachecn/scikit-learn-doc-zh（觉得不错麻烦给个</a> Star，我们一直在努力）<br>贡献者: <a href="https://github.com/apachecn/scikit-learn-doc-zh#贡献者" target="_blank" rel="external">https://github.com/apachecn/scikit-learn-doc-zh#贡献者</a></p><h3 id="安装-scikit-learn"><a href="#安装-scikit-learn" class="headerlink" title="安装 scikit-learn"></a>安装 scikit-learn</h3><blockquote><p><strong>Note:</strong>     如果你想为这个项目做出贡献，建议你<a href="http://sklearn.apachecn.org/cn/0.19.0/developers/advanced_installation.html#install-bleeding-edge" target="_blank" rel="external">安装最新的开发版本</a>.</p></blockquote><h3 id="安装最新版本"><a href="#安装最新版本" class="headerlink" title="安装最新版本"></a>安装最新版本</h3><p>Scikit-learn 要求:</p><ul><li>Python (&gt;= 2.7 or &gt;= 3.3)</li><li>NumPy (&gt;= 1.8.2)</li><li>SciPy (&gt;= 0.13.3)<a id="more"></a>如果你已经有一个安全的 numpy 和 scipy，安装 scikit-learn 的最简单的方法是使用 pip<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">pip install -U scikit-learn</div></pre></td></tr></table></figure></li></ul><p>或者 conda:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">conda install scikit-learn</div></pre></td></tr></table></figure></p><p>如果您还没有安装 NumPy 或 SciPy，还可以使用 conda 或 pip 安装这些。 当使用 pip 时，请确保使用 binary wheels，并且 NumPy 和 SciPy 不会从源重新编译，这可能在使用操作系统和硬件的特定配置（如 Raspberry Pi 上的 Linux）时发生。 从源代码构建 numpy 和 scipy 可能是复杂的（特别是在 Windows 上），并且需要仔细配置，以确保它们与线性代数程序的优化实现链接。而是使用如下所述的第三方分发。<br>如果您必须安装 scikit-learn 及其与 pip 的依赖关系，则可以将其安装为 <code>scikit-learn[alldeps]</code>。 最常见的用例是 <code>requirements.txt</code> 用作 PaaS 应用程序或 Docker 映像的自动构建过程的一部分的文件。此选项不适用于从命令行进行手动安装。</p><h4 id="第三方发行版"><a href="#第三方发行版" class="headerlink" title="第三方发行版"></a>第三方发行版</h4><p>如果您尚未安装具有 numpy 和 scipy 的 python 安装，建议您通过软件包管理器或通过 python 软件包进行安装。 这些与 numpy, scipy, scikit-learn, matplotlib 和许多其他有用的科学和数据处理库。<br>可用选项有:</p><h5 id="Canopy-和-Anaconda-适用于所有支持的平台"><a href="#Canopy-和-Anaconda-适用于所有支持的平台" class="headerlink" title="Canopy 和 Anaconda 适用于所有支持的平台"></a>Canopy 和 Anaconda 适用于所有支持的平台</h5><p><code>Canopy</code> 和 <code>Anaconda</code> 都运送了最新版本的 scikit-learn，另外还有一大批适用于 Windows，Mac OSX 和 Linux 的科学 python 库。<br>Anaconda 提供 scikit-learn 作为其免费分发的一部分.</p><blockquote><p><strong>Warning:</strong>   升级或卸载使用 Anaconda 安装的 scikit-learn，或者 conda 不应该使用 pip 命令。代替:<br>升级 <code>scikit-learn</code>:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">conda update scikit-learn</div></pre></td></tr></table></figure></p></blockquote><p>卸载 <code>scikit-learn</code>:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">conda remove scikit-learn</div></pre></td></tr></table></figure></p><p>使用 <code>pip install -U scikit-learn</code> 升级 or <code>pip uninstall scikit-learn</code> 卸载 可能无法正确删除 <code>conda</code> 命令安装的文件.<br>pip 升级和卸载操作仅适用于通过 <code>pip install</code> 安装的软件包.</p><h5 id="WinPython-适用于-Windows"><a href="#WinPython-适用于-Windows" class="headerlink" title="WinPython 适用于 Windows"></a>WinPython 适用于 Windows</h5><p>该<a href="https://winpython.github.io/" target="_blank" rel="external">WinPython</a> 项目分布 scikit-learn 作为额外的插件。<br>有关特定操作系统的安装说明或汇编出血边缘版本，请参阅 <a href="http://sklearn.apachecn.org/cn/0.19.0/developers/advanced_installation.html#advanced-installation" target="_blank" rel="external">高级安装说明</a>.</p><p>中文文档: <a href="http://sklearn.apachecn.org/cn/0.19.0/tutorial/basic/tutorial.html" target="_blank" rel="external">http://sklearn.apachecn.org/cn/0.19.0/tutorial/basic/tutorial.html</a><br>英文文档: <a href="http://sklearn.apachecn.org/en/0.19.0/tutorial/basic/tutorial.html" target="_blank" rel="external">http://sklearn.apachecn.org/en/0.19.0/tutorial/basic/tutorial.html</a><br>GitHub: <a href="https://github.com/apachecn/scikit-learn-doc-zh（觉得不错麻烦给个" target="_blank" rel="external">https://github.com/apachecn/scikit-learn-doc-zh（觉得不错麻烦给个</a> Star，我们一直在努力）<br>贡献者: <a href="https://github.com/apachecn/scikit-learn-doc-zh#贡献者" target="_blank" rel="external">https://github.com/apachecn/scikit-learn-doc-zh#贡献者</a><br>有兴趣的大佬们也可以和我们一起来维护，持续更新中 。。。<br>机器学习交流群: 629470233</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;中文文档: &lt;a href=&quot;http://sklearn.apachecn.org/cn/0.19.0/tutorial/basic/tutorial.html&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;http://sklearn.apachecn.org/cn/0.19.0/tutorial/basic/tutorial.html&lt;/a&gt;&lt;br&gt;英文文档: &lt;a href=&quot;http://sklearn.apachecn.org/en/0.19.0/tutorial/basic/tutorial.html&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;http://sklearn.apachecn.org/en/0.19.0/tutorial/basic/tutorial.html&lt;/a&gt;&lt;br&gt;GitHub: &lt;a href=&quot;https://github.com/apachecn/scikit-learn-doc-zh（觉得不错麻烦给个&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;https://github.com/apachecn/scikit-learn-doc-zh（觉得不错麻烦给个&lt;/a&gt; Star，我们一直在努力）&lt;br&gt;贡献者: &lt;a href=&quot;https://github.com/apachecn/scikit-learn-doc-zh#贡献者&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;https://github.com/apachecn/scikit-learn-doc-zh#贡献者&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;安装-scikit-learn&quot;&gt;&lt;a href=&quot;#安装-scikit-learn&quot; class=&quot;headerlink&quot; title=&quot;安装 scikit-learn&quot;&gt;&lt;/a&gt;安装 scikit-learn&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;     如果你想为这个项目做出贡献，建议你&lt;a href=&quot;http://sklearn.apachecn.org/cn/0.19.0/developers/advanced_installation.html#install-bleeding-edge&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;安装最新的开发版本&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;安装最新版本&quot;&gt;&lt;a href=&quot;#安装最新版本&quot; class=&quot;headerlink&quot; title=&quot;安装最新版本&quot;&gt;&lt;/a&gt;安装最新版本&lt;/h3&gt;&lt;p&gt;Scikit-learn 要求:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Python (&amp;gt;= 2.7 or &amp;gt;= 3.3)&lt;/li&gt;
&lt;li&gt;NumPy (&amp;gt;= 1.8.2)&lt;/li&gt;
&lt;li&gt;SciPy (&amp;gt;= 0.13.3)
    
    </summary>
    
      <category term="Scikit-Learn中文文档" scheme="http://www.hyperxu.com/categories/Scikit-Learn%E4%B8%AD%E6%96%87%E6%96%87%E6%A1%A3/"/>
    
    
      <category term="Scikit-Learn" scheme="http://www.hyperxu.com/tags/Scikit-Learn/"/>
    
      <category term="ApacheCN" scheme="http://www.hyperxu.com/tags/ApacheCN/"/>
    
      <category term="机器学习" scheme="http://www.hyperxu.com/tags/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    
  </entry>
  
  <entry>
    <title>记一次数据恢复经历</title>
    <link href="http://www.hyperxu.com/2017/11/18/data-recovery/"/>
    <id>http://www.hyperxu.com/2017/11/18/data-recovery/</id>
    <published>2017-11-17T17:02:12.000Z</published>
    <updated>2017-11-17T17:10:33.194Z</updated>
    
    <content type="html"><![CDATA[<p>有将近一周没有写文章了，经历了一场所有IT从业者的梦魇——数据丢失。一块1T的移动硬盘中，约500G的分区数据无故消失，想必挺多人都遇上过这种事，无奈，尽快尝试恢复，能恢复多少是多少了。</p><h3 id="分区格式"><a href="#分区格式" class="headerlink" title="分区格式"></a>分区格式</h3><p>由于此块移动硬盘经常要在windows和mac平台来回切换使用，在不纠结单文件4GB的限制的情况下，当时使用的FAT32格式。还好此类格式和NTFS格式较为常见，恢复起来也较为容易。这里要说一下：</p><ul><li>FAT32格式磁盘可在windows和mac平台读写，但有单个文件4GB限制；</li><li>NTFS完美兼容windows平台，但在mac平台下，默认为只读，但是通过魔改mac系统配置或者通过第三方插件可以取得写入权限；</li><li>exfat格式同样兼容windows和mac平台读写，但是此类格式不太稳定，会有丢数据的现象，且一旦出问题，一般技术手段，基本难以恢复数据。<a id="more"></a><h3 id="恢复工具"><a href="#恢复工具" class="headerlink" title="恢复工具"></a>恢复工具</h3>市面上硬盘数据恢复工具很多，大多是无法完全恢复丢失数据的。一般个人不是很重要的数据，且文件格式比较常规的，在数据丢失后立马停止文件写入，尝试修复还是有可能恢复90%+的数据的。这里说一下，我亲身试用过的一些工具，希望可以帮到有需要的朋友们，少走弯路。</li><li>EasyRecovery (Kroll Ontrack)注意括号内软件厂商名字，由于市面上还有很多国产山寨数据恢复工具也叫EasyRecovery（大小写，空格啥的不同），名字上基本难以区分，所以要注意厂商名字。此款工具在10年前我就有使用过，即便是6.0的老版本，恢复能力也很强，就是扫描速度较慢。曾经恢复了300G的丢失数据，90%基本完美恢复。</li><li>DiskGenius 老牌工具了，装过系统的应该都知道，在PE里基本都集成的磁盘分区工具，也有数据恢复功能。此次400+G的数据就是使用这个工具恢复的，常规文件格式恢复都OK，但是对PSD，拆分的压缩文件恢复成功率不高。总的来说还是可用的，注意某度下载的大部分最新破解版其实都是免费版，正常使用但是对单个大文件恢复有限制，以至于会误导以为数据无法恢复了。推荐使用4.7或者4.2的老版本有功能完整的破解版。</li><li>finaldata 口碑在外的数据恢复工具，实际使用恢复情况并不理想，也可能是我找的版本不对。</li><li>easyrecovery 国产山寨Kroll Ontrack厂商的工具名字，虽然抄袭大厂的工具名字，但是实际恢复能力也不差，基本能恢复。</li></ul><p>数据量较大的推荐使用DiskGenius，扫描进度还能存档。数据量不大的可以选择easyrecovery，国外原版或者国产版均可。<br>具体的恢复过程这里就不细说了，都是windows工具，图形界面，看着就会，主要就是恢复时间较长。</p><h3 id="Linux数据恢复"><a href="#Linux数据恢复" class="headerlink" title="Linux数据恢复"></a>Linux数据恢复</h3><p>既然说到数据恢复了，我们工作中接触的服务器，还是Linux居多，这里也说说从<code>rm -rf /*</code> 到不跑路的常规救急方法。<br>在我初出茅庐，刚干运维的那段岁月，也曾年轻气盛，逮哪都是<code>rm -rf *</code>一把梭，终于有一次梭出问题了，把线上数据给梭没了，然后….此处省略中间办公室精彩剧情部分，直接上恢复过程。</p><h4 id="exundelete恢复Linux文件系统数据"><a href="#exundelete恢复Linux文件系统数据" class="headerlink" title="exundelete恢复Linux文件系统数据"></a>exundelete恢复Linux文件系统数据</h4><p>exundelete是用于针对Linux ext3,ex4的文件系统数据恢复的工具，大致原理就是扫出文件系统的inode的信息，然后根据iNode信息结合日志去查询对应的block位置，包括直接块和间接块，然后通过dd命令备份这些信息，恢复数据。</p><h4 id="exundelete安装"><a href="#exundelete安装" class="headerlink" title="exundelete安装"></a>exundelete安装</h4><p>官网下载最新版，extundelete-0.2.4<br>安装三连不再赘述<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="variable">$tar</span> jxvf  extundelete-0.2.4.tar.bz2</div><div class="line">$./configure</div><div class="line"><span class="variable">$make</span></div><div class="line"><span class="variable">$make</span> install</div></pre></td></tr></table></figure></p><h4 id="extundelete用法"><a href="#extundelete用法" class="headerlink" title="extundelete用法"></a>extundelete用法</h4><p>首先及时卸载需要恢复数据的分区，然后查询分区可恢复的数据信息：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">extundelete  /dev/sdc1  --inode 2</div></pre></td></tr></table></figure></p><p>恢复单个文件<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">extundelete /dev/sdb1 --restore-file hosts</div></pre></td></tr></table></figure></p><p>恢复一个目录<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">extundelete /dev/sdb1 --restore-files <span class="built_in">test</span>/</div></pre></td></tr></table></figure></p><p>恢复整个分区<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">extundelete /dev/sdb1 –-restore-all</div></pre></td></tr></table></figure></p><p>经过实际测试，restore-all比较好用，在初次删除后可以很好的恢复文件及目录结构，但是如果我在相同位置新建了相同的文件名或者目录名，就会恢复失败。而恢复单独的文件或者目录则没有成功，也可能是我的姿势不对。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;有将近一周没有写文章了，经历了一场所有IT从业者的梦魇——数据丢失。一块1T的移动硬盘中，约500G的分区数据无故消失，想必挺多人都遇上过这种事，无奈，尽快尝试恢复，能恢复多少是多少了。&lt;/p&gt;
&lt;h3 id=&quot;分区格式&quot;&gt;&lt;a href=&quot;#分区格式&quot; class=&quot;headerlink&quot; title=&quot;分区格式&quot;&gt;&lt;/a&gt;分区格式&lt;/h3&gt;&lt;p&gt;由于此块移动硬盘经常要在windows和mac平台来回切换使用，在不纠结单文件4GB的限制的情况下，当时使用的FAT32格式。还好此类格式和NTFS格式较为常见，恢复起来也较为容易。这里要说一下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;FAT32格式磁盘可在windows和mac平台读写，但有单个文件4GB限制；&lt;/li&gt;
&lt;li&gt;NTFS完美兼容windows平台，但在mac平台下，默认为只读，但是通过魔改mac系统配置或者通过第三方插件可以取得写入权限；&lt;/li&gt;
&lt;li&gt;exfat格式同样兼容windows和mac平台读写，但是此类格式不太稳定，会有丢数据的现象，且一旦出问题，一般技术手段，基本难以恢复数据。
    
    </summary>
    
      <category term="Linux" scheme="http://www.hyperxu.com/categories/Linux/"/>
    
    
      <category term="Linux" scheme="http://www.hyperxu.com/tags/Linux/"/>
    
  </entry>
  
  <entry>
    <title>初识consul</title>
    <link href="http://www.hyperxu.com/2017/07/01/20170701-2/"/>
    <id>http://www.hyperxu.com/2017/07/01/20170701-2/</id>
    <published>2017-07-01T08:40:28.000Z</published>
    <updated>2017-11-16T04:04:32.540Z</updated>
    
    <content type="html"><![CDATA[<h1 id="初识consul"><a href="#初识consul" class="headerlink" title="初识consul"></a>初识consul</h1><p><img src="https://uploads.hyperxu.com/%E4%B8%8B%E8%BD%BD.png" alt="Alt text"></p><h3 id="Consul是什么"><a href="#Consul是什么" class="headerlink" title="Consul是什么?"></a>Consul是什么?</h3><p>Consul是HashiCorp公司推出的开源工具，用于实现分布式系统的服务发现与配置。Consul是分布式的、高可用的、 可横向扩展的。它具备以下特性:</p><ul><li><strong>服务发现</strong> Consul的客户端可提供一个服务，比如 api 或者mysql，另外一些客户端可使用Consul去发现一个指定服务的提供者。通过DNS或者HTTP接口可以很容易的找到他所依赖的服务。</li><li><strong>健康检查</strong> Consul客户端可提供任意数量的健康检查，指定一个服务(比如:webserver是否返回了200 OK 状态码)或者使用本地节点(比如:内存使用是否大于90%). 这个信息可由operator用来监视集群的健康。服务发现组件用来避免将流量发送到不健康的主机。</li><li><strong>Key/Value存储</strong> 应用程序可根据自己的需要使用Consul的Key/Value存储.比如动态配置,功能标记,协调,领袖选举等等,简单的HTTP API让他更易于使用。</li><li><strong>多数据中心</strong>  Consul支持开箱即用的多数据中心.这意味着用户不需要担心需要建立额外的抽象层让业务扩展到多个区域。<a id="more"></a>Consul面向DevOps和应用开发者友好，使他适合现代弹性的基础设施。</li></ul><h3 id="Consul架构"><a href="#Consul架构" class="headerlink" title="Consul架构"></a>Consul架构</h3><p><img src="https://uploads.hyperxu.com/consul-arch-420ce04a.png" alt="Alt text"></p><p>Consul是一个分布式高可用的系统。<br>Agent与一个和多个Consul Server 进行交互.Consul Server 用于存放和复制数据.server自行选举一个leader。虽然Consul可以运行在一台server , 但是建议使用<font color="red">3到5台</font>来避免失败情况下数据的丢失。每个数据中心建议配置一个server集群。</p><h3 id="Consul部署"><a href="#Consul部署" class="headerlink" title="Consul部署"></a>Consul部署</h3><p>Consul用Golang实现，因此具有天然可移植性(支持 Linux、windows 和macOS)。安装包仅包含一个可执行文件（这是由golang语言特性决定的）。Consul安装非常简单，只需要下载对应系统的软件包并解压后就可使用。</p><p>常见以Linux平台为例：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">$ wget https://releases.hashicorp.com/consul/0.8.5/consul_0.8.5_linux_amd64.zip</div><div class="line">$ unzip consul_0.8.5_linux_amd64.zip</div><div class="line">$ mv consul /usr/<span class="built_in">local</span>/bin/</div></pre></td></tr></table></figure></p><p>安装完即可执行命令验证：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div></pre></td><td class="code"><pre><div class="line">$ consul</div><div class="line">usage: consul [--version] [--help] &lt;<span class="built_in">command</span>&gt; [&lt;args&gt;]</div><div class="line">Available commands are:</div><div class="line">   agent          Runs a Consul agent</div><div class="line">   event          Fire a new event</div><div class="line">   <span class="built_in">exec</span>           Executes a <span class="built_in">command</span> on Consul nodes</div><div class="line">   force-leave    Forces a member of the cluster to enter the <span class="string">"left"</span> state</div><div class="line">   info           Provides debugging information <span class="keyword">for</span> operators.</div><div class="line">   join           Tell Consul agent to join cluster</div><div class="line">   keygen         Generates a new encryption key</div><div class="line">   keyring        Manages gossip layer encryption keys</div><div class="line">   kv             Interact with the key-value store</div><div class="line">   leave          Gracefully leaves the Consul cluster and shuts down</div><div class="line">   lock           Execute a <span class="built_in">command</span> holding a lock</div><div class="line">   maint          Controls node or service maintenance mode</div><div class="line">   members        Lists the members of a Consul cluster</div><div class="line">   monitor        Stream logs from a Consul agent</div><div class="line">   operator       Provides cluster-level tools <span class="keyword">for</span> Consul operators</div><div class="line">   reload         Triggers the agent to reload configuration files</div><div class="line">   rtt            Estimates network round trip time between nodes</div><div class="line">   snapshot       Saves, restores and inspects snapshots of Consul server state</div><div class="line">   validate       Validate config files/directories</div><div class="line">   version        Prints the Consul version</div><div class="line">   watch          Watch <span class="keyword">for</span> changes <span class="keyword">in</span> Consul</div></pre></td></tr></table></figure><h4 id="开发模式"><a href="#开发模式" class="headerlink" title="开发模式"></a>开发模式</h4><p>consul 开发者模式，可以快速开启单节点的 consul服务，具有完整功能，方便开发测试。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">consul agent -dev</div></pre></td></tr></table></figure></p><p><code>consul members</code> 命令查看当前集群的节点情况<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">dev@ubuntu ~$ consul members</div><div class="line">Node    Address         Status  Type    Build  Protocol  DC</div><div class="line">ubuntu  127.0.0.1:8301  alive   server  0.7.2  2         dc1</div></pre></td></tr></table></figure></p><h4 id="HTTP-API"><a href="#HTTP-API" class="headerlink" title="HTTP API"></a>HTTP API</h4><p>members命令选项的输出是基于gossip协议的并且其内容是最终一致。也就是说，在任何时候你在本地代理看到的内容可能与当前服务器中的状态并不是绝对一致的。</p><p>如果需要强一致性的状态信息，使用HTTP API向Consul服务器发送请求：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">dev@ubuntu ~$ curl localhost:8500/v1/catalog/nodes</div><div class="line">[</div><div class="line">    &#123;</div><div class="line">        <span class="string">"Node"</span>: <span class="string">"ubuntu"</span>,</div><div class="line">        <span class="string">"Address"</span>: <span class="string">"127.0.0.1"</span>,</div><div class="line">        <span class="string">"TaggedAddresses"</span>: &#123;</div><div class="line">            <span class="string">"lan"</span>: <span class="string">"127.0.0.1"</span>,</div><div class="line">            <span class="string">"wan"</span>: <span class="string">"127.0.0.1"</span></div><div class="line">        &#125;,</div><div class="line">        <span class="string">"CreateIndex"</span>: 4,</div><div class="line">        <span class="string">"ModifyIndex"</span>: 5</div><div class="line">    &#125;</div><div class="line">]</div></pre></td></tr></table></figure></p><p>服务可以通过配置文件注册，也可以通过HTTP API 添加。这里以配置文件定义服务：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="built_in">cd</span> ~/consul</div><div class="line">// 创建etc 目录用于存放配置文件</div><div class="line">mkdir etc</div><div class="line">// 创建web.json 配置文件</div><div class="line"><span class="built_in">echo</span> <span class="string">'&#123;"service": &#123;"name": "web", "tags": ["nginx"], "port": 80&#125;&#125;'</span> | tee ~/consul/etc/web.json</div><div class="line">// 重启consul，并指定配置文件目录</div><div class="line">consul agent -dev -config-dir=/home/dev/consul/etc</div></pre></td></tr></table></figure></p><h3 id="WEB界面"><a href="#WEB界面" class="headerlink" title="WEB界面"></a>WEB界面</h3><p>Consul自带一个界面美观，功能强大的，开箱即用的Web界面。通过该界面我们可以查看所有的服务以及节点，查看所有的健康监测及其当前的状态，以及读取和设置键/值数据。</p><p>该界面被映射到/ui上，和HTTP API使用相同的端口。默认就是<a href="http://localhost:8500/ui" target="_blank" rel="external">http://localhost:8500/ui</a><br><img src="https://uploads.hyperxu.com/consul_web_ui-3a1e7bf9.png" alt="Alt text"></p><h3 id="服务注册"><a href="#服务注册" class="headerlink" title="服务注册"></a>服务注册</h3><p>Consul会加载配置目录中的所有配置文件，配置文件是以.json结尾，并且以字典顺序加载。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="comment"># 创建配置目录</span></div><div class="line">$ mkdir /etc/consul.d</div><div class="line"><span class="comment"># 创建一个服务定义配置文件，假设有一个名为web服务，它运行在80端口。</span></div><div class="line">$ <span class="built_in">echo</span> <span class="string">'&#123;"service": &#123;"name": "web", "tags": ["rails"], "port": 80&#125;&#125;'</span> &gt;/etc/consul.d/web.json</div></pre></td></tr></table></figure><p>用指定配置文件启动服务<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">$ consul agent -dev -bind=192.168.50.210 -config-dir /etc/consul.d/</div><div class="line">==&gt; Starting Consul agent...</div><div class="line">==&gt; Consul agent running!</div></pre></td></tr></table></figure></p><h3 id="查询服务"><a href="#查询服务" class="headerlink" title="查询服务"></a>查询服务</h3><p>一旦agent启动并且服务同步了.我们可以通过DNS或者HTTP的API来查询服务.</p><h4 id="DNS-API"><a href="#DNS-API" class="headerlink" title="DNS API"></a>DNS API</h4><p>让我们首先使用DNS API来查询.在DNS API中,服务的DNS名字是 NAME.service.consul. 虽然是可配置的,但默认的所有DNS名字会都在consul命名空间下.这个子域告诉Consul,我们在查询服务,NAME则是服务的名称.<br>对于我们上面注册的Web服务.它的域名是 web.service.consul :<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line">$ dig @127.0.0.1 -p 8600  rails.web.service.consul</div><div class="line">; &lt;&lt;&gt;&gt; DiG 9.10.3-P4-Ubuntu &lt;&lt;&gt;&gt; @127.0.0.1 -p 8600 rails.web.service.consul</div><div class="line">; (1 server found)</div><div class="line">;; global options: +cmd</div><div class="line">;; Got answer:</div><div class="line">;; -&gt;&gt;HEADER&lt;&lt;- opcode: QUERY, status: NOERROR, id: 44287</div><div class="line">;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0</div><div class="line">;; WARNING: recursion requested but not available</div><div class="line">;; QUESTION SECTION:</div><div class="line">;rails.web.service.consul.INA</div><div class="line">;; ANSWER SECTION:</div><div class="line">rails.web.service.consul. 0INA192.168.2.210</div><div class="line">;; Query time: 0 msec</div><div class="line">;; SERVER: 127.0.0.1<span class="comment">#8600(127.0.0.1)</span></div><div class="line">;; WHEN: Tue May 09 10:58:16 CST 2017</div><div class="line">;; MSG SIZE  rcvd: 58</div></pre></td></tr></table></figure></p><h4 id="HTTP-API-1"><a href="#HTTP-API-1" class="headerlink" title="HTTP API"></a>HTTP API</h4><p>除了DNS API之外,HTTP API也可以用来进行服务查询:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line">$ curl http://localhost:8500/v1/catalog/service/web</div><div class="line">[</div><div class="line">    &#123;</div><div class="line">        <span class="string">"ID"</span>: <span class="string">"b76ff298-accd-05ff-8c64-5d79d866dfa9"</span>,</div><div class="line">        <span class="string">"Node"</span>: <span class="string">"dev-master-01"</span>,</div><div class="line">        <span class="string">"Address"</span>: <span class="string">"192.168.50.210"</span>,</div><div class="line">        <span class="string">"TaggedAddresses"</span>: &#123;</div><div class="line">            <span class="string">"lan"</span>: <span class="string">"192.168.50.210"</span>,</div><div class="line">            <span class="string">"wan"</span>: <span class="string">"192.168.50.210"</span></div><div class="line">        &#125;,</div><div class="line">        <span class="string">"NodeMeta"</span>: &#123;&#125;,</div><div class="line">        <span class="string">"ServiceID"</span>: <span class="string">"web"</span>,</div><div class="line">        <span class="string">"ServiceName"</span>: <span class="string">"web"</span>,</div><div class="line">        <span class="string">"ServiceTags"</span>: [</div><div class="line">            <span class="string">"rails"</span></div><div class="line">        ],</div><div class="line">        <span class="string">"ServiceAddress"</span>: <span class="string">""</span>,</div><div class="line">        <span class="string">"ServicePort"</span>: 80,</div><div class="line">        <span class="string">"ServiceEnableTagOverride"</span>: <span class="literal">false</span>,</div><div class="line">        <span class="string">"CreateIndex"</span>: 7,</div><div class="line">        <span class="string">"ModifyIndex"</span>: 7</div><div class="line">    &#125;</div><div class="line">]</div></pre></td></tr></table></figure></p><h4 id="调用HTTP-API进行定义"><a href="#调用HTTP-API进行定义" class="headerlink" title="调用HTTP API进行定义"></a>调用HTTP API进行定义</h4><p>Consul提供RESTful HTTP API. API可对节点、服务、健康检查、配置等执行CRUD操作(CRUD是指在做计算处理时的增加(Create)、读取查询(Retrieve)、更新(Update)和删除(Delete))。其语法类似solr和elasticsearch的接口语法。</p><p>Consul Endpoint主要支持以下接口:</p><ul><li>acl – 访问控制列表</li><li>agent – Agent控制</li><li>catalog – 管理nodes和services</li><li>coordinate – 网络协同</li><li>event – 用户事件</li><li>health – 管理健康监测</li><li>kv – K/V存储</li><li>query - Prepared Queries</li><li>session – 管理会话</li><li>status – Consul系统状态</li></ul><p>具体的API使用语法可参照文档：<a href="https://www.consul.io/api/index.html，我就不贴demo了。" target="_blank" rel="external">https://www.consul.io/api/index.html，我就不贴demo了。</a></p>]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;初识consul&quot;&gt;&lt;a href=&quot;#初识consul&quot; class=&quot;headerlink&quot; title=&quot;初识consul&quot;&gt;&lt;/a&gt;初识consul&lt;/h1&gt;&lt;p&gt;&lt;img src=&quot;https://uploads.hyperxu.com/%E4%B8%8B%E8%BD%BD.png&quot; alt=&quot;Alt text&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;Consul是什么&quot;&gt;&lt;a href=&quot;#Consul是什么&quot; class=&quot;headerlink&quot; title=&quot;Consul是什么?&quot;&gt;&lt;/a&gt;Consul是什么?&lt;/h3&gt;&lt;p&gt;Consul是HashiCorp公司推出的开源工具，用于实现分布式系统的服务发现与配置。Consul是分布式的、高可用的、 可横向扩展的。它具备以下特性:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;服务发现&lt;/strong&gt; Consul的客户端可提供一个服务，比如 api 或者mysql，另外一些客户端可使用Consul去发现一个指定服务的提供者。通过DNS或者HTTP接口可以很容易的找到他所依赖的服务。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;健康检查&lt;/strong&gt; Consul客户端可提供任意数量的健康检查，指定一个服务(比如:webserver是否返回了200 OK 状态码)或者使用本地节点(比如:内存使用是否大于90%). 这个信息可由operator用来监视集群的健康。服务发现组件用来避免将流量发送到不健康的主机。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Key/Value存储&lt;/strong&gt; 应用程序可根据自己的需要使用Consul的Key/Value存储.比如动态配置,功能标记,协调,领袖选举等等,简单的HTTP API让他更易于使用。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;多数据中心&lt;/strong&gt;  Consul支持开箱即用的多数据中心.这意味着用户不需要担心需要建立额外的抽象层让业务扩展到多个区域。
    
    </summary>
    
      <category term="Consul" scheme="http://www.hyperxu.com/categories/Consul/"/>
    
    
      <category term="Consul" scheme="http://www.hyperxu.com/tags/Consul/"/>
    
  </entry>
  
  <entry>
    <title>【redis从入门到上线(3)】- redis高可用之sentinel</title>
    <link href="http://www.hyperxu.com/2017/07/01/20170701-1/"/>
    <id>http://www.hyperxu.com/2017/07/01/20170701-1/</id>
    <published>2017-07-01T08:40:07.000Z</published>
    <updated>2017-11-16T04:03:34.753Z</updated>
    
    <content type="html"><![CDATA[<h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><p>本期原本计划是写些redis高可用架构选型，分析，及实战，发现篇幅过长，所以拆开来写了。这期先讲一些官方提供的高可用功能，主从，sentinel，以及redis cluster。</p><h3 id="Redis-Replication"><a href="#Redis-Replication" class="headerlink" title="Redis-Replication"></a>Redis-Replication</h3><p>Redis 支持简单且易用的主从复制（master-slave replication）功能， 该功能可以让从服务器(slave server)成为主服务器(master server)的精确复制品。</p><a id="more"></a><p>以下是关于 Redis 复制功能的几个重要方面：</p><p>Redis 使用异步复制。 从 Redis 2.8 开始， 从服务器会以每秒一次的频率向主服务器报告复制流（replication stream）的处理进度。<br><img src="https://uploads.hyperxu.com/Unknown.png" alt="Alt text"></p><ul><li><p>一个主服务器可以有多个从服务器。</p></li><li><p>不仅主服务器可以有从服务器， 从服务器也可以有自己的从服务器， 多个从服务器之间可以构成一个图状结构。</p></li><li><p>复制功能不会阻塞主服务器： 即使有一个或多个从服务器正在进行初次同步， 主服务器也可以继续处理命令请求。</p></li><li><p>复制功能也不会阻塞从服务器： 只要在 redis.conf 文件中进行了相应的设置， 即使从服务器正在进行初次同步， 服务器也可以使用旧版本的数据集来处理命令查询。</p></li><li><p>复制功能可以单纯地用于数据冗余（data redundancy）， 也可以通过让多个从服务器处理只读命令请求来提升扩展性（scalability）： 比如说， 繁重的 SORT 命令可以交给附属节点去运行。</p></li><li><p>可以通过复制功能来让主服务器免于执行持久化操作： 只要关闭主服务器的持久化功能， 然后由从服务器去执行持久化操作即可。</p></li></ul><h4 id="工作原理"><a href="#工作原理" class="headerlink" title="工作原理"></a>工作原理</h4><p>① 无论是初次连接还是重新连接， 当建立一个从服务器时， 从服务器都将向主服务器发送一个<code>SYNC</code>命令。</p><p>② 接到<code>SYNC</code>命令的主服务器将开始执行<code>BGSAVE</code>， 并在保存操作执行期间， 将所有新执行的写入命令都保存到一个缓冲区里面。</p><p>③ 当<code>BGSAVE</code>执行完毕后， 主服务器将执行保存操作所得的 .rdb 文件发送给从服务器， 从服务器接收这个 .rdb 文件， 并将文件中的数据载入到内存中。</p><p>④ 之后主服务器会以 Redis 命令协议的格式， 将写命令缓冲区中积累的所有内容都发送给从服务器。</p><p>你可以通过<code>telnet</code>命令来亲自验证这个同步过程： 首先连上一个正在处理命令请求的 Redis 服务器， 然后向它发送<code>SYNC</code>命令， 过一阵子， 你将看到 telnet 会话（session）接收到服务器发来的大段数据（.rdb 文件）， 之后还会看到， 所有在服务器执行过的写命令， 都会重新发送到<code>telnet</code>会话来。</p><p>即使有多个从服务器同时向主服务器发送<code>SYNC</code>， 主服务器也只需执行一次<code>BGSAVE</code>命令， 就可以处理所有这些从服务器的同步请求。</p><blockquote><p><strong>注意：</strong>从服务器可以在主从服务器之间的连接断开时进行自动重连， 在 Redis 2.8 版本之前， 断线之后重连的从服务器总要执行一次完整重同步（<strong>full resynchronization</strong>）操作， 但是从 Redis 2.8 版本开始， 从服务器可以根据主服务器的情况来选择执行完整重同步还是部分重同步（<strong>partial resynchronization</strong>）。</p></blockquote><h4 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h4><p>replication的配置非常简单，在之前的文章中也有讲过，这里就不细说了。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">slaveof 192.168.1.1 6379</div></pre></td></tr></table></figure><p>另外一种方法是调用 SLAVEOF 命令， 输入主服务器的 IP 和端口， 然后同步就会开始：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">127.0.0.1:6379&gt; SLAVEOF 192.168.1.1 10086</div><div class="line">OK</div></pre></td></tr></table></figure><h4 id="slave"><a href="#slave" class="headerlink" title="slave"></a>slave</h4><p> 从 Redis 2.6 开始， 从服务器默认为只读模式。<br>只读模式由 <code>redis.conf</code> 文件中的 <code>slave-read-only</code> 选项控制， 也可以通过 <code>CONFIG SET</code> 命令来开启或关闭这个模式。<br>只读从服务器会拒绝执行任何写命令， 所以不会出现因为操作失误而将数据不小心写入到了从服务器的情况。<br>即使从服务器是只读的， DEBUG 和 CONFIG 等管理式命令仍然是可以使用的， 所以我们还是不应该将服务器暴露给互联网或者任何不可信网络。 不过， 使用 <code>redis.conf</code> 中的命令改名选项， 我们可以通过禁止执行某些命令来提升只读从服务器的安全性。<br>你可能会感到好奇， 既然从服务器上的写数据会被重同步数据覆盖， 也可能在从服务器重启时丢失， 那么为什么要让一个从服务器变得可写呢？<br>原因是， 一些不重要的临时数据， 仍然是可以保存在从服务器上面的。 比如说， 客户端可以在从服务器上保存主服务器的可达性（reachability）信息， 从而实现故障转移（failover）策略。</p><blockquote><p><strong>注意：</strong>一般来说，常规业务，普通企业，redis这种内存型nosql是不存在IO瓶颈的，即便存在一般也是通过proxy构建集群，对数据进行分片来分担压力，所以向常规数据库样做读写分离是没有必要的。（这里说的是一般情况，具体场景具体分析）</p></blockquote><h3 id="sentinel"><a href="#sentinel" class="headerlink" title="sentinel"></a>sentinel</h3><p>Redis 的 Sentinel 系统用于管理多个 Redis 服务器（instance）， 该系统执行以下三个任务：</p><ul><li><strong>监控（Monitoring）：</strong> Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。</li><li><strong>提醒（Notification）：</strong> 当被监控的某个 Redis 服务器出现问题时， Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。</li><li><strong>自动故障迁移（Automatic failover）：</strong> 当一个主服务器不能正常工作时， Sentinel 会开始一次自动故障迁移操作， 它会将失效主服务器的其中一个从服务器升级为新的主服务器， 并让失效主服务器的其他从服务器改为复制新的主服务器； 当客户端试图连接失效的主服务器时， 集群也会向客户端返回新主服务器的地址， 使得集群可以使用新主服务器代替失效服务器。</li></ul><h4 id="sentinel原理"><a href="#sentinel原理" class="headerlink" title="sentinel原理"></a>sentinel原理</h4><p>在讲解 Redis 高可用方案之前，我们先来看看 Redis Sentinel 原理是怎么样的。</p><ol><li>Sentinel 集群通过给定的配置文件发现 master，启动时会监控 master。通过向 master 发送 info 信息获得该服务器下面的所有从服务器。</li><li>Sentinel 集群通过命令连接向被监视的主从服务器发送 hello 信息 (每秒一次)，该信息包括 Sentinel 本身的 IP、端口、id 等内容，以此来向其他 Sentinel 宣告自己的存在。</li><li>Sentinel 集群通过订阅连接接收其他 Sentinel 发送的 hello 信息，以此来发现监视同一个主服务器的其他 Sentinel；集群之间会互相创建命令连接用于通信，因为已经有主从服务器作为发送和接收 hello 信息的中介，Sentinel 之间不会创建订阅连接。</li><li>Sentinel 集群使用 ping 命令来检测实例的状态，如果在指定的时间内（<code>down-after-milliseconds</code>）没有回复或则返回错误的回复，那么该实例被判为下线。</li><li>当<code>failover</code>主备切换被触发后，<code>failover</code>并不会马上进行，还需要 Sentinel 中的大多数 Sentinel 授权后才可以进行<code>failover</code>，即进行<code>failover</code>的 Sentinel 会去获得指定 quorum 个的 Sentinel 的授权，成功后进入 ODOWN 状态。如在 5 个 Sentinel 中配置了 2 个 quorum，等到 2 个 Sentinel 认为 master 死了就执行 failover。</li><li>Sentinel 向选为 master 的 slave 发送<code>SLAVEOF NO ONE</code>命令，选择 slave 的条件是 Sentinel 首先会根据 slaves 的优先级来进行排序，优先级越小排名越靠前。如果优先级相同，则查看复制的下标，哪个从 master 接收的复制数据多，哪个就靠前。如果优先级和下标都相同，就选择进程 ID 较小的。</li><li>Sentinel 被授权后，它将会获得宕掉的 master 的一份最新配置版本号 (<code>config-epoch</code>)，当 failover 执行结束以后，这个版本号将会被用于最新的配置，通过广播形式通知其它 Sentinel，其它的 Sentinel 则更新对应 master 的配置。</li></ol><p>1 到 3 是<font color="red">自动发现机制</font>:</p><ul><li>以 10 秒一次的频率，向被监视的 master 发送 info 命令，根据回复获取 master 当前信息。</li><li>以 1 秒一次的频率，向所有 redis 服务器、包含 Sentinel 在内发送 PING 命令，通过回复判断服务器是否在线。</li><li>以 2 秒一次的频率，通过向所有被监视的 master，slave 服务器发送当前 Sentinel master 信息的消息。</li></ul><p>4 是<font color="red">检测机制</font>，5 和 6 是<font color="red">failover 机制</font>，7 是<font color="red">更新配置机制</font>。</p><h4 id="sentinel配置"><a href="#sentinel配置" class="headerlink" title="sentinel配置"></a>sentinel配置</h4><p>Redis 源码中包含了一个名为 sentinel.conf 的默认配置文件。<br>运行一个 Sentinel 所需的配置如下所示：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div></pre></td><td class="code"><pre><div class="line"><span class="comment"># Global</span></div><div class="line">port 26379       <span class="comment">##监听端口</span></div><div class="line">daemonize yes    <span class="comment">##使用daemon方式运行程序，默认为非daemon方式运行</span></div><div class="line">dir <span class="string">"/data/nosql/sentinel"</span></div><div class="line">pidfile <span class="string">"/data/nosql/sentinel/sentinel.pid"</span></div><div class="line">loglevel notice</div><div class="line">logfile <span class="string">"/data/nosql/sentinel/sentinel.log"</span></div><div class="line"><span class="comment">##</span></div><div class="line"><span class="comment">## sentinel monitor &lt;master-group-name&gt; &lt;ip&gt; &lt;port&gt; &lt;quorum&gt;    </span></div><div class="line"><span class="comment">####行尾的&lt;quorum&gt;是数字</span></div><div class="line"><span class="comment">####这个数字表明需要最少多少个sentinel互相沟通来确认某个master是否真的死了</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment">## sentinel &lt;option_name&gt; &lt;master-group-name&gt; &lt;option_value&gt;</span></div><div class="line"><span class="comment">#### down-after-milliseconds : sentinel会向master发送心跳PING来确认master是否存活，如果master在“一定时间范围”内不回应PONG或者是回复了一个错误消息，那么这个sentinel会主观地(单方面地)认为这个master已经不可用了(subjectively down, 也简称为SDOWN)。而这个down-after-milliseconds就是用来指定这个“一定时间范围”的，单位是毫秒。</span></div><div class="line"><span class="comment">#### failover-timeout : 这个选项确定自动转移故障超时时间，单位毫秒</span></div><div class="line"><span class="comment">#### parallel-syncs : 在发生failover主备切换时，这个选项指定了最多可以有多少个slave同时对新的master进行同步</span></div><div class="line"></div><div class="line">sentinel monitor redis161 192.168.1.161 6379 1</div><div class="line">sentinel down-after-milliseconds redis161 5000</div><div class="line">sentinel failover-timeout redis161 10000</div><div class="line">sentinel client-reconfig-script redis161 /data/redis/sentinel/sentinel_hook.sh//故障触发脚本</div><div class="line">sentinel config-epoch redis161 4</div><div class="line"></div><div class="line">sentinel leader-epoch redis161 4</div><div class="line"><span class="comment"># Generated by CONFIG REWRITE</span></div><div class="line">sentinel known-slave redis161 192.168.1.164 6380</div><div class="line">sentinel current-epoch 4</div></pre></td></tr></table></figure><blockquote><p><strong>注意：</strong><code>sentinel client-reconfig-script</code>为sentinel指定故障触发脚本，多用于配合proxy使用，去同步修改proxy配置，屏蔽切换故障节点。</p></blockquote><h4 id="启动Sentinel"><a href="#启动Sentinel" class="headerlink" title="启动Sentinel"></a>启动Sentinel</h4><p>对于 redis-sentinel 程序， 你可以用以下命令来启动 Sentinel 系统：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">redis-sentinel /path/to/sentinel.conf</div></pre></td></tr></table></figure><p>对于 redis-server 程序， 你可以用以下命令来启动一个运行在 Sentinel 模式下的 Redis 服务器：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">redis-server /path/to/sentinel.conf --sentinel</div></pre></td></tr></table></figure><p>两种方法都可以启动一个 Sentinel 实例。<br>启动 Sentinel 实例必须指定相应的配置文件， 系统会使用配置文件来保存 Sentinel 的当前状态， 并在 Sentinel 重启时通过载入配置文件来进行状态还原。</p><blockquote><p>有朋友反映前几篇文章篇幅过长，影响可读性，不应向PC端技术文章那样详细，因此我对后续文章开始逐步精简。<br>下一篇横向分析下redis cluster，twemproxy，codis等集群架.</p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;h3 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h3&gt;&lt;p&gt;本期原本计划是写些redis高可用架构选型，分析，及实战，发现篇幅过长，所以拆开来写了。这期先讲一些官方提供的高可用功能，主从，sentinel，以及redis cluster。&lt;/p&gt;
&lt;h3 id=&quot;Redis-Replication&quot;&gt;&lt;a href=&quot;#Redis-Replication&quot; class=&quot;headerlink&quot; title=&quot;Redis-Replication&quot;&gt;&lt;/a&gt;Redis-Replication&lt;/h3&gt;&lt;p&gt;Redis 支持简单且易用的主从复制（master-slave replication）功能， 该功能可以让从服务器(slave server)成为主服务器(master server)的精确复制品。&lt;/p&gt;
    
    </summary>
    
      <category term="redis" scheme="http://www.hyperxu.com/categories/redis/"/>
    
    
      <category term="redis" scheme="http://www.hyperxu.com/tags/redis/"/>
    
  </entry>
  
  <entry>
    <title>elasticsearch搜索及相关插件</title>
    <link href="http://www.hyperxu.com/2017/06/25/20170625-1/"/>
    <id>http://www.hyperxu.com/2017/06/25/20170625-1/</id>
    <published>2017-06-25T10:47:06.000Z</published>
    <updated>2017-07-01T08:42:01.136Z</updated>
    
    <content type="html"><![CDATA[<h3 id="ES选型"><a href="#ES选型" class="headerlink" title="ES选型"></a>ES选型</h3><ul><li>elasticsearch-2.3.4</li></ul><p>相关组件：</p><ul><li>elasticsearch-jdbc-2.3.4.0-dist</li><li>kibana-4.5.4-linux-x64.tar</li><li>elasticsearch-analysis-ik-1.9.4</li><li>elasticsearch-analysis-pinyin-1.7.4</li><li>_bigdesk</li><li>_head</li></ul><p>以ES2.3.4为基准，其他组件已为测试后最新可用版本，高于以上版本则不兼容。</p><h2 id=""><a href="#" class="headerlink" title=""></a><a id="more"></a></h2><h3 id="ES特性"><a href="#ES特性" class="headerlink" title="ES特性"></a>ES特性</h3><h4 id="SuggestionDiscovery"><a href="#SuggestionDiscovery" class="headerlink" title="SuggestionDiscovery"></a>SuggestionDiscovery</h4><p>SuggestionDiscovery的职责是发现建议词；<br>建议词的来源可以是商品的分类名称、品牌名称、商品标签、商品名称的高频词、热搜词，也可以是一些组合词，比如“分类 + 性别”和“分类 + 标签”，还可以是一些自定义添加的词；<br>建议词维护的时候需要考虑去重，比如“卫衣男”和“卫衣 男”应该是相同的，“Nike”和“nike”也应该是相同的；<br>由于建议词的来源通常比较稳定，所以执行的周期可以比较长一点，比如每周一次；</p><h4 id="SuggestionCounter"><a href="#SuggestionCounter" class="headerlink" title="SuggestionCounter"></a>SuggestionCounter</h4><p>SuggestionCounter的职责是获取建议词关联的商品数量，如果需要可以进行一些聚合操作，比如聚合分类和标签；<br>SuggestionCounter的实现的时候由于要真正地调用搜索接口，应该尽量避免对用户搜索的影响，比如在凌晨执行并且使用单线程调用；<br>为了提升效率，应该使用Elasticsearch的Multi Search接口批量进行count，同时批量更新数据库里建议词的count值；<br>由于SuggestionCounter是比较耗资源的，可以考虑延长执行的周期，但是这可能会带来count值与实际搜索时误差较大的问题，这个需要根据实际情况考虑；</p><h4 id="SuggestionIndexRebuiler"><a href="#SuggestionIndexRebuiler" class="headerlink" title="SuggestionIndexRebuiler"></a>SuggestionIndexRebuiler</h4><p>SuggestionIndexRebuiler的职责是负责重建索引；<br>考虑到用户的搜索习惯，可以使用Multi-fields来给建议词增加多个分析器。比如对于【卫衣 套头】的建议词使用Multi-fields增加不分词字段、拼音分词字段、拼音首字母分词字段、IK分词字段，这样输入【weiyi】和【套头】都可以匹配到该建议词；<br>重建索引时通过是通过bulk批量添加到临时索引中，然后通过别名来更新；<br>重建索引的数据依赖于SuggestionCounter，因此其执行的周期应该与SuggestionCounter保持一致；</p><h4 id="SuggestionService"><a href="#SuggestionService" class="headerlink" title="SuggestionService"></a>SuggestionService</h4><p>SuggestionService是真正处于用户搜索建议的服务类；<br>通常的实现是先到缓存中查询是否能匹配到缓存记录，如果能匹配到则直接返回；否则的话调用Elasticsearch的Prefix Query进行搜索，由于我们在重建索引的时候定义了Multi-fields，在搜索的时候应该用boolQuery来处理；如果此时Elasticsearch返回不为空的结果数据，那么加入缓存并返回即可；</p><hr><h3 id="ES配置"><a href="#ES配置" class="headerlink" title="ES配置"></a>ES配置</h3><h4 id="elasticsearch配置"><a href="#elasticsearch配置" class="headerlink" title="elasticsearch配置"></a>elasticsearch配置</h4><ul><li>elasticsearch.yml</li></ul><p>[elk@M-WEB-098 config]$ cat elasticsearch.yml </p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div><div class="line">90</div><div class="line">91</div><div class="line">92</div><div class="line">93</div><div class="line">94</div><div class="line">95</div><div class="line">96</div><div class="line">97</div><div class="line">98</div><div class="line">99</div><div class="line">100</div><div class="line">101</div><div class="line">102</div><div class="line">103</div><div class="line">104</div><div class="line">105</div><div class="line">106</div><div class="line">107</div><div class="line">108</div><div class="line">109</div><div class="line">110</div><div class="line">111</div></pre></td><td class="code"><pre><div class="line"><span class="comment"># ======================== Elasticsearch Configuration =========================</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># <span class="doctag">NOTE:</span> Elasticsearch comes with reasonable defaults for most settings.</span></div><div class="line"><span class="comment">#       Before you set out to tweak and tune the configuration, make sure you</span></div><div class="line"><span class="comment">#       understand what are you trying to accomplish and the consequences.</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># The primary way of configuring a node is via this file. This template lists</span></div><div class="line"><span class="comment"># the most important settings you may want to configure for a production cluster.</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># Please see the documentation for further information on configuration options:</span></div><div class="line"><span class="comment"># &lt;http://www.elastic.co/guide/en/elasticsearch/reference/current/setup-configuration.html&gt;</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># ---------------------------------- Cluster -----------------------------------</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># Use a descriptive name for your cluster:</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="string">cluster.name:</span> <span class="string">pmh_es</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># ------------------------------------ Node ------------------------------------</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># Use a descriptive name for the node:</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="string">node.name:</span> <span class="string">node-1</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># Add custom attributes to the node:</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># node.rack: r1</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># ----------------------------------- Paths ------------------------------------</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># Path to directory where to store the data (separate multiple locations by comma):</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="string">path.data:</span> <span class="string">/data/elasticsearch/data/</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># Path to log files:</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="string">path.logs:</span> <span class="string">/data/elasticsearch/logs/</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># ----------------------------------- Memory -----------------------------------</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># Lock the memory on startup:</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="string">bootstrap.memory_lock:</span> <span class="literal">true</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># Make sure that the `ES_HEAP_SIZE` environment variable is set to about half the memory</span></div><div class="line"><span class="comment"># available on the system and that the owner of the process is allowed to use this limit.</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># Elasticsearch performs poorly when the system is swapping the memory.</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># ---------------------------------- Network -----------------------------------</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># Set the bind address to a specific IP (IPv4 or IPv6):</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="string">network.host:</span> <span class="number">192.168</span><span class="number">.1</span><span class="number">.98</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># Set a custom port for HTTP:</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="string">http.port:</span> <span class="number">9200</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># For more information, see the documentation at:</span></div><div class="line"><span class="comment"># &lt;http://www.elastic.co/guide/en/elasticsearch/reference/current/modules-network.html&gt;</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># --------------------------------- Discovery ----------------------------------</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># Pass an initial list of hosts to perform discovery when new node is started:</span></div><div class="line"><span class="comment"># The default list of hosts is ["127.0.0.1", "[::1]"]</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># discovery.zen.ping.unicast.hosts: ["host1", "host2"]</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># Prevent the "split brain" by configuring the majority of nodes (total number of nodes / 2 + 1):</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="string">discovery.zen.ping.unicast.hosts:</span> <span class="string">["192.168.1.82",</span> <span class="string">"192.168.1.98"</span><span class="string">]</span></div><div class="line"><span class="string">discovery.zen.ping_timeout:</span> <span class="number">10</span><span class="string">s</span></div><div class="line"><span class="comment"># discovery.zen.minimum_master_nodes: 3</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># For more information, see the documentation at:</span></div><div class="line"><span class="comment"># &lt;http://www.elastic.co/guide/en/elasticsearch/reference/current/modules-discovery.html&gt;</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># ---------------------------------- Gateway -----------------------------------</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># Block initial recovery after a full cluster restart until N nodes are started:</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># gateway.recover_after_nodes: 3</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># For more information, see the documentation at:</span></div><div class="line"><span class="comment"># &lt;http://www.elastic.co/guide/en/elasticsearch/reference/current/modules-gateway.html&gt;</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># ---------------------------------- Various -----------------------------------</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># Disable starting multiple nodes on a single system:</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># node.max_local_storage_nodes: 1</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># Require explicit names when deleting indices:</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># action.destructive_requires_name: true</span></div><div class="line"></div><div class="line"><span class="comment">#ik</span></div><div class="line"><span class="comment">#index.analysis.analyzer.ik.type : "ik"</span></div><div class="line"></div><div class="line"></div><div class="line"><span class="attr">index:</span></div><div class="line"><span class="attr">  analysis:</span></div><div class="line"><span class="attr">    analyzer:</span></div><div class="line"><span class="attr">      ik_max_word:</span></div><div class="line"><span class="attr">          type:</span> <span class="string">ik</span></div><div class="line"><span class="attr">          use_smart:</span> <span class="literal">false</span></div><div class="line"><span class="attr">      ik_smart:</span></div><div class="line"><span class="attr">          type:</span> <span class="string">ik</span></div><div class="line"><span class="attr">          use_smart:</span> <span class="literal">true</span></div><div class="line"><span class="string">bootstrap.memory_lock:</span> <span class="literal">true</span>   <span class="string">//锁定到到内存，防止交换到硬盘</span></div></pre></td></tr></table></figure><h4 id="端口配置"><a href="#端口配置" class="headerlink" title="端口配置"></a>端口配置</h4><p>ES对外提供服务端口默认为：9200<br>可用于访问ES插件及管理界面，如head.</p><p>节点间交互的tcp端口默认为：9300<br>用于提供ES集群节点间相互通信，或内部提供API给业务接口，如提供给JAVA 接口调用。</p><h4 id="安全配置"><a href="#安全配置" class="headerlink" title="安全配置"></a>安全配置</h4><p>由于ES原生是不带有任何安全认证相关的配置及措施，因此任何人都能调用我们的ES服务API，以及管理API，拥有所有的ES操作权限，极不安全。为此：<br>关闭了外网，只将ES服务绑定在内网上<br>通过host本地解析ES IP地址，配合openresty提供域名API服务<br>通过openresty隐藏9200端口，同时配置反向代理ES，为ES提供方便的可扩展性和安全性<br>通过openresty为kibana提供secret http服务，提供安全的数据可视化服务（密码找相关人员索取）</p><h4 id="JDBC配置"><a href="#JDBC配置" class="headerlink" title="JDBC配置"></a>JDBC配置</h4><p>导入ojdbc6.jar包到/usr/local/elasticsearch-2.3.4/elasticsearch-jdbc-2.3.4.0/lib<br>配置索引导入脚本</p><h5 id="oracle-pmh-es-sh"><a href="#oracle-pmh-es-sh" class="headerlink" title="oracle-pmh_es.sh"></a>oracle-pmh_es.sh</h5><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div><div class="line">90</div><div class="line">91</div><div class="line">92</div><div class="line">93</div><div class="line">94</div><div class="line">95</div><div class="line">96</div><div class="line">97</div><div class="line">98</div><div class="line">99</div><div class="line">100</div><div class="line">101</div><div class="line">102</div><div class="line">103</div><div class="line">104</div><div class="line">105</div><div class="line">106</div><div class="line">107</div><div class="line">108</div><div class="line">109</div><div class="line">110</div><div class="line">111</div><div class="line">112</div><div class="line">113</div><div class="line">114</div><div class="line">115</div><div class="line">116</div><div class="line">117</div></pre></td><td class="code"><pre><div class="line"><span class="meta">#!/bin/sh</span></div><div class="line"></div><div class="line"><span class="comment"># This example is a template to connect to Oracle</span></div><div class="line"><span class="comment"># The JDBC URL and SQL must be replaced by working ones.</span></div><div class="line"></div><div class="line">DIR=/usr/<span class="built_in">local</span>/elasticsearch-2.3.4/elasticsearch-jdbc-2.3.4.0</div><div class="line">bin=<span class="variable">$&#123;DIR&#125;</span>/bin</div><div class="line">lib=<span class="variable">$&#123;DIR&#125;</span>/lib</div><div class="line"></div><div class="line"><span class="built_in">echo</span> <span class="string">'</span></div><div class="line">&#123;</div><div class="line">    "type" : "jdbc",</div><div class="line">    "jdbc" : &#123;</div><div class="line">        "url" : "jdbc:oracle:thin:@//192.168.1.129:1521/pomoho",</div><div class="line">        "connection_properties" : &#123;</div><div class="line">            "oracle.jdbc.TcpNoDelay" : false,</div><div class="line">            "useFetchSizeWithLongColumn" : false,</div><div class="line">            "oracle.net.CONNECT_TIMEOUT" : 10000,</div><div class="line">            "oracle.jdbc.ReadTimeout" : 50000</div><div class="line">        &#125;,</div><div class="line">        "user" : "****",</div><div class="line">        "password" : "******",</div><div class="line">        "sql" : "select * from PMH_SOLR",</div><div class="line">        "index" : "pmh_es_smart-test",</div><div class="line">        "type" : "myoracle",</div><div class="line">        "elasticsearch" : &#123;</div><div class="line">            "cluster" : "pmh_es",</div><div class="line">            "host" : "192.168.1.98",</div><div class="line">            "port" : 9300</div><div class="line">        &#125;,</div><div class="line">        "max_bulk_actions" : 20000,</div><div class="line">        "max_concurrent_bulk_requests" : 8,</div><div class="line">        "index_settings" : &#123;</div><div class="line">            "index" : &#123;</div><div class="line">                "number_of_shards" : 1,</div><div class="line">                "number_of_replica" : 1</div><div class="line">            &#125;,</div><div class="line">        "analysis" : &#123;</div><div class="line">                "analyzer" : &#123;</div><div class="line">                    "ik" : &#123;</div><div class="line">                        "tokenizer" : "ik_smart"</div><div class="line">                    &#125;</div><div class="line">                &#125;</div><div class="line">            &#125;</div><div class="line">        &#125;,</div><div class="line">        "type_mapping": &#123;</div><div class="line">                "myoracle":&#123;</div><div class="line">                        "properties" : &#123;</div><div class="line">                                "IMDBID":&#123;</div><div class="line">                                        "type" : "integer"</div><div class="line">                                &#125;,</div><div class="line">                                "FILMNAME":&#123;</div><div class="line">                                        "type" : "string",</div><div class="line">                                        "analyzer" : "ik",</div><div class="line">                                        "search_analyzer": "ik"</div><div class="line">                                &#125;,</div><div class="line">                                "CREATETIME":&#123;</div><div class="line">                                        "type":"date"</div><div class="line">                                &#125;,</div><div class="line">                                "CREATEUSER":&#123;</div><div class="line">                                        "type":"integer"</div><div class="line">                                &#125;,</div><div class="line">                                "PLAYCOST":&#123;</div><div class="line">                                        "type":"integer"</div><div class="line">                                &#125;,</div><div class="line">                                "STATUS":&#123;</div><div class="line">                                        "type":"integer"</div><div class="line">                                &#125;,</div><div class="line">                                "STATUSTIME":&#123;</div><div class="line">                                        "type":"date"</div><div class="line">                                &#125;,</div><div class="line">                                "SOLRTIME":&#123;</div><div class="line">                                        "type":"date"</div><div class="line">                                &#125;,</div><div class="line">                                "DEALSTATUS":&#123;</div><div class="line">                                        "type":"integer"</div><div class="line">                                &#125;,</div><div class="line">                                "FILETYPE":&#123;</div><div class="line">                                        "type":"string"</div><div class="line">                                &#125;,</div><div class="line">                                "TAGS":&#123;</div><div class="line">                                        "type":"string"</div><div class="line">                                &#125;,</div><div class="line">                                "BELONGEDFLAG":&#123;</div><div class="line">                                        "type":"integer"</div><div class="line">                                &#125;,</div><div class="line">                                "CLASSID":&#123;</div><div class="line">                                        "type":"integer"</div><div class="line">                                &#125;,</div><div class="line">                                "CLASSIDTWO":&#123;</div><div class="line">                                        "type":"integer"</div><div class="line">                                &#125;,</div><div class="line">                                "CLASSIDTHREE":&#123;</div><div class="line">                                        "type":"string"</div><div class="line">                                &#125;,</div><div class="line">                                "CLASSIDFOUR":&#123;</div><div class="line">                                        "type":"integer"</div><div class="line">                                &#125;,</div><div class="line">                                "CHANNELID":&#123;</div><div class="line">                                        "type":"integer"</div><div class="line">                                &#125;,</div><div class="line">                                "CHANNELNAME":&#123;</div><div class="line">                                        "type":"string"</div><div class="line">                                &#125;,</div><div class="line">                                "CHANNELDESC":&#123;</div><div class="line">                                        "type":"string"</div><div class="line">                                &#125;</div><div class="line">                        &#125;</div><div class="line">                &#125;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div><div class="line">' | java \</div><div class="line">    -cp <span class="string">"<span class="variable">$&#123;lib&#125;</span>/*"</span> \</div><div class="line">    -D<span class="built_in">log</span>4j.configurationFile=<span class="variable">$&#123;bin&#125;</span>/<span class="built_in">log</span>4j2.xml \</div><div class="line">    org.xbib.tools.Runner \</div><div class="line">    org.xbib.tools.JDBCImporter</div></pre></td></tr></table></figure><h5 id="oracle-pmh-mhh-deltaImport-sh"><a href="#oracle-pmh-mhh-deltaImport-sh" class="headerlink" title="oracle-pmh_mhh_deltaImport.sh"></a>oracle-pmh_mhh_deltaImport.sh</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div><div class="line">90</div><div class="line">91</div><div class="line">92</div><div class="line">93</div><div class="line">94</div><div class="line">95</div><div class="line">96</div><div class="line">97</div><div class="line">98</div><div class="line">99</div><div class="line">100</div><div class="line">101</div><div class="line">102</div><div class="line">103</div><div class="line">104</div><div class="line">105</div><div class="line">106</div><div class="line">107</div><div class="line">108</div></pre></td><td class="code"><pre><div class="line">#!/bin/sh</div><div class="line"></div><div class="line"># This example is a template to connect to Oracle</div><div class="line"># The JDBC URL and SQL must be replaced by working ones.</div><div class="line"></div><div class="line">DIR=/usr/local/elasticsearch-2.3.4/elasticsearch-jdbc-2.3.4.0</div><div class="line">bin=$&#123;DIR&#125;/bin</div><div class="line">lib=$&#123;DIR&#125;/lib</div><div class="line"></div><div class="line">echo &apos;</div><div class="line">&#123;</div><div class="line">    &quot;type&quot; : &quot;jdbc&quot;,</div><div class="line">    &quot;jdbc&quot; : &#123;</div><div class="line">        &quot;url&quot; : &quot;jdbc:oracle:thin:@//IP:PORT/INSTANCE&quot;,</div><div class="line">        &quot;connection_properties&quot; : &#123;</div><div class="line">            &quot;oracle.jdbc.TcpNoDelay&quot; : false,</div><div class="line">            &quot;useFetchSizeWithLongColumn&quot; : false,</div><div class="line">            &quot;oracle.net.CONNECT_TIMEOUT&quot; : 10000,</div><div class="line">            &quot;oracle.jdbc.ReadTimeout&quot; : 50000</div><div class="line">        &#125;,</div><div class="line">        &quot;user&quot; : &quot;****&quot;,</div><div class="line">        &quot;password&quot; : &quot;****&quot;,</div><div class="line">        &quot;statefile&quot; : &quot;statefile-PMH_ES_MHH.json&quot;,</div><div class="line">        &quot;schedule&quot; : &quot;0 55 0/1 * * ?&quot;,</div><div class="line">        &quot;sql&quot; : [</div><div class="line">                &#123;</div><div class="line">                &quot;statement&quot; : &quot;select * from PMH_MHH_SLORUSER where CREATETIME &gt; ?&quot;,</div><div class="line">                &quot;parameter&quot; : [&quot;$metrics.lastexecutionstart&quot;]</div><div class="line">                &#125;</div><div class="line">],</div><div class="line">        &quot;index&quot; : &quot;pmh_es_mhh&quot;,</div><div class="line">        &quot;type&quot; : &quot;myoracle&quot;,</div><div class="line">        &quot;elasticsearch&quot; : &#123;</div><div class="line">            &quot;cluster&quot; : &quot;pmh_es&quot;,</div><div class="line">            &quot;host&quot; : &quot;192.168.1.82&quot;,</div><div class="line">            &quot;port&quot; : 9300</div><div class="line">        &#125;,</div><div class="line">        &quot;max_bulk_actions&quot; : 20000,</div><div class="line">        &quot;max_concurrent_bulk_requests&quot; : 8,</div><div class="line">        &quot;index_settings&quot; : &#123;</div><div class="line">            &quot;index&quot; : &#123;</div><div class="line">                &quot;number_of_shards&quot; : 1,</div><div class="line">                &quot;number_of_replica&quot; : 1</div><div class="line">            &#125;,</div><div class="line">        &quot;analysis&quot; : &#123;</div><div class="line">                &quot;analyzer&quot; : &#123;</div><div class="line">                    &quot;ik&quot; : &#123;</div><div class="line">                        &quot;tokenizer&quot; : &quot;ik_smart&quot;,</div><div class="line">                        &quot;filter&quot; : [&quot;full_pinyin_no_space&quot;,&quot;full_pinyin_with_space&quot;,&quot;first_letter_pinyin&quot;]</div><div class="line">                    &#125;</div><div class="line">                &#125;,</div><div class="line">                &quot;filter&quot; :&#123;</div><div class="line">                &quot;full_pinyin_no_space&quot; : &#123;</div><div class="line">                    &quot;type&quot; : &quot;pinyin&quot;,</div><div class="line">                    &quot;first_letter&quot; : &quot;none&quot;,</div><div class="line">                    &quot;padding_char&quot; : &quot;&quot;</div><div class="line">                &#125;,</div><div class="line">                &quot;full_pinyin_with_space&quot; : &#123;</div><div class="line">                    &quot;type&quot; : &quot;pinyin&quot;,</div><div class="line">                    &quot;first_letter&quot; : &quot;none&quot;,</div><div class="line">                    &quot;padding_char&quot; : &quot; &quot;</div><div class="line"></div><div class="line">            &#125;,</div><div class="line">                &quot;first_letter_pinyin&quot; : &#123;</div><div class="line">                    &quot;type&quot; : &quot;pinyin&quot;,</div><div class="line">                    &quot;first_letter&quot; : &quot;only&quot;,</div><div class="line">                    &quot;padding_char&quot; : &quot;&quot;</div><div class="line"></div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">            &#125;</div><div class="line">        &#125;,</div><div class="line">        &quot;type_mapping&quot;: &#123;</div><div class="line">                &quot;myoracle&quot;:&#123;</div><div class="line">                        &quot;properties&quot; : &#123;</div><div class="line">                                &quot;USERID&quot;:&#123;</div><div class="line">                                        &quot;type&quot; : &quot;integer&quot;</div><div class="line">                                &#125;,</div><div class="line">                                &quot;NICKNAME&quot;:&#123;</div><div class="line">                                        &quot;type&quot; : &quot;string&quot;,</div><div class="line">                                        &quot;analyzer&quot; : &quot;ik&quot;,</div><div class="line">                                        &quot;search_analyzer&quot;: &quot;ik&quot;</div><div class="line">                                &#125;,</div><div class="line">                                &quot;USERTYPE&quot;:&#123;</div><div class="line">                                        &quot;type&quot;:&quot;integer&quot;</div><div class="line">                                &#125;,</div><div class="line">                                &quot;HEADIMAGE&quot;:&#123;</div><div class="line">                                        &quot;type&quot;:&quot;string&quot;</div><div class="line">                                &#125;,</div><div class="line">                                &quot;REMARK&quot;:&#123;</div><div class="line">                                        &quot;type&quot;:&quot;string&quot;</div><div class="line">                                &#125;,</div><div class="line">                                &quot;CREATETIME&quot;:&#123;</div><div class="line">                                        &quot;type&quot;:&quot;date&quot;</div><div class="line">                                &#125;,</div><div class="line">                                &quot;STATUS&quot;:&#123;</div><div class="line">                                        &quot;type&quot;:&quot;integer&quot;</div><div class="line">                                &#125;</div><div class="line">                        &#125;</div><div class="line">                &#125;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div><div class="line">&apos; | java \</div><div class="line">    -cp &quot;$&#123;lib&#125;/*&quot; \</div><div class="line">    -Dlog4j.configurationFile=$&#123;bin&#125;/log4j2.xml \</div><div class="line">    org.xbib.tools.Runner \</div><div class="line">    org.xbib.tools.JDBCImporter</div></pre></td></tr></table></figure><h5 id="oracle-pmh-es-nopinyin-deltaImport-sh"><a href="#oracle-pmh-es-nopinyin-deltaImport-sh" class="headerlink" title="oracle-pmh_es_nopinyin_deltaImport.sh"></a>oracle-pmh_es_nopinyin_deltaImport.sh</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div><div class="line">90</div><div class="line">91</div><div class="line">92</div><div class="line">93</div><div class="line">94</div><div class="line">95</div><div class="line">96</div><div class="line">97</div><div class="line">98</div><div class="line">99</div><div class="line">100</div><div class="line">101</div><div class="line">102</div><div class="line">103</div><div class="line">104</div><div class="line">105</div><div class="line">106</div><div class="line">107</div><div class="line">108</div><div class="line">109</div><div class="line">110</div><div class="line">111</div><div class="line">112</div><div class="line">113</div><div class="line">114</div><div class="line">115</div><div class="line">116</div><div class="line">117</div><div class="line">118</div><div class="line">119</div><div class="line">120</div><div class="line">121</div><div class="line">122</div><div class="line">123</div><div class="line">124</div></pre></td><td class="code"><pre><div class="line">#!/bin/sh</div><div class="line"></div><div class="line"># This example is a template to connect to Oracle</div><div class="line"># The JDBC URL and SQL must be replaced by working ones.</div><div class="line"></div><div class="line">DIR=/usr/local/elasticsearch-2.3.4/elasticsearch-jdbc-2.3.4.0</div><div class="line">bin=$&#123;DIR&#125;/bin</div><div class="line">lib=$&#123;DIR&#125;/lib</div><div class="line"></div><div class="line">echo &apos;</div><div class="line">&#123;</div><div class="line">    &quot;type&quot; : &quot;jdbc&quot;,</div><div class="line">    &quot;jdbc&quot; : &#123;</div><div class="line">        &quot;url&quot; : &quot;jdbc:oracle:thin:@//IP:PORT/INSTANCE&quot;,</div><div class="line">        &quot;connection_properties&quot; : &#123;</div><div class="line">            &quot;oracle.jdbc.TcpNoDelay&quot; : false,</div><div class="line">            &quot;useFetchSizeWithLongColumn&quot; : false,</div><div class="line">            &quot;oracle.net.CONNECT_TIMEOUT&quot; : 10000,</div><div class="line">            &quot;oracle.jdbc.ReadTimeout&quot; : 50000</div><div class="line">        &#125;,</div><div class="line">        &quot;user&quot; : &quot;****&quot;,</div><div class="line">        &quot;password&quot; : &quot;****&quot;,</div><div class="line">        &quot;statefile&quot; : &quot;statefile-PMH_SOLR_NOPY.json&quot;,</div><div class="line">        &quot;schedule&quot; : &quot;0 15 0/1 * * ?&quot;,</div><div class="line">        &quot;sql&quot; : [</div><div class="line">                &#123;</div><div class="line">                &quot;statement&quot; : &quot;select * from PMH_SOLR where SOLRTIME &gt; ?&quot;,</div><div class="line">                &quot;parameter&quot; : [&quot;$metrics.lastexecutionstart&quot;]</div><div class="line">                &#125;</div><div class="line">],</div><div class="line">        &quot;index&quot; : &quot;pmh_es_so_nopy&quot;,</div><div class="line">        &quot;type&quot; : &quot;myoracle&quot;,</div><div class="line">        &quot;elasticsearch&quot; : &#123;</div><div class="line">            &quot;cluster&quot; : &quot;pmh_es&quot;,</div><div class="line">            &quot;host&quot; : &quot;192.168.1.82&quot;,</div><div class="line">            &quot;port&quot; : 9300</div><div class="line">        &#125;,</div><div class="line">        &quot;max_bulk_actions&quot; : 20000,</div><div class="line">        &quot;max_concurrent_bulk_requests&quot; : 8,</div><div class="line">        &quot;index_settings&quot; : &#123;</div><div class="line">            &quot;index&quot; : &#123;</div><div class="line">                &quot;number_of_shards&quot; : 1,</div><div class="line">                &quot;number_of_replica&quot; : 1</div><div class="line">            &#125;,</div><div class="line">        &quot;analysis&quot; : &#123;</div><div class="line">                &quot;analyzer&quot; : &#123;</div><div class="line">                    &quot;ik&quot; : &#123;</div><div class="line">                        &quot;tokenizer&quot; : &quot;ik_smart&quot;</div><div class="line">                    &#125;</div><div class="line">                &#125;</div><div class="line">            &#125;</div><div class="line">        &#125;,</div><div class="line">        &quot;type_mapping&quot;: &#123;</div><div class="line">                &quot;myoracle&quot;:&#123;</div><div class="line">                        &quot;properties&quot; : &#123;</div><div class="line">                                &quot;IMDBID&quot;:&#123;</div><div class="line">                                        &quot;type&quot; : &quot;integer&quot;</div><div class="line">                                &#125;,</div><div class="line">                                &quot;FILMNAME&quot;:&#123;</div><div class="line">                                        &quot;type&quot; : &quot;string&quot;,</div><div class="line">                                        &quot;analyzer&quot; : &quot;ik&quot;,</div><div class="line">                                        &quot;search_analyzer&quot;: &quot;ik&quot;</div><div class="line">                                &#125;,</div><div class="line">                                &quot;CREATETIME&quot;:&#123;</div><div class="line">                                        &quot;type&quot;:&quot;date&quot;</div><div class="line">                                &#125;,</div><div class="line">                                &quot;CREATEUSER&quot;:&#123;</div><div class="line">                                        &quot;type&quot;:&quot;integer&quot;</div><div class="line">                                &#125;,</div><div class="line">                                &quot;PLAYCOST&quot;:&#123;</div><div class="line">                                        &quot;type&quot;:&quot;integer&quot;</div><div class="line">                                &#125;,</div><div class="line">                                &quot;STATUS&quot;:&#123;</div><div class="line">                                        &quot;type&quot;:&quot;integer&quot;</div><div class="line">                                &#125;,</div><div class="line">                                &quot;STATUSTIME&quot;:&#123;</div><div class="line">                                        &quot;type&quot;:&quot;date&quot;</div><div class="line">                                &#125;,</div><div class="line">                                &quot;SOLRTIME&quot;:&#123;</div><div class="line">                                        &quot;type&quot;:&quot;date&quot;</div><div class="line">                                &#125;,</div><div class="line">                                &quot;DEALSTATUS&quot;:&#123;</div><div class="line">                                        &quot;type&quot;:&quot;integer&quot;</div><div class="line">                                &#125;,</div><div class="line">                                &quot;FILETYPE&quot;:&#123;</div><div class="line">                                        &quot;type&quot;:&quot;string&quot;</div><div class="line">                                &#125;,</div><div class="line">                                &quot;TAGS&quot;:&#123;</div><div class="line">                                        &quot;type&quot;:&quot;string&quot;</div><div class="line">                                &#125;,</div><div class="line">                                &quot;BELONGEDFLAG&quot;:&#123;</div><div class="line">                                        &quot;type&quot;:&quot;integer&quot;</div><div class="line">                                &#125;,</div><div class="line">                                &quot;CLASSID&quot;:&#123;</div><div class="line">                                        &quot;type&quot;:&quot;integer&quot;</div><div class="line">                                &#125;,</div><div class="line">                                &quot;CLASSIDTWO&quot;:&#123;</div><div class="line">                                        &quot;type&quot;:&quot;integer&quot;</div><div class="line">                                &#125;,</div><div class="line">                                &quot;CLASSIDTHREE&quot;:&#123;</div><div class="line">                                        &quot;type&quot;:&quot;string&quot;</div><div class="line">                                &#125;,</div><div class="line">                                &quot;CLASSIDFOUR&quot;:&#123;</div><div class="line">                                        &quot;type&quot;:&quot;integer&quot;</div><div class="line">                                &#125;,</div><div class="line">                                &quot;CHANNELID&quot;:&#123;</div><div class="line">                                        &quot;type&quot;:&quot;integer&quot;</div><div class="line">                                &#125;,</div><div class="line">                                &quot;CHANNELNAME&quot;:&#123;</div><div class="line">                                        &quot;type&quot;:&quot;string&quot;</div><div class="line">                                &#125;,</div><div class="line">                                &quot;CHANNELDESC&quot;:&#123;</div><div class="line">                                        &quot;type&quot;:&quot;string&quot;</div><div class="line">                                &#125;</div><div class="line">                        &#125;</div><div class="line">                &#125;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div><div class="line">&apos; | java \</div><div class="line">    -cp &quot;$&#123;lib&#125;/*&quot; \</div><div class="line">    -Dlog4j.configurationFile=$&#123;bin&#125;/log4j2.xml \</div><div class="line">    org.xbib.tools.Runner \</div><div class="line">    org.xbib.tools.JDBCImporter</div></pre></td></tr></table></figure><hr><h3 id="索引"><a href="#索引" class="headerlink" title="索引"></a>索引</h3><h4 id="分片"><a href="#分片" class="headerlink" title="分片"></a>分片</h4><p>当在ElasticSearch集群中配置好你的索引后, 你要明白在集群运行中你无法调整分片设置. 既便以后你发现需要调整分片数量, 你也只能新建创建并对数据进行重新索引(reindex)(虽然reindex会比较耗时, 但至少能保证你不会停机).<br>主分片的配置与硬盘分区很类似, 在对一块空的硬盘空间进行分区时, 会要求用户先进行数据备份, 然后配置新的分区, 最后把数据写到新的分区上.<br>分配分片时主要考虑的你的数据集的增长趋势.</p><p>我们也经常会看到一些不必要的过度分片场景. 从ES社区用户对这个热门主题(分片配置)的分享数据来看, 用户可能认为过度分配是个绝对安全的策略(这里讲的过度分配是指对特定数据集, 为每个索引分配了超出当前数据量(文档数)所需要的分片数).</p><p>Elastic 在早期确实鼓吹过这种做法, 然后很多用户做的更为极端–例如分配1000个分片. 事实上, Elastic目前对此持有 更谨慎的态度 .</p><p>稍有富余是好的, 但过度分配分片却是大错特错. 具体定义多少分片很难有定论, 取决于用户的数据量和使用方式. 100个分片, 即便很少使用也可能是好的;而2个分片, 即便使用非常频繁, 也可能是多余的.</p><p>要知道, 你分配的每个分片都是有额外的成本的:</p><p>每个分片本质上就是一个Lucene索引, 因此会消耗相应的文件句柄, 内存和CPU资源</p><p>每个搜索请求会调度到索引的每个分片中. 如果分片分散在不同的节点倒是问题不太. 但当分片开始竞争相同的硬件资源时, 性能便会逐步下降</p><p>ES使用 词频统计来计算相关性 . 当然这些统计也会分配到各个分片上. 如果在大量分片上只维护了很少的数据, 则将导致最终的文档相关性较差</p><p>我们的客户通常认为随着业务的增长, 他们的数据量也会相应的增加, 所以很有必要为此做长期规划. 很多用户相信他们将会遇到暴发性增长(尽管大多数甚至都没有遇到过峰值), 当然也希望避免重新分片并减少可能的停机时间.</p><p>如果你真的担心数据的快速增长, 我们建议你多关心这条限制: ElasticSearch推荐的最大JVM堆空间 是30~32G, 所以把你的分片最大容量限制为30GB, 然后再对分片数量做合理估算. 例如, 你认为你的数据能达到200GB, 我们推荐你最多分配7到8个分片.</p><p>总之, 不要现在就为你可能在三年后才能达到的10TB数据做过多分配. 如果真到那一天, 你也会很早感知到性能变化的.<br>动态副本<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">PUT /my_temp_index/_settings </div><div class="line">&#123;     </div><div class="line">&quot;number_of_replicas&quot;: 1</div><div class="line">&#125;</div></pre></td></tr></table></figure></p><hr><h3 id="analysis"><a href="#analysis" class="headerlink" title="analysis"></a>analysis</h3><p><strong>standard</strong> 分析器是用于全文字段的默认分析器，对于大部分西方语系来说是一个不错的选择。它考虑了以下几点：<br><strong>standard</strong> 分词器，在词层级上分割输入的文本。<br>standard 标记过滤器，被设计用来整理分词器触发的所有标记（但是目前什么都没做）。<br><strong>lowercase</strong> 标记过滤器，将所有标记转换为小写。<br><strong>stop</strong> 标记过滤器，删除所有可能会造成搜索歧义的停用词，如 a，the，and，is。<br>默认情况下，停用词过滤器是被禁用的。如需启用它，你可以通过创建一个基于 standard 分析器的自定义分析器，并且设置 stopwords 参数。可以提供一个停用词列表，或者使用一个特定语言的预定停用词列表。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">PUT /spanish_docs </div><div class="line">&#123;</div><div class="line">     &quot;settings&quot;: &#123;</div><div class="line">         &quot;analysis&quot;: &#123;</div><div class="line">           &quot;analyzer&quot;: &#123;</div><div class="line">            &quot;es_std&quot;: &#123;</div><div class="line">             &quot;type&quot;:      &quot;standard&quot;,</div><div class="line">             &quot;stopwords&quot;: &quot;_spanish_&quot;</div><div class="line">                    &#125;</div><div class="line">            &#125;</div><div class="line">       &#125;</div><div class="line">   &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure></p><h4 id="中文分词"><a href="#中文分词" class="headerlink" title="中文分词"></a>中文分词</h4><p>使用<a href="https://github.com/medcl/elasticsearch-analysis-ik" target="_blank" rel="external">https://github.com/medcl/elasticsearch-analysis-ik</a><br>配置了<code>ik_max_word</code>和<code>ik_smart</code>，当前使用ik_smart更加人性化。</p><p><code>ik_max_word</code></p><p>ik_smart</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line">配置：</div><div class="line">index:</div><div class="line">  analysis:</div><div class="line">    analyzer:</div><div class="line">      ik_max_word:</div><div class="line">          type: ik</div><div class="line">          use_smart: false</div><div class="line">      ik_smart:</div><div class="line">          type: ik</div><div class="line">          use_smart: true</div><div class="line">&quot;analysis&quot; : &#123;</div><div class="line">                &quot;analyzer&quot; : &#123;</div><div class="line">                    &quot;ik&quot; : &#123;</div><div class="line">                        &quot;tokenizer&quot; : &quot;ik_smart&quot;</div><div class="line">                    &#125;</div><div class="line">                &#125;</div><div class="line">            &#125;</div><div class="line">        &#125;,</div></pre></td></tr></table></figure><h4 id="拼音"><a href="#拼音" class="headerlink" title="拼音"></a>拼音</h4><p>使用<a href="https://github.com/medcl/elasticsearch-analysis-pinyin" target="_blank" rel="external">https://github.com/medcl/elasticsearch-analysis-pinyin</a> 对应1.7.4版本执行mvn打包(打包时间较长，期间可能需要去外网下包）<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div></pre></td><td class="code"><pre><div class="line">wget http://mirror.bit.edu.cn/apache/maven/maven-3/3.3.9/binaries/apache-maven-3.3.9-bin.tar.gz //</div><div class="line">  326  tar zxvf apache-maven-3.3.9-bin.tar.gz //</div><div class="line">  327  ls</div><div class="line">  328  cp  apache-maven-3.3.9 /usr/local/maven</div><div class="line">  329  cp -r apache-maven-3.3.9 /usr/local/maven</div><div class="line">  330  vim /etc/profile //</div><div class="line">  331  . /etc/profile  //</div><div class="line">  332  cd /usr/local/maven/</div><div class="line">  333  ls</div><div class="line">  334  cd bin/</div><div class="line">  335  ls</div><div class="line">  336  vim /etc/profile</div><div class="line">  337  source /etc/profile</div><div class="line">  338  mvn</div><div class="line">  339  vim /etc/profile</div><div class="line">  340  source /etc/profile</div><div class="line">  341  cd /tmp/software/</div><div class="line">  342  ls</div><div class="line">  343  cd product/</div><div class="line">  344  ls</div><div class="line">  345  git clone https://github.com/medcl/elasticsearch-analysis-pinyin.git</div><div class="line">  346  ls</div><div class="line">  347  cd elasticsearch-analysis-pinyin/</div><div class="line">  348  ls</div><div class="line">  349  mvn package</div><div class="line">  350  ls</div><div class="line">  351  cd ..</div><div class="line">  352  ls</div><div class="line">  353  rm -rf elasticsearch-analysis-pinyin</div><div class="line">  354  ls</div><div class="line">  355  wget https://github.com/medcl/elasticsearch-analysis-pinyin/archive/v1.7.4.zip  //</div><div class="line">  356  ls</div><div class="line">  357  mkdir elasticsearch-analysis-pinyin</div><div class="line">  358  mv v1.7.4.zip elasticsearch-analysis-pinyin/  //</div><div class="line">  359  cd elasticsearch-analysis-pinyin/</div><div class="line">  360  ls</div><div class="line">  361  unzip v1.7.4.zip   //</div><div class="line">  362  ls</div><div class="line">  363  cd elasticsearch-analysis-pinyin-1.7.4/  //</div><div class="line">  364  ls</div><div class="line">  365  mvn package    //</div><div class="line">  366  ls</div><div class="line">  367  cd target/  //</div><div class="line">  368  ls</div><div class="line">  369  cd releases/  //</div><div class="line">  370  ls</div><div class="line">  371  cp elasticsearch-analysis-pinyin-1.7.4.zip ../../../</div></pre></td></tr></table></figure></p><p>实现中文分词后再进行pinyin过滤<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">    &quot;index&quot; : &#123;</div><div class="line">        &quot;analysis&quot; : &#123;</div><div class="line">            &quot;analyzer&quot; : &#123;</div><div class="line">                &quot;custom_pinyin_analyzer&quot; : &#123;</div><div class="line">                    &quot;tokenizer&quot; : &quot;ik_smart&quot;,</div><div class="line">                    &quot;filter&quot; : [&quot;full_pinyin_no_space&quot;,&quot;full_pinyin_with_space&quot;,&quot;first_letter_pinyin&quot;]</div><div class="line">                &#125;</div><div class="line">            &#125;,</div><div class="line">            &quot;filter&quot; :&#123;</div><div class="line">                &quot;full_pinyin_no_space&quot; : &#123;</div><div class="line">                    &quot;type&quot; : &quot;pinyin&quot;,</div><div class="line">                    &quot;first_letter&quot; : &quot;none&quot;,</div><div class="line">                    &quot;padding_char&quot; : &quot;&quot;</div><div class="line">                &#125;,</div><div class="line">                &quot;full_pinyin_with_space&quot; : &#123;</div><div class="line">                    &quot;type&quot; : &quot;pinyin&quot;,</div><div class="line">                    &quot;first_letter&quot; : &quot;none&quot;,</div><div class="line">                    &quot;padding_char&quot; : &quot; &quot;</div><div class="line"></div><div class="line">            &#125;,</div><div class="line">                &quot;first_letter_pinyin&quot; : &#123;</div><div class="line">                    &quot;type&quot; : &quot;pinyin&quot;,</div><div class="line">                    &quot;first_letter&quot; : &quot;only&quot;,</div><div class="line">                    &quot;padding_char&quot; : &quot;&quot;</div><div class="line"></div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure></p><h4 id="断词"><a href="#断词" class="headerlink" title="断词"></a>断词</h4><h4 id="同义词"><a href="#同义词" class="headerlink" title="同义词"></a>同义词</h4><h4 id="自定义词库（自定义，第三方）"><a href="#自定义词库（自定义，第三方）" class="headerlink" title="自定义词库（自定义，第三方）"></a>自定义词库（自定义，第三方）</h4><hr><h3 id="插件"><a href="#插件" class="headerlink" title="插件"></a>插件</h3><p>当前使用插件：</p><ul><li><p>elasticsearch-head    集群管理工具<br><a href="http://estest.baomihua.com/_plugin/head/" target="_blank" rel="external">http://estest.baomihua.com/_plugin/head/</a><br>提供索引分片基本信息查看和相关操作，以及基本的增删改查服务，和索引相关配置信息，集群状态，插件配置信息等。  </p></li><li><p>bigdesk    集群监控工具<br><a href="http://estest.baomihua.com/_plugin/bigdesk/#nodes" target="_blank" rel="external">http://estest.baomihua.com/_plugin/bigdesk/#nodes</a><br>提供ES集群性能实时监测，包括JVM，Thread Pools，OS，Process，HTTP &amp; Transport，Indices，File system相关信息。</p></li><li><p>kibana   可视化数据工具<br><a href="http://estest.baomihua.com:5602" target="_blank" rel="external">http://estest.baomihua.com:5602</a><br>kibana是个日志可视化工具，在本环境下用来提供索引记录的实时详细查询，已经根据索引数据建立相关图表分析等。</p></li><li><p>Marvel  可视化ES集群状态监测工具<br><a href="http://estest.baomihua.com:5602/app/marvel" target="_blank" rel="external">http://estest.baomihua.com:5602/app/marvel</a><br>提供更加美观的可视化ES集群性能实时监测。</p></li><li><p>elasticsearch-jdbc    数据导入工具</p></li></ul><hr><h3 id="索引更新"><a href="#索引更新" class="headerlink" title="索引更新"></a>索引更新</h3><h4 id="全量索引"><a href="#全量索引" class="headerlink" title="全量索引"></a>全量索引</h4><p>全量索引类似建立索引，全量导入oracle-pmh_es.sh</p><h4 id="增量索引"><a href="#增量索引" class="headerlink" title="增量索引"></a>增量索引</h4><p>ES-sql参数：<br>获取一个表,select <em> from table可以使用查询。 查询从数据库选择数据的简单的变体。 他们转储表成Elasticsearch逐行。 如果没有_id列名,IDs将自动生成。<br>id as _id 这样的话可以增量同步，_id是es的默认id命名<br>“interval”:”1800”, 这里是同步数据的频率 1800s，半小时，可以按需要设成 1s或其它<br>“schedule” : “0 0/60 0-23 ? </em> *”,   同步数据任务  60分钟一次<br>“flush_interval” : “5s”,    刷新间隔为5S<br>sql.parameter——绑定SQL语句参数(按顺序)。 可以使用一些特殊的值具有以下含义:</p><pre><code>$now——当前时间戳$state——国家之一:BEFORE_FETCH,取回,AFTER_FETCH,无所事事,例外$metrics.counter——一个计数器$lastrowcount——从最后一条语句的行数$lastexceptiondate- SQL时间戳的例外$lastexception——完整的堆栈跟踪的例外$metrics.lastexecutionstart——最后一次执行SQL时间戳的时候开始$metrics.lastexecutionend- SQL时间戳的时候最后一次执行结束$metrics.totalrows——总获取的行数$metrics.totalbytes——获取的字节总数$metrics.failed——失败的SQL执行的总数$metrics.succeeded</code></pre><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">deltaImportQuery=&quot;SELECT * FROM  PHM_SOLR WHERER SOLRTIME &gt;TO_date(&apos;$&#123;metrics.lastexecutionstart&#125;&apos;,&apos;YYYY-MM-DD hh24:mi:ss&apos;)&quot;</div><div class="line"></div><div class="line">&quot;statefile&quot; : &quot;statefile-article.json&quot;,</div><div class="line">        &quot;schedule&quot; : &quot;0 0-59 0-23 ? * *&quot;,</div><div class="line">&quot;sql&quot; : [</div><div class="line">            &#123;</div><div class="line">                &quot;statement&quot; : &quot;select *, id as _id from article where update_time &gt; ?&quot;,</div><div class="line">                &quot;parameter&quot; : [ &quot;$metrics.lastexecutionstart&quot; ]</div><div class="line">            &#125;</div><div class="line">        ]</div></pre></td></tr></table></figure><hr><h3 id="ES查询API"><a href="#ES查询API" class="headerlink" title="ES查询API"></a>ES查询API</h3><h4 id="简易搜索"><a href="#简易搜索" class="headerlink" title="简易搜索"></a>简易搜索</h4><p><a href="http://estest.baomihua.com/pmh_es_smart-test/_search?&amp;pretty" target="_blank" rel="external">http://estest.baomihua.com/pmh_es_smart-test/_search?&amp;pretty</a><br>pretty:美化json</p><p><a href="http://estest.baomihua.co/pmh_es_smart-test/_search?q=FILMNAME:%E4%B8%AD%E5%9B%BD+CHANNELNAME:%E4%B8%AD%E5%9B%BD&amp;pretty" target="_blank" rel="external">http://estest.baomihua.co/pmh_es_smart-test/_search?q=FILMNAME:%E4%B8%AD%E5%9B%BD+CHANNELNAME:%E4%B8%AD%E5%9B%BD&amp;pretty</a><br>字段搜索：_search?q=FILMNAME:中国+CHANNELNAME:中国</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line">GET /_search?timeout=10ms</div><div class="line">定义响应超时时间</div><div class="line">/_search</div><div class="line">在所有索引的所有类型中搜索</div><div class="line">/gb/_search</div><div class="line">在索引gb的所有类型中搜索</div><div class="line">/gb,us/_search</div><div class="line">在索引gb和us的所有类型中搜索</div><div class="line">/g*,u*/_search</div><div class="line">在以g或u开头的索引的所有类型中搜索</div><div class="line">/gb/user/_search</div><div class="line">在索引gb的类型user中搜索</div><div class="line">/gb,us/user,tweet/_search</div><div class="line">在索引gb和us的类型为user和tweet中搜索</div><div class="line">/_all/user,tweet/_search</div><div class="line">在所有索引的user和tweet中搜索 search types user and tweet in all indices</div></pre></td></tr></table></figure><h4 id="分页"><a href="#分页" class="headerlink" title="分页"></a>分页</h4><p><strong>size</strong>: 结果数，默认10<br><strong>from</strong>: 跳过开始的结果数，默认0<br><strong>每页显示5个结果，页码从1到3</strong>：<br>GET /_search?size=5<br>GET /_search?size=5&amp;from=5<br>GET /_search?size=5&amp;from=10</p><h4 id="高亮"><a href="#高亮" class="headerlink" title="高亮"></a>高亮</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">&quot;highlight&quot;: &#123;</div><div class="line">    &quot;pre_tags&quot;: [</div><div class="line">      &quot;&lt;tag1&gt;&quot;,</div><div class="line">      &quot;&lt;tag2&gt;&quot;</div><div class="line">    ],</div><div class="line">    &quot;post_tags&quot;: [</div><div class="line">      &quot;&lt;/tag1&gt;&quot;,</div><div class="line">      &quot;&lt;/tag2&gt;&quot;</div><div class="line">    ],</div><div class="line">    &quot;fields&quot;: &#123;</div><div class="line">      &quot;FILMNAME&quot;: &#123;&#125;</div><div class="line">    &#125;</div><div class="line">  &#125;</div></pre></td></tr></table></figure><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">  &quot;query&quot;: &#123;</div><div class="line">    &quot;match&quot;: &#123;</div><div class="line">      &quot;FILMNAME&quot;: &quot;中国&quot;</div><div class="line">    &#125;</div><div class="line">  &#125;,</div><div class="line">  &quot;highlight&quot;: &#123;</div><div class="line">    &quot;fields&quot;: &#123;</div><div class="line">      &quot;FILMNAME&quot;: &#123;&#125;</div><div class="line">    &#125;</div><div class="line">  &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><hr><h3 id="ES结构化API"><a href="#ES结构化API" class="headerlink" title="ES结构化API"></a>ES结构化API</h3><h4 id="请求体查询"><a href="#请求体查询" class="headerlink" title="请求体查询"></a>请求体查询</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">GET /_search </div><div class="line">&#123;&#125; &lt;1&gt;</div></pre></td></tr></table></figure><p>返回索引中所有的文档</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">GET /_search </div><div class="line">&#123;   </div><div class="line">&quot;from&quot;: 30,   </div><div class="line">&quot;size&quot;: 10 </div><div class="line">&#125;</div><div class="line"></div><div class="line">POST /_search </div><div class="line">&#123;   </div><div class="line">&quot;from&quot;: 30,   </div><div class="line">&quot;size&quot;: 10 </div><div class="line">&#125;</div></pre></td></tr></table></figure><p>分页</p><h4 id="Query-DSL"><a href="#Query-DSL" class="headerlink" title="Query DSL"></a>Query DSL</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">GET /_search </div><div class="line">&#123;     </div><div class="line">      &quot;query&quot;: &#123;         </div><div class="line">          &quot;match_all&quot;: &#123;&#125;     </div><div class="line">  &#125; </div><div class="line">&#125;</div></pre></td></tr></table></figure><p>匹配所有的文档</p><h4 id="合并多子句"><a href="#合并多子句" class="headerlink" title="合并多子句"></a>合并多子句</h4><p>查询子句就像是搭积木一样，可以合并简单的子句为一个复杂的查询语句，比如：<br>叶子子句(leaf clauses)(比如match子句)用以在将查询字符串与一个字段(或多字段)进行比较<br>复合子句(compound)用以合并其他的子句。例如，bool子句允许你合并其他的合法子句，must，must_not或者should，如果可能的话：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">&#123;     </div><div class="line">&quot;bool&quot;: </div><div class="line">&#123;         </div><div class="line">&quot;must&quot;:     &#123; &quot;match&quot;: &#123; &quot;tweet&quot;: &quot;elasticsearch&quot; &#125;&#125;,         </div><div class="line">&quot;must_not&quot;: &#123; &quot;match&quot;: &#123; &quot;name&quot;:  &quot;mary&quot; &#125;&#125;,         </div><div class="line">&quot;should&quot;:   &#123; &quot;match&quot;: &#123; &quot;tweet&quot;: &quot;full text&quot; &#125;&#125;</div><div class="line">     &#125; </div><div class="line">&#125;</div></pre></td></tr></table></figure></p><hr><h3 id="Filter-DSL"><a href="#Filter-DSL" class="headerlink" title="Filter DSL"></a>Filter DSL</h3><h4 id="term-过滤"><a href="#term-过滤" class="headerlink" title="term 过滤"></a>term 过滤</h4><p>term主要用于精确匹配哪些值，比如数字，日期，布尔值或 not_analyzed的字符串(未经分析的文本数据类型)：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">&#123; &quot;term&quot;: &#123; &quot;age&quot;:    26           &#125;&#125;</div><div class="line"> &#123; &quot;term&quot;: &#123; &quot;date&quot;:   &quot;2014-09-01&quot; &#125;&#125;</div><div class="line"> &#123; &quot;term&quot;: &#123; &quot;public&quot;: true         &#125;&#125;</div><div class="line"> &#123; &quot;term&quot;: &#123; &quot;tag&quot;:    &quot;full_text&quot;  &#125;&#125;</div></pre></td></tr></table></figure></p><h4 id="terms-过滤"><a href="#terms-过滤" class="headerlink" title="terms 过滤"></a>terms 过滤</h4><p>terms 跟 term 有点类似，但 terms 允许指定多个匹配条件。 如果某个字段指定了多个值，那么文档需要一起去做匹配：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">     &quot;terms&quot;: &#123;</div><div class="line">              &quot;tag&quot;: [ &quot;search&quot;, &quot;full_text&quot;, &quot;nosql&quot; ]</div><div class="line">            &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure></p><h4 id="range过滤"><a href="#range过滤" class="headerlink" title="range过滤"></a>range过滤</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">     &quot;range&quot;: &#123;</div><div class="line">              &quot;age&quot;: &#123;</div><div class="line">              &quot;gte&quot;:  20,</div><div class="line">              &quot;lt&quot;:   30</div><div class="line">            &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>范围操作符包含：<br><code>gt</code> :: 大于<br><code>gte</code>:: 大于等于<br><code>lt</code> :: 小于<br><code>lte</code>:: 小于等于</p><h4 id="exists-和-missing-过滤"><a href="#exists-和-missing-过滤" class="headerlink" title="exists 和 missing 过滤"></a>exists 和 missing 过滤</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">     &quot;exists&quot;:   &#123;</div><div class="line">              &quot;field&quot;:    &quot;title&quot;    </div><div class="line">      &#125; </div><div class="line">&#125;</div></pre></td></tr></table></figure><h4 id="bool-过滤"><a href="#bool-过滤" class="headerlink" title="bool 过滤"></a>bool 过滤</h4><p>bool 过滤可以用来合并多个过滤条件查询结果的布尔逻辑，它包含一下操作符：<br><code>must</code> :: 多个查询条件的完全匹配,相当于 and。<br><code>must_not</code> :: 多个查询条件的相反匹配，相当于 not。<br><code>should</code> :: 至少有一个查询条件匹配, 相当于 or。</p><h4 id="match-查询"><a href="#match-查询" class="headerlink" title="match 查询"></a>match 查询</h4><h4 id="multi-match-查询"><a href="#multi-match-查询" class="headerlink" title="multi_match 查询"></a>multi_match 查询</h4><p>multi_match查询允许你做match查询的基础上同时搜索多个字段：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">     &quot;multi_match&quot;: &#123;</div><div class="line">              &quot;query&quot;:    &quot;full text search&quot;,</div><div class="line">              &quot;fields&quot;:   [ &quot;title&quot;, &quot;body&quot; ]</div><div class="line">      &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure></p><hr><h3 id="排序"><a href="#排序" class="headerlink" title="排序"></a>排序</h3><h4 id="字段值排序"><a href="#字段值排序" class="headerlink" title="字段值排序"></a>字段值排序</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">GET /_search</div><div class="line"> &#123;</div><div class="line">      &quot;query&quot; : &#123;</div><div class="line">               &quot;filtered&quot; : &#123;</div><div class="line">              &quot;filter&quot; : &#123; &quot;term&quot; : &#123; &quot;user_id&quot; : 1 &#125;&#125;</div><div class="line">                       &#125;</div><div class="line">           &#125;,</div><div class="line">           &quot;sort&quot;: &#123; &quot;date&quot;: &#123; &quot;order&quot;: &quot;desc&quot; &#125;&#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>date排序会转换为毫秒进行排序<br>_score得分排序，最优结果</p><hr><blockquote><p><strong>注意：</strong>本文为工作记录，未进行文档化，部分内容可读性较差，如有啥知识性误导或问题，可以留言反馈。以后或许会写些系列性的ES文档。</p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;h3 id=&quot;ES选型&quot;&gt;&lt;a href=&quot;#ES选型&quot; class=&quot;headerlink&quot; title=&quot;ES选型&quot;&gt;&lt;/a&gt;ES选型&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;elasticsearch-2.3.4&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;相关组件：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;elasticsearch-jdbc-2.3.4.0-dist&lt;/li&gt;
&lt;li&gt;kibana-4.5.4-linux-x64.tar&lt;/li&gt;
&lt;li&gt;elasticsearch-analysis-ik-1.9.4&lt;/li&gt;
&lt;li&gt;elasticsearch-analysis-pinyin-1.7.4&lt;/li&gt;
&lt;li&gt;_bigdesk&lt;/li&gt;
&lt;li&gt;_head&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以ES2.3.4为基准，其他组件已为测试后最新可用版本，高于以上版本则不兼容。&lt;/p&gt;
&lt;h2 id=&quot;&quot;&gt;&lt;a href=&quot;#&quot; class=&quot;headerlink&quot; title=&quot;&quot;&gt;&lt;/a&gt;
    
    </summary>
    
      <category term="ELK" scheme="http://www.hyperxu.com/categories/ELK/"/>
    
    
      <category term="ELK" scheme="http://www.hyperxu.com/tags/ELK/"/>
    
      <category term="elasticsearch" scheme="http://www.hyperxu.com/tags/elasticsearch/"/>
    
      <category term="搜索" scheme="http://www.hyperxu.com/tags/%E6%90%9C%E7%B4%A2/"/>
    
  </entry>
  
  <entry>
    <title>【redis从入门到上线(4)】- redis高可用架构横向对比分析</title>
    <link href="http://www.hyperxu.com/2017/06/20/redis4/"/>
    <id>http://www.hyperxu.com/2017/06/20/redis4/</id>
    <published>2017-06-20T08:23:04.000Z</published>
    <updated>2018-11-29T08:28:50.550Z</updated>
    
    <content type="html"><![CDATA[<h1 id="redis架构分析"><a href="#redis架构分析" class="headerlink" title="redis架构分析"></a>redis架构分析</h1><p>上篇我们讲解完 Redis Sentinel 原理之后，接下来讲解常用的 Redis 高可用架构。</p><ul><li>Redis Sentinel 集群 + 内网 DNS + 自定义脚本</li><li>Redis Sentinel 集群 + VIP + 自定义脚本</li><li>封装客户端直连 Redis Sentinel 端口</li><li>JedisSentinelPool，适合 Java</li><li>PHP 基于 phpredis 自行封装</li><li>Redis Sentinel 集群 + Keepalived/Haproxy</li><li>Redis M/S + Keepalived</li><li>Redis Cluster</li><li>Twemproxy+sentinel+Keepalived</li><li>Codis</li><li>Pika</li></ul><a id="more"></a><h2 id="Redis-Sentinel-集群-内网-DNS-自定义脚本"><a href="#Redis-Sentinel-集群-内网-DNS-自定义脚本" class="headerlink" title="Redis Sentinel 集群 + 内网 DNS + 自定义脚本"></a>Redis Sentinel 集群 + 内网 DNS + 自定义脚本</h2><p><img src="https://uploads.hyperxu.com/15434789614418.jpg" alt=""></p><p>上图是已经在线上环境应用的方案。底层是 <code>Redis Sentinel</code> 集群，代理着 Redis 主从，Web 端连接内网 DNS 提供服务。内网 DNS 按照一定的规则分配，比如 xxxx.redis.cache/queue.port.xxx.xxx，第一个段表示业务简写，第二个段表示这是 Redis 内网域名，第三个段表示 Redis 类型，cache 表示缓存，queue 表示队列，第四个段表示 Redis 端口，第五、第六个段表示内网主域名。</p><p>当主节点发生故障，比如机器故障、Redis 节点故障或者网络不可达，Sentinel 集群会调用 client-reconfig-script 配置的脚本，修改对应端口的内网域名。对应端口的内网域名指向新的 Redis 主节点。</p><p><strong>优点：</strong></p><ul><li>秒级切换，在 10s 内完成整个切换操作</li><li>脚本自定义，架构可控</li><li>对应用透明，前端不用担心后端发生什么变化</li></ul><p><strong>缺点：</strong></p><ul><li>维护成本略高，Redis Sentinel 集群建议投入 3 台机器以上</li><li>依赖 DNS，存在解析延时</li><li>Sentinel 模式存在短时间的服务不可用</li><li>服务通过外网访问不可采用此方案</li></ul><h2 id="Redis-Sentinel-集群-VIP-自定义脚本"><a href="#Redis-Sentinel-集群-VIP-自定义脚本" class="headerlink" title="Redis Sentinel 集群 + VIP + 自定义脚本"></a>Redis Sentinel 集群 + VIP + 自定义脚本</h2><p>此方案和上一个方案相比，略有不同。第一个方案使用了内网 DNS，第二个方案把内网 DNS 换成了虚拟 IP。底层是 Redis Sentinel 集群，代理着 Redis 主从，Web 端通过 VIP 提供服务。在部署 Redis 主从的时候，需要将虚拟 IP 绑定到当前的 Redis 主节点。当主节点发生故障，比如机器故障、Redis 节点故障或者网络不可达，Sentinel 集群会调用 client-reconfig-script 配置的脚本，将 VIP 漂移到新的主节点上。</p><p><strong>优点：</strong></p><ul><li>秒级切换，在 5s 内完成整个切换操作</li><li>脚本自定义，架构可控</li><li>对应用透明，前端不用担心后端发生什么变化</li></ul><p><strong>缺点：</strong></p><ul><li>维护成本略高，Redis Sentinel 集群建议投入 3 台机器以上</li><li>使用 VIP 增加维护成本，存在 IP 混乱风险</li><li>Sentinel 模式存在短时间的服务不可用</li></ul><h2 id="封装客户端直连-Redis-Sentinel-端口"><a href="#封装客户端直连-Redis-Sentinel-端口" class="headerlink" title="封装客户端直连 Redis Sentinel 端口"></a>封装客户端直连 Redis Sentinel 端口</h2><p><img src="https://uploads.hyperxu.com/15434792864633.jpg" alt=""></p><p>部分业务只能通过外网访问 Redis，上述两种方案均不可用，于是衍生出了这种方案。Web 使用客户端连接其中一台 <code>Redis Sentinel</code> 集群中的一台机器的某个端口，然后通过这个端口获取到当前的主节点，然后再连接到真实的 Redis 主节点进行相应的业务员操作。需要注意的是，<code>Redis Sentinel</code> 端口和 Redis 主节点均需要开放访问权限。如果前端业务使用 Java，有 <code>JedisSentinelPool</code> 可以复用；如果前端业务使用 PHP，可以在 <code>phpredis</code> 的基础上做二次封装。</p><p><strong>优点：</strong></p><p>服务探测故障及时<br>DBA 维护成本低</p><p><strong>缺点：</strong></p><ul><li>依赖客户端支持 Sentinel</li><li>Sentinel 服务器和 Redis 节点需要开放访问权限</li><li>对应用有侵入性</li></ul><h2 id="Redis-Sentinel-集群-Keepalived-Haproxy"><a href="#Redis-Sentinel-集群-Keepalived-Haproxy" class="headerlink" title="Redis Sentinel 集群 + Keepalived/Haproxy"></a>Redis Sentinel 集群 + Keepalived/Haproxy</h2><p><img src="https://uploads.hyperxu.com/15434793704212.jpg" alt=""></p><p>底层是 <code>Redis Sentinel</code> 集群，代理着 Redis 主从，Web 端通过 VIP 提供服务。当主节点发生故障，比如机器故障、Redis 节点故障或者网络不可达，Redis 之间的切换通过 <code>Redis Sentinel</code> 内部机制保障，VIP 切换通过 <code>Keepalived</code> 保障。</p><p><strong>优点：</strong></p><ul><li>秒级切换</li><li>对应用透明</li></ul><p><strong>缺点：</strong></p><ul><li>维护成本高</li><li>存在脑裂</li><li>Sentinel 模式存在短时间的服务不可用</li></ul><h2 id="Redis-M-S-Keepalived"><a href="#Redis-M-S-Keepalived" class="headerlink" title="Redis M/S + Keepalived"></a>Redis M/S + Keepalived</h2><p><img src="https://uploads.hyperxu.com/15434794309770.jpg" alt=""></p><p>此方案没有使用到 Redis Sentinel。此方案使用了原生的主从和 Keepalived，VIP 切换通过 Keepalived 保障，Redis 主从之间的切换需要自定义脚本实现。</p><p><strong>优点：</strong></p><ul><li>秒级切换</li><li>对应用透明</li><li>部署简单，维护成本低</li></ul><p><strong>缺点：</strong></p><ul><li>需要脚本实现切换功能</li><li>存在脑裂</li></ul><h2 id="Redis-Cluster"><a href="#Redis-Cluster" class="headerlink" title="Redis Cluster"></a>Redis Cluster</h2><p><img src="https://uploads.hyperxu.com/15434794777752.jpg" alt=""></p><p>Redis 3.0.0 在 2015 年 4 月 2 日正式发布，距今已有两年多的时间。Redis 集群采用 P2P 模式，无中心化。把 key 分成 16384 个 slot，每个实例负责一部分 slot。客户端请求对应的数据，若该实例 slot 没有对应的数据，该实例会转发给对应的实例。另外，Redis 集群通过 Gossip 协议同步节点信息。</p><p><strong>优点：</strong></p><ul><li>组件 all-in-box，部署简单，节约机器资源</li><li>性能比 proxy 模式好</li><li>自动故障转移、Slot 迁移中数据可用</li><li>官方原生集群方案，更新与支持有保障</li></ul><p><strong>缺点：</strong></p><ul><li>架构比较新，最佳实践较少</li><li>多键操作支持有限（驱动可以曲线救国）</li><li>为了性能提升，客户端需要缓存路由表信息</li><li>节点发现、reshard 操作不够自动化</li></ul><h2 id="Twemproxy-sentinel-Keepalived"><a href="#Twemproxy-sentinel-Keepalived" class="headerlink" title="Twemproxy+sentinel+Keepalived"></a>Twemproxy+sentinel+Keepalived</h2><p><img src="https://uploads.hyperxu.com/15434795114409.jpg" alt=""></p><p>多个同构 Twemproxy（配置相同）同时工作，接受客户端的请求，根据 hash 算法，转发给对应的 Redis。<br><code>Twemproxy</code> 方案比较成熟了，之前我们团队长期使用此方案，但是效果并不是很理想。一方面是定位问题比较困难，另一方面是它对自动剔除节点的支持不是很友好。</p><p><strong>优点：</strong></p><ul><li>开发简单，对应用几乎透明</li><li>历史悠久，方案成熟</li></ul><p><strong>缺点：</strong></p><ul><li>代理影响性能</li><li>LVS 和 <code>Twemproxy</code> 会有节点性能瓶颈</li><li>Redis 扩容非常麻烦</li><li>Twitter 内部已放弃使用该方案且不再在GitHub上更新，新使用的架构未开源</li></ul><h2 id="Codis"><a href="#Codis" class="headerlink" title="Codis"></a>Codis</h2><p><img src="https://uploads.hyperxu.com/15434795633823.jpg" alt=""></p><p>Codis 是由豌豆荚开源的产品，涉及组件众多，其中 ZooKeeper 存放路由表和代理节点元数据、分发 Codis-Config 的命令；Codis-Config 是集成管理工具，有 Web 界面供使用；Codis-Proxy 是一个兼容 Redis 协议的无状态代理；Codis-Redis 基于 Redis 2.8 版本二次开发，加入 slot 支持，方便迁移数据。</p><p><strong>优点：</strong></p><ul><li>开发简单，对应用几乎透明</li><li>性能在特定情况下比Twemproxy好</li><li>有图形化界面，扩容容易，运维方便</li></ul><p><strong>缺点：</strong></p><ul><li>代理依旧影响性能</li><li>组件过多，需要很多机器资源</li><li>修改了 Redis 代码，导致和官方无法同步，新特性跟进缓慢</li><li>开发团队准备主推基于 Redis 改造的 reborndb</li></ul><h2 id="Pika"><a href="#Pika" class="headerlink" title="Pika"></a>Pika</h2><p><img src="https://uploads.hyperxu.com/15434795990001.jpg" alt=""></p><p><code>pika</code> 是DBA和基础架构组联合开发的类Redis 存储系统，所以完全支持Redis协议，用户不需要修改任何代码，就可以将服务迁移至pika。Pika是一个可持久化的大容量redis存储服务，兼容string、hash、list、zset、set的绝大接口(兼容详情)，解决redis由于存储数据量巨大而导致内存不够用的容量瓶颈，并且可以像redis一样，通过slaveof命令进行主从备份，支持全同步和部分同步。同时DBA团队还提供了迁移工具， 所以户不会感知这个迁移的过程，迁移是平滑的。</p><p>pika主要是使用持久化存储来解决redis在内存占用超过50G，80G时遇到的如启动恢复时间长，主从同步代价大，硬件成本贵等问题，并且在对外用法上尽可能做到与redis一致，用户基本上对后端是redis或pika无感知。</p><p><strong>优点：</strong></p><ul><li>多线程：较redis单线程更快</li><li>容量大：Pika没有Redis的内存限制, 最大使用空间等于磁盘空间的大小</li><li>加载db速度快：Pika 在写入的时候, 数据是落盘的, 所以即使节点挂了, 不需要rdb或者oplog，pika 重启不用加载所有数据到内存就能恢复之前的数据, 不需要进行回放数据操作</li><li>备份速度快：Pika备份的速度大致等同于cp的速度（拷贝数据文件后还有一个快照的恢复过程，会花费一些时间），这样在对于百G大库的备份是快捷的，更快的备份速度更好的解决了主从的全同步问题</li></ul><p><strong>缺点：</strong></p><ul><li>由于Pika是基于内存和文件来存放数据, 所以性能肯定比Redis低一些, 但是如果使用SSD盘来存放数据, 尽可能跟上Redis的性能</li></ul><h1 id="实践"><a href="#实践" class="headerlink" title="实践"></a>实践</h1><p>其实架构的选型还是得结合实际应用场景来进行评估，个人建议：</p><p>量级一般够用的情况下，采用“Redis Sentinel 集群 + VIP + 自定义脚本”方案，最好配合个好的客户端，调整比较灵活，实施也高效。</p><p>量级较大时，可以考虑“Twemproxy+sentinel+Keepalived”或“Codis”，根据存储数据单key大小进行判断，我自己测试发现value小于KB级时，Codis的set性能是要远高于Twemproxy，大于KB级时，twemproxy性能反而好些。（虽然我看很多文章都说codis性能优于twemproxy，实际应用还是针对实际应用场景进行测试比较靠谱）</p><p>无论codis还是twemproxy相对来说都较复杂，嫌麻烦的朋友直接上360的pika好了</p><p>今天就写到这，之后可能会针对codis和pika单独写些文章。</p>]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;redis架构分析&quot;&gt;&lt;a href=&quot;#redis架构分析&quot; class=&quot;headerlink&quot; title=&quot;redis架构分析&quot;&gt;&lt;/a&gt;redis架构分析&lt;/h1&gt;&lt;p&gt;上篇我们讲解完 Redis Sentinel 原理之后，接下来讲解常用的 Redis 高可用架构。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Redis Sentinel 集群 + 内网 DNS + 自定义脚本&lt;/li&gt;
&lt;li&gt;Redis Sentinel 集群 + VIP + 自定义脚本&lt;/li&gt;
&lt;li&gt;封装客户端直连 Redis Sentinel 端口&lt;/li&gt;
&lt;li&gt;JedisSentinelPool，适合 Java&lt;/li&gt;
&lt;li&gt;PHP 基于 phpredis 自行封装&lt;/li&gt;
&lt;li&gt;Redis Sentinel 集群 + Keepalived/Haproxy&lt;/li&gt;
&lt;li&gt;Redis M/S + Keepalived&lt;/li&gt;
&lt;li&gt;Redis Cluster&lt;/li&gt;
&lt;li&gt;Twemproxy+sentinel+Keepalived&lt;/li&gt;
&lt;li&gt;Codis&lt;/li&gt;
&lt;li&gt;Pika&lt;/li&gt;
&lt;/ul&gt;
    
    </summary>
    
      <category term="redis" scheme="http://www.hyperxu.com/categories/redis/"/>
    
    
      <category term="redis" scheme="http://www.hyperxu.com/tags/redis/"/>
    
  </entry>
  
  <entry>
    <title>【redis从入门到上线(2)】- redis配置要点</title>
    <link href="http://www.hyperxu.com/2017/06/14/20170614-1/"/>
    <id>http://www.hyperxu.com/2017/06/14/20170614-1/</id>
    <published>2017-06-14T10:33:05.000Z</published>
    <updated>2017-06-14T10:37:40.813Z</updated>
    
    <content type="html"><![CDATA[<p>这次我们讲讲redis的一些配置要点，包括日志，持久化，主备，数据压缩，内存分配等，以及一些坑，简单的配置就不说了，可以去看<a href="https://redis.io/topics/config" target="_blank" rel="external">官方文档</a>。</p><a id="more"></a><h3 id="基本配置"><a href="#基本配置" class="headerlink" title="基本配置"></a>基本配置</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">daemonize yes</div></pre></td></tr></table></figure><p>默认情况下，Redis不是在后台运行的，最好在后台运行，把该项的值更改为yes，否则运行时会将运行日志输出到当前终端。</p><hr><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">pidfile /var/run/redis.pid</div></pre></td></tr></table></figure><p>当redis在后台运行的时候，Redis默认会把pid文件放在/var/run/redis.pid，你可以配置到其他地址。当运行多个redis服务时，会用得到，需要指定不同的pid文件和端口。</p><hr><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">port 16379</div></pre></td></tr></table></figure><p>redis的运行端口，务必修改默认端口，尤其是在没有设置密码，且对外网开放时，一般应该也没人开外网。redis的未加密漏洞暴漏在外网会很快被人提权的。</p><hr><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">bind 127.0.0.1</div></pre></td></tr></table></figure><p>官方文档中该处说明bind的是<code>interface</code>，也就是说是网络接口。服务器可以有一个网络接口，或者多个。打个比方说机器上有两个网卡，分别为192.168.205.5 和192.168.205.6，如果bind 192.168.205.5，那么只有该网卡地址接受外部请求，当然也有可能一块网卡上还配置有子网口，上面有两个地址，如果不绑定，则两个网卡口都接受请求。</p><hr><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">timeout 0</div></pre></td></tr></table></figure><p>设置客户端连接时的超时时间，单位为秒。当客户端在这段时间内没有发出任何指令，那么server端关闭该连接。0为关闭该设置。<br>当你碰到有些编码习惯不太好的开发同事，比如连接redis操作完后不关闭，你就会用到timeout设置了，得断掉无操作的连接。（总会有些运维痛点，同行们懂的）<br>当redis为集群架构，前端还有代理时，timeout可能需要根据实际情况来设置，比如保持长连接</p><hr><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">tcp-keepalive 0</div></pre></td></tr></table></figure><p>指定TCP连接是否为长连接,”侦探”信号由server端维护,长连接将会额外的增加server端的开支<br>默认为0.表示禁用,非0值表示开启”长连接” ;”侦探”信号的发送间隔将有Linux系统决定,可以参考Linux tcp连接优化，我后面有空会写一篇相关文章<br>在多次”侦探”后,如果对等端仍不回复,将会关闭连接,否则连接将会被保持开启.<br>client端socket也可以通过配置keepalive选项,开启”长连接”</p><hr><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">databases 16</div></pre></td></tr></table></figure><p>设定redis所允许的最大”db簇”（可以理解为数据库）的个数,默认为16个簇.<br>客户端可以通过”select”指令指定需要使用的”db簇”索引号,默认为0.<br>redis的顶层数据结构中,所有K-V都潜在的包括了”db簇”索引号,任何一个key都将隶属于一个”db”.<br>任何对数据的检索,只会覆盖指定的”db”;例如数据被插入到”db 10″中,那么在”db 1″中去get,将会返回null.<br>对数据归类到不同的db簇中,可以帮助我们实现一些特定的需求,比如根据不同客户端连接,来指定不同的db索引号.</p><hr><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">maxclients 128</div></pre></td></tr></table></figure><p>限制同时连接的客户数量。<br>当连接数超过这个值时，redis 将不再接收其他连接请求，客户端尝试连接时将收到 error 信息</p><p>设置为2时候的会显示一下错误<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">Error: Connection reset by peer</div></pre></td></tr></table></figure></p><hr><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">maxmemory &lt;bytes&gt;</div></pre></td></tr></table></figure><p><strong>内存坑点</strong><br>设置redis能够使用的最大内存。<br>达到最大内存设置后，Redis会先尝试清除已到期或即将到期的Key（设置过expire信息的key，后面会介绍删除方式的算法）<br>生产环境推荐设置最大内存为服务器物理内存大小的3/4，防止生产环境发生内存撑满系统的灾难情况。<br>如果开启了RDB可持久化，最后设定的最大内存最好还要除以2，RDB在转储内存数据到硬盘时，会fork一个redis主程序出来进行数据备份到硬盘，此时可能会造成内存使用的暴涨甚至翻倍，导致内存占满，开始删除生产数据，甚至影响系统运行</p><hr><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">maxmemory-policy volatile-lru</div></pre></td></tr></table></figure><p>当内存达到最大值的时候Redis会选择删除哪些数据？有五种方式可供选择<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">volatile-lru -&gt; 利用LRU算法移除设置过过期时间的key (LRU:最近使用 Least Recently Used )</div><div class="line">allkeys-lru -&gt; 利用LRU算法移除任何key</div><div class="line">volatile-random -&gt; 移除设置过过期时间的随机key</div><div class="line">allkeys-&gt;random -&gt; remove a random key, any key</div><div class="line">volatile-ttl -&gt; 移除即将过期的key(minor TTL)</div><div class="line">noeviction -&gt; 不移除任何可以，只是返回一个写错误</div></pre></td></tr></table></figure></p><blockquote><p><strong>注意</strong>：对于上面的策略，如果没有合适的key可以移除，当写的时候Redis会返回一个错误<br>默认是:maxmemory-policy volatile-lru</p></blockquote><h3 id="日志配置"><a href="#日志配置" class="headerlink" title="日志配置"></a>日志配置</h3><hr><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">syslog-enabled no</div></pre></td></tr></table></figure><p>syslog-enabled设置为yes会把日志输出到系统日志，默认是no</p><p><strong>慢日志记录</strong><br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">slowlog-log-slower-than 10000</div></pre></td></tr></table></figure></p><p>Redis slow log用来记录超过指定执行时间的查询。执行时间不包括I/O计算比如连接客户端，返回结果等，只是命令执行时间<br>可以通过两个参数设置slow log：一个是告诉Redis执行超过多少时间被记录的参数slowlog-log-slower-than(毫秒)，另一个是slow log 的长度。当一个新命令被记录的时候最早的命令将被从队列中移除负数则关闭slow log，0则会导致每个命令都被记录</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">slowlog-max-len</div></pre></td></tr></table></figure><p>对日志长度没有限制，只是要注意它会消耗内存<br>可以通过 <code>SLOWLOG RESET</code>回收被慢日志消耗的内存</p><hr><h3 id="持久化快照"><a href="#持久化快照" class="headerlink" title="持久化快照"></a>持久化快照</h3><h4 id="RDB"><a href="#RDB" class="headerlink" title="RDB"></a>RDB</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">save</div></pre></td></tr></table></figure><p>save ,用来描述”在多少秒期间至少多少个变更操作”触发snapshot<br>snapshot最终将生成新的dump.rdb文件<br>save “”用来禁用snapshot功能<br>例如save 300 1表示5分钟内至少一个key变更,触发snapshot</p><hr><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">rdbcompression yes</div></pre></td></tr></table></figure><p>是否启用rdb文件压缩手段,默认为yes.<br>压缩可能需要额外的cpu开支,不过这能够有效的减小rdb文件的大小,有利于存储/备份/传输/数据恢复.</p><hr><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">rdbchecksum yes</div></pre></td></tr></table></figure><p>是否对rdb文件使用CRC64校验和,默认为”yes”,那么每个rdb文件内容的末尾都会追加CRC校验和.<br>对于其他第三方校验工具,可以很方便的检测文件的完整性</p><hr><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">dbfilename dump.rdb</div></pre></td></tr></table></figure><p>镜像备份文件的文件名</p><p>www@iZ23s8agtagZ:/var/lib/redis$ ls<br>dump.rdb</p><hr><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">dir ./</div></pre></td></tr></table></figure><p>指定rdb/AOF文件的目录位置，只能为文件夹不能为文件</p><hr><h4 id="AOF"><a href="#AOF" class="headerlink" title="AOF"></a>AOF</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">appendonly no</div></pre></td></tr></table></figure><p>默认情况下，Redis会异步的把数据保存到硬盘。如果你的应用场景允许因为系统崩溃等极端情况而导致最新数据丢失的话，那这种做法已经很ok了。<br>否则你应该打开<code>append only</code>模式，开启这种模式后，Redis会在#appendonly.aof<br>文件中添加每一个写操作，这个文件会在Redis启动时被读取来在内存中重新构建数据集。</p><blockquote><p>注意：如果你需要，你可以同时开启‘append only’模式和异步dumps模式（你需要注释掉上面的‘save’表达式来禁#止dumps），这种情况下，Redis重建数据集时会优先使用appendonly.aof而忽略dump.rdb</p></blockquote><hr><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">appendfilename appendonly.aof</div></pre></td></tr></table></figure><p>AOF文件名称 (默认: “appendonly.aof”)</p><hr><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">appendfsync everysec</div></pre></td></tr></table></figure><p>调用fsync()函数通知操作系统立刻向硬盘写数据<br>Redis支持三种同步AOF文件的策略:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"># no:不fsync, 只是通知OS可以flush数据了，具体是否flush取决于OS.性能更好.</div><div class="line"># always: 每次写入append only 日志文件后都会fsync . 性能差，但很安全.</div><div class="line"># everysec: 没间隔1秒进行一次fsync. 折中.</div></pre></td></tr></table></figure></p><p>默认是”everysec”，按照速度和安全折中这是最好的。<br>如果想让Redis能更高效的运行，你也可以设置为”no”，让操作系统决定什么时候去执行或者相反想让数据更安全你也可以设置为”always”</p><p>如果不确定就用 “everysec”.</p><hr><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">no-appendfsync-on-rewrite no</div></pre></td></tr></table></figure><p>AOF策略设置为always或者everysec时，后台处理进程(后台保存或者AOF日志重写)会执行大量的I/O操作<br>在某些linux配置中会阻止过长的fsync()请求。注意现在没有任何修复，即使fsync在另外一个线程进行处理<br>#<br>为了减缓这个问题，可以设置下面这个参数no-appendfsync-on-rewrite</p><hr><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">auto-aof-rewrite-percentage 100</div><div class="line">auto-aof-rewrite-min-size 64mb</div></pre></td></tr></table></figure><p>append only 文件的自动重写<br>当AOF 日志文件即将增长到指定百分比时，Redis可以通过调用BGREWRITEAOF 来自动重写append only文件。<br>它是这么干的：Redis会记住最近一次重写后的AOF 文件size。然后它会把这个size与当前size进行比较，如果当前size比指定的百分比大，就会触发重写。同样，你需要指定AOF文件被重写的最小size，这对避免虽然百分比达到了, 但是实际上文件size还是很小（这种情况没有必要重写）却导致AOF文件重写的情况很有用。</p><hr><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">auto-aof-rewrite-percentage</div></pre></td></tr></table></figure><p>设置为 0 可以关闭AOF重写功能</p><h3 id="数据结构相关设置"><a href="#数据结构相关设置" class="headerlink" title="数据结构相关设置"></a>数据结构相关设置</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">hash-max-zipmap-entries 512</div><div class="line">hash-max-zipmap-value 64</div></pre></td></tr></table></figure><p>redis 2.0后引入了 hash 数据结构。<br>当hash中包含超过指定元素个数并且最大的元素没有超过临界时，<br>hash将以一种特殊的编码方式（大大减少内存使用）来存储，这里可以设置这两个临界值<br>Redis Hash对应Value内部实际就是一个HashMap，实际这里会有2种不同实现，这个Hash的成员比较少时Redis为了节省内存会采用类似一维数组的方式来紧凑存储，而不会采用真正的HashMap结构，对应的value redisObject的encoding为zipmap,当成员数量增大时会自动转成真正的HashMap,此时encoding为ht。</p><hr><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">activerehashing yes</div></pre></td></tr></table></figure><p>是否重置Hash表<br>设置成yes后redis将每100毫秒使用1毫秒CPU时间来对redis的hash表重新hash，可降低内存的使用</p><blockquote><p>当使用场景有较为严格的实时性需求,不能接受Redis时不时的对请求有2毫秒的延迟的话，把这项配置为no。<br>如果没有这么严格的实时性要求,可以设置为 yes,以便能够尽可能快的释放内存</p></blockquote><hr><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">list-max-ziplist-entries 512</div><div class="line">list-max-ziplist-value 64</div></pre></td></tr></table></figure><p>list 数据类型多少节点以下会采用去指针的紧凑存储格式。<br>list 数据类型节点值大小小于多少字节会采用紧凑存储格式。</p><hr><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">set-max-intset-entries 512</div></pre></td></tr></table></figure><p>set数据类型内部数据如果全部是数值型，且包含多少节点以下会采用紧凑格式存储。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">zset-max-ziplist-entries 128</div><div class="line">zset-max-ziplist-value 64</div></pre></td></tr></table></figure><p>zsort 数据类型多少节点以下会采用去指针的紧凑存储格式。<br>zsort 数据类型节点值大小小于多少字节会采用紧凑存储格式。</p><h3 id="Replication-主备"><a href="#Replication-主备" class="headerlink" title="Replication(主备)"></a>Replication(主备)</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">slaveof</div></pre></td></tr></table></figure><p>将当前server做为slave,并为其指定master信息.</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">slaveof &lt;masterip&gt; &lt;masterport&gt;</div></pre></td></tr></table></figure><p>当本机为从服务时，设置主服务的IP及端口</p><hr><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">slave-serve-stale-data yes</div></pre></td></tr></table></figure><p>如果当前server是slave,那么当slave与master失去通讯时,是否继续为客户端提供服务,”yes”表示继续,”no”表示终止.<br>在”yes”情况下,slave继续向客户端提供只读服务,有可能此时的数据已经过期.<br>在”no”情况下,任何向此server发送的数据请求服务(包括客户端和此server的slave)都将被告知”error”，但 INFO 和SLAVEOF命令除外。</p><h3 id="虚拟内存"><a href="#虚拟内存" class="headerlink" title="虚拟内存"></a>虚拟内存</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">vm-enabled no</div></pre></td></tr></table></figure><p>是否开启虚拟内存支持。<br>redis 是一个内存数据库，当内存满时,无法接收新的写请求,所以在redis2.0后,提供了虚拟内存的支持<br>但需要注意的，redis 所有的key都会放在内存中，在内存不够时,只把value 值放入交换区<br>虽使用虚拟内存，但性能基本不受影响，需要注意的是要把vm-max-memory设置到足够来放下所有的key</p><hr><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">vm-swap-file /tmp/redis.swap</div></pre></td></tr></table></figure><p>设置虚拟内存的交换文件路径，<strong>不可多个Redis实例共享</strong></p><hr><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">vm-max-memory 0</div></pre></td></tr></table></figure><p>设置开启虚拟内存后,redis能使用的最大物理内存大小。<br>默认为0，redis将把他所有能放到交换文件的都放到交换文件中，以尽量少的使用物理内存<br>即当vm-max-memory设置为0的时候,其实是所有value都存在于磁盘<br><code>内存够用的前提下，我们还是不要存在交换文件</code><br>在生产环境下,需要根据实际情况设置该值,最好不要使用默认的 0</p><hr><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">vm-page-size 32</div></pre></td></tr></table></figure><p>设置虚拟内存的页面大小<br>视value值大小来定，如果 value 值比较大，比如要在 value 中放置博客、新闻之类的文章内容，就设大一点</p><hr><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">vm-pages 134217728</div></pre></td></tr></table></figure><p>设置交换文件的 page 数量</p><blockquote><p><strong>注意:</strong>  page table信息是放在物理内存中，每8个page 就会占据RAM中的 1 个 byte<br>总的虚拟内存大小 = vm-page-size * vm-pages</p></blockquote><hr><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">vm-max-threads 4</div></pre></td></tr></table></figure><p>设置 VM IO 并发线程数量</p><hr><h4 id="VM设置建议"><a href="#VM设置建议" class="headerlink" title="VM设置建议"></a>VM设置建议</h4><p>一般情况下不建议使用虚拟内存，如果有特殊需求，请考虑以下几种情况：</p><ul><li>当key很小而value很大时,使用VM的效果会比较好.因为这样节约的内存比较大</li><li>当key较大时,可以考虑用一些非常方法将很大的key变成很大的value，如可将key，value组成一个新的value</li><li>最好使用linux ext4 等对稀疏文件支持比较好的文件系统保存你的swap文件</li><li>vm-max-threads参数可设置访问swap文件的线程数，最好不要超过机器的核数；设置为0则所有对swap文件的操作都是串行的，可能会造成比较长时间的延迟,但是对数据完整性有很好的保证</li></ul><h3 id="安全"><a href="#安全" class="headerlink" title="安全"></a>安全</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">rename-command</div></pre></td></tr></table></figure><p>命令重命名.<br>例如:<br>rename-command CONFIG sdfsadfasdfa<br>rename-command CONFIG “”<br>可以把一个命令重命名为空或随机字符来达到取消掉这个命令的功能，让这个命令失效。<br>在多人维护同一个redis时，以及需要开发登陆redis进行操作时，可以屏蔽一些危险命令，提升安全性</p><hr><blockquote><p>下一次讲讲redis集群主备以及架构方面。</p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;这次我们讲讲redis的一些配置要点，包括日志，持久化，主备，数据压缩，内存分配等，以及一些坑，简单的配置就不说了，可以去看&lt;a href=&quot;https://redis.io/topics/config&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;官方文档&lt;/a&gt;。&lt;/p&gt;
    
    </summary>
    
      <category term="redis" scheme="http://www.hyperxu.com/categories/redis/"/>
    
    
      <category term="redis" scheme="http://www.hyperxu.com/tags/redis/"/>
    
  </entry>
  
  <entry>
    <title>【redis从入门到上线(1)】- 初识redis及部署</title>
    <link href="http://www.hyperxu.com/2017/06/03/20170603-1/"/>
    <id>http://www.hyperxu.com/2017/06/03/20170603-1/</id>
    <published>2017-06-03T12:51:20.000Z</published>
    <updated>2017-11-16T04:02:31.885Z</updated>
    
    <content type="html"><![CDATA[<h2 id="redis简介"><a href="#redis简介" class="headerlink" title="redis简介"></a>redis简介</h2><p>Redis是一个开源（BSD许可），内存存储的数据结构服务器，可用作数据库，高速缓存和消息队列代理。它支持字符串、哈希表、列表、集合、有序集合，位图，hyperloglogs等数据类型。内置复制、Lua脚本、LRU收回、事务以及不同级别磁盘持久化功能。<br>如今，互联网业务的数据正以更快的速度在增长，数据类型越来越丰富，这对数据处理的速度和能力提出了更高要求。Redis 是一种开源的内存非关系型数据库，给开发人员带来的体验是颠覆性的。在自始至终的设计过程中，都充分考虑高性能，这使得 Redis 成为当今速度最快的 NoSQL 数据库。</p><a id="more"></a><h3 id="redis特性"><a href="#redis特性" class="headerlink" title="redis特性"></a>redis特性</h3><p>Redis的数据类型：<br>字符串、列表（lists）、集合（sets）、有序集合（sorts sets）、哈希表（hashs）</p><p>Redis和memcache相比的独特之处：</p><ul><li>redis可以用来做存储（storge）、而memcache是来做缓存（cache）。这个特点主要是因为其有“持久化”功能</li><li>存储的数据有“结构”，对于memcache来说，存储的数据，只有一种类型——“字符串”，而redis则可以存储字符串、链表、集合、有序集合、哈希结构</li></ul><p>持久化的两种方式：<br>Redis将数据存储于内存中，或被配置为使用虚拟内存。<br>实现数据持久化的两种方式：</p><ul><li>使用截图的方式，将内存中的数据不断写入磁盘（性能高，但可能会引起一定程度的数据丢失）</li><li>使用类似mysql的方式，记录每次更新的日志</li></ul><p>Redis的主从同步：对提高读取性能非常有益</p><p>Redis服务端的默认端口是6379</p><h2 id="redis安装"><a href="#redis安装" class="headerlink" title="redis安装"></a>redis安装</h2><p>1.官网下载安装包<br>线上环境下载<a href="http://download.redis.io/releases/redis-3.2.9.tar.gz" target="_blank" rel="external">stable版本</a><br><img src="https://uploads.hyperxu.com/f083149c-5c44-44b9-9519-80e84205e83a.png" alt="Alt text"></p><p>2.解压编译安装<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">$ yum -y install gcc tcl//安装可能需要的依赖</div><div class="line">$ tar -zxvf redis-3.2.9.tar.gz</div><div class="line">$ <span class="built_in">cd</span> redis-3.2.9</div><div class="line">$ make</div><div class="line">$ make <span class="built_in">test</span></div><div class="line">$ make install</div></pre></td></tr></table></figure></p><h2 id="redis配置解释"><a href="#redis配置解释" class="headerlink" title="redis配置解释"></a>redis配置解释</h2><blockquote><p><strong>注意：</strong> 此配置仅做解释，请勿随意copy作生产环境配置。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div><div class="line">90</div><div class="line">91</div><div class="line">92</div><div class="line">93</div><div class="line">94</div><div class="line">95</div><div class="line">96</div><div class="line">97</div><div class="line">98</div><div class="line">99</div><div class="line">100</div><div class="line">101</div><div class="line">102</div><div class="line">103</div><div class="line">104</div><div class="line">105</div><div class="line">106</div><div class="line">107</div><div class="line">108</div><div class="line">109</div><div class="line">110</div><div class="line">111</div><div class="line">112</div><div class="line">113</div><div class="line">114</div><div class="line">115</div><div class="line">116</div><div class="line">117</div><div class="line">118</div><div class="line">119</div><div class="line">120</div><div class="line">121</div><div class="line">122</div><div class="line">123</div><div class="line">124</div><div class="line">125</div><div class="line">126</div><div class="line">127</div><div class="line">128</div><div class="line">129</div><div class="line">130</div><div class="line">131</div><div class="line">132</div><div class="line">133</div><div class="line">134</div><div class="line">135</div><div class="line">136</div><div class="line">137</div><div class="line">138</div><div class="line">139</div><div class="line">140</div><div class="line">141</div><div class="line">142</div><div class="line">143</div><div class="line">144</div><div class="line">145</div><div class="line">146</div><div class="line">147</div><div class="line">148</div><div class="line">149</div><div class="line">150</div><div class="line">151</div><div class="line">152</div><div class="line">153</div><div class="line">154</div><div class="line">155</div><div class="line">156</div><div class="line">157</div><div class="line">158</div><div class="line">159</div><div class="line">160</div><div class="line">161</div><div class="line">162</div><div class="line">163</div><div class="line">164</div><div class="line">165</div><div class="line">166</div><div class="line">167</div><div class="line">168</div><div class="line">169</div><div class="line">170</div><div class="line">171</div><div class="line">172</div><div class="line">173</div><div class="line">174</div><div class="line">175</div><div class="line">176</div><div class="line">177</div><div class="line">178</div><div class="line">179</div><div class="line">180</div><div class="line">181</div><div class="line">182</div><div class="line">183</div><div class="line">184</div><div class="line">185</div><div class="line">186</div><div class="line">187</div><div class="line">188</div><div class="line">189</div><div class="line">190</div><div class="line">191</div><div class="line">192</div><div class="line">193</div><div class="line">194</div><div class="line">195</div><div class="line">196</div><div class="line">197</div><div class="line">198</div><div class="line">199</div><div class="line">200</div><div class="line">201</div><div class="line">202</div><div class="line">203</div><div class="line">204</div><div class="line">205</div><div class="line">206</div><div class="line">207</div><div class="line">208</div><div class="line">209</div><div class="line">210</div><div class="line">211</div><div class="line">212</div><div class="line">213</div><div class="line">214</div><div class="line">215</div><div class="line">216</div><div class="line">217</div><div class="line">218</div><div class="line">219</div><div class="line">220</div><div class="line">221</div><div class="line">222</div><div class="line">223</div><div class="line">224</div><div class="line">225</div><div class="line">226</div><div class="line">227</div><div class="line">228</div><div class="line">229</div><div class="line">230</div><div class="line">231</div><div class="line">232</div><div class="line">233</div><div class="line">234</div><div class="line">235</div><div class="line">236</div><div class="line">237</div><div class="line">238</div><div class="line">239</div><div class="line">240</div><div class="line">241</div><div class="line">242</div><div class="line">243</div><div class="line">244</div><div class="line">245</div><div class="line">246</div><div class="line">247</div><div class="line">248</div><div class="line">249</div><div class="line">250</div><div class="line">251</div><div class="line">252</div><div class="line">253</div><div class="line">254</div><div class="line">255</div><div class="line">256</div><div class="line">257</div><div class="line">258</div><div class="line">259</div><div class="line">260</div><div class="line">261</div><div class="line">262</div><div class="line">263</div><div class="line">264</div><div class="line">265</div><div class="line">266</div><div class="line">267</div><div class="line">268</div><div class="line">269</div><div class="line">270</div><div class="line">271</div><div class="line">272</div><div class="line">273</div><div class="line">274</div><div class="line">275</div><div class="line">276</div><div class="line">277</div><div class="line">278</div><div class="line">279</div><div class="line">280</div><div class="line">281</div><div class="line">282</div><div class="line">283</div><div class="line">284</div><div class="line">285</div><div class="line">286</div><div class="line">287</div><div class="line">288</div><div class="line">289</div><div class="line">290</div><div class="line">291</div><div class="line">292</div><div class="line">293</div><div class="line">294</div><div class="line">295</div><div class="line">296</div><div class="line">297</div><div class="line">298</div><div class="line">299</div><div class="line">300</div><div class="line">301</div><div class="line">302</div><div class="line">303</div><div class="line">304</div><div class="line">305</div><div class="line">306</div><div class="line">307</div><div class="line">308</div><div class="line">309</div><div class="line">310</div><div class="line">311</div><div class="line">312</div><div class="line">313</div><div class="line">314</div><div class="line">315</div><div class="line">316</div><div class="line">317</div><div class="line">318</div><div class="line">319</div><div class="line">320</div><div class="line">321</div><div class="line">322</div><div class="line">323</div><div class="line">324</div><div class="line">325</div><div class="line">326</div><div class="line">327</div><div class="line">328</div><div class="line">329</div><div class="line">330</div><div class="line">331</div><div class="line">332</div><div class="line">333</div><div class="line">334</div><div class="line">335</div><div class="line">336</div><div class="line">337</div><div class="line">338</div><div class="line">339</div><div class="line">340</div><div class="line">341</div><div class="line">342</div><div class="line">343</div><div class="line">344</div><div class="line">345</div><div class="line">346</div><div class="line">347</div><div class="line">348</div><div class="line">349</div><div class="line">350</div><div class="line">351</div><div class="line">352</div><div class="line">353</div><div class="line">354</div><div class="line">355</div><div class="line">356</div><div class="line">357</div><div class="line">358</div><div class="line">359</div><div class="line">360</div><div class="line">361</div><div class="line">362</div><div class="line">363</div><div class="line">364</div><div class="line">365</div><div class="line">366</div><div class="line">367</div><div class="line">368</div><div class="line">369</div><div class="line">370</div><div class="line">371</div><div class="line">372</div><div class="line">373</div><div class="line">374</div><div class="line">375</div><div class="line">376</div><div class="line">377</div><div class="line">378</div></pre></td><td class="code"><pre><div class="line"><span class="comment"># Redis 配置文件</span></div><div class="line"></div><div class="line"><span class="comment"># 当配置中需要配置内存大小时，可以使用 1k, 5GB, 4M 等类似的格式，其转换方式如下(不区分大小写)</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># 1k =&gt; 1000 bytes</span></div><div class="line"><span class="comment"># 1kb =&gt; 1024 bytes</span></div><div class="line"><span class="comment"># 1m =&gt; 1000000 bytes</span></div><div class="line"><span class="comment"># 1mb =&gt; 1024*1024 bytes</span></div><div class="line"><span class="comment"># 1g =&gt; 1000000000 bytes</span></div><div class="line"><span class="comment"># 1gb =&gt; 1024*1024*1024 bytes</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># 内存配置大小写是一样的.比如 1gb 1Gb 1GB 1gB</span></div><div class="line"></div><div class="line"><span class="comment"># daemonize no 默认情况下，redis不是在后台运行的，如果需要在后台运行，把该项的值更改为yes</span></div><div class="line">daemonize yes</div><div class="line"><span class="comment"># 当redis在后台运行的时候，Redis默认会把pid文件放在/var/run/redis.pid，你可以配置到其他地址。</span></div><div class="line"><span class="comment"># 当运行多个redis服务时，需要指定不同的pid文件和端口</span></div><div class="line">pidfile /var/run/redis.pid</div><div class="line"></div><div class="line"><span class="comment"># 指定redis运行的端口，默认是6379</span></div><div class="line">port 6379</div><div class="line"></div><div class="line"><span class="comment"># 指定redis只接收来自于该IP地址的请求，如果不进行设置，那么将处理所有请求，</span></div><div class="line"><span class="comment"># 在生产环境中最好设置该项</span></div><div class="line"><span class="built_in">bind</span> 127.0.0.1</div><div class="line"></div><div class="line"><span class="comment"># Specify the path for the unix socket that will be used to listen for</span></div><div class="line"><span class="comment"># incoming connections. There is no default, so Redis will not listen</span></div><div class="line"><span class="comment"># on a unix socket when not specified.</span></div><div class="line"><span class="comment"># unixsocket /tmp/redis.sock</span></div><div class="line"><span class="comment"># </span></div><div class="line">unixsocketperm 755</div><div class="line"></div><div class="line"><span class="comment"># 设置客户端连接时的超时时间，单位为秒。当客户端在这段时间内没有发出任何指令，那么关闭该连接</span></div><div class="line"><span class="comment"># 0是关闭此设置</span></div><div class="line">timeout 0</div><div class="line"></div><div class="line"><span class="comment"># 指定日志记录级别</span></div><div class="line"><span class="comment"># Redis总共支持四个级别：debug、verbose、notice、warning，默认为verbose</span></div><div class="line"><span class="comment"># </span></div><div class="line">debug  记录很多信息，用于开发和测试</div><div class="line"><span class="comment"># varbose 有用的信息，不像debug会记录那么多</span></div><div class="line"><span class="comment"># notice 普通的verbose，常用于生产环境</span></div><div class="line"><span class="comment"># warning 只有非常重要或者严重的信息会记录到日志</span></div><div class="line">loglevel debug</div><div class="line"></div><div class="line"><span class="comment"># 配置log文件地址</span></div><div class="line"><span class="comment"># 默认值为stdout，标准输出，若后台模式会输出到/dev/null</span></div><div class="line"><span class="comment">#logfile stdout</span></div><div class="line">logfile /var/<span class="built_in">log</span>/redis/redis.log</div><div class="line"></div><div class="line"><span class="comment"># To enable logging to the system logger, just set 'syslog-enabled' to yes,</span></div><div class="line"><span class="comment"># and optionally update the other syslog parameters to suit your needs.</span></div><div class="line"><span class="comment"># syslog-enabled no</span></div><div class="line"></div><div class="line"><span class="comment"># Specify the syslog identity.</span></div><div class="line"><span class="comment"># syslog-ident redis</span></div><div class="line"></div><div class="line"><span class="comment"># Specify the syslog facility.  Must be USER or between LOCAL0-LOCAL7.</span></div><div class="line"><span class="comment"># syslog-facility local0</span></div><div class="line"></div><div class="line"><span class="comment"># 可用数据库数</span></div><div class="line"><span class="comment"># 默认值为16，默认数据库为0，数据库范围在0-（database-1）之间</span></div><div class="line">databases 16</div><div class="line"></div><div class="line"><span class="comment">################################ 快照  #################################</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># 保存数据到磁盘，格式如下:</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment">#   save &lt;seconds&gt; &lt;changes&gt;</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment">#   指出在多长时间内，有多少次更新操作，就将数据同步到数据文件rdb。</span></div><div class="line"><span class="comment">#   相当于条件触发抓取快照，这个可以多个条件配合</span></div><div class="line"><span class="comment">#   </span></div><div class="line"><span class="comment">#   比如默认配置文件中的设置，就设置了三个条件</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment">#   save 900 1  900秒内至少有1个key被改变</span></div><div class="line"><span class="comment">#   save 300 10  300秒内至少有300个key被改变</span></div><div class="line"><span class="comment">#   save 60 10000  60秒内至少有10000个key被改变</span></div><div class="line"></div><div class="line">save 900 1</div><div class="line">save 300 10</div><div class="line">save 60 10000</div><div class="line"></div><div class="line"><span class="comment"># 存储至本地数据库时（持久化到rdb文件）是否压缩数据，默认为yes</span></div><div class="line">rdbcompression yes</div><div class="line"></div><div class="line"><span class="comment"># 本地持久化数据库文件名，默认值为dump.rdb</span></div><div class="line">dbfilename dump.rdb</div><div class="line"></div><div class="line"><span class="comment"># 工作目录</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># 数据库镜像备份的文件放置的路径。</span></div><div class="line"><span class="comment"># 这里的路径跟文件名要分开配置是因为redis在进行备份时，先会将当前数据库的状态写入到一个临时文件中，等备份完成时，</span></div><div class="line"><span class="comment"># 再把该该临时文件替换为上面所指定的文件，而这里的临时文件和上面所配置的备份文件都会放在这个指定的路径当中。</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># AOF文件也会存放在这个目录下面</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># 注意这里必须制定一个目录而不是文件</span></div><div class="line">dir ./</div><div class="line"></div><div class="line"><span class="comment">################################# 复制 #################################</span></div><div class="line"><span class="comment"># 主从复制. 设置该数据库为其他数据库的从数据库.</span></div><div class="line"><span class="comment"># 设置当本机为slav服务时，设置master服务的IP地址及端口，在Redis启动时，它会自动从master进行数据同步</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># slaveof &lt;masterip&gt; &lt;masterport&gt;</span></div><div class="line"></div><div class="line"><span class="comment"># 当master服务设置了密码保护时(用requirepass制定的密码)</span></div><div class="line"><span class="comment"># slave服务连接master的密码</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># masterauth &lt;master-password&gt;</span></div><div class="line"></div><div class="line"></div><div class="line"><span class="comment"># 当从库同主机失去连接或者复制正在进行，从机库有两种运行方式：</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># 1) 如果slave-serve-stale-data设置为yes(默认设置)，从库会继续响应客户端的请求</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># 2) 如果slave-serve-stale-data是指为no，除去INFO和SLAVOF命令之外的任何请求都会返回一个</span></div><div class="line"><span class="comment">#    错误"SYNC with master in progress"</span></div><div class="line"><span class="comment">#</span></div><div class="line">slave-serve-stale-data yes</div><div class="line"></div><div class="line"><span class="comment"># 从库会按照一个时间间隔向主库发送PINGs.可以通过repl-ping-slave-period设置这个时间间隔，默认是10秒</span></div><div class="line"><span class="comment"># </span></div><div class="line">repl-ping-slave-period 10</div><div class="line"></div><div class="line"><span class="comment"># repl-timeout 设置主库批量数据传输时间或者ping回复时间间隔，默认值是60秒</span></div><div class="line"><span class="comment"># </span></div><div class="line">一定要确保repl-timeout大于repl-ping-slave-period</div><div class="line"><span class="comment"># repl-timeout 60</span></div><div class="line"></div><div class="line"><span class="comment">################################## 安全 ###################################</span></div><div class="line"></div><div class="line"><span class="comment"># 设置客户端连接后进行任何其他指定前需要使用的密码。</span></div><div class="line"><span class="comment"># 警告：因为redis速度相当快，所以在一台比较好的服务器下，一个外部的用户可以在一秒钟进行150K次的密码尝试，这意味着你需要指定非常非常强大的密码来防止暴力破解</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># </span></div><div class="line">requirepass foobared</div><div class="line"></div><div class="line"><span class="comment"># 命令重命名.</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># 在一个共享环境下可以重命名相对危险的命令。比如把CONFIG重名为一个不容易猜测的字符。</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># </span></div><div class="line">举例:</div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># </span></div><div class="line">如果想删除一个命令，直接把它重命名为一个空字符<span class="string">""</span>即可，如下：</div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># rename-command CONFIG ""</span></div><div class="line"></div><div class="line"><span class="comment">################################### 约束 ####################################</span></div><div class="line"></div><div class="line"><span class="comment"># 设置同一时间最大客户端连接数，默认无限制，Redis可以同时打开的客户端连接数为Redis进程可以打开的最大文件描述符数，</span></div><div class="line"><span class="comment"># 如果设置 maxclients 0，表示不作限制。</span></div><div class="line"><span class="comment"># 当客户端连接数到达限制时，Redis会关闭新的连接并向客户端返回max number of clients reached错误信息</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># maxclients 128</span></div><div class="line"></div><div class="line"><span class="comment"># 指定Redis最大内存限制，Redis在启动时会把数据加载到内存中，达到最大内存后，Redis会先尝试清除已到期或即将到期的Key</span></div><div class="line"><span class="comment"># Redis同时也会移除空的list对象</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># 当此方法处理后，仍然到达最大内存设置，将无法再进行写入操作，但仍然可以进行读取操作</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># 注意：Redis新的vm机制，会把Key存放内存，Value会存放在swap区</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># maxmemory的设置比较适合于把redis当作于类似memcached的缓存来使用，而不适合当做一个真实的DB。</span></div><div class="line"><span class="comment"># 当把Redis当做一个真实的数据库使用的时候，内存使用将是一个很大的开销</span></div><div class="line"><span class="comment"># maxmemory &lt;bytes&gt;</span></div><div class="line"></div><div class="line"><span class="comment"># 当内存达到最大值的时候Redis会选择删除哪些数据？有五种方式可供选择</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># volatile-lru -&gt; 利用LRU算法移除设置过过期时间的key (LRU:最近使用 Least Recently Used )</span></div><div class="line"><span class="comment"># allkeys-lru -&gt; 利用LRU算法移除任何key</span></div><div class="line"><span class="comment"># volatile-random -&gt; 移除设置过过期时间的随机key</span></div><div class="line"><span class="comment"># allkeys-&gt;random -&gt; remove a random key, any key</span></div><div class="line"><span class="comment"># volatile-ttl -&gt; 移除即将过期的key(minor TTL)</span></div><div class="line"><span class="comment"># noeviction -&gt; 不移除任何可以，只是返回一个写错误</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># 注意：对于上面的策略，如果没有合适的key可以移除，当写的时候Redis会返回一个错误</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment">#       写命令包括: set setnx setex append</span></div><div class="line"><span class="comment">#       incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd</span></div><div class="line"><span class="comment">#       sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby</span></div><div class="line"><span class="comment">#       zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby</span></div><div class="line"><span class="comment">#       getset mset msetnx exec sort</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># 默认是:</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># maxmemory-policy volatile-lru</span></div><div class="line"></div><div class="line"><span class="comment"># LRU 和 minimal TTL 算法都不是精准的算法，但是相对精确的算法(为了节省内存)，随意你可以选择样本大小进行检测。</span></div><div class="line"><span class="comment"># Redis默认的灰选择3个样本进行检测，你可以通过maxmemory-samples进行设置</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># maxmemory-samples 3</span></div><div class="line"></div><div class="line"><span class="comment">############################## AOF ###############################</span></div><div class="line"></div><div class="line"></div><div class="line"><span class="comment"># 默认情况下，redis会在后台异步的把数据库镜像备份到磁盘，但是该备份是非常耗时的，而且备份也不能很频繁，如果发生诸如拉闸限电、拔插头等状况，那么将造成比较大范围的数据丢失。</span></div><div class="line"><span class="comment"># 所以redis提供了另外一种更加高效的数据库备份及灾难恢复方式。</span></div><div class="line"><span class="comment"># 开启append only模式之后，redis会把所接收到的每一次写操作请求都追加到appendonly.aof文件中，当redis重新启动时，会从该文件恢复出之前的状态。</span></div><div class="line"><span class="comment"># 但是这样会造成appendonly.aof文件过大，所以redis还支持了BGREWRITEAOF指令，对appendonly.aof 进行重新整理。</span></div><div class="line"><span class="comment"># 你可以同时开启asynchronous dumps 和 AOF</span></div><div class="line">appendonly no</div><div class="line"></div><div class="line"><span class="comment"># AOF文件名称 (默认: "appendonly.aof")</span></div><div class="line"><span class="comment"># appendfilename appendonly.aof</span></div><div class="line"></div><div class="line"><span class="comment"># Redis支持三种同步AOF文件的策略:</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># no: 不进行同步，系统去操作 . Faster.</span></div><div class="line"><span class="comment"># always: always表示每次有写操作都进行同步. Slow, Safest.</span></div><div class="line"><span class="comment"># everysec: 表示对写操作进行累积，每秒同步一次. Compromise.</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># 默认是"everysec"，按照速度和安全折中这是最好的。</span></div><div class="line"><span class="comment"># 如果想让Redis能更高效的运行，你也可以设置为"no"，让操作系统决定什么时候去执行</span></div><div class="line"><span class="comment"># 或者相反想让数据更安全你也可以设置为"always"</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># 如果不确定就用 "everysec".</span></div><div class="line"></div><div class="line"><span class="comment"># appendfsync always</span></div><div class="line">appendfsync everysec</div><div class="line"><span class="comment"># appendfsync no</span></div><div class="line"></div><div class="line"><span class="comment"># AOF策略设置为always或者everysec时，后台处理进程(后台保存或者AOF日志重写)会执行大量的I/O操作</span></div><div class="line"><span class="comment"># 在某些Linux配置中会阻止过长的fsync()请求。注意现在没有任何修复，即使fsync在另外一个线程进行处理</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># 为了减缓这个问题，可以设置下面这个参数no-appendfsync-on-rewrite</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># This means that while another child is saving the durability of Redis is</span></div><div class="line"><span class="comment"># the same as "appendfsync none", that in pratical terms means that it is</span></div><div class="line"><span class="comment"># possible to lost up to 30 seconds of log in the worst scenario (with the</span></div><div class="line"><span class="comment"># default Linux settings).</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># If you have latency problems turn this to "yes". Otherwise leave it as</span></div><div class="line"><span class="comment"># "no" that is the safest pick from the point of view of durability.no-appendfsync-on-rewrite no</span></div><div class="line"></div><div class="line"><span class="comment"># Automatic rewrite of the append only file.</span></div><div class="line"><span class="comment"># AOF 自动重写</span></div><div class="line"><span class="comment"># 当AOF文件增长到一定大小的时候Redis能够调用 BGREWRITEAOF 对日志文件进行重写</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># 它是这样工作的：Redis会记住上次进行些日志后文件的大小(如果从开机以来还没进行过重写，那日子大小在开机的时候确定)</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># 基础大小会同现在的大小进行比较。如果现在的大小比基础大小大制定的百分比，重写功能将启动</span></div><div class="line"><span class="comment"># 同时需要指定一个最小大小用于AOF重写，这个用于阻止即使文件很小但是增长幅度很大也去重写AOF文件的情况</span></div><div class="line"><span class="comment"># 设置 percentage 为0就关闭这个特性</span></div><div class="line"></div><div class="line">auto-aof-rewrite-percentage 100</div><div class="line">auto-aof-rewrite-min-size 64mb</div><div class="line"></div><div class="line"><span class="comment">################################## SLOW LOG ###################################</span></div><div class="line"></div><div class="line"><span class="comment"># Redis Slow Log 记录超过特定执行时间的命令。执行时间不包括I/O计算比如连接客户端，返回结果等，只是命令执行时间</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># 可以通过两个参数设置slow log：一个是告诉Redis执行超过多少时间被记录的参数slowlog-log-slower-than(微妙)，</span></div><div class="line"><span class="comment"># 另一个是slow log 的长度。当一个新命令被记录的时候最早的命令将被从队列中移除</span></div><div class="line"></div><div class="line"><span class="comment"># 下面的时间以微妙微单位，因此1000000代表一分钟。</span></div><div class="line"><span class="comment"># 注意制定一个负数将关闭慢日志，而设置为0将强制每个命令都会记录</span></div><div class="line">slowlog-log-slower-than 10000</div><div class="line"></div><div class="line"><span class="comment"># 对日志长度没有限制，只是要注意它会消耗内存</span></div><div class="line"><span class="comment"># 可以通过 SLOWLOG RESET 回收被慢日志消耗的内存</span></div><div class="line">slowlog-max-len 1024</div><div class="line"></div><div class="line"><span class="comment">################################ VM ###############################</span></div><div class="line"></div><div class="line"><span class="comment">### WARNING! Virtual Memory is deprecated in Redis 2.4</span></div><div class="line"><span class="comment">### The use of Virtual Memory is strongly discouraged.</span></div><div class="line"></div><div class="line"><span class="comment"># Virtual Memory allows Redis to work with datasets bigger than the actual</span></div><div class="line"><span class="comment"># amount of RAM needed to hold the whole dataset in memory.</span></div><div class="line"><span class="comment"># In order to do so very used keys are taken in memory while the other keys</span></div><div class="line"><span class="comment"># are swapped into a swap file, similarly to what operating systems do</span></div><div class="line"><span class="comment"># with memory pages.</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># To enable VM just set 'vm-enabled' to yes, and set the following three</span></div><div class="line"><span class="comment"># VM parameters accordingly to your needs.</span></div><div class="line"></div><div class="line">vm-enabled no</div><div class="line"><span class="comment"># vm-enabled yes</span></div><div class="line"></div><div class="line"><span class="comment"># This is the path of the Redis swap file. As you can guess, swap files</span></div><div class="line"><span class="comment"># can't be shared by different Redis instances, so make sure to use a swap</span></div><div class="line"><span class="comment"># file for every redis process you are running. Redis will complain if the</span></div><div class="line"><span class="comment"># swap file is already in use.</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># The best kind of storage for the Redis swap file (that's accessed at random)</span></div><div class="line"><span class="comment"># is a Solid State Disk (SSD).</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># *** WARNING *** if you are using a shared hosting the default of putting</span></div><div class="line"><span class="comment"># the swap file under /tmp is not secure. Create a dir with access granted</span></div><div class="line"><span class="comment"># only to Redis user and configure Redis to create the swap file there.</span></div><div class="line">vm-swap-file /tmp/redis.swap</div><div class="line"></div><div class="line"><span class="comment"># vm-max-memory configures the VM to use at max the specified amount of</span></div><div class="line"><span class="comment"># RAM. Everything that deos not fit will be swapped on disk *if* possible, that</span></div><div class="line"><span class="comment"># is, if there is still enough contiguous space in the swap file.</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># With vm-max-memory 0 the system will swap everything it can. Not a good</span></div><div class="line"><span class="comment"># default, just specify the max amount of RAM you can in bytes, but it's</span></div><div class="line"><span class="comment"># better to leave some margin. For instance specify an amount of RAM</span></div><div class="line"><span class="comment"># that's more or less between 60 and 80% of your free RAM.</span></div><div class="line">vm-max-memory 0</div><div class="line"></div><div class="line"><span class="comment"># Redis swap files is split into pages. An object can be saved using multiple</span></div><div class="line"><span class="comment"># contiguous pages, but pages can't be shared between different objects.</span></div><div class="line"><span class="comment"># So if your page is too big, small objects swapped out on disk will waste</span></div><div class="line"><span class="comment"># a lot of space. If you page is too small, there is less space in the swap</span></div><div class="line"><span class="comment"># file (assuming you configured the same number of total swap file pages).</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># If you use a lot of small objects, use a page size of 64 or 32 bytes.</span></div><div class="line"><span class="comment"># If you use a lot of big objects, use a bigger page size.</span></div><div class="line"><span class="comment"># If unsure, use the default :)</span></div><div class="line">vm-page-size 32</div><div class="line"></div><div class="line"><span class="comment"># Number of total memory pages in the swap file.</span></div><div class="line"><span class="comment"># Given that the page table (a bitmap of free/used pages) is taken in memory,</span></div><div class="line"><span class="comment"># every 8 pages on disk will consume 1 byte of RAM.</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># The total swap size is vm-page-size * vm-pages</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># With the default of 32-bytes memory pages and 134217728 pages Redis will</span></div><div class="line"><span class="comment"># use a 4 GB swap file, that will use 16 MB of RAM for the page table.</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># It's better to use the smallest acceptable value for your application,</span></div><div class="line"><span class="comment"># but the default is large in order to work in most conditions.</span></div><div class="line">vm-pages 134217728</div><div class="line"></div><div class="line"><span class="comment"># Max number of VM I/O threads running at the same time.</span></div><div class="line"><span class="comment"># This threads are used to read/write data from/to swap file, since they</span></div><div class="line"><span class="comment"># also encode and decode objects from disk to memory or the reverse, a bigger</span></div><div class="line"><span class="comment"># number of threads can help with big objects even if they can't help with</span></div><div class="line"><span class="comment"># I/O itself as the physical device may not be able to couple with many</span></div><div class="line"><span class="comment"># reads/writes operations at the same time.</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># The special value of 0 turn off threaded I/O and enables the blocking</span></div><div class="line"><span class="comment"># Virtual Memory implementation.</span></div><div class="line">vm-max-threads 4</div><div class="line"></div><div class="line"><span class="comment">############################### ADVANCED CONFIG ###############################</span></div><div class="line"><span class="comment"># 当hash中包含超过指定元素个数并且最大的元素没有超过临界时，</span></div><div class="line"><span class="comment"># hash将以一种特殊的编码方式（大大减少内存使用）来存储，这里可以设置这两个临界值</span></div><div class="line"><span class="comment"># Redis Hash对应Value内部实际就是一个HashMap，实际这里会有2种不同实现，</span></div><div class="line"><span class="comment"># 这个Hash的成员比较少时Redis为了节省内存会采用类似一维数组的方式来紧凑存储，而不会采用真正的HashMap结构，对应的value </span></div><div class="line"><span class="comment"># redisObject的encoding为zipmap,</span></div><div class="line"><span class="comment"># 当成员数量增大时会自动转成真正的HashMap,此时encoding为ht。</span></div><div class="line"><span class="built_in">hash</span>-max-zipmap-entries 512</div><div class="line"><span class="built_in">hash</span>-max-zipmap-value 64</div><div class="line"></div><div class="line"><span class="comment"># list数据类型多少节点以下会采用去指针的紧凑存储格式。</span></div><div class="line"><span class="comment"># list数据类型节点值大小小于多少字节会采用紧凑存储格式。</span></div><div class="line">list-max-ziplist-entries 512</div><div class="line">list-max-ziplist-value 64</div><div class="line"></div><div class="line"><span class="comment"># set数据类型内部数据如果全部是数值型，且包含多少节点以下会采用紧凑格式存储。</span></div><div class="line"><span class="built_in">set</span>-max-intset-entries 512</div><div class="line"></div><div class="line"><span class="comment"># zsort数据类型多少节点以下会采用去指针的紧凑存储格式。</span></div><div class="line"><span class="comment"># zsort数据类型节点值大小小于多少字节会采用紧凑存储格式。</span></div><div class="line">zset-max-ziplist-entries 128</div><div class="line">zset-max-ziplist-value 64</div><div class="line"></div><div class="line"><span class="comment"># Redis将在每100毫秒时使用1毫秒的CPU时间来对redis的hash表进行重新hash，可以降低内存的使用</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># 当你的使用场景中，有非常严格的实时性需要，不能够接受Redis时不时的对请求有2毫秒的延迟的话，把这项配置为no。</span></div><div class="line"><span class="comment">#</span></div><div class="line"><span class="comment"># 如果没有这么严格的实时性要求，可以设置为yes，以便能够尽可能快的释放内存</span></div><div class="line">activerehashing yes</div><div class="line"></div><div class="line"><span class="comment">################################## INCLUDES ###################################</span></div><div class="line"></div><div class="line"><span class="comment"># 指定包含其它的配置文件，可以在同一主机上多个Redis实例之间使用同一份配置文件，而同时各个实例又拥有自己的特定配置文件</span></div><div class="line"><span class="comment"># include /path/to/local.conf</span></div><div class="line"><span class="comment"># include /path/to/other.conf</span></div></pre></td></tr></table></figure></p></blockquote><h2 id="redis运行"><a href="#redis运行" class="headerlink" title="redis运行"></a>redis运行</h2><h3 id="启动"><a href="#启动" class="headerlink" title="启动"></a>启动</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div></pre></td><td class="code"><pre><div class="line">/usr/<span class="built_in">local</span>/redis/bin/redis-server /usr/<span class="built_in">local</span>/redis/etc/redis.conf &amp;</div><div class="line"></div><div class="line">81071:M 12 Nov 23:23:13.691 <span class="comment"># You requested maxclients of 10000 requiring at least 10032 max file descriptors.</span></div><div class="line">81071:M 12 Nov 23:23:13.691 <span class="comment"># Redis can't set maximum open files to 10032 because of OS error: Operation not permitted.</span></div><div class="line">81071:M 12 Nov 23:23:13.691 <span class="comment"># Current maximum open files is 4096. maxclients has been reduced to 4064 to compensate for low ulimit. If you need higher maxclients increase 'ulimit -n'.</span></div><div class="line">                _._</div><div class="line">           _.-``__ <span class="string">''</span>-._</div><div class="line">      _.-``    `.  `_.  <span class="string">''</span>-._           Redis 3.0.3 (00000000/0) 64 bit</div><div class="line">  .-`` .-```.  ```\/    _.,_ <span class="string">''</span>-._</div><div class="line"> (    <span class="string">'      ,       .-`  | `,    )     Running in standalone mode</span></div><div class="line"> |`-._`-...-` __...-.``-._|'` _.-<span class="string">'|     Port: 6379</span></div><div class="line"> |    `-._   `._    /     _.-'    |     PID: 81071</div><div class="line">  `-._    `-._  `-./  _.-<span class="string">'    _.-'</span></div><div class="line"> |`-._`-._    `-.__.-<span class="string">'    _.-'</span>_.-<span class="string">'|</span></div><div class="line"> |    `-._`-._        _.-'_.-<span class="string">'    |           http://redis.io</span></div><div class="line">  `-._    `-._`-.__.-'_.-<span class="string">'    _.-'</span></div><div class="line"> |`-._`-._    `-.__.-<span class="string">'    _.-'</span>_.-<span class="string">'|</span></div><div class="line"> |    `-._`-._        _.-'_.-<span class="string">'    |</span></div><div class="line">  `-._    `-._`-.__.-'_.-<span class="string">'    _.-'</span></div><div class="line">      `-._    `-.__.-<span class="string">'    _.-'</span></div><div class="line">          `-._        _.-<span class="string">'</span></div><div class="line">              `-.__.-'</div><div class="line"></div><div class="line">81071:M 12 Nov 23:23:13.694 <span class="comment"># Server started, Redis version 3.0.3</span></div></pre></td></tr></table></figure><h3 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">[root@localhost redis]<span class="comment"># cd /usr/local/redis/bin  </span></div><div class="line">[root@localhost bin]<span class="comment"># ./redis-cli  </span></div><div class="line">127.0.0.1:6379&gt; <span class="built_in">set</span> foo bar  </div><div class="line">OK  </div><div class="line">127.0.0.1:6379&gt; get foo  </div><div class="line"><span class="string">"bar"</span>  </div><div class="line">127.0.0.1:6379&gt;</div></pre></td></tr></table></figure><hr><blockquote><p>下一节讲下生产环境的配置要点与经验好了。</p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;redis简介&quot;&gt;&lt;a href=&quot;#redis简介&quot; class=&quot;headerlink&quot; title=&quot;redis简介&quot;&gt;&lt;/a&gt;redis简介&lt;/h2&gt;&lt;p&gt;Redis是一个开源（BSD许可），内存存储的数据结构服务器，可用作数据库，高速缓存和消息队列代理。它支持字符串、哈希表、列表、集合、有序集合，位图，hyperloglogs等数据类型。内置复制、Lua脚本、LRU收回、事务以及不同级别磁盘持久化功能。&lt;br&gt;如今，互联网业务的数据正以更快的速度在增长，数据类型越来越丰富，这对数据处理的速度和能力提出了更高要求。Redis 是一种开源的内存非关系型数据库，给开发人员带来的体验是颠覆性的。在自始至终的设计过程中，都充分考虑高性能，这使得 Redis 成为当今速度最快的 NoSQL 数据库。&lt;/p&gt;
    
    </summary>
    
      <category term="redis" scheme="http://www.hyperxu.com/categories/redis/"/>
    
    
      <category term="redis" scheme="http://www.hyperxu.com/tags/redis/"/>
    
  </entry>
  
  <entry>
    <title>简单python爬虫程序(爬取百度贴吧帖子)</title>
    <link href="http://www.hyperxu.com/2017/05/20/20170520-1/"/>
    <id>http://www.hyperxu.com/2017/05/20/20170520-1/</id>
    <published>2017-05-20T08:50:04.000Z</published>
    <updated>2017-05-20T14:56:30.531Z</updated>
    
    <content type="html"><![CDATA[<h3 id="1-URL地址分析"><a href="#1-URL地址分析" class="headerlink" title="1.URL地址分析"></a>1.URL地址分析</h3><p>选取的一个百度贴吧帖子的网址是<a href="http://tieba.baidu.com/p/4739169817，各位可以点击进去查看一下。" target="_blank" rel="external">http://tieba.baidu.com/p/4739169817，各位可以点击进去查看一下。</a><br>如果点击只看楼主或者翻页，则会产生两个参数<a href="http://tieba.baidu.com/p/4739169817?pn=2或http://tieba.baidu.com/p/4739169817?see_lz=1，分析一下该网址如下：" target="_blank" rel="external">http://tieba.baidu.com/p/4739169817?pn=2或http://tieba.baidu.com/p/4739169817?see_lz=1，分析一下该网址如下：</a></p><ul><li><a href="http://tieba.baidu.com/p/4739169817?see_lz=1" target="_blank" rel="external">http://tieba.baidu.com/p/4739169817?see_lz=1</a> 这是网址</li><li>see_lz 该参数表示是否只看楼主发的帖子，1表示true</li><li>pn 该参数表示表示第几页</li></ul><a id="more"></a> <h3 id="2-页面爬取"><a href="#2-页面爬取" class="headerlink" title="2.页面爬取"></a>2.页面爬取</h3><p>python2.7版本，代码如下：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line">#coding=utf-8</div><div class="line">__author__ = &apos;xuzhengxi&apos;</div><div class="line">import sys </div><div class="line">sys.path.append(&quot;/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/&quot;)</div><div class="line">import urllib2 </div><div class="line">import string</div><div class="line"> def baidu_tieba(url,begin_page,end_page):</div><div class="line"> for i in range(begin_page,end_page+1): </div><div class="line"> sName = string.zfill(i,5) + &apos;.html&apos; </div><div class="line"> print &apos;正在下载第&apos; + str(i) + &apos;个网页，并将其存储为&apos; + sName + &apos;......&apos;   </div><div class="line">   f = open(sName,&apos;w+&apos;)</div><div class="line"> m = urllib2.urlopen(url,str(i)).read()  </div><div class="line"> f.write(m)</div><div class="line"> f.close()</div><div class="line">bdurl = str(raw_input(u&apos;请输入贴吧地址，去掉pn=后面的数字: \n&apos;))</div><div class="line"> begin_page = int(raw_input(u&apos;请输入开始页数： \n&apos;)) </div><div class="line">end_page = int(raw_input(u&apos;请输入终止页数： \n&apos;)) </div><div class="line">baidu_tieba(bdurl,begin_page,end_page)</div></pre></td></tr></table></figure></p><blockquote><p><strong>注意:</strong>　utf-8编码声明，否则容易出现乱码问题。此demo仅使用一些简单的页面爬取，对于有ajax之类的异步加载技术的网站不适用，需要一些成熟的爬虫框架。</p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;h3 id=&quot;1-URL地址分析&quot;&gt;&lt;a href=&quot;#1-URL地址分析&quot; class=&quot;headerlink&quot; title=&quot;1.URL地址分析&quot;&gt;&lt;/a&gt;1.URL地址分析&lt;/h3&gt;&lt;p&gt;选取的一个百度贴吧帖子的网址是&lt;a href=&quot;http://tieba.baidu.com/p/4739169817，各位可以点击进去查看一下。&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;http://tieba.baidu.com/p/4739169817，各位可以点击进去查看一下。&lt;/a&gt;&lt;br&gt;如果点击只看楼主或者翻页，则会产生两个参数&lt;a href=&quot;http://tieba.baidu.com/p/4739169817?pn=2或http://tieba.baidu.com/p/4739169817?see_lz=1，分析一下该网址如下：&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;http://tieba.baidu.com/p/4739169817?pn=2或http://tieba.baidu.com/p/4739169817?see_lz=1，分析一下该网址如下：&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://tieba.baidu.com/p/4739169817?see_lz=1&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;http://tieba.baidu.com/p/4739169817?see_lz=1&lt;/a&gt; 这是网址&lt;/li&gt;
&lt;li&gt;see_lz 该参数表示是否只看楼主发的帖子，1表示true&lt;/li&gt;
&lt;li&gt;pn 该参数表示表示第几页&lt;/li&gt;
&lt;/ul&gt;
    
    </summary>
    
      <category term="python" scheme="http://www.hyperxu.com/categories/python/"/>
    
    
      <category term="python" scheme="http://www.hyperxu.com/tags/python/"/>
    
      <category term="爬虫" scheme="http://www.hyperxu.com/tags/%E7%88%AC%E8%99%AB/"/>
    
  </entry>
  
  <entry>
    <title>Hello World</title>
    <link href="http://www.hyperxu.com/2017/05/17/hello-world/"/>
    <id>http://www.hyperxu.com/2017/05/17/hello-world/</id>
    <published>2017-05-17T07:40:09.060Z</published>
    <updated>2017-05-17T07:40:09.054Z</updated>
    
    <content type="html"><![CDATA[<p>Welcome to <a href="https://hexo.io/" target="_blank" rel="external">Hexo</a>! This is your very first post. Check <a href="https://hexo.io/docs/" target="_blank" rel="external">documentation</a> for more info. If you get any problems when using Hexo, you can find the answer in <a href="https://hexo.io/docs/troubleshooting.html" target="_blank" rel="external">troubleshooting</a> or you can ask me on <a href="https://github.com/hexojs/hexo/issues" target="_blank" rel="external">GitHub</a>.</p><a id="more"></a><h2 id="Quick-Start"><a href="#Quick-Start" class="headerlink" title="Quick Start"></a>Quick Start</h2><h3 id="Create-a-new-post"><a href="#Create-a-new-post" class="headerlink" title="Create a new post"></a>Create a new post</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ hexo new <span class="string">"My New Post"</span></div></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/writing.html" target="_blank" rel="external">Writing</a></p><h3 id="Run-server"><a href="#Run-server" class="headerlink" title="Run server"></a>Run server</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ hexo server</div></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/server.html" target="_blank" rel="external">Server</a></p><h3 id="Generate-static-files"><a href="#Generate-static-files" class="headerlink" title="Generate static files"></a>Generate static files</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ hexo generate</div></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/generating.html" target="_blank" rel="external">Generating</a></p><h3 id="Deploy-to-remote-sites"><a href="#Deploy-to-remote-sites" class="headerlink" title="Deploy to remote sites"></a>Deploy to remote sites</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ hexo deploy</div></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/deployment.html" target="_blank" rel="external">Deployment</a></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;Welcome to &lt;a href=&quot;https://hexo.io/&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;Hexo&lt;/a&gt;! This is your very first post. Check &lt;a href=&quot;https://hexo.io/docs/&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;documentation&lt;/a&gt; for more info. If you get any problems when using Hexo, you can find the answer in &lt;a href=&quot;https://hexo.io/docs/troubleshooting.html&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;troubleshooting&lt;/a&gt; or you can ask me on &lt;a href=&quot;https://github.com/hexojs/hexo/issues&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
    
    </summary>
    
    
  </entry>
  
</feed>
