<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>YangBao</title>
  
  <subtitle>知识笔记</subtitle>
  <link href="https://ruy9527.github.io/atom.xml" rel="self"/>
  
  <link href="https://ruy9527.github.io/"/>
  <updated>2026-08-08T03:34:02.000Z</updated>
  <id>https://ruy9527.github.io/</id>
  
  <author>
    <name>ruY9527</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>机器学习_介绍篇章</title>
    <link href="https://ruy9527.github.io/article/machine_base/"/>
    <id>https://ruy9527.github.io/article/machine_base/</id>
    <published>2026-08-08T04:00:00.000Z</published>
    <updated>2026-08-08T03:34:02.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>😀 这里写文章的前言：<br>机器学习三大范式（监督&#x2F;无监督&#x2F;强化）与深度网络的关系；回归 vs 分类；输入类型（表格、序列、图像、图结构）；模型作为函数族 + 训练数据选择具体函数；生成模型与潜变量</p></blockquote><h1 id="📝-机器学习的三大范式与深度网络关系"><a href="#📝-机器学习的三大范式与深度网络关系" class="headerlink" title="📝 机器学习的三大范式与深度网络关系"></a>📝 机器学习的三大范式与深度网络关系</h1><h2 id="核心关系"><a href="#核心关系" class="headerlink" title="核心关系"></a>核心关系</h2><p>深度神经网络是目前最强大、最实用的一类机器学习模型。将深度网络应用到数据上的过程称为深度学习。深度网络并非三大范式本身，而是实现这三大范式的核心工具&#x2F;模型家族。三大范式定义了“学什么、如何学”的问题类型和目标，而深度网络提供了强大的函数逼近能力，使这些范式在复杂、高维、结构化数据上取得当前最佳性能。</p><ul><li><strong>监督学习</strong>：深度网络学习从输入 x 到输出 y 的映射 y&#x3D;f[x,ϕ]。</li><li><strong>无监督学习</strong>：深度网络学习数据的内在结构或概率分布（生成模型）。</li><li><strong>强化学习</strong>：深度网络常被用来表示策略（从状态到动作的映射）或价值函数。</li></ul><h3 id="三大范式是否可以通过深度网络实现？"><a href="#三大范式是否可以通过深度网络实现？" class="headerlink" title="三大范式是否可以通过深度网络实现？"></a>三大范式是否可以通过深度网络实现？</h3><p><strong>是的，完全可以，而且目前最前沿的实现几乎都依赖深度网络。</strong></p><p>具体说明如下：</p><h4 id="（1）监督学习"><a href="#（1）监督学习" class="headerlink" title="（1）监督学习"></a>（1）监督学习</h4><ul><li><strong>定义</strong>：从带标签的输入-输出对中学习映射关系。</li><li><strong>深度网络实现方式</strong>：<ul><li>网络作为函数 f[x,ϕ]，参数 ϕ 通过最小化损失函数（如平方损失、交叉熵）来训练。</li><li>可处理回归（连续输出）、分类（概率输出）、结构化输出（语义分割、深度估计、翻译、图像生成描述等）。</li><li>优势：能处理高维、变长、具有内部结构的输入（图像、文本、音频、分子图等），并输出同样复杂的结构。</li></ul></li><li>书中例子：房价预测、情感分类、音乐类型识别、图像物体分类、语义分割、单目深度估计、语音转文字、机器翻译、文本生成图像等。</li></ul><h4 id="（2）无监督学习"><a href="#（2）无监督学习" class="headerlink" title="（2）无监督学习"></a>（2）无监督学习</h4><ul><li><strong>定义</strong>：只有输入数据，没有对应输出标签。目标是描述或理解数据的结构（而非学习映射）。</li><li><strong>深度网络实现方式</strong>（重点是生成式模型）：<ul><li>学习合成与训练数据在统计上难以区分的新样本。</li><li>一些模型直接建模数据的概率分布并从中采样；另一些只学习生成机制。</li><li>常引入<strong>潜变量</strong>（latent variables）来捕捉数据的低维本质结构（例如人脸表情可用约42个肌肉参数近似描述）。</li><li>条件生成：在部分约束下生成（图像修复、文本补全等）。</li></ul></li><li>书中例子：逼真的猫&#x2F;建筑图像生成、短篇故事合成、图像修复、文本续写（如GPT系列）。</li></ul><h4 id="（3）强化学习"><a href="#（3）强化学习" class="headerlink" title="（3）强化学习"></a>（3）强化学习</h4><ul><li><strong>定义</strong>：智能体（agent）在环境中执行动作，改变状态并获得奖励。目标是学习能最大化长期累积奖励的策略。存在时间信用分配问题（奖励延迟）以及探索-利用权衡。</li><li><strong>深度网络实现方式</strong>：<ul><li>最常见的是<strong>策略网络</strong>：深度网络直接将观察到的状态映射到动作（或动作分布）。</li><li>也可用于近似价值函数（状态价值或动作价值）。</li></ul></li><li>书中例子：<ul><li>类人机器人行走：从传感器状态到关节动作的映射。</li><li>下棋：从棋盘状态到走法选择的映射。</li></ul></li></ul><h1 id="回归和分类"><a href="#回归和分类" class="headerlink" title="回归和分类"></a>回归和分类</h1><h2 id="回归"><a href="#回归" class="headerlink" title="回归"></a>回归</h2><p>定义:</p><p>回归问题的目标是预测<strong>连续数值</strong>（或一组连续数值）。模型输出的是实数，而不是离散的类别标签。</p><ul><li>单变量回归：输出一个连续数值。</li><li>多变量回归：输出多个连续数值。</li></ul><p>直观理解:<br>把模型想象成一条（或一组）曲线&#x2F;函数，它根据输入计算出一个“多少”的量。</p><table><thead><tr><th></th><th></th><th></th><th></th><th></th></tr></thead><tbody><tr><td>案例</td><td>输入</td><td>输出</td><td>类型</td><td>说明</td></tr><tr><td>房价预测</td><td>房屋特征（面积、卧室数量等）组成的固定长度向量</td><td>房价（一个连续数字）</td><td>单变量回归</td><td>最经典的例子。模型返回的是一个实数，而不是“便宜&#x2F;贵”这类类别。</td></tr><tr><td>分子性质预测</td><td>分子的化学结构（原子及连接方式）</td><td>熔点 + 沸点（两个连续数字）</td><td>多变量回归</td><td>同时预测多个连续数值。</td></tr><tr><td>单目深度估计（结构化输出）</td><td>RGB 图像</td><td>每个像素的深度值（连续数值矩阵）</td><td>多变量&#x2F;结构化回归</td><td>输出是一张“深度图”，每个像素都是一个连续的深度数字。</td></tr></tbody></table><h2 id="分类"><a href="#分类" class="headerlink" title="分类"></a>分类</h2><p>定义:</p><p>分类问题的目标是把输入分配到<strong>离散的类别</strong>中。模型通常输出每个类别的概率，然后取概率最高的类别作为最终预测。</p><ul><li><strong>二元分类（Binary Classification）</strong>：只有两个类别（是&#x2F;否、正面&#x2F;负面、猫&#x2F;狗等）。</li><li><strong>多类别分类（Multi-class Classification）</strong>：有三个或以上的互斥类别。</li></ul><p>直观理解:</p><p>把模型想象成一个“投票器”或“概率分配器”，它告诉你输入更像哪一类。</p><table><thead><tr><th></th><th></th><th></th><th></th><th></th></tr></thead><tbody><tr><td>案例</td><td>输入</td><td>输出</td><td>类型</td><td>说明</td></tr><tr><td>餐厅评论情感分析</td><td>评论文本字符串</td><td>正面 or 负面（两个概率）</td><td>二元分类</td><td>输出是一个长度为 2 的概率向量，例如 [0.12, 0.88] 表示 88% 概率是负面。</td></tr><tr><td>音乐类型识别</td><td>音频片段</td><td>N 种音乐类型中的一种（N 个概率）</td><td>多类别分类</td><td>模型返回大小为 N 的概率向量，表示属于每种类型的可能性。</td></tr><tr><td>图像物体分类</td><td>图片（像素 RGB 值）</td><td>N 种物体中的一种（N 个概率）</td><td>多类别分类</td><td>经典 ImageNet 风格任务。</td></tr><tr><td>语义分割（结构化输出）</td><td>RGB 图像</td><td>每个像素属于“牛”还是“背景”（每个像素做一次二元分类）</td><td>多变量二元分类 &#x2F; 结构化分类</td><td>输出是一张与输入同样大小的标签图。</td></tr></tbody></table><h2 id="回归-vs-分类-的核心对比"><a href="#回归-vs-分类-的核心对比" class="headerlink" title="回归 vs 分类 的核心对比"></a>回归 vs 分类 的核心对比</h2><table><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td>对比维度</td><td>回归（Regression）</td><td>分类（Classification）</td></tr><tr><td>输出类型</td><td>连续数值</td><td>离散类别（或类别概率）</td></tr><tr><td>典型问题问法</td><td>“是多少？”</td><td>“是哪一类？”</td></tr><tr><td>模型输出示例</td><td>房价 &#x3D; 325.6 万元</td><td>正面概率 0.15，负面概率 0.85</td></tr><tr><td>常用损失</td><td>平方损失、MAE</td><td>交叉熵损失</td></tr><tr><td>决策方式</td><td>直接使用预测的数值</td><td>通常取概率最大的类别</td></tr><tr><td>代表例子</td><td>房价预测、分子熔点沸点</td><td>评论情感、音乐类型、图像物体分类</td></tr></tbody></table><h1 id="数据处理"><a href="#数据处理" class="headerlink" title="数据处理"></a>数据处理</h1><p>现实世界的输入（文本、音频、图像、分子等）必须先被转换成数字向量，模型才能接受并处理。不同输入类型的结构差异很大，处理方式也因此不同。</p><h2 id="表格数据"><a href="#表格数据" class="headerlink" title="表格数据"></a>表格数据</h2><p><strong>特点</strong></p><ul><li>固定长度的特征向量</li><li><strong>没有内在结构</strong>（改变特征顺序，模型预期结果不变）</li><li>典型例子：房屋特征（面积、卧室数、楼层、位置编码等）</li></ul><p><strong>处理方式</strong></p><ul><li>直接把每个特征变成一个数字</li><li>类别特征需要编码（One-hot 或 Embedding）</li><li>数值特征通常做标准化&#x2F;归一化（让数值在相似范围）</li></ul><p><strong>处理后的效果示例</strong><br>假设预测房价，原始数据：</p><p>text</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">面积: 120㎡, 卧室: 3, 楼层: 8, 是否学区: 是</span><br></pre></td></tr></table></figure><p>处理后变成固定长度向量：</p><p>text</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">[120.0, 3.0, 8.0, 1.0]          # 或标准化后 [0.85, 0.6, 0.4, 1.0]</span><br></pre></td></tr></table></figure><p>模型直接接收这个向量。</p><h2 id="文本数据-Text"><a href="#文本数据-Text" class="headerlink" title="文本数据(Text)"></a>文本数据(Text)</h2><p><strong>特点</strong></p><ul><li>长度不固定</li><li><strong>顺序非常重要</strong>（“我的妻子吃了鸡肉” ≠ “鸡肉吃了我的妻子”）</li><li>必须先编码成数字</li></ul><p><strong>处理方式</strong>（书中举例 + 现代常用方法）</p><ol><li><strong>分词（Tokenization）</strong>：把句子拆成词或子词</li><li><strong>建立词汇表</strong>：固定大小（书中例子用 10,000 个词）</li><li><strong>转换成索引序列</strong>：每个词对应一个整数 ID</li><li>现代方法会进一步用 Embedding 层把索引变成稠密向量，或直接用 Transformer 的 tokenizer（如 BPE）</li></ol><p><strong>处理后的效果示例</strong><br>原始文本：</p><p>text</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">&quot;这家餐厅的服务很好&quot;</span><br></pre></td></tr></table></figure><p>处理后（简单索引方式，书中例子）：</p><p>text</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">[4521, 89, 3302, 17, 908]     # 每个数字是词汇表中的索引</span><br></pre></td></tr></table></figure><p>或经过 Embedding 后变成矩阵（每个词变成一个向量）：</p><p>text</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">[[0.12, -0.45, 0.78, ...],    # “这家”的向量</span><br><span class="line"> [0.33, 0.21, -0.09, ...],    # “餐厅”的向量</span><br><span class="line"> ... ]</span><br></pre></td></tr></table></figure><p>长度会不同，实际训练时常做 padding（补零）或截断，变成固定长度</p><h1 id="🤗-总结归纳"><a href="#🤗-总结归纳" class="headerlink" title="🤗 总结归纳"></a>🤗 总结归纳</h1><p>总结文章的内容</p><h1 id="📎-参考文章"><a href="#📎-参考文章" class="headerlink" title="📎 参考文章"></a>📎 参考文章</h1><ul><li>一些引用</li><li>引用文章</li></ul><blockquote><p>💡 有关文章的问题，欢迎您在底部评论区留言，一起交流~</p></blockquote>]]></content>
    
    
    <summary type="html">机器学习基础前置知识</summary>
    
    
    
    <category term="机器学习" scheme="https://ruy9527.github.io/categories/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    
    
    <category term="机器学习" scheme="https://ruy9527.github.io/tags/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    
    <category term="神经网络" scheme="https://ruy9527.github.io/tags/%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C/"/>
    
  </entry>
  
  <entry>
    <title>Antlr4-解析入门一</title>
    <link href="https://ruy9527.github.io/article/altrl4one/"/>
    <id>https://ruy9527.github.io/article/altrl4one/</id>
    <published>2023-12-21T04:00:00.000Z</published>
    <updated>2024-11-22T07:51:05.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>😀 这里写文章的前言：<br>为什么需要解析SQL？解析完SQL后又能做什么？<br>0. 校验SQL的语法是否合规</p><ol><li>数据安全需要解析SQL获取出对应的的操作</li><li>数据血缘的抽取需要解析SQL</li><li>类似Hive&#x2F;Spark等在将SQL转化为mr,执行计划等的时候,也是需要解析SQL的</li><li>等等;欢迎你来补充更多需要解析SQL的话题</li></ol></blockquote><h1 id="📝-解析的方法"><a href="#📝-解析的方法" class="headerlink" title="📝 解析的方法"></a>📝 解析的方法</h1><table><thead><tr><th></th><th></th><th></th><th></th></tr></thead><tbody><tr><td>项目名称</td><td>适合的数据库</td><td>项目地址</td><td>个人观点</td></tr><tr><td>JSqlParser</td><td><strong>RDBMS</strong></td><td>⚠️[此处原为Notion外部链接预览,URL待补充]</td><td>目前只适合关系型的数据库，对于大数据很多领域的数据库目前还不是特别支持</td></tr><tr><td>calcite</td><td>Cassandra、Druid、Elasticsearch、MongoDB、Kafka,FlinkSQL等</td><td><a href="https://github.com/apache/calcite/tree/main">https://github.com/apache/calcite/tree/main</a></td><td>后续再补</td></tr><tr><td>antlr</td><td>可以借助 grammars-v4 这个项目提供,可以解析很多数据库以及编程语言等都可以解析的</td><td>antlr4项目:<a href="https://github.com/antlr/antlr4">https://github.com/antlr/antlr4</a><br><br>语法校验项目模块： <a href="https://github.com/antlr/grammars-v4">grammars-v4</a></td><td>社区构建比较完善,比如Spark,Hive,Iceberg等都有.g4文件来满足antlr4对SQL的解析,并且你也可以借助开源项目提供的g4来解析</td></tr></tbody></table><p>类似阿里开源的druid这种也是有解析SQL的功能的，但是稍微看了下源码，其底层好像也是有利用到antlr</p><h1 id="antlr怎么用"><a href="#antlr怎么用" class="headerlink" title="antlr怎么用"></a>antlr怎么用</h1><h2 id="IDEA插件"><a href="#IDEA插件" class="headerlink" title="IDEA插件"></a>IDEA插件</h2><p>借助IDEA插件来进行安装,但是需要注意的是,项目中的antlr的版本最好是和idea中antlr的版本一致;如果不一致的话，需要使用antlr对应版本的jar来对g4文件生成对应的文件</p><p><img src="/images/posts/altrl4one/img-1.png" alt="Antlr4-解析入门一"></p><h2 id="手动JAR安装"><a href="#手动JAR安装" class="headerlink" title="手动JAR安装"></a>手动JAR安装</h2><p>对应版本下载地址: <a href="https://github.com/antlr/website-antlr4/tree/gh-pages/download">https://github.com/antlr/website-antlr4/tree/gh-pages/download</a></p><p>我们在此处的地址下载自己需要的对应版本;此处的anltr4如果你不想配置环境变量,就替换为 java jar antrlxxx.jar -o… 后面需要的参数</p><p>常用参数说明:</p><ul><li>-o : 生成 tokens,interp,java等路径</li><li>-listener: 生成监听者模式的参数</li><li>-visitor:  生成访问者模式的参数</li><li>-lib:      指定g4文件的路径</li></ul><figure class="highlight powershell"><table><tr><td class="code"><pre><span class="line">antlr4 <span class="literal">-o</span> E:/coding_self/github_learn/grammars<span class="literal">-v4</span>/sql/hive/v3\gen </span><br><span class="line"><span class="literal">-listener</span> <span class="literal">-visitor</span> </span><br><span class="line"><span class="literal">-lib</span> E:/coding_self/github_learn/grammars<span class="literal">-v4</span>/sql/hive/v3 E:/coding_self/github_learn/grammars<span class="literal">-v4</span>/sql/hive/v3\HiveParser.g4 E:/coding_self/github_learn/grammars<span class="literal">-v4</span>/sql/hive/v3\HiveLexer.g4</span><br></pre></td></tr></table></figure><h2 id="其它方法"><a href="#其它方法" class="headerlink" title="其它方法"></a>其它方法</h2><p>官网介绍的方法传送门: <a href="https://github.com/antlr/antlr4/blob/master/doc/java-target.md">https://github.com/antlr/antlr4/blob/master/doc/java-target.md</a></p><h2 id="生成效果"><a href="#生成效果" class="headerlink" title="生成效果"></a>生成效果</h2><p>采用如上,最后生成的效果如下图;那我们就可以愉快的在代码中使用和调试了</p><p><img src="/images/posts/altrl4one/img-2.png" alt="Antlr4-解析入门一"></p><h1 id="使用流程"><a href="#使用流程" class="headerlink" title="使用流程"></a>使用流程</h1><p>我们按照上面的流程,都生成对应的文件之后,就可以导入到项目中进行使用了;假设导入后报错的话,仔细检查下是否包名的路径之类的问题,手动处理下就好了</p><p>我们这里基于访问者来进行介绍</p><h2 id="继承-BaseVisitor"><a href="#继承-BaseVisitor" class="headerlink" title="继承 BaseVisitor"></a>继承 <code>BaseVisitor</code></h2><p>比如我借助 grammars-v4 的hive4生成的访问者base类叫 <code>HiveParserBaseVisitor</code></p><pre><code> 借助 Spark的g4生成的 `SqlBaseBaseVisitor`</code></pre><p>这里我已Hive的为准介绍,<code>HiveSqlAst</code> 来集成 <code>HiveParserBaseVisitor</code></p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">package</span> com.iyang.bootbasicio.sql.hive.utils;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.iyang.bootbasicio.sql.hive.HiveParser;</span><br><span class="line"><span class="keyword">import</span> com.iyang.bootbasicio.sql.hive.HiveParserBaseVisitor;</span><br><span class="line"><span class="keyword">import</span> lombok.extern.slf4j.Slf4j;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.HashMap;</span><br><span class="line"><span class="keyword">import</span> java.util.HashSet;</span><br><span class="line"><span class="keyword">import</span> java.util.Map;</span><br><span class="line"><span class="keyword">import</span> java.util.Set;</span><br><span class="line"></span><br><span class="line"><span class="comment">/***</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span>: yang_bao</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@date</span>: 2023/11/3</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@desc</span>:</span></span><br><span class="line"><span class="comment"> ***/</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HiveSqlAst</span> <span class="keyword">extends</span> <span class="title class_">HiveParserBaseVisitor</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Map&lt;String, Set&lt;String&gt;&gt; dnTableActionMap = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">HiveSqlAst</span><span class="params">()</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">HiveSqlAst</span><span class="params">(Map&lt;String, Set&lt;String&gt;&gt; dnTableActionMap)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.dnTableActionMap = dnTableActionMap;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Map&lt;String, Set&lt;String&gt;&gt; <span class="title function_">getDnTableActionMap</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> dnTableActionMap;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">visitStatement</span><span class="params">(HiveParser.StatementContext ctx)</span> &#123;</span><br><span class="line">        log.info(<span class="string">&quot; enter visitStatement function &quot;</span>);</span><br><span class="line"><span class="comment">/*        HiveParser.DdlStatementContext ddlStatementContext = ctx.execStatement().ddlStatement();</span></span><br><span class="line"><span class="comment">        HiveParser.TableNameContext nameContext = ddlStatementContext.alterStatement().tableName();</span></span><br><span class="line"><span class="comment">        String dbText = nameContext.db.getText();</span></span><br><span class="line"><span class="comment">        String tabText = nameContext.tab.getText();</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">        log.info(&quot;the db is &#123;&#125; , tab is &#123;&#125; , meta is &#123;&#125; &quot; , dbText, tabText,1);*/</span></span><br><span class="line"> <span class="comment">// 这步链式调用是不能少的,如果你发现少了的话,下面的解析方法就不会进去了</span></span><br><span class="line">        <span class="built_in">super</span>.visitStatement(ctx);</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="comment">// return visitChildren(ctx);</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 包含了 select 的查询</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">visitSelectStatement</span><span class="params">(HiveParser.SelectStatementContext ctx)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;\n&quot;</span>);</span><br><span class="line">        log.info(<span class="string">&quot;enter visitSelectStatement function &quot;</span>);</span><br><span class="line"><span class="comment">/*        HiveParser.AtomSelectStatementContext atomSelectStatementContext = ctx.atomSelectStatement();</span></span><br><span class="line"><span class="comment">        String tableName = atomSelectStatementContext.fromClause().fromSource().joinSource().atomjoinSource().tableSource().tabname.getText();</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">        log.info(&quot;the tableName is ---&gt; &#123;&#125; &quot; , tableName);*/</span></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">        System.out.println(ctx.getText());</span></span><br><span class="line"><span class="comment">       try &#123;</span></span><br><span class="line"><span class="comment">           SqlParser sqlParser = SqlParser.create(ctx.getText());</span></span><br><span class="line"><span class="comment">           SqlNode sqlNode = sqlParser.parseQuery();</span></span><br><span class="line"><span class="comment">           System.out.println(sqlNode);</span></span><br><span class="line"><span class="comment">       &#125; catch (Exception e)&#123;</span></span><br><span class="line"><span class="comment">           e.printStackTrace();</span></span><br><span class="line"><span class="comment">       &#125;</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="comment">/*        HiveParser.AtomSelectStatementContext atomCtx = ctx.atomSelectStatement();</span></span><br><span class="line"><span class="comment">        HiveParser.FromSourceContext fromSourceContext = atomCtx.fromClause().fromSource();</span></span><br><span class="line"><span class="comment">        HiveParser.AtomjoinSourceContext atomJoinSource = fromSourceContext.joinSource().atomjoinSource();</span></span><br><span class="line"><span class="comment">        String atomTableName = atomJoinSource.tableSource().getText();</span></span><br><span class="line"><span class="comment">        List&lt;HiveParser.JoinSourcePartContext&gt; partContexts = fromSourceContext.joinSource().joinSourcePart();</span></span><br><span class="line"><span class="comment">        for (HiveParser.JoinSourcePartContext sourcePartContext : partContexts) &#123;</span></span><br><span class="line"><span class="comment">            String partTableName = sourcePartContext.tableSource().getText();</span></span><br><span class="line"><span class="comment">            log.info(&quot;the partTableName value is ---&gt; &#123;&#125; &quot;, partTableName);</span></span><br><span class="line"><span class="comment">        &#125;</span></span><br><span class="line"><span class="comment">        log.info(&quot;the atomJoinTable value is ---&gt; &#123;&#125; &quot; , atomTableName);*/</span></span><br><span class="line">        <span class="comment">// String tabelName = ctx.atomSelectStatement().fromClause().fromSource().joinSource().atomjoinSource().tableSource().getText();</span></span><br><span class="line"></span><br><span class="line">        HiveParser.<span class="type">AtomSelectStatementContext</span> <span class="variable">atomSelectStatement</span> <span class="operator">=</span> ctx.atomSelectStatement();</span><br><span class="line"></span><br><span class="line">        parseSelectSql(atomSelectStatement);</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 包含了 insert </span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">visitInsertClause</span><span class="params">(HiveParser.InsertClauseContext ctx)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;\n&quot;</span>);</span><br><span class="line">        HiveParser.<span class="type">TableOrPartitionContext</span> <span class="variable">tableOrPartition</span> <span class="operator">=</span> ctx.destination().tableOrPartition();</span><br><span class="line">        <span class="type">String</span> <span class="variable">tableName</span> <span class="operator">=</span> tableOrPartition.tableName().getText();</span><br><span class="line">        log.info(<span class="string">&quot;in visitInsertClause, the tableName is ---&gt; &#123;&#125; &quot;</span> , tableName);</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">super</span>.visitInsertClause(ctx);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// alter 语句</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">visitAlterStatement</span><span class="params">(HiveParser.AlterStatementContext ctx)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;\n&quot;</span>);</span><br><span class="line">        log.info(<span class="string">&quot; enter visitAlterStatement func &quot;</span>);</span><br><span class="line">        <span class="type">String</span> <span class="variable">tableName</span> <span class="operator">=</span> ctx.tableName().getText();</span><br><span class="line">        log.info(<span class="string">&quot;the tableName is &#123;&#125; &quot;</span> , tableName);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// tableName 可能会带上catalog的名称；如果需要的话，需要进行catalog的切割</span></span><br><span class="line">        <span class="comment">// ALTER TABLE HIVE_PROD.XIAOBAO_BIGSCREENT.T_ICCE_ENTERPRISE ADD COLUMNS ( SINK_TIME STRING COMMENT &#x27;WRITE DATA TIME&#x27;)</span></span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">super</span>.visitAlterStatement(ctx);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// create 建表</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">visitCreateTableStatement</span><span class="params">(HiveParser.CreateTableStatementContext ctx)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">String</span> <span class="variable">tableName</span> <span class="operator">=</span> ctx.tableName().getText();</span><br><span class="line">        log.info(<span class="string">&quot;in visitCreateTableStatement, the tableName is &#123;&#125; &quot;</span> , tableName);</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">super</span>.visitCreateTableStatement(ctx);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 修改语句</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">visitUpdateStatement</span><span class="params">(HiveParser.UpdateStatementContext ctx)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">tableName</span> <span class="operator">=</span> ctx.tableName().getText();</span><br><span class="line">        log.info(<span class="string">&quot;in visitUpdateStatement, the tableName value is &#123;&#125; &quot;</span>, tableName);</span><br><span class="line">        <span class="comment">// where 中可能保存查询库或者表的条件</span></span><br><span class="line">        setActionToMapByDbTableName(tableName, HiveActionEnum.UPDATE.getAction());</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">super</span>.visitUpdateStatement(ctx);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// delete 删除语句</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">visitDeleteStatement</span><span class="params">(HiveParser.DeleteStatementContext ctx)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">tableName</span> <span class="operator">=</span> ctx.tableName().getText();</span><br><span class="line">        log.info(<span class="string">&quot;in visitDeleteStatement, the tableName value is &#123;&#125; &quot;</span> , tableName);</span><br><span class="line">        <span class="comment">// where 中可能保存查询库或者表的条件</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">super</span>.visitDeleteStatement(ctx);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// drop 删除表数据</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">visitDropTableStatement</span><span class="params">(HiveParser.DropTableStatementContext ctx)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">tableName</span> <span class="operator">=</span> ctx.tableName().getText();</span><br><span class="line">        log.info(<span class="string">&quot; in visitDropTableStatement , the tableName is &#123;&#125; &quot;</span> , tableName);</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">super</span>.visitDropTableStatement(ctx);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// drop删除库的数据</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">visitDropDatabaseStatement</span><span class="params">(HiveParser.DropDatabaseStatementContext ctx)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">dbName</span> <span class="operator">=</span> ctx.db_schema().getText();</span><br><span class="line">        log.info(<span class="string">&quot; in visitDropDatabaseStatement, the dbName is &#123;&#125; &quot;</span> , dbName);</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">super</span>.visitDropDatabaseStatement(ctx);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 解析 select 查询的sql语句,获取对应的库和表信息</span></span><br><span class="line"><span class="comment">     * 对 join,子查询sql,以及子查询SQL等嵌套递归查询</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">parseSelectSql</span><span class="params">(HiveParser.AtomSelectStatementContext atomSelectStatement)</span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// atomSelectStatement 中存在需要解析的sql; fromSource,join,subQuery,</span></span><br><span class="line">        HiveParser.<span class="type">JoinSourceContext</span> <span class="variable">sourceContext</span> <span class="operator">=</span> atomSelectStatement.fromClause().fromSource().joinSource();</span><br><span class="line">        <span class="comment">// 获取传入进来的 atmo的表名字;判断非空即获取</span></span><br><span class="line">        <span class="keyword">if</span> (sourceContext.atomjoinSource().tableSource() != <span class="literal">null</span>)&#123;</span><br><span class="line">            HiveParser.<span class="type">TableNameContext</span> <span class="variable">tabname</span> <span class="operator">=</span> sourceContext.atomjoinSource().tableSource().tabname;</span><br><span class="line">            log.info(<span class="string">&quot;the tabname value us ---&gt; &#123;&#125; &quot;</span>, tabname.getText());</span><br><span class="line">            setActionToMapByDbTableName(tabname.getText(), HiveActionEnum.SELECT.getAction());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">for</span> (HiveParser.JoinSourcePartContext sourcePartContext : sourceContext.joinSourcePart()) &#123;</span><br><span class="line">            <span class="comment">// 解析join部分自身获取的 库和表信息</span></span><br><span class="line">            <span class="keyword">if</span> (sourcePartContext.tableSource() != <span class="literal">null</span>) &#123;</span><br><span class="line">                HiveParser.<span class="type">TableNameContext</span> <span class="variable">sourcePartTableName</span> <span class="operator">=</span> sourcePartContext.tableSource().tabname;</span><br><span class="line">                setActionToMapByDbTableName(sourcePartTableName.getText(), HiveActionEnum.SELECT.getAction());</span><br><span class="line">                log.info(<span class="string">&quot;the sourcePartTableName value is ---&gt; &#123;&#125;&quot;</span> , sourcePartTableName.getText());</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// join部分的sql也是存在子sql的情况下;如果存在子sql的话,就进行迭代处理</span></span><br><span class="line">            HiveParser.<span class="type">SubQuerySourceContext</span> <span class="variable">partSubQuery</span> <span class="operator">=</span> sourcePartContext.subQuerySource();</span><br><span class="line">            <span class="keyword">if</span> (partSubQuery != <span class="literal">null</span>) &#123;</span><br><span class="line">                HiveParser.<span class="type">AtomSelectStatementContext</span> <span class="variable">partSubQueryAtmo</span> <span class="operator">=</span> partSubQuery.queryStatementExpression().queryStatementExpressionBody().regularBody().selectStatement().atomSelectStatement();</span><br><span class="line">                parseSelectSql(partSubQueryAtmo);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 存在没有值的情况sql语句</span></span><br><span class="line">            <span class="comment">// HiveParser.TableNameContext sourcePartTableName = sourcePartContext.tableSource().tabname;</span></span><br><span class="line">            <span class="comment">// log.info(&quot;the sourcePartTableName value is ---&gt; &#123;&#125;&quot; , sourcePartTableName.getText());</span></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 子sql;如果子sql不是null</span></span><br><span class="line">        HiveParser.<span class="type">SubQuerySourceContext</span> <span class="variable">subQuerySourceContext</span> <span class="operator">=</span> sourceContext.atomjoinSource().subQuerySource();</span><br><span class="line">        <span class="keyword">if</span> (subQuerySourceContext != <span class="literal">null</span>) &#123;</span><br><span class="line">            HiveParser.<span class="type">AtomSelectStatementContext</span> <span class="variable">subQueryAtom</span> <span class="operator">=</span> subQuerySourceContext.queryStatementExpression().queryStatementExpressionBody().regularBody().selectStatement().atomSelectStatement();</span><br><span class="line">            parseSelectSql(subQueryAtom);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 封装 db table 对应的action到 map 集合中</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> dbTableName</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> action</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">setActionToMapByDbTableName</span><span class="params">(String dbTableName,String action)</span>&#123;</span><br><span class="line"></span><br><span class="line">        Set&lt;String&gt; dbTableActionSet = dnTableActionMap.getOrDefault(dbTableName, <span class="keyword">new</span> <span class="title class_">HashSet</span>&lt;&gt;());</span><br><span class="line">        dbTableActionSet.add(action);</span><br><span class="line">        dnTableActionMap.put(dbTableName, dbTableActionSet);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="解析使用"><a href="#解析使用" class="headerlink" title="解析使用"></a>解析使用</h2><p>创建一个来进行调用使用;大家可以使用下面的例子来进行校验使用</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">package</span> com.iyang.bootbasicio.sql.hive.utils;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.iyang.bootbasicio.sql.hive.HiveLexer;</span><br><span class="line"><span class="keyword">import</span> com.iyang.bootbasicio.sql.hive.HiveParser;</span><br><span class="line"><span class="keyword">import</span> lombok.extern.slf4j.Slf4j;</span><br><span class="line"><span class="keyword">import</span> org.antlr.v4.runtime.CharStreams;</span><br><span class="line"><span class="keyword">import</span> org.antlr.v4.runtime.CommonTokenStream;</span><br><span class="line"><span class="keyword">import</span> org.antlr.v4.runtime.atn.PredictionMode;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.Map;</span><br><span class="line"><span class="keyword">import</span> java.util.Set;</span><br><span class="line"></span><br><span class="line"><span class="comment">/***</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span>: yang_bao</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@date</span>: 2023/11/3</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@desc</span>:</span></span><br><span class="line"><span class="comment"> ***/</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HiveSqlHelper</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">ALERT_SQL</span> <span class="operator">=</span> <span class="string">&quot;ALTER TABLE HIVE_PROD.XIAOBAO_BIGSCREENT.T_ICCE_ENTERPRISE ADD COLUMNS ( SINK_TIME STRING COMMENT &#x27;WRITE DATA TIME&#x27;)&quot;</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">SIMPLE_QUERY_SQL</span> <span class="operator">=</span> <span class="string">&quot;SELECT * FROM AAA.CCC&quot;</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">MANY_TABLE_SQL</span> <span class="operator">=</span> <span class="string">&quot;SELECT \n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;    NULL                        AS   ID,\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;    CAST(TEMP_A.CREATE_DATE AS TIMESTAMP)          AS   TIMEC,\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;    TEMP_A.ENTERPRISE_NUM       AS   ENTERPRISE_NUM,\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;    TEMP_B.USER_NUM             AS   USER_NUM,\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;    TEMP_A.VIRTUAL_TENANT       AS   VTENANT,\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;    CAST(NOW() AS TIMESTAMP)    AS   ETL_LOAD_TS\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;FROM (\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;    SELECT \n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;        COUNT(DISTINCT A.ENTERPRISE_ID) AS ENTERPRISE_NUM,\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;        A.PLATFORM_VIRTUAL_TENANT       AS VIRTUAL_TENANT,\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;        A.CREATE_DATE                   AS CREATE_DATE\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;    FROM (SELECT ENTERPRISE_ID,PLATFORM_VIRTUAL_TENANT,SUBSTRING(CREATE_DATE,1,10) AS CREATE_DATE FROM DIM_BIGSCREENT.DIM_ECENTERPRISE_LIST_DI) A GROUP BY PLATFORM_VIRTUAL_TENANT,CREATE_DATE\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;) TEMP_A LEFT JOIN\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;(\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;    SELECT\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;        COUNT(DISTINCT A.USER_ID) AS USER_NUM,\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;        A.PLATFORM_VIRTUAL_TENANT AS VIRTUAL_TENANT,\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;        A.CREATE_DATE\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;    FROM (SELECT USER_ID,PLATFORM_VIRTUAL_TENANT,SUBSTRING(CREATE_DATE,1,10) AS CREATE_DATE FROM DIM_BIGSCREENT.DIM_ECUSERENTERPRISE_RELATION_LIST_DI) A GROUP BY PLATFORM_VIRTUAL_TENANT,CREATE_DATE\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;) TEMP_B ON TEMP_A.VIRTUAL_TENANT = TEMP_B.VIRTUAL_TENANT AND TEMP_A.CREATE_DATE = TEMP_B.CREATE_DATE&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">JOIN_SQL</span> <span class="operator">=</span> <span class="string">&quot;select * from aaa.aa as a left join ccc.cc as c on a.id = c.id  left join ddd.dd as d on a.id = d.id&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">INSERT_QUERY_SQL</span> <span class="operator">=</span> <span class="string">&quot;INSERT OVERWRITE TABLE aaa.cc\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;select * from aaa.aa&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">CREATE_DDL_SQL</span> <span class="operator">=</span> <span class="string">&quot;create table if not exists  ads_bigscreent.ads_cloud_info_sum_df (\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;\tid                       bigint   comment &#x27;id(主键)&#x27;  \t\t\t\t\t\t,\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;\tplatform_id\t         \t string   comment &#x27;平台ID&#x27;                          ,\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;\tplatform_name\t         string   comment &#x27;平台名称&#x27;                        ,\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;\tenterprise_num\t         bigint   comment &#x27;上云企业数量&#x27;                    ,\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;\tuser_num\t             bigint   comment &#x27;注册用户数&#x27;                      ,\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;\ttransaction_num\t     \t bigint   comment &#x27;交易用户数&#x27;                      ,\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;\tdeviceconnection_num\t bigint   comment &#x27;设备连接数&#x27;                      ,\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;\tpartners_num\t         bigint   comment &#x27;生态伙伴数量&#x27;                    ,\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;\tapps_num\t             bigint   comment &#x27;工业app数量&#x27;                     ,\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;\tvtenant\t\t\t\t\tstring \t\tcomment &#x27;租户&#x27;\t\t\t\t\t\t\t,\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;\tlongitude\t    \t\tdecimal(25,10)  comment\t&#x27;经度&#x27;\t\t\t\t\t\t,\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;    latitude\t    \t\tdecimal(25,10)  comment\t&#x27;纬度&#x27;\t\t\t\t\t\t,\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;\tetl_load_ts              timestamp comment &#x27;创建时间&#x27;\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;)  comment  &#x27;上云情况详情&#x27;&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">UPDATE_QUERY_SQL</span> <span class="operator">=</span> <span class="string">&quot;update ads_bigscreent.ads_cloud_info_sum_df set name = &#x27;123&#x27; where id in (select id from aaaa.aa) and &quot;</span> +</span><br><span class="line">            <span class="string">&quot; name in (select name from ads_bigscreent.ads_cloud_info_sum_df)&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">DELETE_QUERT_SQl</span> <span class="operator">=</span> <span class="string">&quot;delete from ads_bigscreent.ads_cloud_info_sum_df where id in (select id from aaaa.cc)&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">DROP_TABLE_SQL</span> <span class="operator">=</span> <span class="string">&quot;drop table ads_bigscreent.ads_cloud_info_sum_df&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">DROP_DATABASE_SQL</span> <span class="operator">=</span> <span class="string">&quot;drop database ads_bigscreent&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">QUERY_SUB_SQl</span> <span class="operator">=</span> <span class="string">&quot;select * from aaa.a as a left join (select * from ccc.c) as c on a.id = c.id&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">HiveLexer</span> <span class="variable">lexer</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HiveLexer</span>(CharStreams.fromString(UPDATE_QUERY_SQL));</span><br><span class="line">        <span class="type">CommonTokenStream</span> <span class="variable">tokenStream</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CommonTokenStream</span>(lexer);</span><br><span class="line">        <span class="type">HiveParser</span> <span class="variable">parser</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HiveParser</span>(tokenStream);</span><br><span class="line">        parser.getInterpreter().setPredictionMode(PredictionMode.SLL);</span><br><span class="line"></span><br><span class="line">        <span class="type">HiveSqlAst</span> <span class="variable">hiveSqlAst</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HiveSqlAst</span>();</span><br><span class="line">        hiveSqlAst.visit(parser.statement());</span><br><span class="line"></span><br><span class="line">        Map&lt;String, Set&lt;String&gt;&gt; dnTableActionMap = hiveSqlAst.getDnTableActionMap();</span><br><span class="line">        log.info(<span class="string">&quot;the dnTableActionMap value is &#123;&#125; &quot;</span>, dnTableActionMap);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="使用结果"><a href="#使用结果" class="headerlink" title="使用结果"></a>使用结果</h2><p>比如我们这里使用的一条update中嵌套一个select校验,看最后的结果都是查询出来的;如果只是库和表的校验的话,那么对应的功能就是可以满足了的</p><p><img src="/images/posts/altrl4one/img-3.png" alt="Antlr4-解析入门一"></p><h2 id="错误SQL解析的错误获取"><a href="#错误SQL解析的错误获取" class="headerlink" title="错误SQL解析的错误获取"></a>错误SQL解析的错误获取</h2><p>继承  <code>BaseErrorListener</code>; 我们这里借助传入list来收起错误的error</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HiveErrorListener</span> <span class="keyword">extends</span> <span class="title class_">BaseErrorListener</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> List&lt;String&gt; errorMsg ;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">HiveErrorListener</span><span class="params">(List&lt;String&gt; errorMsg)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.errorMsg = errorMsg;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">syntaxError</span><span class="params">(Recognizer&lt;?, ?&gt; recognizer, Object offendingSymbol, <span class="type">int</span> line,</span></span><br><span class="line"><span class="params">                            <span class="type">int</span> charPositionInLine, String msg, RecognitionException e)</span> &#123;</span><br><span class="line">        errorMsg.add(msg);</span><br><span class="line">        log.info(<span class="string">&quot;the HiveErrorListener(syntaxError) error info is &#123;&#125; &quot;</span> , msg);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们新addErrorListener和addErrorListener,注册上我们的错误监听器</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"> <span class="comment">// 收集错误的集合</span></span><br><span class="line">        List&lt;String&gt; errorList = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">        <span class="type">HiveLexer</span> <span class="variable">lexer</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HiveLexer</span>(CharStreams.fromString(UPDATE_QUERY_SQL));</span><br><span class="line">        <span class="type">CommonTokenStream</span> <span class="variable">tokenStream</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CommonTokenStream</span>(lexer);</span><br><span class="line">        <span class="type">HiveParser</span> <span class="variable">parser</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HiveParser</span>(tokenStream);</span><br><span class="line">        parser.getInterpreter().setPredictionMode(PredictionMode.SLL);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 注册我们的错误监听</span></span><br><span class="line">        lexer.removeErrorListeners();</span><br><span class="line">        parser.removeErrorListeners();</span><br><span class="line">        lexer.addErrorListener(<span class="keyword">new</span> <span class="title class_">HiveErrorListener</span>(errorList));</span><br><span class="line">        parser.addErrorListener(<span class="keyword">new</span> <span class="title class_">HiveErrorListener</span>(errorList));</span><br><span class="line"></span><br><span class="line">        <span class="type">HiveSqlAst</span> <span class="variable">hiveSqlAst</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HiveSqlAst</span>();</span><br><span class="line">        hiveSqlAst.visit(parser.statement());</span><br><span class="line">        log.info(<span class="string">&quot;the error info is : &#123;&#125; &quot;</span> , errorList);</span><br><span class="line"></span><br><span class="line">        Map&lt;String, Set&lt;String&gt;&gt; dnTableActionMap = hiveSqlAst.getDnTableActionMap();</span><br><span class="line">        log.info(<span class="string">&quot;the dnTableActionMap value is &#123;&#125; &quot;</span>, dnTableActionMap);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>我们故意在sql前加个;号,就可以看到错误解析的具体位置在 ; 这里,还是非常的准确的</p><p><img src="/images/posts/altrl4one/img-4.png" alt="Antlr4-解析入门一"></p><h2 id="方法使用小结"><a href="#方法使用小结" class="headerlink" title="方法使用小结"></a>方法使用小结</h2><p>可以看到该接口还提供了很多的对用操作方法,那么我们在使用的时候,就根据对应的方法耐心的调试即可</p><p><img src="/images/posts/altrl4one/img-5.png" alt="Antlr4-解析入门一"></p><h2 id="idea方法小结"><a href="#idea方法小结" class="headerlink" title="idea方法小结"></a>idea方法小结</h2><p>上面不是提到我们安装了antlr的插件么？那么也得使用起来了吧</p><p>还是以grammer-v4为准来看; 点击 <code>statement</code> 会出现下面的Test Rule statement;然后我们就可以将我们SQL放入如下,就可以看到左下角的SQL语法树的结构</p><p>然后我们的代码就可以根据树的调用方法来进行精确的获取,并且同时也可以看到校验的SQL是否有问题</p><p><img src="/images/posts/altrl4one/img-6.png" alt="Antlr4-解析入门一"></p><h1 id="🤗-总结归纳"><a href="#🤗-总结归纳" class="headerlink" title="🤗 总结归纳"></a>🤗 总结归纳</h1><p>目前我这边更多的是解析出来SQL,然后对权限进行校验和拦截操作</p><p>对于多种不同类似的数据库,其语法是可能不同的,我们其实是可以借助一些设计模式来更好的帮助我们维护和扩展代码</p><p>最后,放一个我编写的demo的传送门: <a href="https://github.com/ruY9527/boot-case/blob/master/boot-base/boot-basic-io/src/main/java/com/iyang/bootbasicio/sql/hive/utils/HiveSqlHelper.java">https://github.com/ruY9527/boot-case/blob/master/boot-base/boot-basic-io/src/main/java/com/iyang/bootbasicio/sql/hive/utils/HiveSqlHelper.java</a></p><h1 id="📎-参考文章"><a href="#📎-参考文章" class="headerlink" title="📎 参考文章"></a>📎 参考文章</h1><ul><li><a href="https://makeyourchoice.cn/archives/591/">https://makeyourchoice.cn/archives/591/</a></li><li><a href="https://github.com/antlr/grammars-v4">grammars-v4</a></li></ul><blockquote><p>💡 有关文章的问题，欢迎您在底部评论区留言，一起交流~</p></blockquote>]]></content>
    
    
    <summary type="html">Antlr解析SQL的入门操作</summary>
    
    
    
    <category term="Java" scheme="https://ruy9527.github.io/categories/Java/"/>
    
    
    <category term="Java" scheme="https://ruy9527.github.io/tags/Java/"/>
    
    <category term="Antlr" scheme="https://ruy9527.github.io/tags/Antlr/"/>
    
    <category term="Iceberg" scheme="https://ruy9527.github.io/tags/Iceberg/"/>
    
    <category term="Spark" scheme="https://ruy9527.github.io/tags/Spark/"/>
    
  </entry>
  
  <entry>
    <title>生活碎碎念_多做少想</title>
    <link href="https://ruy9527.github.io/article/more_do_less_think/"/>
    <id>https://ruy9527.github.io/article/more_do_less_think/</id>
    <published>2023-12-08T04:00:00.000Z</published>
    <updated>2023-12-08T15:58:44.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>😀 这是一篇记录生活的碎碎念：<br>欢迎你阅读我的碎碎念,非常感谢<br>稍微学会下记录生活或者想法，毕竟年纪大了，很容易忘记一些事包括之前一些有的没得</p></blockquote><h1 id="📝-多做少想的实际行动"><a href="#📝-多做少想的实际行动" class="headerlink" title="📝 多做少想的实际行动"></a>📝 多做少想的实际行动</h1><p>想多了全是问题，做多了全是答案。想得再多都是焦虑，做得再少也算进步</p><details><summary>个人含义理解</summary><p>减少自己的胡思乱想，既然决定了前行的方向和目标，就不断的坚持和走下去;假设你不断的停留在不停不停不停的空想上,肯定是没有直接开始做得出来的好；并且随着你想着你越多，你放弃的可能程度也是不断的提升，但是你在整个过程中，是没有任何开始和行动的</p><p>所以，让自己行动起来，去贯彻做起来的主义，哪怕再少，也是在持续的不断前进</p></details><h1 id="学会主动远离一些人，告别自己的恋爱脑"><a href="#学会主动远离一些人，告别自己的恋爱脑" class="headerlink" title="学会主动远离一些人，告别自己的恋爱脑"></a>学会主动远离一些人，告别自己的恋爱脑</h1><h2 id="远离那些让你内耗的人"><a href="#远离那些让你内耗的人" class="headerlink" title="远离那些让你内耗的人"></a>远离那些让你内耗的人</h2><p>经历了漫长的一段时间的自我内耗，就搁那么一丢丢给自己整没了;索性好在突然的醒悟;从醒悟到现在差不多快2个月了吧，坚持着做许多自己当初没做的事情</p><p>去慢慢逐渐告别了内耗</p><h2 id="远离那些背叛过的人"><a href="#远离那些背叛过的人" class="headerlink" title="远离那些背叛过的人"></a>远离那些背叛过的人</h2><p>此处借助张爱玲的一句话，来完全诠释这个二级标题的含义：</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">人家撇下你的那一刻，一定是觉得某个瞬间没有你，他会生活得更好，那一刻，一辈子都不值得原谅</span><br></pre></td></tr></table></figure><p>所以对于有过背叛的人，只会给你非常清醒的上一课然后外加pua并且他已经走出教室，而空留你在座位上不断以我怀疑;于是也请你自己大胆承认这一切,大大方方的走出来，去找寻自己</p><h2 id="远离那些故意激怒你的人"><a href="#远离那些故意激怒你的人" class="headerlink" title="远离那些故意激怒你的人"></a>远离那些故意激怒你的人</h2><p>人在最初相识的时候，总是带着好奇和新鲜感并且此时估计也是他对你最尊重的时候;当他和你熟了之后，对你知根知底后，往往随着他的“高人一等”而不那么尊重你的时候，其实是你没透过本质去理解，因为他本质就是这样</p><p>所以那么故意激怒和恶心你的人，大可不必继续往下走;越往下走，越难理解</p><h2 id="远离那些对你一而再，再而三撒谎的人"><a href="#远离那些对你一而再，再而三撒谎的人" class="headerlink" title="远离那些对你一而再，再而三撒谎的人"></a>远离那些对你一而再，再而三撒谎的人</h2><p>假设你认为你最喜欢的人对你是一而再，再而三的撒谎，并且瞒天过海的做着许多过分的事情，也尽可以大方的告别，没必要浪费自己的时间和生命</p><p>坦诚是一个人的为人之本，而不是任何的加分项</p><p>另外，也不要为了遮掩自己的错误，去用 善意的谎言将其替换，其实这是一种本末倒置的操作，事后当局人理解过来后，只会让人更恶心</p><p>做法：戳穿最后所谓的“善意谎言”,让自己彻底失去对他的任何信任信任;同时也会还一个清晰的自己</p><h1 id="保持热情，行动起来，继续前行"><a href="#保持热情，行动起来，继续前行" class="headerlink" title="保持热情，行动起来，继续前行"></a>保持热情，行动起来，继续前行</h1><p>无论我们经历过什么，也要让自己保持自己最初的热爱和选择；在苟且的生活中，慢慢需要和拼凑属于自己的生活碎片;当最后的结果哪怕是不尽如意的时候，也是有碎片可以支撑我们继续前行</p><p>在属于自己的萌芽期和黑暗期，去坚持自己</p><p>在看不到前方任何希望的时候，我们不妨让自己多做点和多坚持下，也许下次就能看到前进的方向和目标</p><p>一定要记住,永远要记住，找寻自己的道路上，始终让自己一个人继续前行，哪怕会经历漫长的黑暗，哪怕只有一瞬间的光芒，就是那一瞬间会变成自己内心永远的光</p><h1 id="保持自己善良和良知"><a href="#保持自己善良和良知" class="headerlink" title="保持自己善良和良知"></a>保持自己善良和良知</h1><p>我这一生,自从明白事理后，自己内心始终有一杠称，永远不要做没有良知的事情以及千万不要抛弃自己的善良</p><h1 id="🤗-总结归纳"><a href="#🤗-总结归纳" class="headerlink" title="🤗 总结归纳"></a>🤗 总结归纳</h1><p>我会继续完善自己,去贯穿自己;同时也希望自己要尽快的彻底恢复过来,虽然已经好了很多，但是还是缺少许多，得慢慢的恢复过来</p><p>我会在漫长的黑暗期以及萌芽期，去坚持和贯彻自己</p><p>最后，此篇碎碎念写于2023-12-08;把时间交还自己,我们回头后再来看看这个碎碎念,是不是会有不同的感悟和理解</p><h1 id="📎-参考文章"><a href="#📎-参考文章" class="headerlink" title="📎 参考文章"></a>📎 参考文章</h1><blockquote><p>💡 如果你也认为这篇生活碎碎念帮助到你的话，</p></blockquote>]]></content>
    
    
    <summary type="html">生活碎碎念_多做少想</summary>
    
    
    
    <category term="生活碎碎念" scheme="https://ruy9527.github.io/categories/%E7%94%9F%E6%B4%BB%E7%A2%8E%E7%A2%8E%E5%BF%B5/"/>
    
    
    <category term="生活碎碎念" scheme="https://ruy9527.github.io/tags/%E7%94%9F%E6%B4%BB%E7%A2%8E%E7%A2%8E%E5%BF%B5/"/>
    
  </entry>
  
  <entry>
    <title>摄影笔记</title>
    <link href="https://ruy9527.github.io/article/post-fd4d0ada97/"/>
    <id>https://ruy9527.github.io/article/post-fd4d0ada97/</id>
    <published>2023-07-18T04:00:00.000Z</published>
    <updated>2025-12-31T04:53:47.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>😀 这里写文章的前言：<br>看摄影文章进行一个笔记记录<br>一个合格的摄影师可以恰当利用镜头虚化所带来的视觉效果,主动控制虚化范围</p></blockquote><p>文件格式:</p><ul><li>RAW: 相机能记录的不加任何渲染的原始图片格式</li><li>JPEG: 相机经过自己的系统和算法算出来的图片</li></ul><h1 id="📝-光圈"><a href="#📝-光圈" class="headerlink" title="📝 光圈"></a>📝 光圈</h1><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p>光圈是镜头里用来控制镜头孔径大小的部件</p><p>孔越小,通过的光自然就越少,相机的感光原件接收到的光少了,画面就会越暗;孔越大,通光量越大,画面就越亮</p><p>f值小的是大光圈,f值大的是小光圈</p><p><img src="/images/posts/post-fd4d0ada97/img-1.png" alt="摄影笔记"></p><p>在不考虑镜头焦段的情况下,同一枚镜头,光圈越大,景深越浅(照片中清晰的范围越小);光圈越小,景深越大(照片中清晰的范围越大)</p><p>看背景后的问题;可以看到f越大,背景其越清晰</p><p><img src="/images/posts/post-fd4d0ada97/img-2.png" alt="摄影笔记"></p><p>物距: 相机离物理的距离;物距越近,景深也就越浅</p><p>长焦镜头: 压缩画面,压缩空间感</p><p>焦段越长,视角越窄,景深越浅,透视畸变越弱;焦段越短,视角越宽,景深越大,透视畸变越强</p><h1 id="📝-ISO"><a href="#📝-ISO" class="headerlink" title="📝 ISO"></a>📝 ISO</h1><p>感光度: 感光度就是相机对光的敏感程度; ISO其实是把ASA和DIN捏合在一起的一种计量单位</p><details><summary>比如</summary><p>如果我们把相机感光度从 ISO 400 调低到 ISO 200，这就等于感光元件的感光能力减小了一倍，等于是减小了一档曝光，在其他参数不变的情况下，画面就会变暗；如果我们把相机感光度从 ISO 400 调高到 ISO 800，这就等于感光元件的感光能力增大了一倍，等于是增大了一档曝光，画面就会变亮。</p></details><p>拉高ISO的代价就是会让画面产生噪点,过高的ISO会直接影响画面的纯净度</p><p><img src="/images/posts/post-fd4d0ada97/img-3.png" alt="摄影笔记"></p><p>白平衡:</p><p>当我们在昏黄的灯光下拍一张白纸，白纸因为灯光照射的原因，会是黄色的；我们在日落之后拍一张白纸，白纸因为天空光线的原因，大概率会是蓝色的。虽然都是白纸，但在不同的光线条件下，它会变成不同的颜色，而相机上的白平衡就是用来平衡这个白色的，让白色在任何情况下都是白色。当白色是准确的白色，而不是乳白、象牙白、冷白、粉白这些飘忽不定的白色，其他颜色也就会跟着该是什么颜色就是什么颜色，以此来矫正一张照片的色温，防止照片偏色</p><p><img src="/images/posts/post-fd4d0ada97/img-4.png" alt="摄影笔记"></p><p>宽容度:</p><p>指的是感光元件所能正确容纳的画面亮度反差的范围,说白了就是,画面最亮的部分和画面最暗的部分,能不能被相机以不损失成像细节的方式给拍下来</p><p>如果能,叫宽容度大;如果不能,扩容度小</p><p>HDR:高动态范围成像</p><p>亮的地方不过曝,暗的地方不死黑;实现的基本原理,在相机上,都是靠多帧合成</p><h1 id="📝-测光"><a href="#📝-测光" class="headerlink" title="📝 测光"></a>📝 测光</h1><p>光比:</p><p>照明环境下,被摄物体暗面与亮面的受光比例</p><p>光比越大,明暗反差也就越大,也就是俗称的”对比度”越高,画面中明亮的部分和阴影部分之间的对比就越明显;光比越小,明暗反差就越小,对比度越低,画面中明亮部分和阴影部分之间的对比就越不明显</p><h1 id="🤗-总结归纳"><a href="#🤗-总结归纳" class="headerlink" title="🤗 总结归纳"></a>🤗 总结归纳</h1><h1 id="📎-参考文章"><a href="#📎-参考文章" class="headerlink" title="📎 参考文章"></a>📎 参考文章</h1><blockquote><p>💡 有关文章的问题，欢迎您在底部评论区留言，一起交流~</p></blockquote>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;😀 这里写文章的前言：&lt;br&gt;看摄影文章进行一个笔记记录&lt;br&gt;一个合格的摄影师可以恰当利用镜头虚化所带来的视觉效果,主动控制虚化范围&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;文件格式:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;RAW:</summary>
        
      
    
    
    
    
  </entry>
  
  <entry>
    <title>开源数据平台_技术篇章</title>
    <link href="https://ruy9527.github.io/article/data_middle_tech/"/>
    <id>https://ruy9527.github.io/article/data_middle_tech/</id>
    <published>2023-06-14T04:00:00.000Z</published>
    <updated>2023-07-22T06:17:48.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>😀 这里写文章的前言：<br>计划一个开源的数据平台; 参考许多数据中台的公司产品 以及 开源项目，进行一个整体的总结;可以大致归纳到如下一些点</p><ul><li>离线</li><li>实时</li><li>算法</li><li>数据服务开发</li><li>数据质量检查</li><li>数据安全</li><li>数据资产</li><li>调度任务</li><li>环境模块</li></ul></blockquote><h1 id="📝系统配置"><a href="#📝系统配置" class="headerlink" title="📝系统配置"></a>📝系统配置</h1><p>最大力度以项目为隔离,来进行隔离 资源&#x2F;用户&#x2F;环境 等信息</p><p>进行如下的内容配置:</p><ul><li>部门  &amp;  租户</li><li>用户  &amp;  角色</li><li>项目   ——&gt;   项目资源(比如yarn,hdfs存储资源,数据源,集群资源)</li><li>业务流程(每个项目下有多个业务流程)</li><li>项目配置(比如这个项目的默认参数等)</li></ul><h1 id="📝离线开发"><a href="#📝离线开发" class="headerlink" title="📝离线开发"></a>📝离线开发</h1><h2 id="开发系统设置"><a href="#开发系统设置" class="headerlink" title="开发系统设置"></a>开发系统设置</h2><ul><li>数据交换 和 数据集成 ： 数据的同步，数据库的同步，csv&#x2F;execl同步,非结构化数据同步</li><li>数据开发 <ol><li>传统数据库 ： MySql &#x2F; GaussDB &#x2F; Oracle &#x2F; PostgreSQL &#x2F; SqlServer &#x2F; DB2 等数据库</li><li>大数据组件： Spark(Sparl&#x2F;SparkSql&#x2F;pySpark) &#x2F; Hive &#x2F; HBase 等</li><li>语言层面: python&#x2F;shell&#x2F;perl</li><li>建表的DDL</li><li>资源文件 : 不同文件格式的类型</li></ol></li></ul><h2 id="表信息"><a href="#表信息" class="headerlink" title="表信息"></a>表信息</h2><p>主要分为数据源类型,以及每种数据源类型的对应的数据库(也就是在项目配置中进行配置了的)</p><p>不同的数据库类型,比如下面的:</p><p>hive&#x2F;mysql&#x2F;oracle等</p><h2 id="运行记录"><a href="#运行记录" class="headerlink" title="运行记录"></a>运行记录</h2><p>对执行的信息进行记录</p><h2 id="资源文件"><a href="#资源文件" class="headerlink" title="资源文件"></a>资源文件</h2><p>对资源文件进行上传,保存以及展示等信息处理(主要是上传到hdfs上)</p><h2 id="自定义函数开发"><a href="#自定义函数开发" class="headerlink" title="自定义函数开发"></a>自定义函数开发</h2><ul><li>Hive函数</li><li>其它函数</li></ul><h2 id="离线作业"><a href="#离线作业" class="headerlink" title="离线作业"></a>离线作业</h2><h3 id="新建业务流程-可以简单的理解为一个工作流"><a href="#新建业务流程-可以简单的理解为一个工作流" class="headerlink" title="新建业务流程(可以简单的理解为一个工作流)"></a>新建业务流程(可以简单的理解为一个工作流)</h3><p>比如 大屏A , 大屏B , 大屏 C 都可以分别建设一条工作流</p><h3 id="新建离线作业"><a href="#新建离线作业" class="headerlink" title="新建离线作业"></a>新建离线作业</h3><ul><li><p>数据同步,抽取数据</p><ol><li>数据源配置:   源头表信息  ——&gt;  目的表信息</li><li>字段映射关系</li><li>参数控制</li></ol><p>属性配置:</p><ul><li>使用的资源组</li><li>运行参数</li><li>本节点输入参数</li><li>本节点输出参数</li><li>依赖资源</li></ul><p>调度配置:</p><ul><li>生成实体的方法</li><li>生效日期</li><li>定时corn表达式(调度周期,根据天&#x2F;星期&#x2F;月等进行配置)</li><li>调度优先级别</li><li>调度依赖: 添加上下游节点 或者 将自身重置为根节点</li><li>调度作用的范围: 当前业务流程 &#x2F; 当前项目 &#x2F; 当前租户</li></ul></li><li><p>脚本开发: 针对不同的数据库进行不同的脚本(还可以选择数据库);不同类型</p><ol><li>Spark : 提交作业的方式,是否数据湖的format文件格式等,使用的版本,spark的参数; 调度的配置依赖上下游的作业</li><li>Hive : 无需多的选择,默认的hive设置即可</li><li>MySql: 数据源类型的选择(假设我配置了多个数据源,是可以进行选择执行和提交;提交版本和发布)</li><li>hbase:  无需多的配置; 本节点输入参数,本节点输出参数</li><li>传统数据库: 支持多个数据源的,都是可以进行选择的,然后进行脚本的编写</li><li>常规语言开发: 对常规的语言进行开发设置(python,shell)</li></ol></li><li><p>建表作业: 不同数据库类型的建表脚本进行开发(不参与工作流进行发布)</p></li></ul><h1 id="📝实时开发"><a href="#📝实时开发" class="headerlink" title="📝实时开发"></a>📝实时开发</h1><h3 id="系统属性设置"><a href="#系统属性设置" class="headerlink" title="系统属性设置"></a>系统属性设置</h3><p>开发系统属性设置:</p><ul><li>数据集成,比如 Flink CDC 等同步</li></ul><p>实时计算</p><ul><li>FlinkSQL</li><li>Flink</li><li>Flink On 数据湖</li></ul><p>建表语句</p><ul><li>Flink DDL</li><li>Flink On 数据湖 DDL</li></ul><p>资源文件类型</p><ul><li>jar</li><li>py</li><li>txt</li><li>json</li><li>xml</li><li>等等</li></ul><h3 id="实时运行的历史"><a href="#实时运行的历史" class="headerlink" title="实时运行的历史"></a>实时运行的历史</h3><p>对实时运行的历史进行一个记录</p><h3 id="实时表的管理"><a href="#实时表的管理" class="headerlink" title="实时表的管理"></a>实时表的管理</h3><p>对实时的表进行获取和管理</p><h3 id="函数管理"><a href="#函数管理" class="headerlink" title="函数管理"></a>函数管理</h3><ul><li>系统函数</li><li>自定义函数</li></ul><h3 id="实时任务开发"><a href="#实时任务开发" class="headerlink" title="实时任务开发"></a>实时任务开发</h3><p>系统参数</p><ul><li>checkpoint 是否开启</li><li>checkpoint的模式是什么配置</li><li>checkpoint的超时设置</li><li>checkpoint的路径设置</li><li>checkpoint保留的个数</li><li>日志配置</li></ul><h1 id="📝服务开发"><a href="#📝服务开发" class="headerlink" title="📝服务开发"></a>📝服务开发</h1><h2 id="自定义函数开发-1"><a href="#自定义函数开发-1" class="headerlink" title="自定义函数开发"></a>自定义函数开发</h2><p>对应一些便捷的函数等进行设置自定义开发</p><h2 id="自定义SQL的API开发"><a href="#自定义SQL的API开发" class="headerlink" title="自定义SQL的API开发"></a>自定义SQL的API开发</h2><ul><li>对应业务流程的选择,也是将什么样的业务给暴露给外侧</li><li>API的名称和请求路径</li><li>请求方式 和 返回类型的配置</li><li>调用的频率和超时时间的设置</li></ul><p>环境的选择:</p><ul><li>数据源类型(比如Mysql,Oracle等;关系型或者mpp型居多,如果是hive或者是spark的话，不是特别建议)</li><li>然后选择对应的 数据源 下面的数据库 ， 来进行脚本的开发，将脚本执行的结果拿到返回数据给返回回去</li><li>API常见的一些分页设置等</li></ul><h1 id="📝运维中心"><a href="#📝运维中心" class="headerlink" title="📝运维中心"></a>📝运维中心</h1><ul><li>对我们的DDL进行统一运维处理</li><li>对我们的离线和实时,统一的任务调度和处理</li></ul><h1 id="🤗-总结归纳"><a href="#🤗-总结归纳" class="headerlink" title="🤗 总结归纳"></a>🤗 总结归纳</h1><h1 id="📎-参考文章"><a href="#📎-参考文章" class="headerlink" title="📎 参考文章"></a>📎 参考文章</h1><blockquote><p>💡 有关文章的问题，欢迎您在底部评论区留言，一起交流~</p></blockquote>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;😀 这里写文章的前言：&lt;br&gt;计划一个开源的数据平台; 参考许多数据中台的公司产品 以及</summary>
        
      
    
    
    
    <category term="数据中台" scheme="https://ruy9527.github.io/categories/%E6%95%B0%E6%8D%AE%E4%B8%AD%E5%8F%B0/"/>
    
    
    <category term="Iceberg" scheme="https://ruy9527.github.io/tags/Iceberg/"/>
    
    <category term="Spark" scheme="https://ruy9527.github.io/tags/Spark/"/>
    
    <category term="源码分析" scheme="https://ruy9527.github.io/tags/%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/"/>
    
    <category term="使用入门" scheme="https://ruy9527.github.io/tags/%E4%BD%BF%E7%94%A8%E5%85%A5%E9%97%A8/"/>
    
    <category term="数据开发" scheme="https://ruy9527.github.io/tags/%E6%95%B0%E6%8D%AE%E5%BC%80%E5%8F%91/"/>
    
    <category term="数据中台" scheme="https://ruy9527.github.io/tags/%E6%95%B0%E6%8D%AE%E4%B8%AD%E5%8F%B0/"/>
    
  </entry>
  
  <entry>
    <title>插件推荐</title>
    <link href="https://ruy9527.github.io/article/plugs_records_info/"/>
    <id>https://ruy9527.github.io/article/plugs_records_info/</id>
    <published>2023-05-31T04:00:00.000Z</published>
    <updated>2025-12-31T05:04:20.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>😀 这里写文章的前言：<br>记录各种软件好用的插件</p></blockquote><h1 id="📝-Google浏览器插件"><a href="#📝-Google浏览器插件" class="headerlink" title="📝 Google浏览器插件"></a>📝 Google浏览器插件</h1><h2 id="沉浸式翻译"><a href="#沉浸式翻译" class="headerlink" title="沉浸式翻译"></a>沉浸式翻译</h2><p>这里我已 Spring 官网为例，它会翻译每段，效果还是很好的</p><p><img src="/images/posts/plugs_records_info/img-1.png" alt="插件推荐"></p><h2 id="Sexy-Undo-Close-Tab"><a href="#Sexy-Undo-Close-Tab" class="headerlink" title="Sexy Undo Close Tab"></a>Sexy Undo Close Tab</h2><p>记录你刚刚关闭的网页，不小心关错了，再也不用去历史记录中进行查找</p><p><img src="/images/posts/plugs_records_info/img-2.png" alt="插件推荐"></p><h2 id="SuperCopy-超级复制"><a href="#SuperCopy-超级复制" class="headerlink" title="SuperCopy 超级复制"></a>SuperCopy 超级复制</h2><p>一键破解禁止右键、破解禁止选择、破解禁止复制、破解禁止粘贴，启用复制，启用右键，启用选择，启用粘贴</p><h2 id="Chrono下载管理器"><a href="#Chrono下载管理器" class="headerlink" title="Chrono下载管理器"></a>Chrono下载管理器</h2><p>比如我们有时候找一个网站的静态资源，就可以直接用这个插件的 资源嗅探 功能</p><p><img src="/images/posts/plugs_records_info/img-3.png" alt="插件推荐"></p><h2 id="Octotree-GitHub-code-tree"><a href="#Octotree-GitHub-code-tree" class="headerlink" title="Octotree - GitHub code tree"></a>Octotree - GitHub code tree</h2><p>当我们访问 github 项目的时候，就不需要挨个点进去看，通过旁边的 ctotree 就可以看到项目的层级等</p><p><img src="/images/posts/plugs_records_info/img-4.png" alt="插件推荐"></p><h2 id="微信公众号同步助手"><a href="#微信公众号同步助手" class="headerlink" title="微信公众号同步助手"></a>微信公众号同步助手</h2><p>微信公众号文章同步助手，支持头条号、简书、知乎、WordPress</p><h2 id="WebChatGPT"><a href="#WebChatGPT" class="headerlink" title="WebChatGPT"></a>WebChatGPT</h2><h1 id="📝-Notion插件"><a href="#📝-Notion插件" class="headerlink" title="📝 Notion插件"></a>📝 Notion插件</h1><h2 id="Notion流程图插件"><a href="#Notion流程图插件" class="headerlink" title="Notion流程图插件"></a>Notion流程图插件</h2><ol><li>Diagrams.net：这个插件是Notion的官方合作伙伴插件，它提供了丰富的绘图工具，包括流程图、组织结构图、UML图等。您可以使用它来在Notion中创建专业的流程图。</li><li>Excalidraw：这个插件允许您在Notion中创建手绘风格的流程图和草图。它提供了简单易用的绘图工具，可以让您快速绘制流程图并进行协作。</li><li>Miro：Miro是一个在线协作白板工具，它提供了丰富的绘图功能，包括流程图、思维导图、用户旅程图等。您可以在Notion中使用Miro插件，将Miro的流程图嵌入到您的Notion页面中。</li></ol><h1 id="📝-IDEA插件"><a href="#📝-IDEA插件" class="headerlink" title="📝 IDEA插件"></a>📝 IDEA插件</h1><ul><li>Bito</li><li>jclasslib Bytecode Viewer</li><li>Maven Helper</li><li>CodeGlance Pro</li><li>Lombok Builder Helper</li><li>MyBatisX</li><li>SonarLint</li></ul><h1 id="google浏览器前端插件"><a href="#google浏览器前端插件" class="headerlink" title="google浏览器前端插件"></a>google浏览器前端插件</h1><h2 id="React-Developer-Tools"><a href="#React-Developer-Tools" class="headerlink" title="React Developer Tools"></a>React Developer Tools</h2><h2 id="Vue-js-devtools"><a href="#Vue-js-devtools" class="headerlink" title="Vue.js devtools"></a>Vue.js devtools</h2><h1 id="🤗-总结归纳"><a href="#🤗-总结归纳" class="headerlink" title="🤗 总结归纳"></a>🤗 总结归纳</h1><p>总结文章的内容</p><h1 id="📎-参考文章"><a href="#📎-参考文章" class="headerlink" title="📎 参考文章"></a>📎 参考文章</h1><ul><li>一些引用</li><li>引用文章</li></ul><blockquote><p>💡 有关文章的问题，欢迎您在底部评论区留言，一起交流~</p></blockquote>]]></content>
    
    
    <summary type="html">一些好用软件的推荐记录</summary>
    
    
    
    <category term="插件" scheme="https://ruy9527.github.io/categories/%E6%8F%92%E4%BB%B6/"/>
    
    
    <category term="工具" scheme="https://ruy9527.github.io/tags/%E5%B7%A5%E5%85%B7/"/>
    
    <category term="插件" scheme="https://ruy9527.github.io/tags/%E6%8F%92%E4%BB%B6/"/>
    
  </entry>
  
  <entry>
    <title>编译问题记录和各种环境配置</title>
    <link href="https://ruy9527.github.io/article/all_env_project_complied/"/>
    <id>https://ruy9527.github.io/article/all_env_project_complied/</id>
    <published>2022-12-22T04:00:00.000Z</published>
    <updated>2023-08-02T01:23:00.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>😀 这里写文章的前言：<br>记录编译各种项目以及开源项目遇到的问题</p><p>本地安装jar包<br>pentaho-aggdesigner-algorithm-5.1.5-jhyde<br>eigenbase-properties-1.1.4<br>greenplum-jdbc-5.1.4</p></blockquote><h1 id="📝-缺少jar包本地编译"><a href="#📝-缺少jar包本地编译" class="headerlink" title="📝 缺少jar包本地编译"></a>📝 缺少jar包本地编译</h1><h2 id="pentaho-aggdesigner-algorithm-5-1-5-jhyde"><a href="#pentaho-aggdesigner-algorithm-5-1-5-jhyde" class="headerlink" title="pentaho-aggdesigner-algorithm-5.1.5-jhyde"></a>pentaho-aggdesigner-algorithm-5.1.5-jhyde</h2><p>jar包</p><p><a href="https://github.com/ruY9527/source-notes/blob/master/java/localhostJars/pentaho-aggdesigner-algorithm-5.1.5-jhyde.jar">📎 pentaho-aggdesigner-algorithm-5.1.5-jhyde.jar</a></p><p>本地安装指令:  -Dfile 切换为自己本地下载存放jar的位置</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">mvn install:install-file -Dfile=/home/luohong/Downloads/pentaho-aggdesigner-algorithm-<span class="number">5.1</span><span class="number">.5</span>-jhyde.jar -DgroupId=org.pentaho -DartifactId=pentaho-aggdesigner-algorithm -Dversion=<span class="number">5.1</span><span class="number">.5</span>-jhyde -Dpackaging=jar</span><br></pre></td></tr></table></figure><h2 id="eigenbase-properties-1-1-4"><a href="#eigenbase-properties-1-1-4" class="headerlink" title="eigenbase-properties-1.1.4"></a>eigenbase-properties-1.1.4</h2><p>jar包</p><p><a href="https://github.com/ruY9527/source-notes/blob/master/java/localhostJars/eigenbase-properties-1.1.4.jar">📎 eigenbase-properties-1.1.4.jar</a></p><p>本地安装指令: -Dfile 切换为自己本地下载存放jar的位置</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">mvn install:install-file -DgroupId=eigenbase -DartifactId=eigenbase-properties -Dversion=<span class="number">1.1</span><span class="number">.4</span> -Dpackaging=jar -Dfile=/home/luohong/Downloads/eigenbase-properties-<span class="number">1.1</span><span class="number">.4</span>.jar</span><br></pre></td></tr></table></figure><h2 id="greenplum-jdbc-5-1-4"><a href="#greenplum-jdbc-5-1-4" class="headerlink" title="greenplum-jdbc-5.1.4"></a>greenplum-jdbc-5.1.4</h2><p>jar包</p><p><a href="https://github.com/ruY9527/source-notes/blob/master/java/localhostJars/greenplum-jdbc-5.1.4.jar">📎 greenplum-jdbc-5.1.4.jar</a></p><p>本地安装指令: -Dfile 切换为自己本地下载存放jar的位置</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">mvn install:install-file -DgroupId=com.pivotal -DartifactId=greenplum-jdbc -Dversion=<span class="number">5.1</span><span class="number">.4</span> -Dpackaging=jar -Dfile=/home/luohong/Downloads/greenplum-jdbc-<span class="number">5.1</span><span class="number">.4</span>.jar</span><br></pre></td></tr></table></figure><h1 id="📝-前端环境配置"><a href="#📝-前端环境配置" class="headerlink" title="📝 前端环境配置"></a>📝 前端环境配置</h1><p>查看镜像源设置:  npm config get registry</p><h2 id="设置镜像源"><a href="#设置镜像源" class="headerlink" title="设置镜像源"></a>设置镜像源</h2><p>阿里: npm config set registry <a href="https://registry.npm.taobao.org/">https://registry.npm.taobao.org</a></p><h2 id="vite问题"><a href="#vite问题" class="headerlink" title="vite问题"></a>vite问题</h2><p>‘vite’ 不是内部或外部命令，也不是可运行的程序 或批处理文件</p><p>处理方法:  npm i 进行安装</p><h1 id="📝-python环境配置"><a href="#📝-python环境配置" class="headerlink" title="📝 python环境配置"></a>📝 python环境配置</h1><h2 id="设置镜像源-1"><a href="#设置镜像源-1" class="headerlink" title="设置镜像源"></a>设置镜像源</h2><p><code>pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple</code></p><p>其它镜像源地址</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">清华：https://pypi.tuna.tsinghua.edu.cn/simple</span><br><span class="line"></span><br><span class="line">阿里云：http://mirrors.aliyun.com/pypi/simple/</span><br><span class="line"></span><br><span class="line">中国科技大学 https://pypi.mirrors.ustc.edu.cn/simple/</span><br><span class="line"></span><br><span class="line">华中理工大学：http://pypi.hustunique.com/</span><br><span class="line"></span><br><span class="line">山东理工大学：http://pypi.sdutlinux.org/ </span><br><span class="line"></span><br><span class="line">豆瓣：http://pypi.douban.com/simple/</span><br></pre></td></tr></table></figure><h2 id="python去除背景方法"><a href="#python去除背景方法" class="headerlink" title="python去除背景方法"></a>python去除背景方法</h2><ul><li>rembg 框架</li></ul><h2 id="python相关资源学习"><a href="#python相关资源学习" class="headerlink" title="python相关资源学习"></a>python相关资源学习</h2><table><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td>名字</td><td>作用</td><td>地址</td></tr><tr><td></td><td>python爬虫学习资料</td><td><a href="https://vip.fxxkpython.com/?p=4699">https://vip.fxxkpython.com/?p=4699</a></td></tr><tr><td></td><td></td><td></td></tr></tbody></table><h1 id="📝-资源记录相关-主要学习资源记录"><a href="#📝-资源记录相关-主要学习资源记录" class="headerlink" title="📝 资源记录相关(主要学习资源记录)"></a>📝 资源记录相关(主要学习资源记录)</h1><h2 id="AI相关"><a href="#AI相关" class="headerlink" title="AI相关"></a>AI相关</h2><table><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td>名字</td><td>作用</td><td>网址</td></tr><tr><td>内容生成</td><td></td><td><a href="http://writesonic.com/">writesonic.com</a></td></tr><tr><td>针对网络的研究操作系统</td><td></td><td><a href="http://nette.io/">nette.io</a></td></tr><tr><td>生成病毒式推文和话题串</td><td></td><td><a href="http://postwise.ai/">postwise.ai</a></td></tr><tr><td>YouTube视频编辑器</td><td></td><td><a href="http://flexclip.com/">FlexClip.com</a></td></tr><tr><td>文本转视频</td><td></td><td><a href="http://synthesia.io/">synthesia.io</a></td></tr><tr><td>设计工具</td><td></td><td><a href="http://designer.microsoft.com/">designer.microsoft.com</a></td></tr></tbody></table><h2 id="AI绘画"><a href="#AI绘画" class="headerlink" title="AI绘画"></a>AI绘画</h2><table><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td>名字</td><td>作用</td><td>网址</td></tr><tr><td>光影文字</td><td>光影文字</td><td><a href="https://mp.weixin.qq.com/s/zbQCwOHsMS4GmMLJJIU_1A">https://mp.weixin.qq.com/s/zbQCwOHsMS4GmMLJJIU_1A</a></td></tr><tr><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td></tr></tbody></table><h2 id="区块链"><a href="#区块链" class="headerlink" title="区块链"></a>区块链</h2><table><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td>名字</td><td>作用</td><td>网址</td></tr><tr><td>区块链学习</td><td></td><td><a href="https://learnweb3.io/">https://learnweb3.io/</a></td></tr><tr><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td></tr></tbody></table><h1 id="📝-IDEA插件记录"><a href="#📝-IDEA插件记录" class="headerlink" title="📝 IDEA插件记录"></a>📝 IDEA插件记录</h1><table><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td>插件名称</td><td>插件作用</td><td>插件备注</td></tr><tr><td><strong>Simple Object Copy</strong></td><td></td><td>一键生成 dto,vo 等插件</td></tr><tr><td><strong>Maven Helper</strong></td><td></td><td>查看maven依赖树情况</td></tr><tr><td><strong>EasyCode</strong></td><td></td><td>一键生成逆向工程</td></tr><tr><td><strong>Iedis</strong></td><td></td><td>Redis连接插件</td></tr><tr><td><strong>Lombok</strong></td><td></td><td>方法自动生成</td></tr><tr><td><strong>Jrebel</strong></td><td></td><td>热部署相关的插件</td></tr><tr><td><strong>Alibaba Java Coding Guidelines</strong></td><td></td><td>阿里巴巴代码检测规范</td></tr><tr><td><strong>VisualVM Launcher</strong></td><td></td><td>性能监控的插件</td></tr><tr><td><strong>CodeGlance</strong></td><td></td><td>左侧窗口的上下滑动框</td></tr><tr><td><strong>Material Theme UI</strong></td><td></td><td>IDEA的UI框架</td></tr><tr><td><strong>stackoverflow</strong></td><td></td><td>链接到   stackoverflow 检查问题</td></tr><tr><td><strong>Codota</strong></td><td></td><td>好</td></tr><tr><td><strong>jclasslib bytecode viewer</strong></td><td></td><td>字节码编译</td></tr><tr><td><strong>PlantUML</strong></td><td></td><td>对象类设计</td></tr><tr><td><strong>Stack trace to UML</strong></td><td></td><td>调用链图</td></tr><tr><td><strong>Git Commit Template</strong></td><td></td><td>git提交规范代码</td></tr><tr><td></td><td></td><td></td></tr></tbody></table><h1 id="📝-资讯资源"><a href="#📝-资讯资源" class="headerlink" title="📝 资讯资源"></a>📝 资讯资源</h1><table><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td>名字</td><td>作用</td><td>网址</td></tr><tr><td>新闻资源</td><td>国外的新闻资源</td><td><a href="https://mgreader.com/">https://mgreader.com/</a></td></tr><tr><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td></tr></tbody></table><h1 id="📝-想看纪录片"><a href="#📝-想看纪录片" class="headerlink" title="📝 想看纪录片"></a>📝 想看纪录片</h1><table><thead><tr><th></th><th></th><th></th><th></th></tr></thead><tbody><tr><td>名字</td><td>状态</td><td>总结</td><td></td></tr><tr><td>诈骗王</td><td></td><td></td><td></td></tr><tr><td>富豪谷底求翻身</td><td></td><td></td><td></td></tr><tr><td>货币崛起</td><td></td><td></td><td></td></tr><tr><td>但是还有书籍</td><td></td><td></td><td></td></tr><tr><td>宇宙</td><td></td><td></td><td></td></tr><tr><td>急诊室的故事</td><td></td><td></td><td></td></tr><tr><td>心智斗争</td><td></td><td></td><td></td></tr><tr><td>女人</td><td></td><td></td><td></td></tr><tr><td>解码比尔盖茨</td><td></td><td></td><td></td></tr><tr><td>绿色星球</td><td></td><td></td><td></td></tr></tbody></table><h1 id="🤗-总结归纳"><a href="#🤗-总结归纳" class="headerlink" title="🤗 总结归纳"></a>🤗 总结归纳</h1><h1 id="📎-参考文章"><a href="#📎-参考文章" class="headerlink" title="📎 参考文章"></a>📎 参考文章</h1><blockquote><p>💡 有关文章的问题，欢迎您在底部评论区留言，一起交流~</p></blockquote>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;😀</summary>
        
      
    
    
    
    <category term="问题记录" scheme="https://ruy9527.github.io/categories/%E9%97%AE%E9%A2%98%E8%AE%B0%E5%BD%95/"/>
    
    
    <category term="使用入门" scheme="https://ruy9527.github.io/tags/%E4%BD%BF%E7%94%A8%E5%85%A5%E9%97%A8/"/>
    
    <category term="框架" scheme="https://ruy9527.github.io/tags/%E6%A1%86%E6%9E%B6/"/>
    
  </entry>
  
  <entry>
    <title>数据中台(技术篇章)</title>
    <link href="https://ruy9527.github.io/article/data_middle_work_tech1/"/>
    <id>https://ruy9527.github.io/article/data_middle_work_tech1/</id>
    <published>2022-12-13T04:00:00.000Z</published>
    <updated>2023-07-23T05:17:03.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>😀 这里写文章的前言：<br>数据中台技术记录</p></blockquote><h1 id="📝-数据底座"><a href="#📝-数据底座" class="headerlink" title="📝 数据底座"></a>📝 数据底座</h1><ul><li>CDH闭源前的最后一个版本</li><li>CDP</li><li>USDP</li><li>DataSophon</li></ul><h1 id="📝-数据交换-数据集成"><a href="#📝-数据交换-数据集成" class="headerlink" title="📝 数据交换(数据集成)"></a>📝 数据交换(数据集成)</h1><p>从各种数据源采集结构化和非结构化数据，包括实时数据采集和批量数据采集</p><p>将来自不同系统和不同格式的数据进行整合，实现一个统一个的数据视图</p><h2 id="数据交换"><a href="#数据交换" class="headerlink" title="数据交换"></a>数据交换</h2><ul><li>API: 采集第三方的API的数据</li><li>日志采集： 采集应用日志</li><li>爬虫:  采集网页数据</li><li>数据源抽取:  直接通过数据源进行抽取</li></ul><h2 id="数据集成"><a href="#数据集成" class="headerlink" title="数据集成"></a>数据集成</h2><h3 id="数据提取-Data-Extraction"><a href="#数据提取-Data-Extraction" class="headerlink" title="数据提取(Data Extraction)"></a>数据提取(Data Extraction)</h3><h3 id="数据清洗（Data-Cleaning"><a href="#数据清洗（Data-Cleaning" class="headerlink" title="数据清洗（Data Cleaning)"></a>数据清洗（Data Cleaning)</h3><h3 id="数据转换（Data-Transformation）"><a href="#数据转换（Data-Transformation）" class="headerlink" title="数据转换（Data Transformation）"></a>数据转换（Data Transformation）</h3><h3 id="数据加载（Data-Loading）"><a href="#数据加载（Data-Loading）" class="headerlink" title="数据加载（Data Loading）"></a>数据加载（Data Loading）</h3><h1 id="数据清洗"><a href="#数据清洗" class="headerlink" title="数据清洗"></a>数据清洗</h1><p>对采集的数据进行过滤、修正、归一化等,提高数据质量,修复脏数据。</p><h1 id="📝-数据开发"><a href="#📝-数据开发" class="headerlink" title="📝 数据开发"></a>📝 数据开发</h1><h2 id="离线开发"><a href="#离线开发" class="headerlink" title="离线开发"></a>离线开发</h2><ul><li>Spark</li><li>Hive</li><li>Flink</li></ul><h2 id="实时开发"><a href="#实时开发" class="headerlink" title="实时开发"></a>实时开发</h2><ul><li>Kafka</li><li>Spark Streaming</li><li>Flink</li></ul><h2 id="算法开发"><a href="#算法开发" class="headerlink" title="算法开发"></a>算法开发</h2><p>暂不支持</p><h1 id="📝-任务调度"><a href="#📝-任务调度" class="headerlink" title="📝 任务调度"></a>📝 任务调度</h1><ul><li>Azkaban</li><li>Airflow</li><li>海滩调度器</li></ul><h1 id="📝-数据可视化"><a href="#📝-数据可视化" class="headerlink" title="📝 数据可视化"></a>📝 数据可视化</h1><p>通告报表,dashboard等对数据和洞见进行直观的展示;帮助用户理解和决策</p><ul><li>Apache Superset:开源的数据可视化和探索工具</li><li>Grafana:开源的数据监测和可视化工具,用于构建监控面板和报表</li><li>Tableau:商业数据可视化工具,用于数据分析和报表制作</li></ul><h1 id="数据存储"><a href="#数据存储" class="headerlink" title="数据存储"></a>数据存储</h1><p>数据湖,HDFS,数据库等进行存储</p><ul><li>Apache Hive:数据仓库,基于HDFS存储</li><li>Apache HBase:NoSQL数据库,基于HDFS存储</li><li>Apache Kudu:列式存储,基于HDFS存储</li><li>Elasticsearch:搜索引擎,文档型数据库</li></ul><h1 id="数据治理"><a href="#数据治理" class="headerlink" title="数据治理"></a>数据治理</h1><p>管理数据中台的用户、权限、流程等,确保数据中台的高效和稳定运行</p><h1 id="元数据管理"><a href="#元数据管理" class="headerlink" title="元数据管理"></a>元数据管理</h1><ul><li>Apache Atlas:统一元数据管理平台</li><li>datahub[<a href="https://github.com/datahub-project/datahub">datahub-project&#x2F;datahub</a>]</li></ul><h1 id="数据管理和数据权限"><a href="#数据管理和数据权限" class="headerlink" title="数据管理和数据权限"></a>数据管理和数据权限</h1><ul><li>Apache Atlas:数据治理平台</li><li>Apache Ranger:实现权限管理</li></ul><h1 id="数据应用"><a href="#数据应用" class="headerlink" title="数据应用"></a>数据应用</h1><p>通过API、数据服务等形式,将数据和分析能力提供给各业务系统使用</p><h1 id="开源项目记录"><a href="#开源项目记录" class="headerlink" title="开源项目记录"></a>开源项目记录</h1><ul><li>[<a href="https://github.com/DTStack">https://github.com/DTStack</a>]</li><li><a href="https://github.com/DTStack/chunjun">https://github.com/DTStack/chunjun</a></li><li><a href="https://github.com/DTStack/Taier">https://github.com/DTStack/Taier</a></li><li><a href="https://github.com/DataLinkDC/dinky">https://github.com/DataLinkDC/dinky</a></li><li><a href="https://github.com/apache/incubator-streampark">https://github.com/apache/incubator-streampark</a></li><li><a href="https://github.com/apache/dolphinscheduler">https://github.com/apache/dolphinscheduler</a></li><li><a href="https://github.com/ververica/flink-cdc-connectors">https://github.com/ververica/flink-cdc-connectors</a></li><li><a href="https://github.com/alldatacenter/alldata">https://github.com/alldatacenter/alldata</a></li><li><a href="https://github.com/WeBankFinTech/WeDataSphere">https://github.com/WeBankFinTech/WeDataSphere</a></li></ul><h1 id="🤗-总结归纳"><a href="#🤗-总结归纳" class="headerlink" title="🤗 总结归纳"></a>🤗 总结归纳</h1><h1 id="📎-参考文章"><a href="#📎-参考文章" class="headerlink" title="📎 参考文章"></a>📎 参考文章</h1><blockquote><p>💡 有关文章的问题，欢迎您在底部评论区留言，一起交流~</p></blockquote>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;😀 这里写文章的前言：&lt;br&gt;数据中台技术记录&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;📝-数据底座&quot;&gt;&lt;a href=&quot;#📝-数据底座&quot; class=&quot;headerlink&quot; title=&quot;📝 数据底座&quot;&gt;&lt;/a&gt;📝</summary>
        
      
    
    
    
    <category term="数据中台" scheme="https://ruy9527.github.io/categories/%E6%95%B0%E6%8D%AE%E4%B8%AD%E5%8F%B0/"/>
    
    
    <category term="思考" scheme="https://ruy9527.github.io/tags/%E6%80%9D%E8%80%83/"/>
    
    <category term="工具" scheme="https://ruy9527.github.io/tags/%E5%B7%A5%E5%85%B7/"/>
    
    <category term="数据中台" scheme="https://ruy9527.github.io/tags/%E6%95%B0%E6%8D%AE%E4%B8%AD%E5%8F%B0/"/>
    
  </entry>
  
  <entry>
    <title>数仓开发规范</title>
    <link href="https://ruy9527.github.io/article/data_warehouse_guide/"/>
    <id>https://ruy9527.github.io/article/data_warehouse_guide/</id>
    <published>2022-08-12T04:00:00.000Z</published>
    <updated>2025-12-31T05:04:05.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>😀 这里写文章的前言：<br>数仓开发规范</p></blockquote><h1 id="📝-ETL规范-Execl文件"><a href="#📝-ETL规范-Execl文件" class="headerlink" title="📝 ETL规范(Execl文件)"></a>📝 ETL规范(Execl文件)</h1><h2 id="文档规范"><a href="#文档规范" class="headerlink" title="文档规范"></a>文档规范</h2><p>execl表头记录所有表的信息，类似mysql的索引</p><table><thead><tr><th></th><th></th><th></th><th></th><th></th></tr></thead><tbody><tr><td>序号</td><td>层次类别</td><td>表名</td><td>中文名称</td><td>备注</td></tr><tr><td>1</td><td>ods</td><td>user_list</td><td>用户信息</td><td>用户信息</td></tr></tbody></table><h2 id="单个表记录-单个execl页"><a href="#单个表记录-单个execl页" class="headerlink" title="单个表记录(单个execl页)"></a>单个表记录(单个execl页)</h2><table><thead><tr><th></th><th></th><th></th><th></th></tr></thead><tbody><tr><td>表英文名</td><td>dwd_ls_md_item_store_df</td><td>设计人</td><td>鲍洋</td></tr><tr><td>表备注</td><td>商品门店级别</td><td></td><td></td></tr><tr><td>备注</td><td>每天做快照，根据后期需求，再扩展计算指标</td><td>ETL策略</td><td>日全量</td></tr><tr><td></td><td></td><td></td><td></td></tr><tr><td>源表基本信息</td><td></td><td></td><td></td></tr><tr><td>表中文名称</td><td>表英文名称</td><td>别名</td><td>数据总量</td></tr><tr><td>商品所有基本信息</td><td>ods_erp.o_bas_club_item</td><td>ci</td><td>835943</td></tr><tr><td>商品所有基本信息1</td><td>ods_erp.o_bas_club_item1</td><td>ci1</td><td>835943</td></tr><tr><td></td><td></td><td></td><td></td></tr><tr><td>表关联关系描述</td><td></td><td></td><td></td></tr><tr><td>源表别名1</td><td>关联类型</td><td>源表别名2</td><td>备注</td></tr><tr><td>ci</td><td>inner join</td><td>ci1</td><td></td></tr><tr><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td></tr><tr><td>字段映射描述(组1)</td><td></td><td></td><td></td></tr><tr><td>目标表</td><td>源表</td><td>规则</td><td></td></tr><tr><td>列名</td><td>字段类型</td><td>列描述</td><td>列名</td></tr><tr><td>club_nbr</td><td>bigint</td><td>门店号</td><td>ci.club_nbr</td></tr><tr><td>item_nbr</td><td>bigint</td><td>商品号</td><td>ci.item_nbr</td></tr><tr><td>item_on_shelf_date</td><td>date</td><td>上架日期</td><td>ci.item_on_shelf_date</td></tr><tr><td>item_off_shelf_dt</td><td>date</td><td>下架日期</td><td>ci.item_off_shelf_dt</td></tr><tr><td>markdown_status_cd</td><td>bigint</td><td></td><td>ci.markdown_status_cd</td></tr><tr><td>item_status_code</td><td>string</td><td>商品状态</td><td>ci.item_status_code</td></tr><tr><td>unit_retail_amt</td><td>decimal(25,10)</td><td>单位零售金额</td><td>ci.unit_retail_amt</td></tr><tr><td>link_item_nbr</td><td>bigint</td><td>链接上号号</td><td>ci.link_item_nbr</td></tr><tr><td>last_change_ts</td><td>timestamp</td><td>最近变更时间</td><td>ci.last_change_ts</td></tr><tr><td>last_change_userid</td><td>string</td><td>最近变更人</td><td>ci.last_change_userid</td></tr><tr><td>non_mbr_upchrg_ind</td><td>string</td><td></td><td>ci.non_mbr_upchrg_ind</td></tr><tr><td>unit_retail_chg_dt</td><td>date</td><td></td><td>ci.unit_retail_chg_dt</td></tr><tr><td>last_markdown_ind</td><td>string</td><td></td><td>ci.last_markdown_ind</td></tr><tr><td>whpk_sell_amt</td><td>decimal(25,10)</td><td></td><td>ci.whpk_sell_amt</td></tr><tr><td>vnpk_cost_amt</td><td>decimal(25,10)</td><td></td><td>ci.vnpk_cost_amt</td></tr><tr><td>last_update_pgm_id</td><td>string</td><td></td><td>ci1.last_update_pgm_id</td></tr><tr><td>item_create_dt</td><td>date</td><td></td><td>ci.item_create_dt</td></tr><tr><td>cancel_whn_out_dt</td><td>date</td><td></td><td>ci.cancel_whn_out_dt</td></tr><tr><td>cncl_unit_rtl_amt</td><td>decimal(25,10)</td><td></td><td>ci.cncl_unit_rtl_amt</td></tr><tr><td>lead_time_qty</td><td>smallint</td><td></td><td>ci.lead_time_qty</td></tr><tr><td>prompt_price_ind</td><td>string</td><td>促销价</td><td>ci.prompt_price_ind</td></tr><tr><td>lease_sales_pct</td><td>decimal(25,10)</td><td></td><td>ci.lease_sales_pct</td></tr><tr><td>lease_eff_date</td><td>date</td><td></td><td>ci.lease_eff_date</td></tr><tr><td>lease_exp_date</td><td>date</td><td></td><td>ci.lease_exp_date</td></tr><tr><td>last_sold_date</td><td>date</td><td></td><td>ci.last_sold_date</td></tr><tr><td>lease_dflt_sls_pct</td><td>decimal(25,10)</td><td></td><td>ci.lease_dflt_sls_pct</td></tr><tr><td>itemfile_source_nm</td><td>string</td><td></td><td>ci.itemfile_source_nm</td></tr><tr><td>max_retail_amt</td><td>decimal(25,10)</td><td>最大零售金额</td><td>ci.max_retail_amt</td></tr><tr><td>whpk_sell_chg_rsn_cd</td><td>bigint</td><td></td><td>ci.whpk_sell_chg_rsn_cd</td></tr></tbody></table><h1 id="ODS作业规范-Execl文件"><a href="#ODS作业规范-Execl文件" class="headerlink" title="ODS作业规范(Execl文件)"></a>ODS作业规范(Execl文件)</h1><h2 id="模板说明-首页execl"><a href="#模板说明-首页execl" class="headerlink" title="模板说明(首页execl)"></a>模板说明(首页execl)</h2><table><thead><tr><th></th><th></th><th></th><th></th></tr></thead><tbody><tr><td>基本信息</td><td></td><td></td><td></td></tr><tr><td>制定人</td><td>鲍某</td><td>编写时间</td><td>2022&#x2F;9&#x2F;28</td></tr><tr><td>审核人</td><td></td><td>审核时间</td><td></td></tr><tr><td>版本</td><td>V1.0</td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td></tr><tr><td>文档修订历史</td><td></td><td></td><td></td></tr><tr><td>序号</td><td>版本号</td><td>修订章节</td><td>修订原因</td></tr><tr><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td></tr></tbody></table><h2 id="工程情况-单页execl"><a href="#工程情况-单页execl" class="headerlink" title="工程情况(单页execl)"></a>工程情况(单页execl)</h2><table><thead><tr><th></th><th></th><th></th><th></th><th></th></tr></thead><tbody><tr><td>版本时间</td><td>工程</td><td>调度数</td><td>&gt;&#x3D;100W数量表</td><td>备注</td></tr><tr><td>2022&#x2F;9&#x2F;1</td><td>ods_user</td><td>2</td><td>0</td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td></tr></tbody></table><h2 id="ods作业梳理"><a href="#ods作业梳理" class="headerlink" title="ods作业梳理"></a>ods作业梳理</h2><table><thead><tr><th></th><th></th><th></th><th></th><th></th><th></th><th></th><th></th><th></th><th></th><th></th><th></th><th></th><th></th></tr></thead><tbody><tr><td>TASK</td><td>属主</td><td>SRC_TBL</td><td>TGT_DB</td><td>TGT_TBL</td><td>抽取方式</td><td>上线时间</td><td>调度频率</td><td>调度时间</td><td>更新时间</td><td>平台</td><td>当前状态</td><td>数据量</td><td>备注</td></tr><tr><td>目标表名</td><td>userdb</td><td>源表名</td><td>ods_userdb</td><td>o_[源表名]_di</td><td>增量</td><td>2022&#x2F;9&#x2F;1</td><td>每天一次</td><td>每天02:01</td><td></td><td>dolphinscheduler</td><td>激活</td><td>1379</td><td></td></tr><tr><td>目标表名</td><td>userdb</td><td>源表名</td><td>ods_userdb</td><td>o_[源表名]_df</td><td>全量</td><td>2022&#x2F;9&#x2F;1</td><td>每天一次</td><td>每天02:01</td><td></td><td>dolphinscheduler</td><td>激活</td><td>1379</td><td></td></tr></tbody></table><h1 id="数仓命名规范"><a href="#数仓命名规范" class="headerlink" title="数仓命名规范"></a>数仓命名规范</h1><h2 id="库命名规范"><a href="#库命名规范" class="headerlink" title="库命名规范"></a>库命名规范</h2><table><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td></td><td>正式库</td><td>临时库</td></tr><tr><td></td><td>ods_[source database name ]</td><td>ods_[source database name ]_tmp</td></tr><tr><td></td><td>dwd_db</td><td>dwd_db_tmp</td></tr><tr><td></td><td>dws_db</td><td>dws_db_tmp</td></tr><tr><td></td><td>ads_db</td><td>ads_db_tmp</td></tr><tr><td></td><td>dim_db</td><td>dim_db_tmp</td></tr></tbody></table><h2 id="表命名规范"><a href="#表命名规范" class="headerlink" title="表命名规范"></a>表命名规范</h2><ul><li>所有层级表必须要加字段包括：etl_load_ts</li><li>表命名使用英文小写字母，单词之间用下划线分开，总长度不能超过 40 个字符</li></ul><h2 id="表类型"><a href="#表类型" class="headerlink" title="表类型"></a>表类型</h2><p>明细表：dtl 中间表：mid 信息：info 配置表：cfg<br>拉链表：zip 临时表：tmp 汇总表：sum 日志表：log<br>手工填报：imp(开头) 实时表：re</p><h2 id="数据周期"><a href="#数据周期" class="headerlink" title="数据周期"></a>数据周期</h2><p>时全量：hf 时增量：hi 时快照：hs<br>日全量：df 日增量：di 日快照：ds<br>周全量：wf 周增量：wi 周快照：ws<br>月全量：mf 月增量：mi 月快照：ms<br>注： df 每天全量（表没有分区），di 每天增量（每天一个分区），ds 每天增量保留<br>历史到目前记录（一天一个分区）</p><h2 id="ods命名规范"><a href="#ods命名规范" class="headerlink" title="ods命名规范"></a>ods命名规范</h2><ul><li>增量：ods_源库名.o_{表名}_di</li><li>全量：ods_源库名.o_{表名}_df</li><li>接口：ods_源库名.o_api_[api 数据名称]_[数据周期]</li><li>文件：ods_源库名.o_flie_[业务数据名称]_[数据周期]</li><li>数据量：历史数据量小于 50W 条记录，且未来增长慢的全量同步</li><li>字段命名规范：字段默认使用源系统的字段名</li><li>字段名与关键字冲突：在源字段名后加上_col，即源字段名_col</li></ul><h2 id="dwd命名规范"><a href="#dwd命名规范" class="headerlink" title="dwd命名规范"></a>dwd命名规范</h2><p>{库}.{层级}<em>{业务域}</em>{数据域}<em>{业务内容}</em>{表类型}<em>{数据周期} 例如：dwd_db.dwd_ds_user</em>{order_info}_dtl_di</p><h2 id="dws命名规范"><a href="#dws命名规范" class="headerlink" title="dws命名规范"></a>dws命名规范</h2><p>{库}.{层级}<em>{业务域}</em>{数据域}<em>{业务内容}</em>{表类型}<em>{数据周期} 例如：dws_db.dws_ds_user</em>{order_info}_sum_di</p><h2 id="dim命名规范"><a href="#dim命名规范" class="headerlink" title="dim命名规范"></a>dim命名规范</h2><p>{库}.{层级}<em>{pub&#x2F;维度内容}</em>{数据周期} </p><p>例如：dim_db.dim_pub_area_df </p><p><strong>其中的 pub 与具体业务无关，各个业务部都可以共用，例如时间维度</strong></p><h2 id="ads命名规范"><a href="#ads命名规范" class="headerlink" title="ads命名规范"></a>ads命名规范</h2><p>{库}.{层级}<em>{项目内容|自定义}</em>{表类型}_{数据周期} 例如：ads_db.ads_user_dmp_sum_df</p><h2 id="临时表"><a href="#临时表" class="headerlink" title="临时表"></a>临时表</h2><p>{临时库}.{表名}_tmp{序号} 例如：</p><p>dwd_db_tmp.dwd_order_info_dtl_di_tmp01 dwd_db_tmp.dwd_order_info_dtl_di_tmp02</p><h2 id="分区表"><a href="#分区表" class="headerlink" title="分区表"></a>分区表</h2><p>分区字段：</p><ul><li>分 mi（00-59） 如 28</li><li>时 hh（00-23） 如 10</li><li>日 yyyy-mm-dd 如 2022-09-28</li><li>月 yyyy-mm    如 2022-09</li><li>年 yyyy       如 2022</li></ul><p>区分表：分区表取数必须要加分区限制,分区注释必须注明根据那个字段进行分区</p><p>表分区到<strong>时、分</strong>分区级别时，表设计使用多级分区 ，如 时分区表；ts：表示日，hh： 表示时；分分区表：ts：表示日，hh：表示时；mi:表示分</p><h2 id="实时表命名规范"><a href="#实时表命名规范" class="headerlink" title="实时表命名规范"></a>实时表命名规范</h2><ul><li>各数仓层次表设计以 re_ 开头</li><li>ODS 层表设计：{库}.re_{ods}<em>{业务域}</em>{数据域}<em>{业务内容}</em>{数据周期}，其他命 名规范见</li><li>DWD 层表设计：{库}.re_{dwd}<em>{业务域}</em>{数据域}<em>{业务内容}</em>{数据周期}，其他命 名规范见</li><li>DWS 层表设计：{库}.re_{dws}<em>{业务域}</em>{数据域}<em>{业务内容}</em>{数据周期}，其他命 名规范见</li><li>DIM 层表设计：{库}.re_{dim}<em>{pub&#x2F;维度内容}</em>{数据周期}，其他命名规范见</li></ul><h1 id="字段类型规范"><a href="#字段类型规范" class="headerlink" title="字段类型规范"></a>字段类型规范</h1><ul><li>字符：字符串类型使用string</li><li>数字：整形最大值 10 位以上：bigint ; 整形最大值在 3 位到 9 位：int（不考虑）; 非整形数字类型：decimal(25,10)</li><li>时间类型：最小粒度为天： date ；时间： timestamp</li><li>布尔类型是否判断：is_{业务}，不允许出现空值 ; 0: 表示否， 1：表示是</li></ul><h1 id="工作流命名"><a href="#工作流命名" class="headerlink" title="工作流命名"></a>工作流命名</h1><h2 id="ods"><a href="#ods" class="headerlink" title="ods"></a>ods</h2><p>工作流: job_{ods_库名}_{序号} 例如：job_ods_ams</p><p>task: 目标表名 例如：o_order_info_df</p><h2 id="dwd"><a href="#dwd" class="headerlink" title="dwd"></a>dwd</h2><p>工作流: job_dwd_{业务域}<em>{数据域}</em>{序号} 例如：job_dwd_ds_user</p><p>task: 目标表名 例如：dwd_ds_user_xxx_df</p><h2 id="dws"><a href="#dws" class="headerlink" title="dws"></a>dws</h2><p>工作流：job_dws_{业务域}<em>{数据域}</em>{序号} 例如：job_dws_ds_user</p><p>task：目标表名 例如：dws_ds_user_xxx_df</p><h2 id="ads"><a href="#ads" class="headerlink" title="ads"></a>ads</h2><p>工作流：job_ads_{业务域}_{序号} 例如：job_ads_ds</p><p>task: 目标表名 例如：ads_user_dmp_xxx_df</p><h1 id="数据模型设计"><a href="#数据模型设计" class="headerlink" title="数据模型设计"></a>数据模型设计</h1><h2 id="横向分层"><a href="#横向分层" class="headerlink" title="横向分层"></a>横向分层</h2><h3 id="ods-1"><a href="#ods-1" class="headerlink" title="ods"></a>ods</h3><p>操作数据层，即原始数据层。ODS 层有些时候会细分为两层，一个 STG 数据缓冲层，存原 始数据，一个 ODS，存简单清洗的数据</p><h3 id="dwd-1"><a href="#dwd-1" class="headerlink" title="dwd"></a>dwd</h3><p>明细数据层，对数据进行清洗、代码统一、字段统一、格式统一、简单聚合等工作。DWD 层存在的意义是做数据的标准化，为后续的处理提供干净、统一、标准的数据</p><h3 id="dws-1"><a href="#dws-1" class="headerlink" title="dws"></a>dws</h3><p>数据汇总层，按照业务目标，对已经处理好的数据进行横向汇聚、纵向汇总。按照宽表模型 进行数据冗余和预计算，以空间换时间</p><h3 id="dim"><a href="#dim" class="headerlink" title="dim"></a>dim</h3><p>维度层，即存储所有维度（可以理解为码表）。其实 DIM 不应该单独作为一层，因为从 DWD 层开始向上的所有层都需要用到 DIM 中的内容，是横跨多层的</p><h3 id="ads-1"><a href="#ads-1" class="headerlink" title="ads"></a>ads</h3><p>应用服务层，一般就直接对接 OLAP 分析，或者业务层数据调用接口</p><h2 id="纵向分域"><a href="#纵向分域" class="headerlink" title="纵向分域"></a>纵向分域</h2><p>主题域通常是联系较为紧密的数据主题的集合，方便寻找和使用数据</p><h3 id="dw层"><a href="#dw层" class="headerlink" title="dw层"></a>dw层</h3><p>正式库： dw_db 临时库：dw_db</p><p>划分依据：按照业务或业务过程划分,比如一个靠销售广告位置的门户网站主题域可能会有 广告域，客户域等，而广告域可能就会有广告的库存，销售分析、内部投放分析等主题</p><h3 id="DIM层"><a href="#DIM层" class="headerlink" title="DIM层"></a>DIM层</h3><p>正式库： dim_db 临时库：dim_db_tmp </p><p>划分依据：按照公司主数据划分</p><h3 id="DM层"><a href="#DM层" class="headerlink" title="DM层"></a>DM层</h3><p>正式库： dm_db 临时库：dm_db_tmp </p><p>划分依据：按照主题域进行划分</p><h3 id="ADS层"><a href="#ADS层" class="headerlink" title="ADS层"></a>ADS层</h3><p>正式库： ads_db_tables 临时库：ads_db_tmp</p><p>划分依据：</p><ol><li>根据需求方划分：比如需求方为财务部，就可以设定对应的财务主题域（业务场景）， 而财务主题域里面可能就会有员工工资分析</li><li>根据项目划分</li></ol><p><img src="/images/posts/data_warehouse_guide/img-1.png" alt="数仓开发规范"></p><h1 id="🤗-总结归纳"><a href="#🤗-总结归纳" class="headerlink" title="🤗 总结归纳"></a>🤗 总结归纳</h1><h1 id="📎-参考文章"><a href="#📎-参考文章" class="headerlink" title="📎 参考文章"></a>📎 参考文章</h1><blockquote><p>💡 有关文章的问题，欢迎您在底部评论区留言，一起交流~</p></blockquote>]]></content>
    
    
    <summary type="html">数仓开发规范记录</summary>
    
    
    
    <category term="数据开发" scheme="https://ruy9527.github.io/categories/%E6%95%B0%E6%8D%AE%E5%BC%80%E5%8F%91/"/>
    
    
    <category term="数据开发" scheme="https://ruy9527.github.io/tags/%E6%95%B0%E6%8D%AE%E5%BC%80%E5%8F%91/"/>
    
    <category term="规范" scheme="https://ruy9527.github.io/tags/%E8%A7%84%E8%8C%83/"/>
    
  </entry>
  
  <entry>
    <title>hadoop以及Iceberg在win10运行环境配置</title>
    <link href="https://ruy9527.github.io/article/hadoop_iceberg_in_win/"/>
    <id>https://ruy9527.github.io/article/hadoop_iceberg_in_win/</id>
    <published>2022-07-02T04:00:00.000Z</published>
    <updated>2025-12-31T05:03:02.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>😀 这里写文章的前言：<br>一个简单的开头,简述这篇文章讨论的问题、目标、人物、背景是什么？并简述你给出的答案。</p></blockquote><blockquote><p>可以说说你的故事：阻碍、努力、结果成果，意外与转折。</p></blockquote><h1 id="📝-下载包准备"><a href="#📝-下载包准备" class="headerlink" title="📝 下载包准备"></a>📝 下载包准备</h1><h2 id="hadoop下载"><a href="#hadoop下载" class="headerlink" title="hadoop下载"></a>hadoop下载</h2><p>下载地址：<a href="https://archive.apache.org/dist/hadoop/common/">https://archive.apache.org/dist/hadoop/common/</a></p><p>选择我们对应的hadoop版本;比如我这里是是3.1.1就选择3.1.1</p><p><img src="/images/posts/hadoop_iceberg_in_win/img-1.png" alt="hadoop以及Iceberg在win10运行环境配置"></p><h2 id="win10-需要的脚本下载"><a href="#win10-需要的脚本下载" class="headerlink" title="win10 需要的脚本下载"></a>win10 需要的脚本下载</h2><p><a href="https://github.com/cdarlint/winutils/tree/master">https://github.com/cdarlint/winutils/tree/master</a></p><p>选择我们对应的hadoop的版本</p><p><img src="/images/posts/hadoop_iceberg_in_win/img-2.png" alt="hadoop以及Iceberg在win10运行环境配置"></p><h2 id="对应版本的脚本拷贝"><a href="#对应版本的脚本拷贝" class="headerlink" title="对应版本的脚本拷贝"></a>对应版本的脚本拷贝</h2><h3 id="操作一"><a href="#操作一" class="headerlink" title="操作一"></a>操作一</h3><p>将 hadoop.ddl 和 winutils.exe 拷贝到 下载的hadoop的版本的bin中</p><p><img src="/images/posts/hadoop_iceberg_in_win/img-3.png" alt="hadoop以及Iceberg在win10运行环境配置"></p><h3 id="操作二"><a href="#操作二" class="headerlink" title="操作二"></a>操作二</h3><p>将 hadoop.ddl 文件同时拷贝一份到 C:\Windows\System32 目录</p><p><img src="/images/posts/hadoop_iceberg_in_win/img-4.png" alt="hadoop以及Iceberg在win10运行环境配置"></p><h3 id="操作三"><a href="#操作三" class="headerlink" title="操作三"></a>操作三</h3><p>配置hadoop中的etc\hadoop的 hadoop-env.cmd 文件的java地址;切换为自己的java路径;</p><p>如果是C盘的话，Program Files需要替换为 PROGRA~1 ; 如下</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">set <span class="variable constant_">JAVA_HOME</span>=<span class="attr">C</span>:\\<span class="variable constant_">PROGRA</span>~<span class="number">1</span>\\<span class="title class_">Java</span>\\jdk1<span class="number">.8</span><span class="number">.0_161</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><p><img src="/images/posts/hadoop_iceberg_in_win/img-5.png" alt="hadoop以及Iceberg在win10运行环境配置"></p><h3 id="操作四-配置环境变量"><a href="#操作四-配置环境变量" class="headerlink" title="操作四: 配置环境变量"></a>操作四: 配置环境变量</h3><p>HADOO_HOME配置</p><p><img src="/images/posts/hadoop_iceberg_in_win/img-6.png" alt="hadoop以及Iceberg在win10运行环境配置"></p><p>PATH中配置%HADOOP_HOME%\bin</p><p><img src="/images/posts/hadoop_iceberg_in_win/img-7.png" alt="hadoop以及Iceberg在win10运行环境配置"></p><h3 id="操作五：测试是否成功"><a href="#操作五：测试是否成功" class="headerlink" title="操作五：测试是否成功"></a>操作五：测试是否成功</h3><p>cmd中输入hadoop version;如果可以看到版本，就说明配置是成功的</p><p><img src="/images/posts/hadoop_iceberg_in_win/img-8.png" alt="hadoop以及Iceberg在win10运行环境配置"></p><h2 id="操作hdfs以及Iceberg"><a href="#操作hdfs以及Iceberg" class="headerlink" title="操作hdfs以及Iceberg"></a>操作hdfs以及Iceberg</h2><h3 id="注意点"><a href="#注意点" class="headerlink" title="注意点:"></a>注意点:</h3><ol><li>通过iceberg和hive去读取hdfs的时候，会读取集群的名字;所以这里需要配置根据集群的名字能映射到NameNode(状态为Active的最保险)</li><li>比如这里的usdp215对应的active namenode是 172.21.129.218 节点的话;则在host里进行配置即可</li><li>到底就可以通过iceberg或者hdfs等api在win10上读取hadoop的数据等</li></ol><p><img src="/images/posts/hadoop_iceberg_in_win/img-9.png" alt="hadoop以及Iceberg在win10运行环境配置"></p><p><img src="/images/posts/hadoop_iceberg_in_win/img-10.png" alt="hadoop以及Iceberg在win10运行环境配置"></p><p><img src="/images/posts/hadoop_iceberg_in_win/img-11.png" alt="hadoop以及Iceberg在win10运行环境配置"></p><h1 id="🤗-总结归纳"><a href="#🤗-总结归纳" class="headerlink" title="🤗 总结归纳"></a>🤗 总结归纳</h1><h1 id="📎-参考文章"><a href="#📎-参考文章" class="headerlink" title="📎 参考文章"></a>📎 参考文章</h1><ul><li><a href="https://blog.csdn.net/qq_42970173/article/details/123714605">https://blog.csdn.net/qq_42970173&#x2F;article&#x2F;details&#x2F;123714605</a></li><li><a href="https://blog.csdn.net/yy8623977/article/details/124047743">https://blog.csdn.net/yy8623977/article/details/124047743</a></li></ul><blockquote><p>💡 有关文章的问题，欢迎您在底部评论区留言，一起交流~</p></blockquote>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;😀</summary>
        
      
    
    
    
    <category term="数据湖" scheme="https://ruy9527.github.io/categories/%E6%95%B0%E6%8D%AE%E6%B9%96/"/>
    
    
    <category term="hadoop" scheme="https://ruy9527.github.io/tags/hadoop/"/>
    
    <category term="hive" scheme="https://ruy9527.github.io/tags/hive/"/>
    
    <category term="数据湖" scheme="https://ruy9527.github.io/tags/%E6%95%B0%E6%8D%AE%E6%B9%96/"/>
    
  </entry>
  
  <entry>
    <title>Spark3.0.x合并Iceberg小文件</title>
    <link href="https://ruy9527.github.io/article/spark_3_0_x_iceberg_small_file/"/>
    <id>https://ruy9527.github.io/article/spark_3_0_x_iceberg_small_file/</id>
    <published>2022-07-02T04:00:00.000Z</published>
    <updated>2025-12-31T05:04:08.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>😀 这里写文章的前言：<br>Spark3.0.1合并Iceberg的小文件</p></blockquote><h1 id="📝-Iceberg小文件处理"><a href="#📝-Iceberg小文件处理" class="headerlink" title="📝 Iceberg小文件处理"></a>📝 Iceberg小文件处理</h1><h2 id="Spark3-0-1"><a href="#Spark3-0-1" class="headerlink" title="Spark3.0.1"></a>Spark3.0.1</h2><p>Spark3.0.1版本的sql执行</p><p>参数说明:</p><ul><li>如果有 catalog 的设置的话,在system前面加上 catalog. (catalog是你具体的catalog名字,比如spark_hadoop)</li><li>ods_bigscreen.o_ecuser_list: 具体的数据库名字和表名字</li><li>TIMESTAMP ‘2022-09-08 00:00:00.000’: 该时间之前的数据进行清除</li><li>1 是保留的版本数量，比如这里的1就是1</li></ul><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">CALL system.expire_snapshots(<span class="string">&#x27;ods_bigscreen.o_ecuser_list&#x27;</span>, TIMESTAMP <span class="string">&#x27;2022-09-08 00:00:00.000&#x27;</span>, <span class="number">1</span>)</span><br></pre></td></tr></table></figure><p>执行结果如下图:</p><p>参数解析:</p><ul><li>deleted_data_files_count 删除对应表的data文件夹个数。如果我们的文件格式选择的是 parquet，那么文件是以 .parquet 结尾，比如 00000-0-0eca9076-9c03-4077-baa9-e68769e15c58-00001.parquet 就是一个数据文件。</li><li>deleted_manifest_files_count: 删除元数据metadata文件夹的数据。其里面列出了组成某个快照（snapshot）的数据文件列表。每行都是每个数据文件的详细描述，包括数据文件的状态、文件路径、分区信息、列级别的统计信息（比如每列的最大最小值、空值数等）、文件的大小以及文件里面数据的行数等信息。其中列级别的统计信息在 Scan 的时候可以为算子下推提供数据，以便可以过滤掉不必要的文件。每次更新会产生多个清单文件。</li><li>deleted_manifest_lists_count: 清单列表也是元数据文件，其里面存储的是清单文件的列表，每个清单文件占据一行。每行中存储了清单文件的路径、清单文件里面存储数据文件的分区范围、增加了几个数据文件、删除了几个数据文件等信息。这些信息可以用来在查询时提供过滤。清单列表也是 avro 格式进行存储的，所以是以 .avro 后缀结尾的；而且这个文件是以 snap- 开头的，比如 snap-7389540589641972921-1-aa90f6ed-aee2-49c7-a61c-bd13ed411c66.avro，其中 7389540589641972921 这串数字是代表快照 id（snapshot_id）。每次更新都会产生一个清单列表文件。</li></ul><p><img src="/images/posts/spark_3_0_x_iceberg_small_file/img-1.png" alt="Spark3.0.x合并Iceberg小文件"></p><h2 id="pySpark进行处理"><a href="#pySpark进行处理" class="headerlink" title="pySpark进行处理"></a>pySpark进行处理</h2><p>使用 use 获取 某个数据库的表，然后对这些表进行 合并小文件处理</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">#!/user/bin/env spark-python</span><br><span class="line">#-*-coding:utf-<span class="number">8</span> -*-</span><br><span class="line"><span class="keyword">import</span> sys,os</span><br><span class="line">from pyspark.sql <span class="keyword">import</span> SparkSession</span><br><span class="line"></span><br><span class="line"># init spark</span><br><span class="line">def <span class="title function_">getSpark</span><span class="params">()</span>:</span><br><span class="line">    # spark连接初始化</span><br><span class="line">    spark = SparkSession \</span><br><span class="line">        .builder \</span><br><span class="line">        .appName(<span class="string">&quot;PySpark_Platform_DataLake&quot;</span>) \</span><br><span class="line">        .config(<span class="string">&quot;spark.jars.packages&quot;</span>,<span class="string">&quot;org.apache.iceberg:iceberg-spark3:0.13.2&quot;</span>) \</span><br><span class="line">        .config(<span class="string">&quot;spark.sql.extensions&quot;</span>,<span class="string">&quot;org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions&quot;</span>) \</span><br><span class="line">        .config(<span class="string">&quot;spark.sql.catalog.spark_catalog&quot;</span>,<span class="string">&quot;org.apache.iceberg.spark.SparkSessionCatalog&quot;</span>) \</span><br><span class="line">        .config(<span class="string">&quot;spark.sql.catalog.spark_catalog.type&quot;</span>,<span class="string">&quot;hive&quot;</span>) \</span><br><span class="line">        .config(<span class="string">&quot;spark.sql.catalog.iceberg&quot;</span>,<span class="string">&quot;org.apache.iceberg.spark.SparkCatalog&quot;</span>) \</span><br><span class="line">        .config(<span class="string">&quot;spark.sql.catalog.iceberg.type&quot;</span>,<span class="string">&quot;hadoop&quot;</span>) \</span><br><span class="line">        .config(<span class="string">&quot;spark.sql.catalog.iceberg.warehouse&quot;</span>,<span class="string">&quot;hdfs:///yundeecluster/datalake&quot;</span>) \</span><br><span class="line">        .enableHiveSupport() \</span><br><span class="line">        .getOrCreate()</span><br><span class="line">    <span class="keyword">return</span> spark</span><br><span class="line"></span><br><span class="line">        </span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">        spark = getSpark()</span><br><span class="line">        spark.sql(<span class="string">&quot;use xiaobao_bigscreent&quot;</span>)</span><br><span class="line">        df = spark.sql(<span class="string">&quot;show tables&quot;</span>)</span><br><span class="line">        <span class="keyword">for</span> row in df.collect():</span><br><span class="line">                <span class="keyword">if</span> row[<span class="string">&#x27;tableName&#x27;</span>] == <span class="string">&#x27;login_record_info&#x27;</span>:</span><br><span class="line">                    <span class="keyword">continue</span>;</span><br><span class="line">                baseTable = <span class="string">&#x27;xiaobao_bigscreent.&#x27;</span> + row[<span class="string">&#x27;tableName&#x27;</span>]</span><br><span class="line">                # 重写数据文件, data 目录</span><br><span class="line">                rewrite_manifests_sql = <span class="string">&quot;CALL system.rewrite_manifests(&#x27;&#123;0&#125;&#x27;)&quot;</span>.format(baseTable)</span><br><span class="line">                # 重写 metadata 目录</span><br><span class="line">                rewrite_data_files_sql = <span class="string">&quot;call system.rewrite_data_files(&#x27;&#123;0&#125;&#x27;)&quot;</span>.format(baseTable)</span><br><span class="line">                # 清除快照过期文件</span><br><span class="line">                snapshots_sql = <span class="string">&quot;CALL system.expire_snapshots(&#x27;&#123;0&#125;&#x27;, TIMESTAMP &#x27;2122-12-30 00:00:00.000&#x27;, 1)&quot;</span>.format(baseTable)</span><br><span class="line">                # 清除未关联上数据的 metedata 目录</span><br><span class="line">                remove_orphan_files_sql = <span class="string">&quot;CALL system.remove_orphan_files(table =&gt; &#x27;&#123;0&#125;&#x27;)&quot;</span>.format(baseTable)</span><br><span class="line">                print(<span class="string">&#x27;开始库和表是:&#x27;</span>)</span><br><span class="line">                print(baseTable)</span><br><span class="line">                spark.sql(rewrite_data_files_sql)</span><br><span class="line">                spark.sql(rewrite_manifests_sql)</span><br><span class="line">                spark.sql(snapshots_sql)</span><br><span class="line">                spark.sql(remove_orphan_files_sql)</span><br></pre></td></tr></table></figure><h2 id="建表语句"><a href="#建表语句" class="headerlink" title="建表语句"></a>建表语句</h2><p>在见表语句上加上对应的参数配置</p><table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td><strong>Property</strong></td><td><strong>Description</strong></td></tr><tr><td>write.metadata.delete-after-commit.enabled</td><td>每次表提交后是否删除旧的元数据文件</td></tr><tr><td>write.metadata.previous-versions-max</td><td>要保留旧的元数据文件数量</td></tr></tbody></table><p><code>CREATE TABLE $&#123;CataLog名称&#125;.$&#123;库名&#125;.$&#123;表名&#125; (  id bigint,    name string) using icebergPARTITIONED BY (  loc string) TBLPROPERTIES (    &#39;write.metadata.delete-after-commit.enabled&#39;= true,&#39;write.metadata.previous-versions-max&#39; = 3)</code></p><h1 id="🤗-总结归纳"><a href="#🤗-总结归纳" class="headerlink" title="🤗 总结归纳"></a>🤗 总结归纳</h1><h1 id="📎-参考文章"><a href="#📎-参考文章" class="headerlink" title="📎 参考文章"></a>📎 参考文章</h1><blockquote><p>💡 有关文章的问题，欢迎您在底部评论区留言，一起交流~</p></blockquote>]]></content>
    
    
    <summary type="html">Spark3.0.1合并Iceberg小文件</summary>
    
    
    
    <category term="数据湖" scheme="https://ruy9527.github.io/categories/%E6%95%B0%E6%8D%AE%E6%B9%96/"/>
    
    
    <category term="Iceberg" scheme="https://ruy9527.github.io/tags/Iceberg/"/>
    
    <category term="Spark" scheme="https://ruy9527.github.io/tags/Spark/"/>
    
    <category term="大数据" scheme="https://ruy9527.github.io/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
    <category term="数据湖" scheme="https://ruy9527.github.io/tags/%E6%95%B0%E6%8D%AE%E6%B9%96/"/>
    
  </entry>
  
  <entry>
    <title>CDH6.3.2搭建</title>
    <link href="https://ruy9527.github.io/article/cdh6_3_2_create1/"/>
    <id>https://ruy9527.github.io/article/cdh6_3_2_create1/</id>
    <published>2022-07-02T04:00:00.000Z</published>
    <updated>2025-12-31T04:52:02.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>😀 这里写文章的前言：<br>CDH搭建，减少了hadoop集群生态的维护</p></blockquote><h1 id="📝-CDH端口开放"><a href="#📝-CDH端口开放" class="headerlink" title="📝 CDH端口开放"></a>📝 CDH端口开放</h1><table><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td>端口号</td><td>组件</td><td>备注</td></tr><tr><td>7180</td><td>CDH web UI</td><td></td></tr><tr><td>9870</td><td>HDFS web UI</td><td></td></tr><tr><td>8088</td><td>Yarn web UI</td><td></td></tr><tr><td>19888</td><td>Historyserver web UI</td><td></td></tr><tr><td>8080</td><td></td><td></td></tr><tr><td>8888</td><td>Hue（未优化）</td><td></td></tr><tr><td>8889</td><td>Hue（优化）</td><td></td></tr><tr><td>3306</td><td>MySQL</td><td></td></tr><tr><td>6379</td><td>Redis</td><td></td></tr><tr><td>9092</td><td>kafka</td><td></td></tr><tr><td>2181</td><td>zk</td><td></td></tr><tr><td>4040</td><td>Spark</td><td></td></tr><tr><td>18088</td><td>Spark</td><td></td></tr><tr><td>10000</td><td>Hiveserve2</td><td></td></tr><tr><td>10002</td><td>Hive</td><td></td></tr><tr><td>11000</td><td>Oozie</td><td></td></tr><tr><td>51000</td><td>Sentry</td><td></td></tr><tr><td>8020</td><td>HDFS</td><td></td></tr><tr><td></td><td></td><td></td></tr></tbody></table><h1 id="修改主机hosts配置"><a href="#修改主机hosts配置" class="headerlink" title="修改主机hosts配置"></a>修改主机hosts配置</h1><h2 id="host"><a href="#host" class="headerlink" title="host"></a>host</h2><p>比如这里的配置,每台集群的机器都需要配置, 具体自身的ip和hostname等进行配置</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"># 在文件末尾添加每个实例的内网IP及对应的hostname</span><br><span class="line"></span><br><span class="line"><span class="number">172.17</span><span class="number">.79</span><span class="number">.117</span>   hadoop102       hadoop102</span><br><span class="line"><span class="number">172.17</span><span class="number">.79</span><span class="number">.116</span>   hadoop103       hadoop103</span><br><span class="line"><span class="number">172.17</span><span class="number">.79</span><span class="number">.118</span>   hadoop104       hadoop104</span><br></pre></td></tr></table></figure><h2 id="ssh免密配置"><a href="#ssh免密配置" class="headerlink" title="ssh免密配置"></a>ssh免密配置</h2><p>配置所有机器之间相互的免密<br>CDH的服务是有server和agent概念的，agent收集到往server进行汇报情况</p><p>执行ssh-keygen -t rsa并敲三次回车，会在我们的&#x2F;root&#x2F;.ssh目录下生成两个文件id_rsa（私钥）、id_rsa.pub（公钥）</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">ssh-keygen -t rsa</span><br><span class="line">cd .ssh</span><br></pre></td></tr></table></figure><p>将公钥拷贝到免密登录的目标机器上</p><p>ssh-copy-id hadoop102</p><p>ssh-copy-id hadoop103</p><p>ssh-copy-id hadoop104</p><p>每台机器上重复ssh等操作</p><h2 id="集群同步脚步"><a href="#集群同步脚步" class="headerlink" title="集群同步脚步"></a>集群同步脚步</h2><p>vim xsync : 将下面的脚步内容给复制到 xsync 中</p><p>chmod u+x xsync : 复制完值，给脚步加上对应的权限</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">#!/bin/bash</span><br><span class="line">#<span class="number">1.</span> 判断参数个数</span><br><span class="line"><span class="keyword">if</span> [ $# -lt <span class="number">1</span> ]</span><br><span class="line">then</span><br><span class="line">  echo Not Enough Arguement!</span><br><span class="line">  exit;</span><br><span class="line">fi</span><br><span class="line">#<span class="number">2.</span> 遍历集群所有机器</span><br><span class="line"><span class="keyword">for</span> host in hadoop102 hadoop103 hadoop104</span><br><span class="line"><span class="keyword">do</span></span><br><span class="line">  echo ====================  $host  ====================</span><br><span class="line">  #<span class="number">3.</span> 遍历所有目录，挨个发送</span><br><span class="line">  <span class="keyword">for</span> file in $@</span><br><span class="line">  <span class="keyword">do</span></span><br><span class="line">    #<span class="number">4.</span> 判断文件是否存在</span><br><span class="line">    <span class="keyword">if</span> [ -e $file ]</span><br><span class="line">    then</span><br><span class="line">      #<span class="number">5.</span> 获取父目录</span><br><span class="line">      pdir=$(cd -P $(dirname $file); pwd)</span><br><span class="line">      #<span class="number">6.</span> 获取当前文件的名称</span><br><span class="line">      fname=$(basename $file)</span><br><span class="line">      ssh $host <span class="string">&quot;mkdir -p $pdir&quot;</span></span><br><span class="line">      rsync -av $pdir/$fname $host:$pdir</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">      echo $file does not exists!</span><br><span class="line">    fi</span><br><span class="line">  done</span><br><span class="line">done</span><br></pre></td></tr></table></figure><h2 id="同步执行指令脚步"><a href="#同步执行指令脚步" class="headerlink" title="同步执行指令脚步"></a>同步执行指令脚步</h2><p>vim <a href="http://xcall.sh/">xcall.sh</a></p><p>chmod u+x <a href="http://xcall.sh/">xcall.sh</a></p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">#! /bin/bash</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> i in hadoop102 hadoop103 hadoop104</span><br><span class="line"><span class="keyword">do</span></span><br><span class="line">        echo --------- $i ----------</span><br><span class="line">        ssh $i <span class="string">&quot;$*&quot;</span></span><br><span class="line">done</span><br></pre></td></tr></table></figure><h2 id="防火墙检测是否关闭"><a href="#防火墙检测是否关闭" class="headerlink" title="防火墙检测是否关闭"></a>防火墙检测是否关闭</h2><p>查看状态: sudo systemctl status firewalld</p><p>禁用：    sudo systemctl disable firewalld</p><p>停止：    sudo systemctl disable firewalld</p><h2 id="检查hostname是否成功"><a href="#检查hostname是否成功" class="headerlink" title="检查hostname是否成功"></a>检查hostname是否成功</h2><p>hostname</p><p>hostnamectl status</p><h2 id="禁用SELinux-mode"><a href="#禁用SELinux-mode" class="headerlink" title="禁用SELinux mode"></a>禁用SELinux mode</h2><p><strong>安全增强的Linux (SELinux)允许通过策略设置访问控制。如果在部署CDH时遇到困难，请在将CDH部署到集群之前，在每个主机上将SELinux设置为permissive模式</strong></p><p>getenforce</p><details><summary>如果输出结果为Permissive 或Disabled</summary><ol><li>打开 &#x2F;etc&#x2F;selinux&#x2F;config 文件</li><li>将SELINUX &#x3D; enforcing 这一行改为 SELINUX&#x3D; permissive</li><li>保存并关闭文件</li><li>重新启动系统或运行 setenforce 0 命令来立即禁用 SELinux</li></ol></details><h2 id="禁用透明大页配置"><a href="#禁用透明大页配置" class="headerlink" title="禁用透明大页配置"></a>禁用透明大页配置</h2><p>在每台服务器上分别执行</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">echo never &gt; /sys/kernel/mm/transparent_hugepage/defrag</span><br><span class="line">echo never &gt; /sys/kernel/mm/transparent_hugepage/enabled</span><br><span class="line">echo <span class="string">&#x27;echo never &gt; /sys/kernel/mm/transparent_hugepage/defrag&#x27;</span> &gt;&gt; /etc/rc.local</span><br><span class="line">echo <span class="string">&#x27;echo never &gt; /sys/kernel/mm/transparent_hugepage/enabled&#x27;</span> &gt;&gt; /etc/rc.local</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"># 前两行代码只能暂时生效，当重启后配置会消失</span><br><span class="line">[root<span class="meta">@hadoop102</span> parcel-repo]# echo never &gt; /sys/kernel/mm/transparent_hugepage/defrag</span><br><span class="line">[root<span class="meta">@hadoop102</span> parcel-repo]# echo never &gt; /sys/kernel/mm/transparent_hugepage/enabled</span><br><span class="line"></span><br><span class="line"># 永久生效需要把信息加到启动项中</span><br><span class="line">[root<span class="meta">@hadoop102</span> parcel-repo]# echo <span class="string">&#x27;echo never &gt; /sys/kernel/mm/transparent_hugepage/defrag&#x27;</span> &gt;&gt; /etc/rc.local</span><br><span class="line">[root<span class="meta">@hadoop102</span> parcel-repo]# echo <span class="string">&#x27;echo never &gt; /sys/kernel/mm/transparent_hugepage/enabled&#x27;</span> &gt;&gt; /etc/rc.loca</span><br></pre></td></tr></table></figure><h2 id="启动NTP服务"><a href="#启动NTP服务" class="headerlink" title="启动NTP服务"></a>启动NTP服务</h2><p>CDH要求在集群中的每台机器上配置一个网络时间协议(NTP)服务。大多数操作系统使用时间同步的ntpd服务。<br>注：RHEL 7兼容的操作系统默认使用chronyd而不是ntpd。如果chronyd正在运行，Cloudera Manager将使用它来确定主机时钟是否同步。否则，Cloudera Manager使用ntpd</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">ps -ef |grep chronyd</span><br></pre></td></tr></table></figure><h1 id="yum需要安装的源"><a href="#yum需要安装的源" class="headerlink" title="yum需要安装的源"></a>yum需要安装的源</h1><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">yum -y install httpd</span><br><span class="line"></span><br><span class="line">systemctl status httpd</span><br><span class="line"></span><br><span class="line">systemctl start httpd</span><br><span class="line"></span><br><span class="line"># 设置开机自启</span><br><span class="line">systemctl enable httpd.service</span><br></pre></td></tr></table></figure><h2 id="yum源工具安装"><a href="#yum源工具安装" class="headerlink" title="yum源工具安装"></a>yum源工具安装</h2><p>yum -y install yum-utils createrepo</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">yum -y install yum-utils createrepo</span><br></pre></td></tr></table></figure><h2 id="导入-cm-资源包"><a href="#导入-cm-资源包" class="headerlink" title="导入 cm 资源包"></a>导入 cm 资源包</h2><p>创建&#x2F;var&#x2F;www&#x2F;html&#x2F;cm文件夹</p><p>将资料包中cm文件夹下的文件放入&#x2F;var&#x2F;www&#x2F;html&#x2F;cm文件夹中</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">创建/<span class="keyword">var</span>/www/html/cm文件夹</span><br><span class="line">mkdir cm</span><br></pre></td></tr></table></figure><h2 id="创建-repo"><a href="#创建-repo" class="headerlink" title="创建 repo"></a>创建 repo</h2><p>使用createrepo命令创建本地yum源;在&#x2F;etc&#x2F;yum.repos.d&#x2F;文件夹下创建新的yum repo文件</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">使用createrepo命令创建本地yum源</span><br></pre></td></tr></table></figure><p>配置了hosts文件的情况下我们可以直接使用hadoop102作为域名访问</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">===========手动编写repo============</span><br><span class="line">vim /etc/yum.repos.d/cloudera-manager.repo</span><br><span class="line">===========把下面五行代码写入repo文件中=========</span><br><span class="line">[cloudera-manager]</span><br><span class="line">name=Cloudera Manager, Version yum</span><br><span class="line">baseurl=http:<span class="comment">//hadoop102/cm</span></span><br><span class="line">gpgcheck=<span class="number">0</span></span><br><span class="line">enabled=<span class="number">1</span></span><br></pre></td></tr></table></figure><h2 id="清除缓存"><a href="#清除缓存" class="headerlink" title="清除缓存"></a>清除缓存</h2><p>清除缓存并建立新的元数据缓存</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 清除缓存</span></span><br><span class="line">yum clean all</span><br><span class="line"></span><br><span class="line"><span class="comment">// 建立元数据缓存</span></span><br><span class="line">yum makecache</span><br><span class="line"></span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">xsync /etc/yum.repos.d/cloudera-manager.repo </span><br><span class="line">yum clean all</span><br><span class="line">yum makecache</span><br><span class="line"></span><br><span class="line"># 验证源是否配置生效</span><br><span class="line">yum list | grep cloudera-manager</span><br></pre></td></tr></table></figure><h1 id="CM及依赖组件安装"><a href="#CM及依赖组件安装" class="headerlink" title="CM及依赖组件安装"></a>CM及依赖组件安装</h1><h2 id="java环境"><a href="#java环境" class="headerlink" title="java环境"></a>java环境</h2><p>xcall yum -y install oracle-j2sdk1.8.x86_64</p><p>配置java的环境变量，因为我们在上面已经把&#x2F;etc&#x2F;profile中的内容追加到&#x2F;root&#x2F;.bashrc中，所以我们现在只需要配置&#x2F;root&#x2F;.bashrc文件即可</p><p>vim &#x2F;root&#x2F;bin&#x2F;setjavahome.sh</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">sed -i <span class="string">&#x27;$a\# JAVA_HOME&#x27;</span> /root/.bashrc</span><br><span class="line">sed -i <span class="string">&#x27;$a\export JAVA_HOME=/usr/java/jdk1.8.0_181-cloudera&#x27;</span> /root/.bashrc</span><br><span class="line">sed -i <span class="string">&#x27;$a\export PATH=$JAVA_HOME/bin:$PATH&#x27;</span> /root/.bashrc</span><br><span class="line">sed -i <span class="string">&#x27;$a\export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar&#x27;</span> /root/.bashrc</span><br><span class="line"></span><br><span class="line">xsync /root/bin/setjavahome.sh</span><br><span class="line"></span><br><span class="line"># 在三台上同时执行添加环境变量的和同步环境变量的操作</span><br><span class="line">xcall  bash /root/bin/setjavahome.sh &amp;&amp; source /root/.bashrc</span><br><span class="line"></span><br><span class="line"># 验证java是否安装成功</span><br><span class="line">xcall.sh java -version</span><br></pre></td></tr></table></figure><h2 id="mysql安装"><a href="#mysql安装" class="headerlink" title="mysql安装"></a>mysql安装</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">wget -v http:<span class="comment">//repo.mysql.com/mysql-community-release-el7-5.noarch.rpm</span></span><br><span class="line">sudo rpm -ivh mysql-community-release-el7-<span class="number">5.</span>noarch.rpm</span><br><span class="line">sudo yum -y update</span><br><span class="line">sudo yum -y install mysql-server</span><br><span class="line">sudo systemctl start mysqld</span><br><span class="line"></span><br><span class="line">sudo systemctl stop mysqld</span><br></pre></td></tr></table></figure><p>InnoDB log files 做备份</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">mkdir /root/backup/</span><br><span class="line">mv /<span class="keyword">var</span>/lib/mysql/ib_logfile0 /<span class="keyword">var</span>/lib/mysql/ib_logfile1 /root/backup/</span><br></pre></td></tr></table></figure><p>&#x2F;etc&#x2F;my.cnf文件修改</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">[mysqld]</span><br><span class="line">datadir=/<span class="keyword">var</span>/lib/mysql</span><br><span class="line">socket=/<span class="keyword">var</span>/lib/mysql/mysql.sock</span><br><span class="line"># To prevent deadlocks, set the isolation level to READ-COMMITTED.</span><br><span class="line">transaction-isolation = READ-COMMITTED</span><br><span class="line"># Disabling symbolic-links is recommended to prevent assorted security risks;</span><br><span class="line"># to <span class="keyword">do</span> so, uncomment <span class="built_in">this</span> line:</span><br><span class="line">symbolic-links = <span class="number">0</span></span><br><span class="line"></span><br><span class="line">key_buffer_size = 32<span class="type">M</span></span><br><span class="line"><span class="variable">max_allowed_packet</span> <span class="operator">=</span> 16<span class="type">M</span></span><br><span class="line"><span class="variable">thread_stack</span> <span class="operator">=</span> 256<span class="type">K</span></span><br><span class="line"><span class="variable">thread_cache_size</span> <span class="operator">=</span> <span class="number">64</span></span><br><span class="line">query_cache_limit = 8<span class="type">M</span></span><br><span class="line"><span class="variable">query_cache_size</span> <span class="operator">=</span> 64<span class="type">M</span></span><br><span class="line"><span class="variable">query_cache_type</span> <span class="operator">=</span> <span class="number">1</span></span><br><span class="line"></span><br><span class="line">#Allow <span class="number">100</span> maximum connections <span class="keyword">for</span> each database and then add 50extra connections. For example, <span class="keyword">for</span> two databases, set the maximum connections to <span class="number">250.</span> If you store five databases on one <span class="title function_">host</span> <span class="params">(the databases <span class="keyword">for</span> Cloudera Manager Server, Activity Monitor, Reports Manager, Cloudera Navigator, and Hive metastore)</span>, set the maximum connections to <span class="number">550.</span></span><br><span class="line">max_connections = <span class="number">550</span></span><br><span class="line">#expire_logs_days = <span class="number">10</span></span><br><span class="line">#max_binlog_size = 100M</span><br><span class="line"></span><br><span class="line">#log_bin should be on a disk with enough free space.</span><br><span class="line">#Replace <span class="string">&#x27;/var/lib/mysql/mysql_binary_log&#x27;</span> with an appropriate path <span class="keyword">for</span> your</span><br><span class="line">#system and chown the specified folder to the mysql user.</span><br><span class="line">#如果需要替换mysql的log文件存放位置，需要此处拥有足够的空间并且把文件夹的属主改为mysql用户</span><br><span class="line">log_bin=/<span class="keyword">var</span>/lib/mysql/mysql_binary_log</span><br><span class="line"></span><br><span class="line">#In later versions of MySQL, <span class="keyword">if</span> you enable the binary log and <span class="keyword">do</span> not set</span><br><span class="line">#a server_id, MySQL will not start. The server_id must be unique within</span><br><span class="line">#the replicating group.</span><br><span class="line">server_id=<span class="number">1</span></span><br><span class="line"></span><br><span class="line">binlog_format = <span class="type">mixed</span></span><br><span class="line"></span><br><span class="line"><span class="variable">read_buffer_size</span> <span class="operator">=</span> 2<span class="type">M</span></span><br><span class="line"><span class="variable">read_rnd_buffer_size</span> <span class="operator">=</span> 16<span class="type">M</span></span><br><span class="line"><span class="variable">sort_buffer_size</span> <span class="operator">=</span> 8<span class="type">M</span></span><br><span class="line"><span class="variable">join_buffer_size</span> <span class="operator">=</span> 8M</span><br><span class="line"></span><br><span class="line"># InnoDB <span class="type">settings</span></span><br><span class="line"><span class="variable">innodb_file_per_table</span> <span class="operator">=</span> <span class="number">1</span></span><br><span class="line">innodb_flush_log_at_trx_commit  = <span class="number">2</span></span><br><span class="line">innodb_log_buffer_size = 64<span class="type">M</span></span><br><span class="line"><span class="variable">innodb_buffer_pool_size</span> <span class="operator">=</span> 4<span class="type">G</span></span><br><span class="line"><span class="variable">innodb_thread_concurrency</span> <span class="operator">=</span> <span class="number">8</span></span><br><span class="line">#The <span class="keyword">default</span> settings in the MySQL installations in most distributions use conservative buffer sizes and memory usage. Cloudera Management Service roles need high write throughput because they might insert many records in the database. Cloudera recommends that you set the innodb_flush_method property to O_DIRECT.</span><br><span class="line">innodb_flush_method = <span class="type">O_DIRECT</span></span><br><span class="line"><span class="variable">innodb_log_file_size</span> <span class="operator">=</span> 512M</span><br><span class="line"></span><br><span class="line">[mysqld_safe]</span><br><span class="line">log-error=/<span class="keyword">var</span>/log/mysqld.log</span><br><span class="line">pid-file=/<span class="keyword">var</span>/run/mysqld/mysqld.pid</span><br><span class="line">sql_mode=STRICT_ALL_TABLES</span><br></pre></td></tr></table></figure><p>初始化mysql</p><p>配置my.cnf完成后，启动MySQL;需要注意:</p><ol><li>因为当前无密码，所以启动时直接回车</li><li>Disallow root login remotely 选项需要输入 N , 这样 root 用户才能远程访问</li></ol><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">sudo systemctl enable mysqld</span><br><span class="line"></span><br><span class="line">sudo systemctl start mysqld</span><br><span class="line"></span><br><span class="line">sudo /usr/bin/mysql_secure_installation</span><br><span class="line"></span><br><span class="line">[...]这里需要配置密码，密码可以为简单密码，例如<span class="number">123456</span></span><br><span class="line">Enter current password <span class="keyword">for</span> <span class="title function_">root</span> <span class="params">(enter <span class="keyword">for</span> none)</span>:（这里需要直接回车）</span><br><span class="line">OK, successfully used password, moving on...</span><br><span class="line">[...]</span><br><span class="line">Set root password? [Y/n] Y</span><br><span class="line">New password:（这里输入自定义的密码）</span><br><span class="line">Re-enter <span class="keyword">new</span> <span class="title class_">password</span>:（这里输入自定义的密码）</span><br><span class="line">Remove anonymous users? [Y/n] Y</span><br><span class="line">[...]</span><br><span class="line">Disallow root login remotely? [Y/n] N</span><br><span class="line">[...]</span><br><span class="line">Remove test database and access to it [Y/n] Y</span><br><span class="line">[...]</span><br><span class="line">Reload privilege tables now? [Y/n] Y</span><br><span class="line">All done!</span><br></pre></td></tr></table></figure><p>将JDBC的jar放入到&#x2F;usr&#x2F;share&#x2F;java文件夹中</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">mkdir -p /usr/share/java/</span><br><span class="line"></span><br><span class="line">cd /usr/share/java/</span><br><span class="line">wget https:<span class="comment">//cdh-sgg.oss-cn-shenzhen.aliyuncs.com/fileshare/mysql-connector-java-5.1.46-bin.jar</span></span><br><span class="line"></span><br><span class="line">mv mysql-connector-java-<span class="number">5.1</span><span class="number">.46</span>-bin.jar mysql-connector-java.jar</span><br></pre></td></tr></table></figure><p>创建各用户数据库（注意hive的数据库叫metastore）</p><table><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td>Service</td><td>Database</td><td>User</td></tr><tr><td>Cloudera Manager Server</td><td>scm</td><td>scm</td></tr><tr><td>Activity Monitor</td><td>amon</td><td>amon</td></tr><tr><td>Reports Manager</td><td>rman</td><td>rman</td></tr><tr><td>Hue</td><td>hue</td><td>hue</td></tr><tr><td>Hive Metastore Server</td><td>metastore</td><td>hive</td></tr><tr><td>Sentry Server</td><td>sentry</td><td>sentry</td></tr><tr><td><del>Cloudera Navigator Audit Server</del></td><td><del>nav</del></td><td><del>nav</del></td></tr><tr><td><del>Cloudera Navigator Metadata Server</del></td><td><del>navms</del></td><td><del>navms</del></td></tr><tr><td>Oozie</td><td>oozie</td><td>oozie</td></tr></tbody></table><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">mysql -u root -p</span><br><span class="line">Enter password:</span><br><span class="line"># 建表及修改权限的格式：</span><br><span class="line">CREATE DATABASE &lt;database&gt; DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;</span><br><span class="line">GRANT ALL ON &lt;database&gt;.* TO <span class="string">&#x27;&lt;user&gt;&#x27;</span>@<span class="string">&#x27;%&#x27;</span> IDENTIFIED BY <span class="string">&#x27;&lt;password&gt;&#x27;</span>;</span><br><span class="line"># 以下是需要执行的SQL语句</span><br><span class="line"># 替换之后需要执行的SQL为：</span><br><span class="line">CREATE DATABASE scm DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;</span><br><span class="line">CREATE DATABASE amon DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;</span><br><span class="line">CREATE DATABASE rman DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;</span><br><span class="line">CREATE DATABASE hue DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;</span><br><span class="line">CREATE DATABASE metastore DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;</span><br><span class="line">CREATE DATABASE sentry DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;</span><br><span class="line">CREATE DATABASE nav DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;</span><br><span class="line">CREATE DATABASE navms DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;</span><br><span class="line">CREATE DATABASE oozie DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;</span><br><span class="line">GRANT ALL ON scm.* TO <span class="string">&#x27;scm&#x27;</span>@<span class="string">&#x27;%&#x27;</span> IDENTIFIED BY <span class="string">&#x27;scm&#x27;</span>;</span><br><span class="line">GRANT ALL ON amon.* TO <span class="string">&#x27;amon&#x27;</span>@<span class="string">&#x27;%&#x27;</span> IDENTIFIED BY <span class="string">&#x27;amon&#x27;</span>;</span><br><span class="line">GRANT ALL ON rman.* TO <span class="string">&#x27;rman&#x27;</span>@<span class="string">&#x27;%&#x27;</span> IDENTIFIED BY <span class="string">&#x27;rman&#x27;</span>;</span><br><span class="line">GRANT ALL ON hue.* TO <span class="string">&#x27;hue&#x27;</span>@<span class="string">&#x27;%&#x27;</span> IDENTIFIED BY <span class="string">&#x27;hue&#x27;</span>;</span><br><span class="line">GRANT ALL ON metastore.* TO <span class="string">&#x27;hive&#x27;</span>@<span class="string">&#x27;%&#x27;</span> IDENTIFIED BY <span class="string">&#x27;hive&#x27;</span>;</span><br><span class="line">GRANT ALL ON sentry.* TO <span class="string">&#x27;sentry&#x27;</span>@<span class="string">&#x27;%&#x27;</span> IDENTIFIED BY <span class="string">&#x27;sentry&#x27;</span>;</span><br><span class="line">GRANT ALL ON nav.* TO <span class="string">&#x27;nav&#x27;</span>@<span class="string">&#x27;%&#x27;</span> IDENTIFIED BY <span class="string">&#x27;nav&#x27;</span>;</span><br><span class="line">GRANT ALL ON navms.* TO <span class="string">&#x27;navms&#x27;</span>@<span class="string">&#x27;%&#x27;</span> IDENTIFIED BY <span class="string">&#x27;navms&#x27;</span>;</span><br><span class="line">GRANT ALL ON oozie.* TO <span class="string">&#x27;oozie&#x27;</span>@<span class="string">&#x27;%&#x27;</span> IDENTIFIED BY <span class="string">&#x27;oozie&#x27;</span>;</span><br><span class="line">#####注意此处再授权一个本主机名地址，不然web页面配置很容易出错，注意修改本地主机名hostname</span><br><span class="line">GRANT ALL ON amon.* TO <span class="string">&#x27;amon&#x27;</span>@<span class="string">&#x27;hadoop102&#x27;</span> IDENTIFIED BY <span class="string">&#x27;amon&#x27;</span>;</span><br><span class="line"></span><br><span class="line">FLUSH PRIVILEGES;</span><br><span class="line"></span><br><span class="line">show grants <span class="keyword">for</span> <span class="string">&#x27;amon&#x27;</span>@<span class="string">&#x27;%&#x27;</span>;  </span><br><span class="line">show grants <span class="keyword">for</span> <span class="string">&#x27;rman&#x27;</span>@<span class="string">&#x27;%&#x27;</span>;  </span><br><span class="line">show grants <span class="keyword">for</span> <span class="string">&#x27;hive&#x27;</span>@<span class="string">&#x27;%&#x27;</span>;</span><br><span class="line">show grants <span class="keyword">for</span> <span class="string">&#x27;hue&#x27;</span>@<span class="string">&#x27;%&#x27;</span>;</span><br><span class="line">show grants <span class="keyword">for</span> <span class="string">&#x27;oozie&#x27;</span>@<span class="string">&#x27;%&#x27;</span>;</span><br><span class="line"></span><br><span class="line">quit</span><br><span class="line"></span><br><span class="line">systemctl restart mysql.service</span><br></pre></td></tr></table></figure><h2 id="通过yum安装daemons-agent-server"><a href="#通过yum安装daemons-agent-server" class="headerlink" title="通过yum安装daemons,agent,server"></a>通过yum安装daemons,agent,server</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">yum list | grep cloudera-manager</span><br><span class="line">yum -y  install cloudera-manager-daemons cloudera-manager-agent cloudera-manager-server</span><br><span class="line"></span><br><span class="line">yum list | grep cloudera-manager</span><br><span class="line">yum -y  install cloudera-manager-daemons cloudera-manager-agent</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">[root<span class="meta">@hadoop102</span> ~]# sudo /opt/cloudera/cm/schema/scm_prepare_database.sh mysql scm scm</span><br><span class="line">Enter SCM password: (scm)</span><br><span class="line"></span><br><span class="line">JAVA_HOME=/usr/java/jdk1<span class="number">.8</span><span class="number">.0_181</span>-cloudera</span><br><span class="line">Verifying that we can write to /etc/cloudera-scm-server</span><br><span class="line">Creating SCM configuration file in /etc/cloudera-scm-server</span><br><span class="line">Executing:  /usr/java/jdk1<span class="number">.8</span><span class="number">.0_181</span>-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:/opt/cloudera/cm/schema/../lib<span class="comment">/* com.cloudera.enterprise.dbutil.DbCommandExecutor /etc/cloudera-scm-server/db.properties com.cloudera.cmf.db.</span></span><br><span class="line"><span class="comment">log4j:ERROR Could not find value for key log4j.appender.Alog4j:ERROR Could not instantiate appender named &quot;A&quot;.</span></span><br><span class="line"><span class="comment">[2021-06-22 21:14:01,762] INFO     0[main] - com.cloudera.enterprise.dbutil.DbCommandExecutor.testDbConnection(DbCommandExecutor.java) - Successfully connected to database.</span></span><br><span class="line"><span class="comment">All done, your SCM database is configured correctly!</span></span><br></pre></td></tr></table></figure><h2 id="配置本地Parcel存储库"><a href="#配置本地Parcel存储库" class="headerlink" title="配置本地Parcel存储库"></a>配置本地Parcel存储库</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">systemctl start cloudera-scm-server</span><br><span class="line"># 重启命令为：systemctl start cloudera-scm-server</span><br><span class="line"></span><br><span class="line"># 可以用下面命令查看server的信息</span><br><span class="line">tail -f /<span class="keyword">var</span>/log/cloudera-scm-server/cloudera-scm-server.log</span><br><span class="line"># 另开启一个窗口，监控日志中是否提示启动成功</span><br><span class="line">tail -f /<span class="keyword">var</span>/log/cloudera-scm-server/cloudera-scm-server.log|grep <span class="string">&quot;Started Jetty server&quot;</span></span><br><span class="line"># 也可以用下面命令查看端口号占用情况</span><br><span class="line">netstat -tunlp | grep <span class="number">7180</span></span><br><span class="line"></span><br><span class="line"># 当你看到以下信息时候 证明启动成功</span><br><span class="line">INFO WebServerImpl:com.cloudera.server.cmf.WebServerImpl: Started Jetty server. </span><br><span class="line">下面可能会出现报错信息，这个是因为<span class="number">2021</span>年CDH开始正式收费，从官方网站渠道已经无法拉取到任何安装包和校验文件，所以我们在<span class="number">4.4</span>章节中手动上传文件进行校验。</span><br><span class="line"><span class="number">2022</span>-<span class="number">03</span>-<span class="number">01</span> <span class="number">14</span>:08:<span class="number">23</span>,<span class="number">203</span> ERROR ParcelUpdateService:com.cloudera.parcel.components.ParcelDownloaderImpl: Failed to download manifest. Status code: <span class="number">401</span> URI: https:<span class="comment">//archive.cloudera.com/p/cdh6/6.3.4/parcels/manifest.json</span></span><br><span class="line"><span class="number">2022</span>-<span class="number">03</span>-<span class="number">01</span> <span class="number">14</span>:08:<span class="number">23</span>,<span class="number">203</span> ERROR ParcelUpdateService:com.cloudera.parcel.components.ParcelDownloaderImpl: Could not retrieve repository info <span class="keyword">for</span> repo https:<span class="comment">//archive.cloudera.com/cdh6/6.3/parcels/. Got HTTP response code 401</span></span><br><span class="line"></span><br><span class="line"># 此时登录网页</span><br><span class="line">http:<span class="comment">//你的hadoop102的IP地址:7180</span></span><br><span class="line"># 就能访问CDH集群 </span><br><span class="line">默认的用户名：admin</span><br><span class="line">默认的密码为：admin</span><br></pre></td></tr></table></figure><h1 id="参数配置"><a href="#参数配置" class="headerlink" title="参数配置"></a>参数配置</h1><ul><li>dfs.client.use.datanode.hostname</li><li>yarn.nodemanager.resource.cpu-vcores</li><li>yarn.scheduler.maximum-allocation-vcore</li><li>yarn.scheduler.maximum-allocation-mb</li><li>yarn.nodemanager.resource.memory-mb</li><li>spark.dynamicAllocation.enabled</li></ul><p>调度相关参数设置:</p><p>yarn.scheduler.capacity.root.queues<br>yarn.scheduler.capacity.root.capacity<br>yarn.scheduler.capacity.root.spark.capacity<br>yarn.scheduler.capacity.root.hive.capacity</p><h1 id="🤗-总结归纳"><a href="#🤗-总结归纳" class="headerlink" title="🤗 总结归纳"></a>🤗 总结归纳</h1><h1 id="📎-参考文章"><a href="#📎-参考文章" class="headerlink" title="📎 参考文章"></a>📎 参考文章</h1><blockquote><p>💡 有关文章的问题，欢迎您在底部评论区留言，一起交流~</p></blockquote>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;😀 这里写文章的前言：&lt;br&gt;CDH搭建，减少了hadoop集群生态的维护&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;📝-CDH端口开放&quot;&gt;&lt;a href=&quot;#📝-CDH端口开放&quot; class=&quot;headerlink&quot;</summary>
        
      
    
    
    
    
  </entry>
  
  <entry>
    <title>RocketMQ基础知识</title>
    <link href="https://ruy9527.github.io/article/rocker_base1/"/>
    <id>https://ruy9527.github.io/article/rocker_base1/</id>
    <published>2022-05-08T04:00:00.000Z</published>
    <updated>2023-07-23T05:18:17.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>😀 这里写文章的前言：<br>RocketMQ 基础知识</p></blockquote><h1 id="📝-整理流程"><a href="#📝-整理流程" class="headerlink" title="📝 整理流程"></a>📝 整理流程</h1><p><img src="/images/posts/rocker_base1/img-1.png" alt="RocketMQ基础知识"></p><h1 id="📝-模型概念"><a href="#📝-模型概念" class="headerlink" title="📝 模型概念"></a>📝 模型概念</h1><p><img src="/images/posts/rocker_base1/img-2.png" alt="RocketMQ基础知识"></p><h1 id="📔主题（Topic）"><a href="#📔主题（Topic）" class="headerlink" title="📔主题（Topic）"></a><strong>📔主题（Topic）</strong></h1><p>主题是 Apache RocketMQ 中消息传输和存储的顶层容器，用于标识同一类业务逻辑的消息</p><ul><li>定义数据的分类隔离;在Apache RocketMQ的方案设计中，建议将不同业务的数据拆分到不同的主题中管理，通过主题实现存储的隔离性和订阅隔离性</li><li>主题下存在 MessageQueue : 队列，才是topic实际的操作单元，一个topic可能有多个队列，方便扩容</li></ul><p>在 Apache RocketMQ 架构中，主题属于顶层资源和容器，拥有独立的权限管理、可观测性指标采集和监控等能力，创建和管理主题会占用一定的系统资源。因此，生产环境需要严格管理主题资源，请勿随意进行增、删、改、查操作</p><h1 id="📔队列（MessageQueue）"><a href="#📔队列（MessageQueue）" class="headerlink" title="📔队列（MessageQueue）"></a><strong>📔队列（MessageQueue）</strong></h1><p>队列是RocketMQ实际存储和传输的实际容器，也是RocketMQ消息的最小存储单元。Apache的一个主题由多个队列组成，以实现队列数量的水平拆分和队列内部的流式存储，在向内发送消息，但实际消息发送到该主题下的某个队列里</p><details><summary>存储顺序性</summary><p>队列天然具备顺序性，即消息按照进入队列的顺序写入存储，同一队列的消息天然存在顺序关系，队列头部为最早写入的消息，队列尾部为最新写入的消息;消息在队列中的位置和消息之间的顺序通过位点(Offset)进行标记管理（顺序消费可以通过定义hashkey指定某个队列，消费者再顺序设置成顺序消费）。</p></details><details><summary>流式操作语义</summary><p>RocketMQ基于队列的存储模型可确保消息从任意位点读取任意数量的消息，以实现类似聚合读取，回溯读取等特性;这些特性是RabbitMQ，ActiveMQ等不具备的</p></details><p>队列数量的设置应遵循少用够用原则，避免随意添加队列数量，造成下列问题</p><ul><li>集群元数据膨胀：RocketMQ会以队列的粒度采集指标和监控数据，队列过多容易造成管控元数据膨胀</li><li>客户端压力过大：RocketMQ的消息读写都是针对队列进行操作，队列过多容易产生空轮询请求，增加系统负荷</li></ul><h1 id="📔消息（Message）"><a href="#📔消息（Message）" class="headerlink" title="📔消息（Message）"></a><strong>📔消息（Message）</strong></h1><ul><li>消息由生产者初始化并发送到RocketMQ服务端</li><li>消息按照达到RocketMQ服务端的顺序存储到队列中</li><li>消费者按照订阅的关系从RocketMQ服务端中获取消息并消费</li></ul><p>四种类型：</p><ul><li>Normal： 普通消息，消息本身无特殊语义，消息之间也没有任何关联</li><li>FIFO：顺序消息，RocketMQ通过消息分组 MessageGroup 标记一组特定消息的先后顺序，可以保证消息的投递顺序严格按照消息发送时的顺序</li><li>Delay：定时&#x2F;延时消息，通过指定延时时间控制消息生产后不要立即投递，而是在延时间隔后才对消费者可见</li><li>Transaction：RocketMq支持事务消息，支持应用数据库更新和消息的事务一致性保障</li></ul><h1 id="📔生产者（Producer）"><a href="#📔生产者（Producer）" class="headerlink" title="📔生产者（Producer）"></a><strong>📔生产者（Producer）</strong></h1><p>生产者是RocketMQ系统中用来构建并传输消息服务端的运行实体</p><p>生产者通常被集成在业务系统中，将业务消息按照要求封装成RocketMQ的消息(Message)并发送到服务端</p><ul><li>发送方式：生产者可通过API接口设置消息发送的方式。Apache RocketMQ 支持同步传输和异步传输。</li><li>批量发送：生产者可通过API接口设置消息批量传输的方式。例如，批量发送的消息条数或消息大小。</li><li>事务行为：Apache RocketMQ 支持事务消息，对于事务消息需要生产者配合进行事务检查等行为保障事务的最终一致性</li></ul><h1 id="📔消费者分组（ConsumerGroup）"><a href="#📔消费者分组（ConsumerGroup）" class="headerlink" title="📔消费者分组（ConsumerGroup）"></a><strong>📔消费者分组（ConsumerGroup）</strong></h1><p>消费者分组是 Apache RocketMQ 系统中承载多个消费行为一致的消费者的负载均衡分组</p><p>和消费者不同，消费者分组并不是运行实体，而是一个逻辑资源。在 Apache RocketMQ 中，通过消费者分组内初始化多个消费者实现消费性能的水平扩展以及高可用容灾</p><p>在消费者分组中，统一定义以下消费行为，同一分组下的多个消费者将按照分组内统一的消费行为和负载均衡策略消费消息</p><ul><li>订阅关系：Apache RocketMQ 以消费者分组的粒度管理订阅关系，实现订阅关系的管理和追溯</li><li>投递顺序性：Apache RocketMQ 的服务端将消息投递给消费者消费时，支持顺序投递和并发投递，投递方式在消费者分组中统一配置</li><li>消费重试策略： 消费者消费消息失败时的重试策略，包括重试次数、死信队列设置等</li></ul><p>整体流程：</p><ol><li>消息有生产者初始化并发送到RocketMQ服务端</li><li>消息按照到达RocketMQ服务端顺序存储到主题指定队列中</li><li>消费者按照指定的订阅关系从RocketMQ服务端中获取消息并消费</li></ol><h1 id="📔消费者（Consumer）"><a href="#📔消费者（Consumer）" class="headerlink" title="📔消费者（Consumer）"></a><strong>📔消费者（Consumer）</strong></h1><p>消费者是 Apache RocketMQ 中用来接收并处理消息的运行实体。 消费者通常被集成在业务系统中，从 Apache RocketMQ 服务端获取消息，并将消息转化成业务可理解的信息，供业务逻辑处理</p><h1 id="📔订阅关系（Subscription）"><a href="#📔订阅关系（Subscription）" class="headerlink" title="📔订阅关系（Subscription）"></a><strong>📔订阅关系（Subscription）</strong></h1><p>定义：消息过滤规则的类型。订阅关系中设置消息过滤规则后，系统将按照过滤规则匹配主题中的消息，只将符合条件的消息投递给消费者消费，实现消息的再次分类</p><ul><li>TAG过滤：按照tag字符串进行全文过滤匹配</li><li>SQL92过滤：按照SQL语法对消息属性进行过滤匹配</li></ul><h1 id="消息持久化"><a href="#消息持久化" class="headerlink" title="消息持久化"></a>消息持久化</h1><p><img src="/images/posts/rocker_base1/img-3.png" alt="RocketMQ基础知识"></p><ul><li>Commitlog: 存储消息的元数据，所有的消息都会按照顺序存入到commitLog文件当中;CommitLog是由多个文件组成，每个文件固定大小1G;以第一条偏移量为名</li><li>ConsumerQueue：存储消息在CommitLog的索引；一个MessageQueue一个文件，记录当前MessageQueue被那些消费者组消费到了那条数据</li><li>IndexFile: 为了消息查询提供了一种通过key或者时间区间来查询消息的方法，这种通过IndexFile来查找消息的方法不影响发送和消费消息的主流程</li><li>checkpoint:数据存盘检查;主要记录commitLog文件,consumerQueue文件以及IndexFile文件最后一次刷盘的时间戳</li><li>config&#x2F;*.json: 这些文件是将RocketMQ的一些关键配置信息进行存盘保存。例如Topic配置，消费者组配置，消费者组消息偏移量offset等等一些信息</li><li>abort: 该文件用来RocketMQ判断程序是否正常关闭的一个标识文件。正常情况下，会再启动创建，而关闭服务时删除。但是如果遇到一些宕机，或者kill -9这些非正常的关闭服务，这个abort文件就不会被删除；因此RocketMQ就可以判断上一次服务是非正常关闭，后续就会做一些数据恢复的操作</li></ul><h2 id="commitLog"><a href="#commitLog" class="headerlink" title="commitLog"></a>commitLog</h2><p>CommitLog存储了所有的消息实体；所有生产者发过来的消息，都会无差别的依次的存储到CommitLog文件当中；</p><p>好处： 减少查找目标文件的时间，让消息以最快的速度落盘</p><p>对比Kafka存文件，需要寻找消息所属的Partition文件，在完成写入，当Topic较多的时候，这样的Partition寻址就会比较浪费时间，所以Kafka不适合多Topic的场景</p><p>RocketMQ的这种快速落盘的方式在多Topic的场景下，优势会比较明显</p><p>commitLog的文件大小是固定的，但是其存储的每个消息单元长度是不固定的</p><p>正因为消息的记录大小不固定，所以RocketMQ在每次存CommitLog文件时，都会去检查当前CommitLog文件空间是否足够，如果不够的话，会重新创建一个CommitLog文件</p><h2 id="ConsumeQueue"><a href="#ConsumeQueue" class="headerlink" title="ConsumeQueue"></a>ConsumeQueue</h2><p>consumeQueue文件主要是加速消费者的消费索引；它的每个文件夹对应RocketMQ中的一个MessageQueue，文件夹下的文件记录了每个MessageQueue中的消息在CommitLog文件当中的偏移量；这样，消费者通过ConsumeQueue文件，就可以快速找到CommitLog文件中感兴趣的消息记录；而消费者在ConsumeQueue文件当中的消费进度，会保存在config&#x2F;consumerOffset.json文件中</p><p>每个ConsumeQueue文件由固定30万个固定大小20byte的数组块组成，数据块的内容包括：msgPhyOffset(8 byte,消息在文件中的起始位置) + msgSize(4 byte,消息在文件中占用的长度) + msgTagCode(8 byte,消息的tag的hash值)</p><h2 id="IndexFile"><a href="#IndexFile" class="headerlink" title="IndexFile"></a>IndexFile</h2><p>辅助消息的检索；消费者进行消息消费时，通过ConsumeQueue文件就足够完成消息检索了，但是如果按照MessageId或者messageKey来检索文件，就不够了（比如RocketMQ的管理台）；IndexFile文件就是用来辅助这类消息检索的</p><p>他的文件名比较特殊，不是以消息偏移量命名，而是用的时间命名。但是其实，他也是一个固定大小的文件</p><p>文件由 indexHeader(固定40byte) + slot(固定500w个，每个固定20byte) + index(最多500w*4个,每个固定20byte)三部分组成</p><h1 id="过期文件删除"><a href="#过期文件删除" class="headerlink" title="过期文件删除"></a>过期文件删除</h1><p>消息既然要持久化,就必然有对应的删除机制</p><p>RocketMQ内置了一套过期文件的删除机制</p><h2 id="如何判断过期文件"><a href="#如何判断过期文件" class="headerlink" title="如何判断过期文件"></a>如何判断过期文件</h2><p>RocketMQ中,CommitLog文件和ConsumeQueue文件是以偏移量命名的,对于非当前写的文件，如果超过了一定的保留时间，那么这些文件都会被认为是过期文件，随时可以删除。</p><p>保留时间就是broker.conf中配置的fileReservedTime属性</p><p>注意，RocketMQ判断文件是否过期的唯一标准就是非当前写文件的保留时间，而并不关心文件当中的消息是否被消费过。所以，RocketMQ的消息堆积也是有时间限度的</p><h2 id="如何删除过期文件"><a href="#如何删除过期文件" class="headerlink" title="如何删除过期文件"></a>如何删除过期文件</h2><p>RocketMQ内部有一个定时任务，对文件进行扫描，并且触发文件删除的操作。用户可以指定文件删除操作的执行时间。在broker.conf中deleteWhen属性指定。默认是凌晨四点</p><p>RocketMQ还会检查服务器的磁盘空间是否足够，如果磁盘空间的使用率达到一定阈值，也会触发过期文件删除；所以RocketMQ官方就特别建议，borker的磁盘空间不要少于4G</p><h1 id="高效写文件"><a href="#高效写文件" class="headerlink" title="高效写文件"></a>高效写文件</h1><h2 id="零拷贝加速文件读写"><a href="#零拷贝加速文件读写" class="headerlink" title="零拷贝加速文件读写"></a>零拷贝加速文件读写</h2><p>零拷贝是操作系统层面提供的一种加速文件读写的操作机制，对应多的开源项目都在大量使用零拷贝，来提升IO操作的性能</p><p>对于java层面，对应着mmap和sendFile两种方式</p><h3 id="理解CPU拷贝和DMA拷贝"><a href="#理解CPU拷贝和DMA拷贝" class="headerlink" title="理解CPU拷贝和DMA拷贝"></a>理解CPU拷贝和DMA拷贝</h3><p>操作系统对于内存空间，是分为用户态和内核态的。用户态的应用程序无法直接操作硬件，需要通过内核空间进行操作转换，才能真正操作硬件。这其实是为了保护操作系统的安全。正因为如此，应用程序需要与网卡、磁盘等硬件进行数据交互时，就需要在用户态和内核态之间来回的复制数据。而这些操作，原本都是需要由CPU来进行任务的分配、调度等管理步骤的，早先这些IO接口都是由CPU独立负责，所以当发生大规模的数据读写操作时，CPU的占用率会非常高</p><p>之后，操作系统为了避免CPU完成被各种IO调用给占用，引入了DMA(直接存储器).有DMA来负责这些频繁的IO操作</p><p>DMA是一套独立的指令集，不会直接占用CPU的计算资源；这样CPU就不需要参与具体的数据复制的工作，只需要管理DMA权限即可</p><p>DMA拷贝极大的释放了CPU的性能，因此他的拷贝速度会比CPU拷贝要快很多；但是DMA拷贝本身，也在不断优化</p><p><img src="/images/posts/rocker_base1/img-4.png" alt="RocketMQ基础知识"></p><p>引入DMA拷贝之后，在读写请求的过程中，CPU不需要参与具体的工作，DMA可以独立完成数据在系统内部的复制。但是数据的复制，依然需要借助数据总线进行；但系统内的IO操作过多时，还是会占用过多的数据总线，造成总线冲突，最终还是会影响数据读写的性能</p><p>为了避免DMA总线冲突对性能的影响，后来又引入了Channel通道的方式。Channel，是一个完全独立的处理器，专门负责IO操作。既然是处理器，Channel就有自己的IO指令，与CPU无关，他也更适合大型的IO操作，性能更高</p><p><img src="/images/posts/rocker_base1/img-5.png" alt="RocketMQ基础知识"></p><p>这也是java层面的零拷贝相关的操作都是通过Channel的子类实现的</p><p>零拷贝技术，其实并不是不拷贝，而是经历减少了CPU拷贝</p><h3 id="mmap文件映射机制"><a href="#mmap文件映射机制" class="headerlink" title="mmap文件映射机制"></a>mmap文件映射机制</h3><p>主要是通过java.nio.channels.FileChannel的map方法完成映射</p><p>以一次文件的读写操作为例子，应用程序对磁盘文件的读与写，都需要经过内核态与用户态之间的的状态切换，每次状态切换的过程中，都需要大量的数据复制</p><p>在这个过程中,总共需要进行四次数据拷贝；而磁盘与内核态之间的数据拷贝，在操作系统层面已经由CPU拷贝优化为了DMA拷贝；而内核态与用户态之间拷贝依然是CPU拷贝；在这个场景下，零拷贝技术优化的重点，就是内核态与用户态之间的两次拷贝</p><p>而mmap文件映射的方式，就是在用户态不在保存文件的内容，而只保存文件的映射，包括文件的内存起始地址，文件大小等</p><p>真实的数据，也不需要在用户态留存，可以直接通过操作映射，在内核态完成数据复制</p><p><img src="/images/posts/rocker_base1/img-6.png" alt="RocketMQ基础知识"></p><p><img src="/images/posts/rocker_base1/img-7.png" alt="RocketMQ基础知识"></p><p>这种mmap的映射机制由于还是需要用户态保存文件的映射信息，数据复制的过程也需要用户态的参与，这其中的变数是非常多的。</p><p>所以mmap机制非常适合操作小文件，如果文件太大，映射信息也会过大，容易造成问多问题</p><p>通过mmap机制建议的映射文件不要超过2g,而RocketMQ做大的commitLog文件保持在1g固定大小,也是为了方便文件映射</p><h3 id="sendFile机制运行"><a href="#sendFile机制运行" class="headerlink" title="sendFile机制运行"></a>sendFile机制运行</h3><p>主要是通过java.nio.channels.FileChannel的transferTo方法完成</p><p>早期的sendFile机制其实还是依靠CPU进行页缓存与socket缓存区之间的数据拷贝；但是在后期不断改进过程中，sendfile优化了实现机制</p><p>在拷贝过程中，并不直接拷贝文件的内容，而是只拷贝一个带有文件位置和长度等信息的文件描述符FD，这样就大大减少了需要传递的数据；而真实的数据内容，会交由DMA控制器，从缓存页中打包异步发送到socket中</p><p><img src="/images/posts/rocker_base1/img-8.png" alt="RocketMQ基础知识"></p><p><img src="/images/posts/rocker_base1/img-9.png" alt="RocketMQ基础知识"></p><h2 id="顺序写加速文件写入磁盘"><a href="#顺序写加速文件写入磁盘" class="headerlink" title="顺序写加速文件写入磁盘"></a>顺序写加速文件写入磁盘</h2><p>通常应用程序往磁盘写文件时，由于磁盘空间不是连续的，会有很多碎片。所以我们去写一个文件时，也就无法把一个文件写在一块连续的磁盘空间中，而需要在磁盘多个扇区之间进行大量的随机写。这个过程中有大量的寻址操作，会严重影响写数据的性能。而顺序写机制是在磁盘中提前申请一块连续的磁盘空间，每次写数据时，就可以避免这些寻址操作，直接在之前写入的地址后面接着写就行</p><p>Kafka官方详细分析过顺序写的性能提升问题。Kafka官方曾说明，顺序写的性能基本能够达到内存级别。而如果配备固态硬盘，顺序写的性能甚至有可能超过写内存。而RocketMQ很大程度上借鉴了Kafka的这种思想</p><p>org.apache.rocketmq.store.CommitLog#DefaultAppendMessageCallback中的doAppend方法。在这个方法中，会以追加的方式将消息先写入到一个堆外内存byteBuffer中，然后再通过fileChannel写入到磁盘</p><h2 id="刷盘机制保证消息不丢失"><a href="#刷盘机制保证消息不丢失" class="headerlink" title="刷盘机制保证消息不丢失"></a>刷盘机制保证消息不丢失</h2><p>在操作系统层面，当应用程序写入一个文件时，文件内容并不会直接写入到硬件当中，而是会先写入到操作系统中的一个缓存PageCache中。PageCache缓存以4K大小为单位，缓存文件的具体内容</p><p>这些写入PageCache中的文件,在应用程序看来,是已经完全落盘保存好了的，可以修改，复制等等</p><p>但是，本质上，PageCache依然是内存状态，所以一断电就会丢失。因此，需要将内存状态的数据写入到磁盘当中，这样数据才能真正完成持久化，断电也不会丢失。这个过程就称为刷盘</p><p>PageCache是源源不断产生的，而Linux操作系统显然不可能时时刻刻往硬盘写文件。所以，操作系统只会在某些特定的时刻将PageCache写入到磁盘。例如当我们正常关机时，就会完成PageCache刷盘。另外，在Linux中，对于有数据修改的PageCache，会标记为Dirty(脏页)状态。当Dirty Page的比例达到一定的阈值时，就会触发一次刷盘操作。例如在Linux操作系统中，可以通过&#x2F;proc&#x2F;meminfo文件查看到Page Cache的状态</p><p>但是操作系统的刷盘不是时时刻刻执行的，那么对于用户态的应用程序来说，那就避免不了非正常宕机时的数据丢失问题。因此，操作系统也提供了一个系统调用，应用程序可以自行调用这个系统调用，完成page cache的强制刷盘</p><p>RocketMQ对何时刷盘也提供了两种刷盘机制,同步刷盘和异步刷盘</p><ul><li>同步刷盘： 在返回成功状态前,消息被写入磁盘;具体流程是，消息写入内存的PAGECACHE后，立刻通知刷盘线程刷盘， 然后等待刷盘完成，刷盘线程执行完成后唤醒等待的线程，返回消息写 成功的状态</li><li>异步刷盘:  在返回写成功状态时，消息可能只是被写入了内存的PAGECACHE，写操作的返回快，吞吐量大；当内存里的消息量积累到一定程度时，统一触发写磁盘动作，快速写入</li><li>配置方式： borker配置文件里的flushDiskType参数设置的,这个参数被配置成 SYNC_FLUSH , ASYNC_FLUSH 中的一个</li></ul><p>同步刷盘机制会更频繁的调用fsync，所以吞吐量相比异步刷盘会降低，但是数据的安全性会得到提高</p><p><img src="/images/posts/rocker_base1/img-10.png" alt="RocketMQ基础知识"></p><h1 id="🤗-总结归纳"><a href="#🤗-总结归纳" class="headerlink" title="🤗 总结归纳"></a>🤗 总结归纳</h1><h1 id="📎-参考文章"><a href="#📎-参考文章" class="headerlink" title="📎 参考文章"></a>📎 参考文章</h1><ul><li>官网地址： <a href="https://rocketmq.apache.org/">https://rocketmq.apache.org/</a></li></ul><blockquote><p>💡 有关文章的问题，欢迎您在底部评论区留言，一起交流~</p></blockquote>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;😀 这里写文章的前言：&lt;br&gt;RocketMQ 基础知识&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;📝-整理流程&quot;&gt;&lt;a href=&quot;#📝-整理流程&quot; class=&quot;headerlink&quot; title=&quot;📝</summary>
        
      
    
    
    
    <category term="中间件" scheme="https://ruy9527.github.io/categories/%E4%B8%AD%E9%97%B4%E4%BB%B6/"/>
    
    
    <category term="RocketMQ" scheme="https://ruy9527.github.io/tags/RocketMQ/"/>
    
  </entry>
  
  <entry>
    <title>Spark基础知识</title>
    <link href="https://ruy9527.github.io/article/spark_base_info/"/>
    <id>https://ruy9527.github.io/article/spark_base_info/</id>
    <published>2022-04-08T04:00:00.000Z</published>
    <updated>2023-07-23T05:13:44.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>😀 这里写文章的前言：<br>Hadoop: 海量数据的存储和海量数据的分析计算<br>Spark是一种基于内存的快速,通用,可扩展的大数据分析引擎</p></blockquote><h1 id="📝-Spark基础概念"><a href="#📝-Spark基础概念" class="headerlink" title="📝 Spark基础概念"></a>📝 Spark基础概念</h1><h2 id="基本概念"><a href="#基本概念" class="headerlink" title="基本概念"></a>基本概念</h2><p>Spark是一种基于内存的快速和通用,可扩展的大数据分析计算引擎</p><details><summary>与MR进行对比</summary><ol><li>MR是基于磁盘的,Spark是基于内存</li><li>MR的task是进程</li><li>spark的task是线程，在executor进程里执行的线程</li><li>MR在Container里执行（留有接口方便插入），spark在worker里执行（自己用，没有接口）</li><li>MR适合做一次计算，Spark适合做迭代计算</li></ol></details><p>hadoop MR 框架溢出写磁盘次数多,不合适迭代算,只适合一次计算;Spark框架计算块的原因是中间结果不罗盘,spark的shuffle也是要罗盘的</p><h2 id="基础模块"><a href="#基础模块" class="headerlink" title="基础模块"></a>基础模块</h2><ul><li>Spark Core : 实现了Spark的基本功能,包含了任务调度,内存管理,错误恢复,与存储系统交互等模块.Spark Core中还包含了对弹性分布式数据集(Resilient Distrubuted DataSet,简称RDD)的API定义</li><li>Spark SQL : Spark用来操作结构化数据的程序包,通过Spark SQL,我们可以使用SQL或者Hive版本的HQL来查询数据;SparkSQL支持多种数据源,比如Hive表,Parquet及Join等</li><li>Spakr Streaming : Spark提供的对实时的数据进行流式计算组件,提供了用来操作数据流的API,并且与Spark Core中的RDD API高度对应</li><li>Spark MLlib : 提供常见的机器学习功能的程序库。包括分类、回归、聚类、协同过滤等，还提供了模型评估、数据 导入等额外的支持功能</li><li>Spark GraphX : 主要用于图形并行计算和图挖掘系统的组件</li></ul><h2 id="运行模式"><a href="#运行模式" class="headerlink" title="运行模式"></a>运行模式</h2><p>   Local : 本地</p><details><summary>Standalone模式</summary><p>Master和Worker模式,Master职责负责资源的管理和分配,相当于ReousrceManager;Worker资源节点与任务执行节点(相当于NodeManager);Master和Worker都是随着集群的启动而启动,集群的消失而消失;Master和Worker只有standalone模式才有</p></details><details><summary>Mesos模式</summary><p>使用mesos平台进行资源与任务的调度</p></details><details><summary>Yarn模式</summary><p>Driver职责:</p><ul><li>将代码转化成job执行</li><li>提交task到executor</li><li>监控task执行状况</li><li>负责程序运行过中ui界面展示</li></ul><p>Executor职责:</p><ul><li>负责执行task</li></ul><p>Driver和Executor是随着任务提交而启动的,随着任务完成而消失的</p></details><details><summary>Spark on yarn client模式</summary><p>Driver和Client都在SparkSubmit进程中,此时SparkSubmit进程不能关闭,关闭之后Driver消失程序中止</p><p>工作流程</p><ol><li>通过bin&#x2F;sparksubmit提交任务生成SparkSubmit进程,在进程中创建Client客户端与Driver</li><li>Client向ResourceManager注册任务</li><li>RM向Client返回路径,任务id</li><li>Client会将RM返回的路径与任务id拼接成新路径,上传jar包到路径中</li><li>Client向RM申请启动ApplicationMaster</li><li>RM会在其中一个NodeManager中启动AM</li><li>AM向RM申请计算资源</li><li>RM会将资源列表返回给AM</li><li>AM会向NM申请启动Executor</li><li>Executor启动之后会向Driver反向注册</li><li>Driver提交task到executor执行</li><li>执行完之后,AM会注销自己释放资源</li></ol></details><details><summary>Spark on yarn cluster模式</summary><p>Driver在ApplicationMaster进程中,此时SparkSubmit进程关闭不受影响的,程序不会停止</p><p>执行流程如下:</p><ol><li>通过bin&#x2F;sparksubmit提交任务生成SparkSubmit进程,在进程中创建Client客户端</li><li>Client向ResourceManager注册任务</li><li>RM向Client返回路径,任务id</li><li>Client会将RM返回的路径与任务id拼接成新路径,上传jar包到路径中</li><li>Client向RM申请启动ApplicationMaster</li><li>RM会在其中一个NodeManager中启动AM</li><li>在AM中启动Driver线程</li><li>AM向RM申请计算资源</li><li>RM会将资源列表返回给AM</li><li>AM会向NM申请启动Executor</li><li>Executor启动之后会向Driver反向注册</li><li>Driver提交task到executor执行</li><li>执行完成之后,AM会注销自己释放资源</li></ol></details><p>SparkSubmit常用参数</p><table><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td>参数</td><td>作用</td><td>说明</td></tr><tr><td>master</td><td>指定任务提交到哪个资源调度器中</td><td>local模式: local&#x2F;local[*]&#x2F;local[N]<br><br>stanadalone： spark:&#x2F;&#x2F;master主机名:7077,..<br><br>yarn模式: yarn</td></tr><tr><td>class</td><td>指定待运行的带有main方法object全类名</td><td></td></tr><tr><td>deploy-mode</td><td>指定部署模式[client&#x2F;cluster]</td><td></td></tr><tr><td>driver-memory</td><td>指定Driver的内存大小</td><td></td></tr><tr><td>executor-memory</td><td>指定每个executor的内存大小</td><td></td></tr><tr><td>executor-cores</td><td>指定每个executor的cpu个数</td><td></td></tr><tr><td>total-executor-cores</td><td>指定所有executor的cpu总个数[仅用于standalone模式]</td><td></td></tr><tr><td>num-executors</td><td>指定需要的executor总个数[仅用于yarn模式]</td><td></td></tr><tr><td>queue</td><td>指定任务提交到哪个资源队列中[仅用于yarn模式]</td><td></td></tr></tbody></table><h1 id="📝-Spark-RDD"><a href="#📝-Spark-RDD" class="headerlink" title="📝 Spark RDD"></a>📝 Spark RDD</h1><h2 id="基本概念-1"><a href="#基本概念-1" class="headerlink" title="基本概念"></a>基本概念</h2><p>RDD是弹性分布式数据集,RDD代表弹性,可分区,不可变,元素可并行计算的计算</p><details><summary>弹性</summary><ul><li>存储的弹性: 如果内存充足,中间结果会全部保存在内存中,如果内存不足,数据一部分保存在内存中,一部分保存在磁盘中</li><li>计算的弹性: 如果计算出错会自动重试</li><li>容错的弹性: 如果RDD数据丢失可以根据依赖关系+封装的计算逻辑重新读取数据重新计算得到数据</li><li>分区的弹性: RDD的分区可以自动根据文件的切片动态生成</li></ul></details><details><summary>不可变</summary><p>RDD中只只封装了数据的处理逻辑,如果想要重新改变数据只能生成新的RDD</p></details><details><summary>可分区</summary><p>spark是分布式计算框架,Spark根据文件的切片生成分区,一个切片对应一个分区,后续每个分区计算逻辑是一样,处理的数据不一样,每个分区间是并行的</p></details><details><summary>可并行计算</summary><p>每个分区计算逻辑是一样,处理的数据不一样,每个分区间是并行的</p></details><details><summary>不存储数据</summary><p>RDD中只封装了数据的处理逻辑,不存储数据</p></details><ul><li>一组分区列表: RDD是分布式的,会根据文件切片划分分区,由多少切片就有多少分区</li><li>作用在每个分区上的计算函数:  RDD每个分区处理不同的切片数据,每个分区计算逻辑是一样</li><li>对其他RDD的依赖关系: RDD会记录父RDD,后续如果RDD分区数据丢失可以根据依赖关系重新计算得到数据</li><li>分区器[可选]: RDD的分区分布在不同的机器上,后续如果想要将key相同的数据聚在一起,此时必须使用分区器规划key的数据在shuffle之后续落在RDD哪个分区上</li><li>优先位置: spark在分配task的时候会考虑将计算逻辑分配在数据所在的位置,避免计算的时候通过网络拉取数据影响效率</li></ul><h2 id="分区"><a href="#分区" class="headerlink" title="分区"></a>分区</h2><p>通过本地集合创建RDD分区数:</p><p>如果有设置numSlices参数,此时RDD分区数 &#x3D; 设置numSlices参数</p><p>如果没有设置numSlices参数,此时RDD分区数 &#x3D; defaultParallelism:</p><ol><li>如果在sparkConf中有设置spark.default.parallelism参数的值,此时defaultParallelism&#x3D;spark.default.parallelism参数值</li><li>如果没有在sparkconf中设置spark.default.parallelism参数的值</li></ol><ul><li>master&#x3D;local,此时defaultParallelism&#x3D;1</li><li>master&#x3D;local[N],此时defaultParallelism&#x3D;N</li><li>master&#x3D;local[*],此时defaultParallelism&#x3D;本地cpu个数</li><li>master&#x3D;spark:&#x2F;&#x2F;…,此时defaultParallelism &#x3D; max( 所有executor总核数,2 )</li></ul><p>通过读取文件创建RDD分区数</p><ol><li>如果有指定minPartition参数值,此时RDD分区数 ≥ 指定minPartition参数值</li><li>如果没有指定minPartition参数值,此时RDD分区数 ≥ min(defaultParallelism,2)</li></ol><p>通过其它RDD衍生出的新RDD的分区数 &#x3D; 依赖第一个父RDD的分区数</p><h2 id="算子"><a href="#算子" class="headerlink" title="算子"></a>算子</h2><p>Spark的算子分为两类: Transformation转换算子(生成的是新RDD,不会触发任务计算);Action行动算子(没有返回值或者返回scala数据类型,会触发任务的计算)</p><table><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td>算子名字</td><td>作用</td><td>备注</td></tr><tr><td>map</td><td>map（func：RDD元素类型你&#x3D;&gt;B（任意类型）</td><td>map里面的函数是针对RDD每个元素操作，元素有多少个，函数就执行多少次<br>map生成新的RDD元素个数和原RDD的元素个数相同</td></tr><tr><td>flatMap</td><td>flatMap（func:RDD元素类型&#x3D;&gt;B(集合)）：转换 + 压平</td><td>flatMap里面的函数是针对每个元素操作的<br>flatMap生成新RDD元素的个数一般是大于等于原RDD元素个数（除非是空集合，可能会是小于）<br>flatMap的应用场景：一对多</td></tr><tr><td>filter</td><td>filter（func：RDD元素类型 &#x3D; Boolean）：根据指定条件过滤</td><td>filter里面的函数是针对每个元素操作的<br>filter保留的是函数返回值为true的元素<br>filter的应用场景：过滤数据</td></tr><tr><td>mapPartitons()</td><td>mapPartitons(func:Iterator[Rdd元素类型]&#x3D;&gt;Iterator[B])：一对一转换，原RDD的一个分区计算得到一个新RDD分区</td><td>函数是针对每个分区操作的，多少分区操作多少次<br>一般用于MySQL&#x2F;Hbase&#x2F;redis查询数据，可以减少链接创建与销毁的次数</td></tr><tr><td>mapPartitons()</td><td>与map的区别<br>1、函数针对的对象不一样，一个是单个元素，一个是整个分区<br>2、函数的返回值类型不一样<br>3、元素内存回收的实际不一样</td><td>map的函数返回的是新RDD的元素，新Rdd的元素个数&#x3D;原RDD的个数<br>mapPartitions里面的函数是针对没分翻去，返回的是新RDD分区所有数据，新RDD元素个数不一定 &#x3D; 原RDD元素个数，但是分区数相等<br>map每个元素操作完成之后就可以进行垃圾回收<br>mapPartitions必须等到分区迭代器遍历完成之后才会垃圾回收，如果RDD分区数据量过大，则可能出现内存溢出的现象（OOM），此时应用map代替之</td></tr><tr><td>groupBy</td><td>groupBy(func: RDD元素类型&#x3D;&gt;K ): 按照指定字段进行分组</td><td>groupBy里面函数是针对每个元素操作,元素有多少个,函数就执行多少次<br>groupBy是根据函数的返回值对元素进行分组<br>groupBy返回RDD是KV键值对,K是函数的返回值,V是原RDD中K对应的所有元素的集合<br>groupBy会产生shuffle操作</td></tr><tr><td>distinct</td><td>去重</td><td>distinct会产生shuffle操作</td></tr><tr><td>coalesce</td><td>coalesce(分区数): 合并分区</td><td>coalesce默认只能减少分区数,此时没有shuffle操作<br>coalesce如果想要增大分区数,需要设置shuffle&#x3D;true，此时会产生shuffle操作<br>coalesce一般用于减少分区数,一般是搭配filter使用</td></tr><tr><td>repartition</td><td>重分区</td><td>repartition既可以增大分区数也可以减少分区数,但是都有shuffle操作<br>repartition的使用场景: 增大分区数的时候</td></tr><tr><td>coalesce与repartition的区别</td><td></td><td>coalesce默认只能减少分区数,此时没有shuffle操作,coalesce如果想要增大分区数,需要设置shuffle&#x3D;true，此时会产生shuffle操作<br>repartition既可以增大分区数也可以减少分区数,但是都有shuffle操作</td></tr><tr><td>sortBy</td><td>sortBy(func: RDD元素类型&#x3D;&gt;K，ascending&#x3D;true&#x2F;false): 根据指定字段排序</td><td>sortBy里面的函数是针对每个元素,元素有多少个,函数执行多少次<br>sortBy是根据函数的返回值对RDD元素进行排序(true是升序,false是降序)<br>sortBy会产生shuffle</td></tr><tr><td>intersecetion交集</td><td>intersecetion会产生shuffle操作</td><td></td></tr><tr><td>subtract()</td><td>交集</td><td></td></tr><tr><td>union</td><td>并集</td><td></td></tr><tr><td>zip</td><td>拉链</td><td>两个RDD要想拉链要求数量条数与分区数都必须一致。</td></tr><tr><td>partitionBy</td><td>partitionBy(partitioner): 根据指定的分区器重分区</td><td></td></tr><tr><td>groupByKey</td><td>groupByKey： 根据key分组</td><td>groupByKey生成的新RDD的元素类型是KV键值对<br>K是分组的key<br>V是key在原RDD中对应的所有的value值</td></tr><tr><td>reduceByKey</td><td>reduceByKey(func: (value值类型,value值类型)&#x3D;&gt;Value值类型): 按照key分组,对value值聚合</td><td>reduceByKey里面的函数的第一个参数代表该组上一次的聚合结果,第一个聚合的时候初始值 &#x3D; 该组第一个value值<br>reduceByKey里面的函数的第二个参数代表该组当前待聚合的value值</td></tr><tr><td>reduceByKey与groupByKey的区别</td><td>reduceByKey与groupByKey的区别</td><td>reduceByKey有预聚合操作,性能更高,工作中推荐使用这种高性能shuffle算子<br>groupByKey没有预聚合操作</td></tr><tr><td>mapPartitionsWithIndex</td><td>mapPartitionsWithIndex</td><td>mapPartitionsWithIndex((index,it)</td></tr><tr><td>自定义分区器</td><td>自定义class继承Partitioner</td><td>override def numPartitions: Int &#x3D; num      &#x2F;&#x2F; 获取新RDD分区<br>override def getPartition(key: Any): Int   &#x2F;&#x2F; 获取key获取分区号</td></tr></tbody></table><h1 id="📝-Spark优化"><a href="#📝-Spark优化" class="headerlink" title="📝 Spark优化"></a>📝 Spark优化</h1><p>根据木桶效应,最短的木板决定了木桶的容量,因此,对于一只有短板的木桶,其它木板调节得再高也无济于事,最短的木板才是木桶容量的瓶颈</p><details><summary>性能优化本质</summary><ol><li>性能调优不是一锤子买卖，补齐一个短板，其他板子可能会成为新的短板。因此，它是一个动态、持续不断的过程</li><li>性能调优的手段和方法是否高效，取决于它针对的是木桶的长板还是瓶颈。针对瓶颈，事半功倍；针对长板，事倍功半</li><li>性能调优的方法和技巧，没有一定之规，也不是一成不变，随着木桶短板的此消彼长需要相应的动态切换</li><li>性能调优的过程收敛于一种所有木板齐平、没有瓶颈的状态</li></ol></details><p>Spark UI提供了丰富的面板,来展示DAG,Stages划分,执行计划,Executor负载均衡情况,GC时间,内存缓存消耗等等详尽的运行时状态数据;对于硬件消耗资源,可利用监控应用进行查看</p><p><strong>性能调优目的: 所有参数计算的硬件资源之间寻求协调与平衡,让硬件资源达到一种平衡,无瓶颈的状态</strong></p><h1 id="📝-Spark参数"><a href="#📝-Spark参数" class="headerlink" title="📝 Spark参数"></a>📝 Spark参数</h1><table><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td>配置项</td><td>含义</td><td>使用说明</td></tr><tr><td>spark.cores.max</td><td>集群范围内满配CPU核数</td><td></td></tr><tr><td>spark.executor.cores</td><td>单个Executor内CPU参数</td><td></td></tr><tr><td>spark.task.cpus</td><td>单个任务消耗的CPU核数</td><td></td></tr><tr><td>spark.default.parallelism</td><td>未指定分区数时的默认并行度</td><td></td></tr><tr><td>spark.sql.shuffle.partitions</td><td>数据关联,聚合操作中Reducer的并行度</td><td></td></tr><tr><td></td><td></td><td></td></tr><tr><td>spark.executor.memory</td><td>单个Executor内堆内内存总大小</td><td></td></tr><tr><td>spark.memory.offHeap.size</td><td>单个Executor内堆外内存总大小(spark.memory.offHeap.enabled为true才生效)</td><td></td></tr><tr><td>spark.menory.fraction</td><td>堆内内存中,用于缓存RDD和执行计算的内存比例</td><td></td></tr><tr><td>spark.menory.storageFraction</td><td>用于缓存RDD的内存占比,执行内存占比为1-spark.memory.stroageFraction</td><td></td></tr><tr><td>spark.rdd.compress</td><td>RDD缓存是否压缩,默认不压缩</td><td></td></tr><tr><td></td><td></td><td></td></tr><tr><td>spark.local.dir</td><td>用于缓存RDD和Shuffle中间文件的磁盘目录</td><td></td></tr><tr><td></td><td></td><td></td></tr><tr><td>spark.shuffle.file.buffer</td><td>Map输出端的写入缓冲区大小</td><td>Map端</td></tr><tr><td>spark.reducer.maxSizeInFlight</td><td>Reduce输入端的读取缓冲区大小</td><td>Reduce端</td></tr><tr><td></td><td></td><td></td></tr><tr><td>spark.shuffle.sort.bypassMergeThreshold</td><td>Map阶段不进行排序的分区阈值</td><td></td></tr><tr><td></td><td></td><td></td></tr><tr><td>spark.sql.adaptive.enabled</td><td>是否启用AQE(Adaptive query execution);默认是禁用的</td><td></td></tr><tr><td></td><td></td><td></td></tr><tr><td>spark.sql.adaptive.coalescePartition.enabled</td><td>是否启用AQE中的自动分区合并,默认启用</td><td></td></tr><tr><td>spark.sql.adaptive.advisoryParitionSizeInBytes</td><td>由开发者指定分区合并后的推荐尺寸</td><td></td></tr><tr><td>spark.sql.adaptive.coalescePartitions.minPartitionNum</td><td>分区合并后,数据集的分区数不低于该值</td><td></td></tr><tr><td></td><td></td><td></td></tr><tr><td>spark.sql.adaptive.shewJoin.enabled</td><td>是否开启AQE中自动处理数据倾斜的功能,默认开启</td><td></td></tr><tr><td>spark.sql.adaptive.shewJoin.shewdPartitionFactor</td><td>判断数据分区是否倾斜的比例系数</td><td></td></tr><tr><td>spark.sql.adaptive.shewJoin.shewdPartitionThresholdInBytes</td><td>判断数据分区是否倾斜的最低阈值</td><td></td></tr><tr><td>spark.sql.adaptive.advisoryPartitionSizeInBytes</td><td>以字节为单位,拆分倾斜分区的数据粒度</td><td></td></tr><tr><td></td><td></td><td></td></tr><tr><td>spark.sql.autoBroadcastJoinThreadld</td><td>数据表采用broadcast join实现方式的最低阈值</td><td></td></tr><tr><td>spark.sql.adaptive.nonEmptyPartitionRatioForBoradcasejoin</td><td>非空分区比例小于该值,才会调整Join策略</td><td></td></tr></tbody></table><h1 id="🤗-总结归纳"><a href="#🤗-总结归纳" class="headerlink" title="🤗 总结归纳"></a>🤗 总结归纳</h1><h1 id="📎-参考文章"><a href="#📎-参考文章" class="headerlink" title="📎 参考文章"></a>📎 参考文章</h1><blockquote><p>💡 有关文章的问题，欢迎您在底部评论区留言，一起交流~</p></blockquote>]]></content>
    
    
    <summary type="html">Spark基础知识</summary>
    
    
    
    <category term="Spark" scheme="https://ruy9527.github.io/categories/Spark/"/>
    
    
    <category term="Spark" scheme="https://ruy9527.github.io/tags/Spark/"/>
    
    <category term="使用入门" scheme="https://ruy9527.github.io/tags/%E4%BD%BF%E7%94%A8%E5%85%A5%E9%97%A8/"/>
    
    <category term="大数据" scheme="https://ruy9527.github.io/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
  </entry>
  
  <entry>
    <title>MySql分库分表</title>
    <link href="https://ruy9527.github.io/article/mysql_partition_databases_and_tables/"/>
    <id>https://ruy9527.github.io/article/mysql_partition_databases_and_tables/</id>
    <published>2022-04-07T04:00:00.000Z</published>
    <updated>2023-05-30T15:18:57.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>😀 这里写文章的前言：<br>分库分表</p></blockquote><h1 id="📝-分库分表要解决的问题"><a href="#📝-分库分表要解决的问题" class="headerlink" title="📝 分库分表要解决的问题"></a>📝 分库分表要解决的问题</h1><ol><li>解决B+树设计上本身的瓶颈问题(数据过多, 以及内存不够放索引,需要发生IO之类的问题)</li><li>解决技术问题之后,要考虑业务的拆分情况,尽可能的减少业务代码的改动逻辑</li><li>分库分表的本质就是把B+树的设计瓶颈重新拆散,变成无瓶颈</li></ol><h1 id="📔-垂直切分"><a href="#📔-垂直切分" class="headerlink" title="📔  垂直切分"></a><strong>📔  垂直切分</strong></h1><p>通过将不同业务的数据，放到不同的数据库中，实现不同业务之间数据库层面的隔离</p><p><img src="/images/posts/mysql_partition_databases_and_tables/img-1.png" alt="MySql分库分表"></p><h1 id="📔-水平切分"><a href="#📔-水平切分" class="headerlink" title="📔  水平切分"></a><strong>📔  水平切分</strong></h1><p>可以按照某种规则将某些字段分散到多个库中，每个表中只包含一部分数据 ,字段是一样的(比如日期字段，用户id字段，区域字字段等，比较理想的切分字段)</p><p><img src="/images/posts/mysql_partition_databases_and_tables/img-2.png" alt="MySql分库分表"></p><h1 id="📔-如何实施"><a href="#📔-如何实施" class="headerlink" title="📔   如何实施"></a><strong>📔   如何实施</strong></h1><p><img src="/images/posts/mysql_partition_databases_and_tables/img-3.png" alt="MySql分库分表"></p><h2 id="1-先评估是否需要拆分"><a href="#1-先评估是否需要拆分" class="headerlink" title="1. 先评估是否需要拆分"></a>1. <strong>先评估是否需要拆分</strong></h2><p>能不能有其他更好的优化方案，分库分表的开发周期比较长，一般只作为兜底方案去执行</p><h2 id="2-定制拆分的详细方案"><a href="#2-定制拆分的详细方案" class="headerlink" title="2. 定制拆分的详细方案"></a>2. <strong>定制拆分的详细方案</strong></h2><ol><li>选择合适的分表策略</li><li>再通知可能要更改的下游</li><li>尽量做好设计，避免分库分表带来的常见问题</li></ol><h2 id="3-目的评估"><a href="#3-目的评估" class="headerlink" title="3. 目的评估"></a>3. 目的评估</h2><ol><li>评估：拆成几个表，几个库</li><li>目标：读写能力提升多少，负载降低多少，<strong>容量支持未来N年的发展</strong></li></ol><p>举个例子：</p><p>当前30亿数据，如何拆分</p><p>解答：</p><p>找一个合理的拆分情况，比如按区域，按门店去拆分，不影响原来的业务，并且颗粒度细致到足够未来N年的发展就可以了</p><h2 id="4-拆分完成后防止问题兜底方案"><a href="#4-拆分完成后防止问题兜底方案" class="headerlink" title="4. 拆分完成后防止问题兜底方案"></a>4. 拆分完成后防止问题兜底方案</h2><p>**作用：**保证切换数据源之后，正常的增量数据也同步写入一份到旧数据源，这样能保证新数据发生不可短时间解决的问题的时候，避免回滚可以通过开关配置切换到旧数据源上正常执行业务</p><p><strong>方案：</strong></p><ol><li>同步双写：同步写新、旧数据源（代码层面上直接改）</li><li>异步双写：通过监听binlog的方式去写旧数据源</li><li>中间件同步工具：通过中间件工具去实施同步数据</li></ol><p><strong>注意点：做好补偿机制， 因为可能写其中一个库失败，需要做好最终一致性问题</strong></p><h2 id="5-存量数据的迁移问题"><a href="#5-存量数据的迁移问题" class="headerlink" title="5.存量数据的迁移问题"></a>5.存量数据的迁移问题</h2><p><strong>作用</strong>：迁移旧数据源的数据同步到新的数据源上，保证原有业务的正常CRUD</p><p><strong>方案：</strong></p><ol><li>自己写job开发，查询旧数据源，写入新库</li></ol><p>2.用中间件去同步</p><p>3.交给DBA去同步</p><p>注意点：一定要校验数据的准确性，因为这是分库分表后的最后一步，一定要再三确认数据的一致性校验问题，并且要有相关的job做好补偿方案</p><h1 id="分库分表后存在的问题"><a href="#分库分表后存在的问题" class="headerlink" title="分库分表后存在的问题"></a>分库分表后存在的问题</h1><h2 id="分布式id问题"><a href="#分布式id问题" class="headerlink" title="分布式id问题"></a>分布式id问题</h2><ol><li>雪花算法，</li><li>全局的自增id，</li><li>美团分布式id方案</li></ol><h2 id="跨表查询问题"><a href="#跨表查询问题" class="headerlink" title="跨表查询问题"></a>跨表查询问题</h2><ol><li>从设计上解决跨表查询的情况，设计上基本能解决95%以上的问题，比如冗余字段，拆分的时候按业务隔离性去拆分</li><li>分开查询，通过java代码去组合数据</li><li>同一个线程需要跨库的，可以通过JDBC的方式去查询</li><li>用ES等查询工具查询出主要信息，再组装数据</li></ol><h2 id="分布式事务问题"><a href="#分布式事务问题" class="headerlink" title="分布式事务问题"></a>分布式事务问题</h2><ol><li>做好补偿方案</li><li>最大努力通知（使用MQ）</li><li>人工兜底补偿 - 保底</li></ol><h1 id="📔-切分策略"><a href="#📔-切分策略" class="headerlink" title="📔  切分策略"></a><strong>📔  切分策略</strong></h1><details><summary>范围切片</summary><ul><li>优点： 天然水平扩展;单表大小可控</li><li>缺点：存在明显的写偏移</li></ul><p><img src="/images/posts/mysql_partition_databases_and_tables/img-4.png" alt="MySql分库分表"></p></details><details><summary>中间映射表</summary><ul><li>优点： 灵活</li><li>缺点： 引入额外的单点，增加了流程的复杂度</li></ul><p><img src="/images/posts/mysql_partition_databases_and_tables/img-5.png" alt="MySql分库分表"></p></details><details><summary>hash切分</summary><ul><li>优点： 数据分片比较均匀，不容易出现热点和访问并发的瓶颈</li><li>缺点： 后续扩容需要迁移数据，存在跨节点查询等问题</li></ul><p><img src="/images/posts/mysql_partition_databases_and_tables/img-6.png" alt="MySql分库分表"></p></details><h1 id="🤗-总结归纳"><a href="#🤗-总结归纳" class="headerlink" title="🤗 总结归纳"></a>🤗 总结归纳</h1><h1 id="📎-参考文章"><a href="#📎-参考文章" class="headerlink" title="📎 参考文章"></a>📎 参考文章</h1><blockquote><p>💡 有关文章的问题，欢迎您在底部评论区留言，一起交流~</p></blockquote>]]></content>
    
    
    <summary type="html">分库分表</summary>
    
    
    
    <category term="数据库" scheme="https://ruy9527.github.io/categories/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
    
    
    <category term="思考" scheme="https://ruy9527.github.io/tags/%E6%80%9D%E8%80%83/"/>
    
    <category term="使用入门" scheme="https://ruy9527.github.io/tags/%E4%BD%BF%E7%94%A8%E5%85%A5%E9%97%A8/"/>
    
    <category term="mysql" scheme="https://ruy9527.github.io/tags/mysql/"/>
    
  </entry>
  
  <entry>
    <title>hive基础知识</title>
    <link href="https://ruy9527.github.io/article/hive_base/"/>
    <id>https://ruy9527.github.io/article/hive_base/</id>
    <published>2022-03-02T04:00:00.000Z</published>
    <updated>2023-07-23T05:16:03.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>😀 这里写文章的前言：<br>HDFS来存储海量数据,MapReduce来对海量数据进行分布式并行计算,yarn来实现资源管理和作业调度<br>开发人员需要编写MR来对数据进行统计分析难度极大,效率极低,并且对开发者的java功底要求<br>Hive可以帮助人员来完成这些苦活(将SQL语句转化MapReduce在yarn上跑)</p></blockquote><h1 id="📝-Hive架构模块"><a href="#📝-Hive架构模块" class="headerlink" title="📝 Hive架构模块"></a>📝 Hive架构模块</h1><p><img src="/images/posts/hive_base/img-1.png" alt="hive基础知识"></p><p><img src="/images/posts/hive_base/img-2.png" alt="hive基础知识"></p><h2 id="Client"><a href="#Client" class="headerlink" title="Client"></a>Client</h2><p>Cli: command-line interface</p><p>JDBC&#x2F;ODBC: jdbc访问hive</p><details><summary>Hive客户端</summary><ol><li>Thrift Clients: Hive的Server是基于Apache Thrift的,所以支持thrift客户端的查询请求</li><li>JDBC Clinet: 使用Java的JDBC driver连接Hive, JDBC driver使用 Thrift与Hive进行通信</li><li>ODBC Client: Hive的ODBC driver使用基于ODBC协议连接hive,与JDBC driver类似,ODBC driver也是通过thrift与Hive server进行通信</li></ol></details><h2 id="Metastore"><a href="#Metastore" class="headerlink" title="Metastore"></a>Metastore</h2><p>元数据: 表命,表所属的数据库(默认是default),表的拥有者,列&#x2F;分区字段,表的类型(是否是外部表),表的数据所在的目录</p><h2 id="Hadoop"><a href="#Hadoop" class="headerlink" title="Hadoop"></a>Hadoop</h2><p>使用hadoop进行存储,使用MapReduce进行计算</p><h2 id="驱动器"><a href="#驱动器" class="headerlink" title="驱动器"></a>驱动器</h2><ul><li>SQL Parse(解析器): 将SQL字符串转换成抽象语法树AST(第三方工具);对AST进行语法分析,比如表是否存在,字段是否存在,SQL语义是否错误</li><li>Physical Plan(编译器): 将AST编译生成逻辑执行计划</li><li>Query Optimizer(优化器): 对逻辑执行计划进行优化<ul><li>Execution(执行器): 把逻辑执行计划转换成可以运行的屋里计划,对于Hive来说,就是MR&#x2F;Spark</li></ul></li></ul><h2 id="优缺点"><a href="#优缺点" class="headerlink" title="优缺点"></a>优缺点</h2><details><summary>优点</summary><ul><li>提供了类SQL语法操作接口,具备快速开发的能力(简单,容易上手)</li><li>避免了去写MapReduce,减少了开发者的学习成本</li><li>Hive优势在于处理大数据,在处理小数据时没有优势,因为Hive的执行延迟较高</li><li>Hive支持用户自定义函数,用户可以根据自己的需求来实现自己的函数</li></ul></details><details><summary>缺点</summary><ul><li>Hive自动生成MapReduce作业，通常情况下不够智能化;数据挖掘方面不擅长（多个子查询），由于MapReduce数据处理流程的限制，效率更高的算法却无法实现</li><li>Hive的执行延迟比较高，因为Hive常用于数据分析，对实时性要求不高的场合;Hive调优比较困难，粒度较粗</li><li>hive分析的数据是存储在HDFS上的，而HDFS仅支持追加写，所以在hive中不能update和delete，只能select和insert</li></ul></details><p>Hive 3.x版本之上是可以支持acid的</p><h1 id="📝-Hive数据类型"><a href="#📝-Hive数据类型" class="headerlink" title="📝 Hive数据类型"></a>📝 Hive数据类型</h1><table><thead><tr><th></th><th></th><th></th><th></th></tr></thead><tbody><tr><td>类型</td><td>比如</td><td>备注</td><td>存储例子</td></tr><tr><td>TINYINT</td><td></td><td></td><td></td></tr><tr><td>SMALINT</td><td></td><td></td><td></td></tr><tr><td>INT</td><td></td><td></td><td></td></tr><tr><td>BIGINT</td><td></td><td></td><td></td></tr><tr><td>BOOLEAN</td><td></td><td></td><td></td></tr><tr><td>FLOAT</td><td></td><td></td><td></td></tr><tr><td>DOUBLE</td><td></td><td></td><td></td></tr><tr><td>STRING</td><td></td><td></td><td></td></tr><tr><td>TIMESTAMP</td><td></td><td></td><td></td></tr><tr><td>BINARY</td><td></td><td></td><td></td></tr><tr><td>STRUCT</td><td>struct&lt;street:string, city:string&gt;</td><td>和c语言中的struct类似，都可以通过“点”符号访问元素内容</td><td>如果某个列的数据类型是STRUCT{first STRING, last STRING},那么第1个元素可以通过字段.first来引用</td></tr><tr><td>MAP</td><td>map&lt;string, int&gt;</td><td>MAP是一组键-值对元组集合，使用数组表示法可以访问数据</td><td>如果某个列的数据类型是MAP，其中键-&gt;值对是’first’-&gt;’John’和’last’-&gt;’Doe’，那么可以通过字段名[‘last’]获取最后一个元素</td></tr><tr><td>ARRAY</td><td>array<string></td><td>数组是一组具有相同类型和名称的变量的集合;这些变量称为数组的元素，每个数组元素都有一个编号，编号从零开始</td><td>数组值为[‘John’, ‘Doe’]，那么第2个元素可以通过数组名[1]进行引用</td></tr></tbody></table><h2 id="类型转换"><a href="#类型转换" class="headerlink" title="类型转换"></a>类型转换</h2><details><summary>隐式类型转换规则</summary><p>任何整数类型都可以隐式的转换为一个范围更广的类型,如INT可以转换成BIGINT</p><p>所有的整数类型,FLOAT和STRING类型可以隐式转换成DOUBLE</p><p>TINTINT,SMALLINT,INT都可以转换为FLOAT</p><p>BOOLEAN类型不可以转换成其它的类型</p></details><details><summary>CAST操作显示进行数据类型转换</summary><p>CAST(’1’ as INT) 将字符串转换整数</p><p>强制类型转换失败,如执行CAST(’x’ as INT),表达式就会返回null</p></details><h1 id="📝-Hive语句"><a href="#📝-Hive语句" class="headerlink" title="📝 Hive语句"></a>📝 Hive语句</h1><h2 id="数据库语句"><a href="#数据库语句" class="headerlink" title="数据库语句"></a>数据库语句</h2><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> DATABASE [IF <span class="keyword">NOT</span> <span class="keyword">EXISTS</span>] database_name</span><br><span class="line">[COMMENT database_comment]</span><br><span class="line">[LOCATION hdfs_path]</span><br><span class="line">[<span class="keyword">WITH</span> DBPROPERTIES (property_name<span class="operator">=</span>property_value, ...)];</span><br></pre></td></tr></table></figure><p>避免要创建的数据库已经存在错误,增加if nof exists判断</p><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">create</span> database if <span class="keyword">not</span> <span class="keyword">exists</span> bigdata;</span><br></pre></td></tr></table></figure><h3 id="查询数据库的信息"><a href="#查询数据库的信息" class="headerlink" title="查询数据库的信息"></a>查询数据库的信息</h3><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="comment">-- 过滤显示查询的数据库</span></span><br><span class="line"><span class="keyword">show</span> databases <span class="keyword">like</span> <span class="string">&#x27;bigdata*&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 显示数据库信息</span></span><br><span class="line"><span class="keyword">desc</span> database bigdata;</span><br></pre></td></tr></table></figure><h3 id="操作数据库"><a href="#操作数据库" class="headerlink" title="操作数据库"></a>操作数据库</h3><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="comment">-- bigdata2可能不存在,加 ifx exists 进行判断</span></span><br><span class="line"><span class="keyword">drop</span> database if <span class="keyword">exists</span> bigdata2;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 如果数据库不为空,可以使用 cascade 强制删除</span></span><br><span class="line"><span class="keyword">drop</span> database bigdata cascade;</span><br></pre></td></tr></table></figure><h2 id="表语句"><a href="#表语句" class="headerlink" title="表语句"></a>表语句</h2><h3 id="创建表"><a href="#创建表" class="headerlink" title="创建表"></a>创建表</h3><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> [<span class="keyword">EXTERNAL</span>] <span class="keyword">TABLE</span> [IF <span class="keyword">NOT</span> <span class="keyword">EXISTS</span>] table_name </span><br><span class="line">[(col_name data_type [COMMENT col_comment], ...)] </span><br><span class="line">[COMMENT table_comment] </span><br><span class="line">[PARTITIONED <span class="keyword">BY</span> (col_name data_type [COMMENT col_comment], ...)] </span><br><span class="line">[CLUSTERED <span class="keyword">BY</span> (col_name, col_name, ...) </span><br><span class="line">[SORTED <span class="keyword">BY</span> (col_name [<span class="keyword">ASC</span><span class="operator">|</span><span class="keyword">DESC</span>], ...)] <span class="keyword">INTO</span> num_buckets BUCKETS] </span><br><span class="line">[<span class="type">ROW</span> FORMAT row_format] </span><br><span class="line">[STORED <span class="keyword">AS</span> file_format] </span><br><span class="line">[LOCATION hdfs_path]</span><br><span class="line">[TBLPROPERTIES (property_name<span class="operator">=</span>property_value, ...)]</span><br><span class="line">[<span class="keyword">AS</span> select_statement]</span><br><span class="line">[LIKES existing_table_or_view_name]</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 修改表</span></span><br><span class="line"><span class="keyword">ALTER TABLE</span> table_name RENAME <span class="keyword">TO</span> new_table_name</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 更新列</span></span><br><span class="line"><span class="keyword">ALTER TABLE</span> table_name CHANGE [<span class="keyword">COLUMN</span>] col_old_name col_new_name column_type [COMMENT col_comment] [<span class="keyword">FIRST</span><span class="operator">|</span>AFTER column_name]</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 增删除列</span></span><br><span class="line"><span class="keyword">ALTER TABLE</span> table_name <span class="keyword">ADD</span><span class="operator">|</span>REPLACE COLUMNS (col_name data_type [COMMENT col_comment], ...)</span><br></pre></td></tr></table></figure><table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td>建表字段</td><td>属性意义</td></tr><tr><td>LOCATION</td><td>在建表的同时可以指定一个指向实际数据的路径</td></tr><tr><td>COMMENT</td><td>为表和列添加注释</td></tr><tr><td>PARTITIONED BY</td><td>创建分区表</td></tr><tr><td>CLUSTERED BY</td><td>创建分桶表</td></tr><tr><td>SORTED BY</td><td>对桶中的一个或多个列另外排序</td></tr><tr><td>STORE AS</td><td>指定存储文件类型</td></tr></tbody></table><h3 id="删除表"><a href="#删除表" class="headerlink" title="删除表"></a>删除表</h3><p>清除表中数据（Truncate） : Truncate只能删除管理表，不能删除外部表中数据</p><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">drop</span> <span class="keyword">table</span> test2;</span><br></pre></td></tr></table></figure><h2 id="DML语句"><a href="#DML语句" class="headerlink" title="DML语句"></a>DML语句</h2><h3 id="load语句"><a href="#load语句" class="headerlink" title="load语句"></a>load语句</h3><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line">load data [<span class="keyword">local</span>] inpath <span class="string">&#x27;数据的path&#x27;</span> [overwrite] <span class="keyword">into</span> <span class="keyword">table</span> table_name [<span class="keyword">partition</span> (partcol1<span class="operator">=</span>val1,…)];</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 创建表时,通过Location指定加载数据路径</span></span><br><span class="line"><span class="keyword">create</span> <span class="keyword">external</span> <span class="keyword">table</span> if <span class="keyword">not</span> <span class="keyword">exists</span> student5(</span><br><span class="line">              id <span class="type">int</span>,</span><br><span class="line">              name string</span><br><span class="line">)</span><br><span class="line"><span class="type">row</span> format delimited fields terminated <span class="keyword">by</span> <span class="string">&#x27;\t&#x27;</span></span><br><span class="line">location <span class="string">&#x27;/input/student&#x27;</span>;</span><br></pre></td></tr></table></figure><table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td>属性</td><td>属性含义</td></tr><tr><td>load data</td><td>加载数据</td></tr><tr><td>local</td><td>表示从本地加载数据到hive表，否则是从HDFS加载数据到Hive表</td></tr><tr><td>Inpath</td><td>加载数据的路径</td></tr><tr><td>Overwrite</td><td>表示覆盖表中已有数据,否则表示追加</td></tr><tr><td>Into table</td><td>加载数据到那张表中</td></tr><tr><td>Partition</td><td>加载数据到指定的分区</td></tr></tbody></table><h2 id="分区表"><a href="#分区表" class="headerlink" title="分区表"></a>分区表</h2><h3 id="分区表-1"><a href="#分区表-1" class="headerlink" title="分区表"></a>分区表</h3><ul><li>分区表实际上就是对应一个HDFS文件系统上的独立的文件夹</li><li>该文件夹下是该分区所有的数据文件</li><li>Hive中的分区就是分目录，把一个大的数据集根据业务需要分割成小的数据集</li><li>在查询时通过WHERE子句中的表达式选择查询所需要的指定的分区，这样的查询效率会提高很多</li></ul><h3 id="分区操作"><a href="#分区操作" class="headerlink" title="分区操作"></a>分区操作</h3><p>创建表指定分区字段 : 在查询时通过WHERE子句中的表达式选择查询所需要的指定的分区，这样的查询效率会提高很多</p><p>增加分区 : alter table dept_partition add partition(day&#x3D;’20200404’) </p><p>删除分区 : alter table dept_partition drop partition (day&#x3D;’20200406’)</p><h3 id="二级分区"><a href="#二级分区" class="headerlink" title="二级分区"></a>二级分区</h3><p>创建表指定分区字段 : partitioned by (day string, hour string)</p><p>hdfs存储的路径 : &#x2F;user&#x2F;hive&#x2F;warehouse&#x2F;mydb.db&#x2F;dept_partition2&#x2F;day&#x3D;20200401&#x2F;hour&#x3D;14;</p><h3 id="动态分区"><a href="#动态分区" class="headerlink" title="动态分区"></a>动态分区</h3><p>对分区表insert数据时候,数据库自动会根据分区字段的值,将数据插入到相应的分区中</p><p>设置开启动态分区 : set hive.exec.dynamic.partition&#x3D;true;</p><p>设置为非严格模式 : set hive.exec.dynamic.partition.mode&#x3D;nonstrict</p><p>整个MR Job中,最大可以创建多少个HDFS文件,默认是100000; set hive.exec.max.created.files&#x3D;100000;</p><p>在每个执行MR的节点上,最大可以创建多少个动态分区: set hive.exec.max.dynamic.partitions.pernode&#x3D;100;</p><p>在所有执行MR的节点上,最大一共可以创建多少个动态分区,默认是1000; set hive.exec.max.dynamic.partitions&#x3D;1000;</p><p>动态分区的模式,默认strict,表示必须指定至少一个分区为静态分区,nonstrict模式表示许所有的分区字段都可以使用动态分区</p><p>当有空分区生成时,是否抛出异常;一般不需要设置,默认false; set hive.error.on.empty.partition&#x3D;false;</p><h2 id="分桶表"><a href="#分桶表" class="headerlink" title="分桶表"></a>分桶表</h2><p>分桶是将数据集分解成更容易管理的若干部分的另一种技术</p><p>分区针对的是数据存储路径(细分文件夹);分桶针对的是数据文件(按规则多文件放一起)</p><p>创建分桶表</p><p>要想将表创建为4个桶,需要将hive中mapreduce.job.reduces参数设置为 ≥ 4或设置为 -1</p><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">create table</span> stu_bucket(id <span class="type">int</span>, name string)</span><br><span class="line">clustered <span class="keyword">by</span>(id) </span><br><span class="line"><span class="keyword">into</span> <span class="number">4</span> buckets</span><br><span class="line"><span class="type">row</span> format delimited fields terminated <span class="keyword">by</span> <span class="string">&#x27;\t&#x27;</span>;</span><br></pre></td></tr></table></figure><p>分桶规则: Hive的分桶采用对分桶字段的值进行哈希(hash),然后除以桶的个数求余的方 式决定该条记录存放在哪个桶当中</p><p>mapreduce.job.reduces&#x3D;-1,让Job自行决定需要用多少个reduce或者将reduce的个数设置为大于等于分桶表的桶数</p><h1 id="📝-Hive函数"><a href="#📝-Hive函数" class="headerlink" title="📝 Hive函数"></a>📝 Hive函数</h1><h2 id="内置函数"><a href="#内置函数" class="headerlink" title="内置函数"></a>内置函数</h2><p>show function: 查询系统自带的函数</p><p>desc function 函数名: 显示自带的函数的用法</p><p>desc function extended 函数名: 展开详细说明</p><table><thead><tr><th>作用</th><th>函数</th><th>功能</th></tr></thead><tbody><tr><td>空字段赋值-NVL（防止空字段参与计算）</td><td>NVL(value,default_value)</td><td>如果value为NULL，则NVL函数返回default_value的值，否则返回value的值<br>如果两个参数都为NULL ，则返回NULL</td></tr><tr><td>CASE WHEN THEN ELSE END</td><td>CASE a WHEN b THEN c [WHEN d THEN e]* [ELSE f] END</td><td>根据不同的数据,返回不同的值<br>当a&#x3D;b时，返回c；当a&#x3D;d时，返回d；当a&#x3D;e时，放回e；其他情况返回f</td></tr><tr><td>行转列（组函数）</td><td>CONCAT(string A&#x2F;col, string B&#x2F;col…)</td><td>返回输入字符串连接后的结果，支持任意个输入字符串</td></tr><tr><td>行转列（组函数）</td><td>CONCAT_WS(separator, str1, str2,…)</td><td>指定字符separator，连接str<br>sparator: 分割符<br>分割符将被加到被连接的字符串之间</td></tr><tr><td>行转列（组函数）</td><td>COLLECT_SET(col)</td><td>函数只接受基本数据类型;<br>它的主要作用是将某字段的值进行去重汇总,产生array类型字段</td></tr><tr><td>行转列（组函数）</td><td>COLLECT_LIST(col)</td><td>函数值接受基本数据类型;<br>它的主要作用是将某字段的值进行不去重汇总,产生array类型字段</td></tr><tr><td>列转行</td><td>EXPLODE(col)</td><td>将hive表的一列中复杂的array或者map结构拆分成多行;<br>会出现改行与其它字段行数不匹配报错<br>因此<br>必须和lateral view连用</td></tr><tr><td>列转行</td><td>SPLIT(string str, string regex)</td><td>按照regex字符串分割str，会返回分割后的字符串数组</td></tr><tr><td>列转行</td><td>LATERAL VIEW</td><td>用于和split,explode等UDF一起使用,它能够将一列数据拆成多行数据,在次基础上可以对拆分后的数据进行聚合<br>lateral view首先为原始表的每行调用UDTF,UDTF会报一行拆分成一行或者多行,lateral view再把结果组合,产生一个支持别名表的虚拟表</td></tr></tbody></table><h2 id="窗口函数（开窗函数）"><a href="#窗口函数（开窗函数）" class="headerlink" title="窗口函数（开窗函数）"></a>窗口函数（开窗函数）</h2><p>为每行数据进行一次计算,返回一个值</p><p>灵活运用窗口函数可以解决很多复杂的问题,如去重,排名,同步以及环比,连续登录等</p><details><summary>语法</summary><p>Function（arg1 ……） over（[partition by arg1 ……] [order by arg1 ……] [<window_expression>]）</p></details><details><summary>聚合函数</summary><p>sum()、max()、min()、avg()</p></details><details><summary>排序函数</summary><ul><li>rank() 排序相同时会重复,总数不变</li><li>row_number() 排序相同时会重复,总数会减少</li><li>dens_rank() 会根据顺序计算,不重复不减少</li><li>ntile() Ntile函数,为已排序的行,均分为指定数量的组,组号按顺序排列,返回组号;不支持rows between;ntile(5) over(order by orderdate) sorted from business</li></ul></details><details><summary>统计比较函数</summary><p>lead()</p><p>lag(): LAG (scalar_expression [,offset] [,default]) OVER ([query_partition_clause] order_by_clause); lag函数用于统计窗口内往上的第n行值;参数scalar_pexpression为列名,参数offset为往上几行,参数default是设置的默认值(当往上第n行为null时,取默认值,否则就是null)</p><p>first_value(): 返回partition by的第一个分区</p></details><h2 id="自定义函数"><a href="#自定义函数" class="headerlink" title="自定义函数"></a>自定义函数</h2><ul><li>UDF(User-Defined-Function) : 一进一出</li><li>UDAF(User-Defined-Aggregation Function): 聚合函数,多进一出,类似:max,count,min</li><li>UDTF(User-Defined Table-Generating Functions): 炸裂函数,一进多出</li></ul><p>引入过程:</p><ol><li>添加jar: add jar linux_jar_path</li><li>创建function: function [dbname.]function_name AS class_name;</li><li>在hive的命令行窗口删除函数: drop [temporary] function [if exists] [dbname.]function_name;</li></ol><h2 id="创建临时函数"><a href="#创建临时函数" class="headerlink" title="创建临时函数"></a>创建临时函数</h2><p>create temporary function my_len as “com.atguigu.hive. MyStringLength”;</p><p>临时函数只和会话有关系,跟库没有关系,只要创建临时函数的会话不断,再当前会话下,任意一个库都可以使用,其它会话全部不能使用</p><h1 id="📝-Hive优化"><a href="#📝-Hive优化" class="headerlink" title="📝 Hive优化"></a>📝 Hive优化</h1><h2 id="执行计划"><a href="#执行计划" class="headerlink" title="执行计划"></a>执行计划</h2><p>Explain : EXPLAIN [EXTENDED | DEPENDENCY | AUTHORIZATION] query</p><p>查看执行语句运行时的信息&#x2F;详细信息</p><h2 id="Hive建表优化"><a href="#Hive建表优化" class="headerlink" title="Hive建表优化"></a>Hive建表优化</h2><p>分区表: 先用where过滤缩小查询范围,减小数据量</p><p>分桶表: 提供一个隔离数据和优化查询的便利方式</p><p>合格的文件格式: 采用压缩: 输入端&#x2F;map输出端&#x2F;reduce输出端</p><h2 id="HQl语法优化"><a href="#HQl语法优化" class="headerlink" title="HQl语法优化"></a>HQl语法优化</h2><details><summary>列裁剪和分区裁剪</summary><p>只读取查询中所需要的列,忽视其它的列,这样可以做可以节省读取开销</p><ol><li>列裁剪: 查询时只读取需要的列</li><li>分区裁剪: 查询时只读取需要的分区</li></ol></details><details><summary>group by</summary><p>map端聚合参数设置: set hive.map.aggr &#x3D; true</p><p>map端进行聚合操作的条目数目: set hive.groupby.mapaggr.checkinterval &#x3D; 100000</p><p>有数据倾斜的时候进行负载均衡（默认是false）: set hive.groupby.skewindata &#x3D; true</p><p>当开启数据负载的时候,会生成两个查询计划会有两个MRJob:</p><ol><li>第一个MRJob中，Map的输出结果会随机分布到Reduce中，每个Reduce做部分聚合操作，并输出结果，这样处理的结果是相同的Group By Key有可能被分发到不同的Reduce中，从而达到负载均衡的目的</li><li>第二个MRJob再根据预处理的数据结果按照Group By Key分布到Reduce中（这个过程可以保证相同的Group By Key被分布到同一个Reduce中），最后完成最终的聚合操作</li></ol></details><details><summary>CBO优化</summary><p>CBO成本优化器,代价最小的执行计划就是最好的执行计划;Hive在提供最终执行前,优化每个查询的执行逻辑和物理逻辑执行计划</p><p>join的时候表的顺序关系: 前面的表都会被加载到内存中,后面的表进行磁盘顺序扫描</p><p>通过 “hive.cbo.enable” 来开启。在 Hive 1.1.0 之后，这个属性是默认开启的，它可以自动优化HQL中多个Join的顺序，并选择合适的Join算法</p></details><details><summary>谓词下推</summary><p>保证结果正确的前提下,将SQL语句中的where谓词逻辑都尽可能提前执行,减少下游处理的数据量</p><p>通过谓词下推,过滤条件将在map端提前执行,减少map端的输出,降低了数据IO,节约资源,提升性能</p><p>set hive.optimize.ppd &#x3D; true; #谓词下推，默认是true</p></details><details><summary>MapJoin</summary><p>MapJoin 是将 Join 双方比较小的表直接分发到各个 Map 进程的内存中，在 Map 进程中进行 Join 操 作，这样就不用进行 Reduce 步骤，从而提高了速度</p><p>用MapJoin把小表全部加载到内存在Map端进行Join，避免Reducer处理</p><p>设置自动选择MapJoin,set hive.auto.convert.join&#x3D;true; #默认为true</p><p>大表小表阈值设置(默认25M以下认为是小表): set hive.mapjoin.smalltable.filesize&#x3D;25000000;</p></details><details><summary>大表,小表SMB JOIN</summary><p>sort merge bucket join</p></details><details><summary>数据倾斜</summary><p>数据倾斜现象: 一个或者少数几个任务执行的很慢甚至最终执行失败</p><p>数据过量现象: 所有任务都执行的很慢</p><p>按照key分组后,少量的任务负载着绝大部分数据的计算,也就是说,产生数据倾斜的HQL中一定存在着分组的操作;单表携带了Group by字段的查询和两表(多表)join的查询</p></details><details><summary>表单数据倾斜</summary><p>参数优化：</p><ul><li>在Map端进行聚合，默认为True: set hive.map.aggr &#x3D; true</li><li>Map端进行聚合操作的条目数目: set hive.groupby.mapaggr.checkinterval &#x3D; 100000</li><li>数据倾斜的时候进行负载均衡（默认是false）</li><li>set hive.groupby.skewindata &#x3D; true</li></ul><p>增加reduce数量:</p><ul><li>每个reduce处理的数据量默认是256MB: set hive.exec.reducers.bytes.per.reducer&#x3D;256000000; 每个任务最大的reduce数,默认是1009,set hive.exec.reducers.max&#x3D;1009;N&#x3D;min(参数2,总输入数据量&#x2F;参数1)</li><li>调整reduce个数方法,set mapreduce.job.reduces &#x3D; 15;</li></ul></details><details><summary>join数据倾斜优化</summary><ul><li>join的键对应的记录条数超过这个值则会进行分拆,值根据具体数据量设置;set hive.skewjoin.key&#x3D;100000;</li><li>join过程中出现倾斜应该设置为true;set hive.optimize.skewjoin&#x3D;false;</li></ul></details><h1 id="🤗-总结归纳"><a href="#🤗-总结归纳" class="headerlink" title="🤗 总结归纳"></a>🤗 总结归纳</h1><h1 id="📎-参考文章"><a href="#📎-参考文章" class="headerlink" title="📎 参考文章"></a>📎 参考文章</h1><blockquote><p>💡 有关文章的问题，欢迎您在底部评论区留言，一起交流~</p></blockquote>]]></content>
    
    
    <summary type="html">Hive基础知识</summary>
    
    
    
    <category term="大数据" scheme="https://ruy9527.github.io/categories/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
    
    <category term="大数据" scheme="https://ruy9527.github.io/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
    <category term="hive" scheme="https://ruy9527.github.io/tags/hive/"/>
    
  </entry>
  
  <entry>
    <title>MySql执行语句过程</title>
    <link href="https://ruy9527.github.io/article/mysql_process/"/>
    <id>https://ruy9527.github.io/article/mysql_process/</id>
    <published>2022-03-02T04:00:00.000Z</published>
    <updated>2025-12-31T04:54:05.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>😀 这里写文章的前言：<br>MySql 执行一条 upate,select等语句期间发生了什么</p></blockquote><h1 id="📝-执行一条Select语句"><a href="#📝-执行一条Select语句" class="headerlink" title="📝 执行一条Select语句"></a>📝 执行一条Select语句</h1><p>可以看到， MySQL 的架构共分为两层：Server 层和存储引擎层，</p><ul><li>Server 层负责建立连接、分析和执行 SQL。MySQL 大多数的核心功能模块都在这实现，主要包括连接器，查询缓存、解析器、预处理器、优化器、执行器等。另外，所有的内置函数（如日期、时间、数学和加密函数等）和所有跨存储引擎的功能（如存储过程、触发器、视图等。）都在 Server 层实现。</li><li>存储引擎层负责数据的存储和提取。支持 InnoDB、MyISAM、Memory 等多个存储引擎，不同的存储引擎共用一个 Server 层。现在最常用的存储引擎是 InnoDB，从 MySQL 5.5 版本开始， InnoDB 成为了 MySQL 的默认存储引擎。我们常说的索引数据结构，就是由存储引擎层实现的，不同的存储引擎支持的索引类型也不相同，比如 InnoDB 支持索引类型是 B+树 ，且是默认使用，也就是说在数据表中创建的主键索引和二级索引默认使用的是 B+ 树索引</li></ul><p><img src="/images/posts/mysql_process/img-1.png" alt="MySql执行语句过程"></p><h2 id="连接器"><a href="#连接器" class="headerlink" title="连接器"></a>连接器</h2><p>连接的过程需要先经过 TCP 三次握手，因为 MySQL 是基于 TCP 协议进行传输的，如果 MySQL 服务并没有启动，则会收到如下的报错：</p><p><img src="/images/posts/mysql_process/img-2.png" alt="MySql执行语句过程"></p><p>如果 MySQL 服务正常运行，完成 TCP 连接的建立后，连接器就要开始验证你的用户名和密码，如果用户名或密码不对，就收到一个”Access denied for user”的错误，然后客户端程序结束执行:</p><p><img src="/images/posts/mysql_process/img-3.png" alt="MySql执行语句过程"></p><h2 id="查询缓存"><a href="#查询缓存" class="headerlink" title="查询缓存"></a>查询缓存</h2><p>连接器得工作完成后，客户端就可以向 MySQL 服务发送 SQL 语句了，MySQL 服务收到 SQL 语句后，就会解析出 SQL 语句的第一个字段，看看是什么类型的语句。<br>如果 SQL 是查询语句（select 语句），MySQL 就会先去查询缓存（ Query Cache ）里查找缓存数据，看看之前有没有执行过这一条命令，这个查询缓存是以 key-value 形式保存在内存中的，key 为 SQL 查询语句，value 为 SQL 语句查询的结果。<br>如果查询的语句命中查询缓存，那么就会直接返回 value 给客户端。如果查询的语句没有命中查询缓存中，那么就要往下继续执行，等执行完后，查询的结果就会被存入查询缓存中。<br>这么看，查询缓存还挺有用，但是其实查询缓存挺鸡肋的。<br>对于更新比较频繁的表，查询缓存的命中率很低的，因为只要一个表有更新操作，那么这个表的查询缓存就会被清空。如果刚缓存了一个查询结果很大的数据，还没被使用的时候，刚好这个表有更新操作，查询缓冲就被清空了，相当于缓存了个寂寞。<br>所以，MySQL 8.0 版本直接将查询缓存删掉了，也就是说 MySQL 8.0 开始，执行一条 SQL 查询语句，不会再走到查询缓存这个阶段了。<br>对于 MySQL 8.0 之前的版本，如果想关闭查询缓存，我们可以通过将参数 query_cache_type 设置成 DEMAND。<br>TIP<br>这里说的查询缓存是 server 层的，也就是 MySQL 8.0 版本移除的是 server 层的查询缓存，并不是 Innodb 存储引擎中的 buffer poo</p><h2 id="解析SQL"><a href="#解析SQL" class="headerlink" title="解析SQL"></a>解析SQL</h2><p>在正式执行 SQL 查询语句之前， MySQL 会先对 SQL 语句做解析，这个工作交由「解析器」来完成</p><h3 id="解析器"><a href="#解析器" class="headerlink" title="解析器"></a>解析器</h3><h1 id="执行SQL"><a href="#执行SQL" class="headerlink" title="执行SQL"></a>执行SQL</h1><p>经过解析器后，接着就要进入执行 SQL 查询语句的流程了，每条<code>SELECT</code> 查询语句流程主要可以分为下面这三个阶段：</p><ul><li>prepare 阶段，也就是预处理阶段；</li><li>optimize 阶段，也就是优化阶段；</li><li>execute 阶段，也就是执行阶段</li></ul><h3 id="预处理器"><a href="#预处理器" class="headerlink" title="预处理器"></a>预处理器</h3><h3 id="优化器"><a href="#优化器" class="headerlink" title="优化器"></a>优化器</h3><p>经过预处理阶段后，还需要为 SQL 查询语句先制定一个执行计划，这个工作交由「优化器」来完成的。</p><p>优化器主要负责将 SQL 查询语句的执行方案确定下来，比如在表里面有多个索引的时候，优化器会基于查询成本的考虑，来决定选择使用哪个索引。</p><p>当然，我们本次的查询语句（select * from product where id &#x3D; 1）很简单，就是选择使用主键索引。</p><p>要想知道优化器选择了哪个索引，我们可以在查询语句最前面加个 <code>explain</code> 命令，这样就会输出这条 SQL 语句的执行计划，然后执行计划中的 key 就表示执行过程中使用了哪个索引，比如下图的 key 为 <code>PRIMARY</code> 就是使用了主键索引。</p><p><img src="/images/posts/mysql_process/img-4.png" alt="MySql执行语句过程"></p><p>如果查询语句的执行计划里的 key 为 null 说明没有使用索引，那就会全表扫描（type &#x3D; ALL），这种查询扫描的方式是效率最低档次的，如下图：</p><p><img src="/images/posts/mysql_process/img-5.png" alt="MySql执行语句过程"></p><p>这张 product 表只有一个索引就是主键，现在我在表中将 name 设置为普通索引（二级索引）。</p><p><img src="/images/posts/mysql_process/img-6.png" alt="MySql执行语句过程"></p><p>这时 product 表就有主键索引（id）和普通索引（name）。假设执行了这条查询语句：</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="keyword">select</span> <span class="built_in">id</span> from product <span class="built_in">where</span> <span class="built_in">id</span> &gt; 1  and name like <span class="string">&#x27;i%&#x27;</span>;</span><br></pre></td></tr></table></figure><p>这条查询语句的结果既可以使用主键索引，也可以使用普通索引，但是执行的效率会不同。这时，就需要优化器来决定使用哪个索引了。<br>很显然这条查询语句是覆盖索引，直接在二级索引就能查找到结果（因为二级索引的 B+ 树的叶子节点的数据存储的是主键值），就没必要在主键索引查找了，因为查询主键索引的 B+ 树的成本会比查询二级索引的 B+ 的成本大，优化器基于查询成本的考虑，会选择查询代价小的普通索引。<br>在下图中执行计划，我们可以看到，执行过程中使用了普通索引（name），Exta 为 Using index，这就是表明使用了覆盖索引优化。</p><p><img src="/images/posts/mysql_process/img-7.png" alt="MySql执行语句过程"></p><h1 id="一条Update语句"><a href="#一条Update语句" class="headerlink" title="一条Update语句"></a>一条Update语句</h1><p><img src="/images/posts/mysql_process/img-8.png" alt="MySql执行语句过程"></p><h1 id="🤗-总结归纳"><a href="#🤗-总结归纳" class="headerlink" title="🤗 总结归纳"></a>🤗 总结归纳</h1><p>总结文章的内容</p><h1 id="📎-参考文章"><a href="#📎-参考文章" class="headerlink" title="📎 参考文章"></a>📎 参考文章</h1><ul><li><a href="https://xiaolincoding.com/mysql/">https://xiaolincoding.com/mysql/</a></li></ul><blockquote><p>💡 有关文章的问题，欢迎您在底部评论区留言，一起交流~</p></blockquote>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;😀 这里写文章的前言：&lt;br&gt;MySql 执行一条 upate,select等语句期间发生了什么&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;📝-执行一条Select语句&quot;&gt;&lt;a href=&quot;#📝-执行一条Select语句&quot;</summary>
        
      
    
    
    
    
  </entry>
  
  <entry>
    <title>Hadoop_HA机制</title>
    <link href="https://ruy9527.github.io/article/hadoop_ha1/"/>
    <id>https://ruy9527.github.io/article/hadoop_ha1/</id>
    <published>2022-03-01T04:00:00.000Z</published>
    <updated>2025-12-31T04:52:25.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>😀 这里写文章的前言：<br>HA : High Availablity,高可用<br>高可用最关键的策略是 消除单点故障</p></blockquote><h1 id="📝-HA"><a href="#📝-HA" class="headerlink" title="📝 HA"></a>📝 HA</h1><p>同时出现两个 active 状态,namenode的术语叫脑裂</p><details><summary>防止脑裂</summary><ol><li>ssh发送kill指令</li><li>调用用户自定义脚步程序</li></ol></details><p><img src="/images/posts/hadoop_ha1/img-1.png" alt="Hadoop_HA机制"></p><h2 id="问题记录"><a href="#问题记录" class="headerlink" title="问题记录"></a>问题记录</h2><p>怎么保证三台namenode的数据一致:</p><ol><li>Fsimages : 让一台 nn 生成数据，让其它机器nn同步</li><li>Edits:     需要引进新的模块JournalNode来保证edits的文件和数据一致性</li></ol><p>怎么让同时只有一台nn是active,其它的都是standby</p><ul><li>手动分配</li><li>自动分配</li></ul><p>2nn在ha架构中并不存在，定期合并fsimage和edtis的活谁来干</p><p>由standby的nn来干</p><p>如果nn真的发生了问题，怎么让其他的nn上位干活</p><ul><li>手动故障转移</li><li>自动故障转移</li></ul><h1 id="YARN-HA"><a href="#YARN-HA" class="headerlink" title="YARN HA"></a>YARN HA</h1><p><img src="/images/posts/hadoop_ha1/img-2.png" alt="Hadoop_HA机制"></p><h1 id="🤗-总结归纳"><a href="#🤗-总结归纳" class="headerlink" title="🤗 总结归纳"></a>🤗 总结归纳</h1><h1 id="📎-参考文章"><a href="#📎-参考文章" class="headerlink" title="📎 参考文章"></a>📎 参考文章</h1><blockquote><p>💡 有关文章的问题，欢迎您在底部评论区留言，一起交流~</p></blockquote>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;😀 这里写文章的前言：&lt;br&gt;HA : High Availablity,高可用&lt;br&gt;高可用最关键的策略是 消除单点故障&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;📝-HA&quot;&gt;&lt;a href=&quot;#📝-HA&quot;</summary>
        
      
    
    
    
    
  </entry>
  
  <entry>
    <title>hadoop基础知识</title>
    <link href="https://ruy9527.github.io/article/hadoop_base/"/>
    <id>https://ruy9527.github.io/article/hadoop_base/</id>
    <published>2022-02-04T04:00:00.000Z</published>
    <updated>2023-07-23T05:14:51.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>😀 这里写文章的前言：<br>hadoop基础知识学习概念<br>大数据的特点: volume大量;velcity高速;variety多样;value低价值密度</p></blockquote><h1 id="📝-hadoop框架组件"><a href="#📝-hadoop框架组件" class="headerlink" title="📝 hadoop框架组件"></a>📝 hadoop框架组件</h1><h2 id="Hdfs"><a href="#Hdfs" class="headerlink" title="Hdfs"></a>Hdfs</h2><p>Hadoop Distributed File System:分布式文件系统</p><ul><li>NameNode: 存储文件的元数据</li><li>DataNode: 存储文件的块数据,以及校验和</li><li>Secondary NameNode: 每隔一段时间备份元数据</li></ul><h2 id="Yarn"><a href="#Yarn" class="headerlink" title="Yarn"></a>Yarn</h2><p>Yet Another Resource Negotiator: 另一种资源协调者;hadoop的资源管理器</p><ul><li>Resource: 管理整个集群资源</li><li>DataManager: 单个节点资源管理器的老大</li><li>Application Master: 单个运行任务的老大</li><li>Container: 容器,封装了一个任务运行所需的资源</li></ul><h2 id="MapReduce"><a href="#MapReduce" class="headerlink" title="MapReduce"></a>MapReduce</h2><p>Map阶段并行处理输入数据</p><p>Reduce阶段对Map结果进行汇总</p><h1 id="📝-HDFS"><a href="#📝-HDFS" class="headerlink" title="📝 HDFS"></a>📝 HDFS</h1><p>一种系统来管理多台机器上的文件,分布式文件管理;适合一次写入,多次读出的场景</p><details><summary>优点</summary><ul><li>高容错性: 数据保存为多个副本分布于多个datanode;如果误删,是可以协助其它nodedata帮忙协助的</li><li>处理大量数据: 数据规模大,pb级别;数据文件量多,百万规模以上</li><li>在廉价机器服务器上,通过副本机制,提高可用性</li></ul></details><details><summary>缺点</summary><ul><li>不适合低延迟的数据</li><li>无法高效对大量小文件进行存储;默认分块是按文件切分的,文件太大也会占用一块</li><li>不支持并发写入和随机修改: 一个文件只许有一个写,不支持多线程同时写;仅仅支持append追加,不支持随机修改</li></ul></details><h2 id="NameNode"><a href="#NameNode" class="headerlink" title="NameNode"></a>NameNode</h2><p>管理hdfs的名称空间,配置副本策略,管理数据块映射信息,处理客户端的读写请求</p><h2 id="DataNode"><a href="#DataNode" class="headerlink" title="DataNode"></a>DataNode</h2><p>存储实际块;NameNode发出命令,DataNode执行客户端的读写指令</p><p>一个数据块在DataNode上以文件形式存储在磁盘上,包括两个文件,一个是数据本身,一个是元数据包括数据块的长度,块数据的检验以及时间戳</p><p>DataNode启动后向NameNode注册,通过后,周期性(6小时)向NameNode上报所有的快信息;心跳是每3秒一次,心跳返回结果带有NameNode给该DataNode的命令如复制块数据到另一台机器,或删除某个数据块;如果超过10分钟+30s没有收到某个DataNode的心跳,则认为该节点不可用</p><details><summary>数据完整性</summary><ul><li>当DataNode读取Block的时候,它会计算checkSum</li><li>如果计算后checkSum,与Block创建的值不一样,说明Block已经损坏</li><li>Client读取其它的DataNode上的Block</li><li>DataNode在其文件创建后周期验证checkSum</li></ul></details><details><summary>**块大小或者块太多问题**</summary><ul><li>块大小设置的大小,块的数量相对会更多,而块存储起始位置需要寻址时间,块多寻址时间就更长</li><li>快太大,磁盘的传输时间明显大于寻址的时间,因为处理块数据时,会需要很多时间,比较慢</li></ul></details><p>块大小的设置,最好根据磁盘的传输速度来决定</p><h2 id="Yarn-1"><a href="#Yarn-1" class="headerlink" title="Yarn"></a>Yarn</h2><p>Yarn是一个资源调度平台,负责为程序提供服务器运行资源;相当于一个分布式操作系统平台;而MapReduce等运行程序则相当于运行于操作系统之上的应用程序</p><h3 id="resourceManager"><a href="#resourceManager" class="headerlink" title="resourceManager"></a>resourceManager</h3><ul><li>集群资源的分配和调度</li><li>管理nodeManager</li><li>管理&#x2F;启动AppMaster</li><li>处理客户端请求</li></ul><h3 id="nodeManager"><a href="#nodeManager" class="headerlink" title="nodeManager"></a>nodeManager</h3><ul><li>处理resoureManager命令</li><li>处理AppMaster命令</li><li>管理单个节点上的资源调配</li></ul><h3 id="AppMaster"><a href="#AppMaster" class="headerlink" title="AppMaster"></a>AppMaster</h3><ul><li>管理单个任务的资源申请和分配</li><li>对任务的监控和容错</li></ul><h3 id="Container"><a href="#Container" class="headerlink" title="Container"></a>Container</h3><ul><li>封装某个节点上的资源,cpu,ram,rom等</li></ul><h3 id="调度器和调度算法"><a href="#调度器和调度算法" class="headerlink" title="调度器和调度算法"></a>调度器和调度算法</h3><ul><li>先进先出调度器(FIFO): 单队列,根据作业提交的顺序,先来先处理;不支持多队列,紧急的job无法插队</li><li>容量调度器(Capacity Scheduler): 多队列,每个队列采用fifo策略,分配一定的资源;容量可进行设置;多租户,支持多用户共享集群,多任务并行防止同一用户作业占据所有资源</li><li>公平调度器(Fair Scheduler): 优先资源缺额比较大的;fifo,fair,drf(同时考虑不同资源将不同资源按比例分配)</li></ul><h3 id="一个作业流程"><a href="#一个作业流程" class="headerlink" title="一个作业流程"></a>一个作业流程</h3><details><summary>作业提交</summary><ol><li>Client端调用job.waitForCompletion方法,向整个集群提交MapReduce作业</li><li>Client向RM申请一个作业id</li><li>RM给client返回改job资源的提交路径和作业id</li><li>Client提交jar包,切片信息和配置文件到资源的资源提交路径</li><li>Client提交完资源后,向RM申请运行MrAppMaster</li></ol></details><details><summary>作业初始化</summary><ol><li>当RM收到Client请求后,将该job添加到容量调度器中</li><li>某一个空闲的NN领取到该Job</li><li>该NN创建Container并产生MRAppMaster</li><li>下载Client提交的资源到本地</li></ol></details><details><summary>任务分配</summary><ol><li>MrAppMaster向MR申请运行多个MapTask任务资源</li><li>RM将运行MapTask任务分配给另外两个NodeManager,另外两个NodeManager分别领取任务并创建任务</li></ol></details><details><summary>任务运行</summary><ol><li>MR向两个接受到任务的NodeManager发送程序启动脚本,这两个NodeManager分别启动MapTask,MapTask对数据分区排序</li><li>MrAppMaster等待所有MapTask运行完毕后,向RM申请容器,运行ReduceTask</li><li>ReduceTask向MapTask获取相应分区的数据</li><li>程序运行完毕后,MR会向RM申请注销自己</li></ol></details><details><summary>进度和状态更新</summary><p>Yarn中的任务将其进度和状态(包括counter)返回给应用管理器,客户端每秒(通过mapreduce.client.progressmonitor.pollinterval设置)向应用管理器请求进度更新,展示给用户</p></details><details><summary>作业完成</summary><p>除了向应用管理器请求作业进度外,客户端每5秒都会通过调用waitForCompletion()来检查作业是否完成;时间间隔可以通过mapredue.client.completion.pollinterval来设置,作业完成之后,应用管理器和Container会清理工作状态</p><p>作业的信息会被作业历史服务器存储以备之后用户检查</p></details><h3 id="yarn工作机制"><a href="#yarn工作机制" class="headerlink" title="yarn工作机制"></a>yarn工作机制</h3><ol><li>MR程序提交到客户端所在节点</li><li>YarnRunner向ResourceManager申请一个Application</li><li>RM将该应用程序的资源路径返回YarnRunner</li><li>该程序运行所需要资源提交到HDFS上</li><li>程序资源提交完毕后,申请运行mrAppMaster</li><li>RM将用户的请求初始化成一个Task</li><li>其中一个NodeManager领取到Task任务</li><li>该NodeManager创建容器Container,并产生MRAppmaster</li><li>Container从HDFS上拷贝资源到本地</li><li>MRAppMaster向RM申请运行MapTask资源</li><li>RM将运行MapTask任务分配给另外两个NodeManager,另两个NodeManager分别领取任务并创建容器</li><li>MR向两个接受到任务的NodeManager发送程序启动脚本,这两个NodeManager分别启动MapTask,MapTask对数据分区排序</li><li>MrAppMaster等待所有MapTask运行完毕后,向RM申请容器,运行ReduceTask</li><li>ReduceTask向MapTask获取相应分区的数据</li><li>程序运行完毕后,MR会向RM申请注销自己</li></ol><h2 id="Secondary-NameNode"><a href="#Secondary-NameNode" class="headerlink" title="Secondary NameNode"></a>Secondary NameNode</h2><p>辅助NameNode,定期合并fsimages和edits,并推送给nn;紧急情况辅助恢复NameNode</p><h2 id="HDFS写流程"><a href="#HDFS写流程" class="headerlink" title="HDFS写流程"></a>HDFS写流程</h2><p><img src="/images/posts/hadoop_base/img-1.png" alt="hadoop基础知识"></p><h1 id="📝-HA模式"><a href="#📝-HA模式" class="headerlink" title="📝 HA模式"></a>📝 HA模式</h1><p><img src="/images/posts/hadoop_base/img-2.png" alt="hadoop基础知识"></p><p>HA集群中包含的进程职业各不同,为了使主节点和备用节点的状态一致,采用了Quorum Journal Manager(QJM)方案解决了主备节点共享存储问题</p><ul><li>Active NameNode: 负责执行整个文件系统中命名空间的所有操作;维护着数据的元数据,包括文件名,副本数,文件的blockId以及Block块所对应的节点信息;另外还接受Client端读写请求和DataNode汇报Block信息</li><li>Standby NameNode: 它是Active NameNode的备用节点,一旦主节点宕机,备节点会切换成主节点对外提供服务.它主要是监听JournalNode Cluster上editlog变化,以保证当前命名空间尽可能的与主节点同步。</li><li>JouranlNode Cluster: 用于主备节点共享editlog日志文件的共享存储系统;它负责存储editlog日志文件,当Active Node执行了修改命名空间操作时,它会定期将执行的操作记录在editlog中,并写入journalNode Cluster中;Standby NameNode会一直监听JournalNode Cluster上editlog的变化;如果发现editlog有改动,备用主节点会读取journalNode上的editlog并于自己当前的命名空间合并,从而实现了主备节点的数据一致</li><li>ZKFailoverController: ZKFC以独立进程进行,每个ZKFC都监控自己负责的NameNode,它可以实现NameNode自动故障切换:即当前主节点的ZKFC则会断开与Zookeeper的连接,释放分布式锁;监控备用节点的ZKFC进程会去释放,同步把备用NameNode切换成主NameNode</li><li>Zookeeper: 为ZKFC进行实现自动故障转移提供统一协调服务;通过Zookeeper中Watcher监听机制,通知ZKFC异常NameNode下线;保证同一时刻只有一个主节点</li></ul><h2 id="Fsimage"><a href="#Fsimage" class="headerlink" title="Fsimage"></a>Fsimage</h2><p>让一个NameNode生成数据,让其它机器NameNode同步</p><h2 id="Edits"><a href="#Edits" class="headerlink" title="Edits"></a>Edits</h2><p>需要引进新的模块JournalNode来保证edits的文件数据一致性</p><h1 id="🤗-总结归纳"><a href="#🤗-总结归纳" class="headerlink" title="🤗 总结归纳"></a>🤗 总结归纳</h1><h1 id="📎-参考文章"><a href="#📎-参考文章" class="headerlink" title="📎 参考文章"></a>📎 参考文章</h1><blockquote><p>💡 有关文章的问题，欢迎您在底部评论区留言，一起交流~</p></blockquote>]]></content>
    
    
    <summary type="html">Hadoop基础模块</summary>
    
    
    
    <category term="大数据" scheme="https://ruy9527.github.io/categories/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
    
    <category term="大数据" scheme="https://ruy9527.github.io/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    
    <category term="hdfs" scheme="https://ruy9527.github.io/tags/hdfs/"/>
    
    <category term="hadoop" scheme="https://ruy9527.github.io/tags/hadoop/"/>
    
    <category term="yarn" scheme="https://ruy9527.github.io/tags/yarn/"/>
    
  </entry>
  
  <entry>
    <title>Redis主从,哨兵，集群</title>
    <link href="https://ruy9527.github.io/article/redis_ha1/"/>
    <id>https://ruy9527.github.io/article/redis_ha1/</id>
    <published>2022-01-28T04:00:00.000Z</published>
    <updated>2023-07-23T05:22:36.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>😀 这里写文章的前言：<br>Redis的主从,哨兵,集群</p></blockquote><h1 id="📝-主从复制"><a href="#📝-主从复制" class="headerlink" title="📝 主从复制"></a>📝 主从复制</h1><p>主从复制是Redis分布式基石，也是Redis高可用的保障</p><p>被复制的服务器称为主服务器（Master）</p><p>对主服务器进行复制的服务器称为从服务器(Slave)</p><p>1个master节点，多个slave节点</p><p>master 节点负责写(可读可写)</p><p>slave  节点负责读(只读)，分担压力，slave接受master节点的同步信息一直保持同步</p><p><img src="/images/posts/redis_ha1/img-1.png" alt="Redis主从,哨兵，集群"></p><h2 id="配置redis-config"><a href="#配置redis-config" class="headerlink" title="配置redis.config"></a>配置redis.config</h2><p>多个redis.config配置, 配置不同的端口启动, 从节点 slave 主节点. 需要手动配置</p><p>3种方式</p><p>replicaof（Redis 5.0 之前使用 slaveof）</p><ul><li>在 slave 直接执行命令：replicaof <masterip> <masterport></li><li>在 slave 配置文件中加入：replicaof <masterip> <masterport></li><li>使用启动命令：–replicaof <masterip> <masterport></li></ul><p>主节点不需要修改配置,从节点需要配置 replica of master ip poid</p><h2 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h2><ul><li>2.8版本以前,从服务器对主服务器的同步需要从服务器向主服务器发送sync命令来完成，主从服务器需要占用的资源：每次都执行一次RDB持久化</li></ul><details><summary>缺点记录</summary><ol><li>主服务器执行bgsave生成rdb文件，会占用大量的CPU,磁盘I&#x2F;O和内存资源</li><li>主服务器将生成的RDB文件发生给从服务器，会占用大量网络宽带</li><li>从服务器接受RDB文件并载入，会导致从服务器阻塞，无法提供服务</li></ol></details><p><img src="/images/posts/redis_ha1/img-2.png" alt="Redis主从,哨兵，集群"></p><ul><li>2.8版本以后,减少全量同步(full resynchronizaztion)的发生，尽可能使用增量同(partial resynchronization). 使用psync命令代替了sync命令来执行同步操作;psync命令同时具备全量同步和增量同步的功能</li></ul><details><summary>psync命令</summary><ul><li>全量同步与上一版本（sync）一致</li><li>增量同步中对于断线重连后的复制，会根据情况采取不同措施；如果条件允许，仍然只发送从服务缺失的部分数据。</li><li>偏移量过大, 还是会触发全量同步</li><li>在bgsave的过程中,新生成的数据会放到环形buffer中, rdb完成后,再传输buffer数据</li><li>如果bgsave过程中,buffer超出了最大限制,会触发全量同步</li></ul></details><p><img src="/images/posts/redis_ha1/img-3.png" alt="Redis主从,哨兵，集群"></p><h2 id="主从复制过程"><a href="#主从复制过程" class="headerlink" title="主从复制过程"></a>主从复制过程</h2><ol><li>主从之间建立链接，由于从节点皮质了replica of主ip，此时是知道主节点ip地址的</li><li>从节点发送一个psync ？ -1 的命令给主节点，主节点接受到后执行bgsave，发送给从节点，psync包括二个参数： 主服务器的runID和复制进度offset;第一次同步时，其值为-1,主节点响应的fullersync代表全量复制</li><li>从节点接收到rdb文件，清空自己的数据，执行全量同步，后续有新的数据，通过replication Buffer进行增量同步</li><li>加入主从之间的差距还是过大，还是会进行全量同步，buffer超过了大小限制，也会触发一次全量同步</li></ol><p><img src="/images/posts/redis_ha1/img-4.png" alt="Redis主从,哨兵，集群"></p><details><summary>?如果是主服务切换时，是否也能进行增量同步</summary><p>psync2跑了服务器运行id，采用了replid和replid2来代替，其中replid存储的是当前主服务的允许id，replid2保存的是上一个主服务器运行ID</p><ul><li>主服务器运行id（replid）</li><li>上个主服务器运行id（replid2）</li></ul><p>通过replid和replid2可以解决主服务器切换时候，增量同步问题:</p><ul><li>如果replid等于当前主服务器的运行id，那么判断同步方式增量&#x2F;全量同步</li><li>如果replid不想等，则判断replid2是否相等（是否同属于上一个主服务器的从服务器），如果相等，依然可以选择增量&#x2F;全量同步;如果不想等则只能进行全量同步</li></ul></details><details><summary>?主从库间网络断了怎么办</summary><p>redis2.8之前，断开后重新连接会出现一次全量复制，开销非常大</p><p>从redis2.8开始，网络断开之后，主从库采用了增量复制的方式继续同步;增量复制只会把主从库从网络断开期间主库受到的命令，同步给从库</p><p>增量复制时，主从库之间具体是怎么保持同步的呢？这里的奥妙就在于 repl_backlog_buffer 这个缓冲区。我们先来看下它是如何用于增量命令的同步的。当主从库断连后，主库会把断连期间收到的写操作命令，写入 replication buffer，同时也会把这些操作命令也写入 repl_backlog_buffer 这个缓冲区</p><p>如果replaceBuffer满了，也会触发重新的全量复制 </p><p><img src="/images/posts/redis_ha1/img-5.png" alt="Redis主从,哨兵，集群"></p></details><details><summary>？为什么主从库的复制不使用AOF</summary><ul><li>RDB文件是二进制文件，无论是要把RDB写入磁盘，还是通过网络传输RDB，IO效率都比记录和传输AOF的高</li><li>在从库断进行恢复时，用RDB的恢复效率要高于用AOF</li></ul></details><details><summary>如果主从之间，从节点过多，可能导致主节点传输RDB文件效率问题，可以采纳树结构</summary><p><img src="/images/posts/redis_ha1/img-6.png" alt="Redis主从,哨兵，集群"></p></details><h1 id="哨兵模式"><a href="#哨兵模式" class="headerlink" title="哨兵模式"></a>哨兵模式</h1><p>主从模式都是手动处理的，假设出了问题大部分不能第一时间解决主从故障等问题，哨兵模式可以很好的解决自动化监听并且处理主从故障的问题(保证服务的高可用);推选出新的master，并且将旧的master退级为slave</p><h2 id="监控"><a href="#监控" class="headerlink" title="监控"></a>监控</h2><p>哨兵会每隔1秒给所有的主从节点发送PING命令，当主从节点受到PING命令后，会发送一个响应命令给哨兵，这样就可以判断它们是否正常运行</p><p><img src="/images/posts/redis_ha1/img-7.png" alt="Redis主从,哨兵，集群"></p><h2 id="发现故障"><a href="#发现故障" class="headerlink" title="发现故障"></a>发现故障</h2><ul><li>如果主节点或者从节点没有在规定的时间内响应哨兵，哨兵就会将它们标记为 [主观下线]</li><li>并且告知其他哨兵，ping该节点，超过半数确认是否 [客观下线]</li></ul><p><img src="/images/posts/redis_ha1/img-8.png" alt="Redis主从,哨兵，集群"></p><h2 id="故障转移"><a href="#故障转移" class="headerlink" title="故障转移"></a>故障转移</h2><h3 id="1-选出leader哨兵"><a href="#1-选出leader哨兵" class="headerlink" title="1.选出leader哨兵"></a>1.选出leader哨兵</h3><p>为了更加“客观”的判断主节点故障了，一般不会只由单个哨兵的检测结果来判断，而是多个哨兵一起判断，这样可以减少误判概率，所以<strong>哨兵是以哨兵集群的方式存在的</strong></p><p>需要在哨兵集群中选出一个leader，让leader来执行主从切换;选举leader的过程其实是一个投票的过程，在投票开始前，肯定得有个  [候选者]</p><p>举个栗子：假设有三个哨兵;当哨兵B先判断到主节点[主管下线后],就会给其他实例发送is-master-down-by-addr命令;接着，其他哨兵会根据自己和主节点的网络连接情况,做出赞成投票或者拒绝投票的响应</p><p>当哨兵 B 收到赞成票数达到哨兵配置文件中的 quorum 配置项设定的值后，就会将主节点标记为「客观下线」，此时的哨兵 B 就是一个Leader 候选者</p><p>候选者会向其他哨兵发送命令，表明希望成为Leader来执行主从切换，让其他哨兵对它投票</p><p>每个哨兵只有一次投票机会，如果用完了就不能参与投票，可以投给自己或投给别人，但是只有候选者才能把票投给自己</p><p>那么在投票的过程中，任何一个 候选者 ， 要满足两个条件：</p><ul><li>第一，拿到半数以上的赞成票</li><li>第二，拿到的票数同时还需要大于等于哨兵配置文件中的quorum值</li></ul><p><img src="/images/posts/redis_ha1/img-9.png" alt="Redis主从,哨兵，集群"></p><details><summary>哨兵节点至少要有3个</summary><p>如果哨兵集群中有2个哨兵节点，此时如果一个哨兵想要成为leader，必须获得2票，而不是1票;所以，如果哨兵集群中有个哨兵挂掉了，那么就只剩一个哨兵了，如果这个哨兵想要成为 Leader，这时票数就没办法达到 2 票，就无法成功成为 Leader，这时是无法进行主从节点切换的</p><p>因此，通常我们至少配置3个哨兵节点; 这时,如果哨兵集群中有哨兵挂掉了，那么还剩下两个哨兵，如果这个哨兵想成为leader，这时还是有机会达到2票的，所以还是可以选举成功的，不会导致无法进行主从节点切换</p><p>你要问，如果 3 个哨兵节点，挂了 2 个怎么办？这个时候得人为介入了，或者增加多一点哨兵节点。</p></details><h3 id="2-选举新节点"><a href="#2-选举新节点" class="headerlink" title="2.选举新节点"></a>2.选举新节点</h3><details><summary>**第一步，从已下线的主节点属下的所有从节点中，挑选出一个状态良好，数据完整的从节点，然后向这个从节点发送 SLAVE no one 命令 ，将这个从节点转换为主节点**</summary><h3 id="先剔除网络不好的"><a href="#先剔除网络不好的" class="headerlink" title="先剔除网络不好的"></a>先剔除网络不好的</h3><ul><li>首先要把网络状态不好的从节点给过滤掉( 5 秒内未回复 info 命令的 )</li><li>首先把已经下线的从节点过滤掉</li><li>然后把以往网络连接状态不好的从节点也给过滤掉。(down-after-milliseconds * 10)</li></ul><h3 id="第一轮考察：-优先级最高的从节点胜出"><a href="#第一轮考察：-优先级最高的从节点胜出" class="headerlink" title="第一轮考察： 优先级最高的从节点胜出"></a>第一轮考察： 优先级最高的从节点胜出</h3><p> slave-priority 配置项，可以给从节点设置优先级</p><h3 id="第二轮考察：选择复制偏移量最大，也就是复制最完整的从节点"><a href="#第二轮考察：选择复制偏移量最大，也就是复制最完整的从节点" class="headerlink" title="第二轮考察：选择复制偏移量最大，也就是复制最完整的从节点"></a>第二轮考察：选择复制偏移量最大，也就是复制最完整的从节点</h3><p>如果某个从节点的 slave_repl_offset 最接近 master_repl_offset，说明它的复制进度是最靠前的，于是就可以将它选为新主节点</p><h3 id="第三轮考察：ID-号小的从节点胜出"><a href="#第三轮考察：ID-号小的从节点胜出" class="headerlink" title="第三轮考察：ID 号小的从节点胜出"></a><strong>第三轮考察：ID 号小的从节点胜出</strong></h3><p>什么是 ID 号？每个从节点都有一个编号，这个编号就是 ID 号，是用来唯一标识从节点的</p><p><img src="/images/posts/redis_ha1/img-10.png" alt="Redis主从,哨兵，集群"></p><p>选举完成后,哨兵leader向被选中的server2从节点发送 SLAVEOF no one命令，让这个从节点接触从节点的身份，将其变为新主节点</p><p><img src="/images/posts/redis_ha1/img-11.png" alt="Redis主从,哨兵，集群"></p></details><h3 id="3-从节点指向新主节点"><a href="#3-从节点指向新主节点" class="headerlink" title="3.从节点指向新主节点"></a>3.从节点指向新主节点</h3><p>当新主节点出现之后，哨兵 leader 下一步要做的就是，让已下线主节点属下的所有「从节点」指向「新主节点」，这一动作可以通过向「从节点」发送 <code>SLAVEOF</code> 命令来实现</p><p><img src="/images/posts/redis_ha1/img-12.png" alt="Redis主从,哨兵，集群"></p><p>所有从节点指向新主节点后的拓扑图如下：</p><p><img src="/images/posts/redis_ha1/img-13.png" alt="Redis主从,哨兵，集群"></p><h3 id="4-通知客户端的主节点已更换"><a href="#4-通知客户端的主节点已更换" class="headerlink" title="4.通知客户端的主节点已更换"></a>4.通知客户端的主节点已更换</h3><p>经过前面三步，哨兵集群终于完成主从节点切换的工作，那么新节点的信息如何通知给客户端呢？</p><p>这主要<strong>通过 Redis 的发布者&#x2F;订阅者机制来实现</strong>的。每个哨兵节点提供发布者&#x2F;订阅者机制，客户端可以从哨兵订阅消息</p><p><img src="/images/posts/redis_ha1/img-14.png" alt="Redis主从,哨兵，集群"></p><p>客户端和哨兵建立连接后，客户端会订阅哨兵提供的频道</p><p>主从切换完成后,哨兵就会向 +switch-master 频道发布新主节点的ip地址和端口信息，这个时候客户端就可以接收到这条信息，然后用这里面的新主节点的ip地址和端口进行通信</p><h3 id="5-将旧主节点变为从节点"><a href="#5-将旧主节点变为从节点" class="headerlink" title="5.将旧主节点变为从节点"></a>5.将旧主节点变为从节点</h3><p>故障转移操作最后要做的是，继续监视旧主节点，当旧主节点重新上线时，哨兵集群就会向它发送 <code>SLAVEOF</code> 命令，让它成为新主节点的从节点，如下图</p><p><img src="/images/posts/redis_ha1/img-15.png" alt="Redis主从,哨兵，集群"></p><h2 id="哨兵集群如何工作"><a href="#哨兵集群如何工作" class="headerlink" title="哨兵集群如何工作"></a>哨兵集群如何工作</h2><p>前面提到了 Redis 的发布者&#x2F;订阅者机制，那就不得不提一下哨兵集群的组成方式，因为它也用到了这个技术</p><p>在我第一次搭建哨兵集群的时候，当时觉得很诧异。因为在配置哨兵的信息时，竟然只需要填下面这几个参数，设置主节点名字、主节点的 IP 地址和端口号以及 quorum 值</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">sentinel monitor &lt;master-name&gt; &lt;ip&gt; &lt;redis-port&gt; &lt;quorum&gt;</span><br></pre></td></tr></table></figure><p>不需要填其他哨兵节点信息，是如何感知对方的，又是如何组成哨兵集群的？</p><p>哨兵节点之间通过redis的发布者&#x2F;订阅者机制来互相发现的</p><p>在主从集群中，主节点上有一个名为<code>__sentinel__:hello</code>的频道，不同哨兵就是通过它来相互发现，实现互相通信的</p><p>在下图中，哨兵 A 把自己的 IP 地址和端口的信息发布到<code>__sentinel__:hello</code> 频道上，哨兵 B 和 C 订阅了该频道。那么此时，哨兵 B 和 C 就可以从这个频道直接获取哨兵 A 的 IP 地址和端口号。然后，哨兵 B、C 可以和哨兵 A 建立网络连接</p><p><img src="/images/posts/redis_ha1/img-16.png" alt="Redis主从,哨兵，集群"></p><p>通过这个方式，哨兵 B 和 C 也可以建立网络连接，这样一来，哨兵集群就形成了</p><h2 id="脑裂问题"><a href="#脑裂问题" class="headerlink" title="脑裂问题"></a>脑裂问题</h2><p>旧的master出了问题，有一些还没同步到redis上，从redis通过选举成为一个新的master，有新的master起来后，由于一些问题，哨兵通知不到旧的master，导致2个master，就出现了脑裂</p><p>本质是2个master，新master是可以接受写入数据的， 但是有一些数据写到旧的mater上了, 但是从redis 只会同步新的mater, 当旧mater的恢复正常后, 哨兵会把旧的mater降级为从, 降级是要清除数据的,清了数据 之前写在旧mater上的数据还没同步的也就没了</p><p>问题出在,当从升级为master的时候，有2个master，并且2个都在接受写入数据;旧的 新的master都在写入数据，但是从的只同步了新的master，写进旧的master的数据没同步，并且随着自己的降级也没了，丢失了</p><details><summary>?脑裂如何处理</summary><p><img src="/images/posts/redis_ha1/img-17.png" alt="Redis主从,哨兵，集群"></p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"># master 须要有至少 x 个副本连接。</span><br><span class="line">min-slaves-to-write x</span><br><span class="line"># 数据复制和同步的延迟不能超过 x 秒。</span><br><span class="line">min-slaves-max-lag x</span><br></pre></td></tr></table></figure><p>注意高版本中,redis已经修改了这2个配置</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"># min-replicas-to-write x</span><br><span class="line"># min-replicas-max-lag x</span><br></pre></td></tr></table></figure></details><p>CAP理论认为，一个分布式系统不可能同时满足一致性（Consistency）、可用性（Availability）和分区容错性（Partition tolerance）这三个基本需求，最多只能同时满足其中两项。</p><p>在Redis哨兵模式下出现脑裂问题时，主从节点之间的分区容错性得以保证，但同时需要在一致性和可用性之间做出取舍。</p><p>首先，由于数据复制和同步的延迟可能会导致主从节点之间的数据不一致，因此在Redis的哨兵模式中，我们需要配置 <code>min-slaves-to-write</code> 和 <code>min-slaves-max-lag</code> 这两个参数，以保证数据的一致性。</p><p>其次，在节点出现脑裂时，如果旧的主节点和新的主节点都在接受写入数据，那么可能会出现数据写入不一致的情况。为了解决这个问题，我们可以在哨兵模式中配置 <code>min-replicas-to-write</code> 和 <code>min-replicas-max-lag</code> 这两个参数，以保证数据的一致性和可用性。</p><p>但是需要注意的是，以上的配置都只能减少发生脑裂的可能性，而不能完全避免。因此，对于Redis的哨兵模式来说，我们需要在一致性和可用性之间做出取舍，根据具体的业务需求来设置合理的参数。</p><ul><li>redis集群是可以让脑裂存在的,其实是一种高可用的特性，但不保证数据一致</li><li>redis通过设置两个参数，一定程度上其实是降低可用性，以提供数据一致性</li><li>为什么愿意降低可用性？因为那部分的数据会因为主从切换而丢失，所以宁愿不可用</li></ul><h1 id="Cluster集群"><a href="#Cluster集群" class="headerlink" title="Cluster集群"></a>Cluster集群</h1><p>哨兵模式最大的缺点就是所有的数据都放在同一台服务器上，无法较好的进行水平扩展</p><p>为了解决哨兵模式存在的问题，集群模式应运而生。在高可用上，集群基本是直接复用的哨兵模式的逻辑，并且针对水平扩展进行了优化</p><p>Redis Cluster集群模式具备的特点如下:</p><ol><li>采取去中心化的集群模式，将数据按照槽存储分布在多个redis节点上。集群共有16382槽，每个节点负责处理部分槽</li><li>使用CRC16算法来计算key所属的槽，CRC16（key,key len） &amp;16383</li><li>所有Redis节点批次互联，通过PING-PONG机制来节点间的心跳检测</li><li>分片内采用一主多从保证高可用，并提供复制和故障恢复功能。在实际中，通过会将主从分布在不同的机房，避免机房出现故障导致整个分片出问题</li><li>客户端与Redis节点直连，不需要中间代理层(Proxy);客户端不需要连接集群所有节点，连接集群中任何一个可用的节点即可</li></ol><p><img src="/images/posts/redis_ha1/img-18.png" alt="Redis主从,哨兵，集群"></p><p>目前redis的分区规则是： hash分区，分成了 16384 个哈希槽</p><p>RedisCluster采用了哈希分区的“虚拟槽分区”方式（哈希分区分节点取余，一致性哈希分区和虚拟槽分区）</p><p><img src="/images/posts/redis_ha1/img-19.png" alt="Redis主从,哨兵，集群"></p><details><summary>集群之后的扩容</summary><p>如何保证集群在线扩容的安全性？ Redis集群要添加分片，槽的迁移怎么保证无损</p><p>比如集群已经对外提供服务，原本有3分片，准备新增2个，怎么在不下线的情况下，无损的从原有的3个分片指派若干槽这2个分片？</p><p>Redis使用ASK错误来保证在线扩容的安全性;在槽的迁移过程中若有客户端访问，依旧先访问源节点，源节点会在自己的数据库里查找指定的键，如果找得到的话，就直接执行客户端发送的命令</p><p>如果没有找到，说明该键可能被迁移到目标节点了，源节点将向客户端返回一个ASK错误，该错误会将客户端转向正在倒入槽的目标节点，并再次发送之前要指令的命令,从而获取结果</p></details><h2 id="故障发现"><a href="#故障发现" class="headerlink" title="故障发现"></a>故障发现</h2><ol><li>Redis Cluster通过ping&#x2F;pong消息实现故障发现：<strong>不需要sentinel</strong></li><li>ping&#x2F;pong不仅能传递节点与槽的对应消息，也能传递其他状态，比如：节点主从状态，节点故障等</li><li>故障发现就是通过这种模式来实现，分为主观下线和客观下线</li></ol><h3 id="主观下线"><a href="#主观下线" class="headerlink" title="主观下线"></a>主观下线</h3><ul><li>某个节点认为宁外一个节点不可用，‘偏见’，只代表一个节点对另外一个节点的判断，不是所有节点的认知</li><li>如果节点1发现与节点2最后通信时间超过node-timeout，则把节点2标识为pfail状态</li></ul><p><img src="/images/posts/redis_ha1/img-20.png" alt="Redis主从,哨兵，集群"></p><h3 id="客观下线"><a href="#客观下线" class="headerlink" title="客观下线"></a>客观下线</h3><ol><li>当半数以上持有槽的主节点都标记某节点主观下线时，可以保证判断的公平性</li><li>集群模式下，只有主节点(master)才有读写权限和集群槽的维护权限，从节点(slave)只有复制的权限</li><li>客观下线流程:</li></ol><p>1.某个节点接收到其他节点发送的ping消息，如果接收到的ping消息中包含了其他pfail节点，这个节点会将主观下线的消息内容添加到自身的故障列表中，故障列表中包含了当前节点接收到的每一个节点对其他节点的状态信息</p><p>2.当前节点把主观下线的消息内容添加到自身的故障列表之后，会尝试对故障节点进行客观下线操作</p><p><img src="/images/posts/redis_ha1/img-21.png" alt="Redis主从,哨兵，集群"></p><h2 id="准备选举时间"><a href="#准备选举时间" class="headerlink" title="准备选举时间"></a>准备选举时间</h2><p><strong>使偏移量最大的从节点具备优先级成为主节点的条件</strong></p><p><img src="/images/posts/redis_ha1/img-22.png" alt="Redis主从,哨兵，集群"></p><h2 id="替换主节点"><a href="#替换主节点" class="headerlink" title="替换主节点"></a>替换主节点</h2><p>当前从节点取消复制变成离节点(slave of no one)<br>执行cluster del slot撤销故障主节点负责的槽，并执行cluster add slot把这些槽分配给自己<br>向集群广播自己的pong消息，表明已经替换了故障从节点</p><h2 id="集群选举流程"><a href="#集群选举流程" class="headerlink" title="集群选举流程"></a>集群选举流程</h2><p>故障转移的第一步就是选举出新的主节点，以下是集群选举新的主节点的方法：</p><p>1）当从节点发现自己正在复制的主节点进入已下线状态时，会发起一次选举：将 currentEpoch（配置纪元）加1，然后向集群广播一条 CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST 消息，要求所有收到这条消息、并且具有投票权的主节点向这个从节点投票。</p><p>2）其他节点收到消息后，会判断是否要给发送消息的节点投票，判断流程如下：</p><ol><li>当前节点是 slave，或者当前节点是 master，但是不负责处理槽，则当前节点没有投票权，直接返回。</li><li>请求节点的 currentEpoch 小于当前节点的 currentEpoch，校验失败返回。因为发送者的状态与当前集群状态不一致，可能是长时间下线的节点刚刚上线，这种情况下，直接返回即可。</li><li>当前节点在该 currentEpoch 已经投过票，校验失败返回。</li><li>请求节点是 master，校验失败返回。</li><li>请求节点的 master 为空，校验失败返回。</li><li>请求节点的 master 没有故障，并且不是手动故障转移，校验失败返回。因为手动故障转移是可以在 master 正常的情况下直接发起的。</li><li>上一次为该master的投票时间，在cluster_node_timeout的2倍范围内，校验失败返回。这个用于使获胜从节点有时间将其成为新主节点的消息通知给其他从节点，从而避免另一个从节点发起新一轮选举又进行一次没必要的故障转移</li><li>请求节点宣称要负责的槽位，是否比之前负责这些槽位的节点，具有相等或更大的 configEpoch，如果不是，校验失败返回。</li></ol><p>如果通过以上所有校验，那么主节点将向要求投票的从节点返回一条 CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK 消息，表示这个主节点支持从节点成为新的主节点。</p><p>3）每个参与选举的从节点都会接收 CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK 消息，并根据自己收到了多少条这种消息来统计自己获得了多少个主节点的支持。</p><p>4）如果集群里有N个具有投票权的主节点，那么当一个从节点收集到大于等于N&#x2F;2+1 张支持票时，这个从节点就会当选为新的主节点。因为在每一个配置纪元里面，每个具有投票权的主节点只能投一次票，所以如果有 N个主节点进行投票，那么具有大于等于 N&#x2F;2+1 张支持票的从节点只会有一个，这确保了新的主节点只会有一个。</p><p>5）如果在一个配置纪元里面没有从节点能收集到足够多的支持票，那么集群进入一个新的配置纪元，并再次进行选举，直到选出新的主节点为止。</p><p>这个选举新主节点的方法和选举领头 Sentinel 的方法非常相似，因为两者都是基于 Raft 算法的领头选举（leader election）方法来实现的</p><h2 id="ASK错误"><a href="#ASK错误" class="headerlink" title="ASK错误"></a>ASK错误</h2><p>当redis<strong>正在</strong>对集群进行扩容和缩容时，需要对槽及槽中数据进行迁移</p><p>当客户端向某个节点发送命令，节点向客户端返回moved异常，告诉客户端数据对应的槽的节点信息</p><p>如果此时正在进行集群扩展或者缩空操作，当客户端向正确的节点发送命令时，槽及槽中数据已经被迁移到别的节点了，就会返回ask，这就是ask重定向机制</p><p><img src="/images/posts/redis_ha1/img-23.png" alt="Redis主从,哨兵，集群"></p><h2 id="moved重定向"><a href="#moved重定向" class="headerlink" title="moved重定向"></a>moved重定向</h2><p>是路由层面的，直接修改迁移之后的槽映射关系，不用像ask错误一样，返回错误再重新路由</p><p><img src="/images/posts/redis_ha1/img-24.png" alt="Redis主从,哨兵，集群"></p><details><summary>**moved异常与ask异常的相同点和不同点**</summary><ol><li>两者都是客户端重定向</li><li>moved异常： 槽已经确定迁移，即槽已经不在当前节点</li><li>ask异常：   槽还在迁移中</li></ol></details><h2 id="集群不可用时机"><a href="#集群不可用时机" class="headerlink" title="集群不可用时机"></a>集群不可用时机</h2><ul><li>挂掉的master没有对应的slave，即有任一slot不可用，集群都不可用</li><li>超过半数master都挂掉了，不管有没有slave，都将不可用</li></ul><h1 id="🤗-总结归纳"><a href="#🤗-总结归纳" class="headerlink" title="🤗 总结归纳"></a>🤗 总结归纳</h1><h1 id="📎-参考文章"><a href="#📎-参考文章" class="headerlink" title="📎 参考文章"></a>📎 参考文章</h1><ul><li><a href="https://juejin.cn/post/7017017990667304973">https://juejin.cn/post/7017017990667304973</a></li><li><a href="https://www.xiaolincoding.com/redis/cluster/sentinel.html">https://www.xiaolincoding.com/redis/cluster/sentinel.html</a></li></ul><blockquote><p>💡 有关文章的问题，欢迎您在底部评论区留言，一起交流~</p></blockquote>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;😀 这里写文章的前言：&lt;br&gt;Redis的主从,哨兵,集群&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;📝-主从复制&quot;&gt;&lt;a href=&quot;#📝-主从复制&quot; class=&quot;headerlink&quot; title=&quot;📝</summary>
        
      
    
    
    
    <category term="中间件" scheme="https://ruy9527.github.io/categories/%E4%B8%AD%E9%97%B4%E4%BB%B6/"/>
    
    
    <category term="Redis" scheme="https://ruy9527.github.io/tags/Redis/"/>
    
  </entry>
  
</feed>
