<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>Wednesday</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <id>https://studyflowblog.com/</id>
  <link href="https://studyflowblog.com/" rel="alternate"/>
  <link href="https://studyflowblog.com/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, Wednesday</rights>
  <subtitle>Tech | Study | Life</subtitle>
  <title>Wednesday`s Blog</title>
  <updated>2026-04-07T11:44:33.620Z</updated>
  <entry>
    <author>
      <name>Wednesday</name>
    </author>
    <category term="生活其他" scheme="https://studyflowblog.com/categories/%E7%94%9F%E6%B4%BB%E5%85%B6%E4%BB%96/"/>
    <category term="留学" scheme="https://studyflowblog.com/tags/%E7%95%99%E5%AD%A6/"/>
    <category term="奖学金" scheme="https://studyflowblog.com/tags/%E5%A5%96%E5%AD%A6%E9%87%91/"/>
    <category term="澳洲" scheme="https://studyflowblog.com/tags/%E6%BE%B3%E6%B4%B2/"/>
    <content>
      <![CDATA[<h2 id="1-0-澳洲北领地政府奖学金是什么"><a href="#1-0-澳洲北领地政府奖学金是什么" class="headerlink" title="1.0 澳洲北领地政府奖学金是什么"></a>1.0 澳洲北领地政府奖学金是什么</h2><p>北领地政府为了资助从澳洲别的洲或者海外来北领地上学的学生而设立的奖学金。</p><p>简单来讲就是从北领地以外的地方来的学生都能够申请的奖学金，S1学期入学的会在当年的1月份左右开启申请，S2入学是6月份左右开启申请，具体时间可能会浮动（具体在<a href="https://grantsnt.nt.gov.au/welcome">https://grantsnt.nt.gov.au/welcome</a> 搜索Study in Australia’s Northern Territory Scholarship就能看到）。</p><p>也是来CDU上学，每个人除了默认的30%学费减免；还能申请北领地奖学金和住宿奖学金(1000刀)，但是后两者不可兼得。北领地奖学金本科是$4200，研究生是$5200，这个是必须要申请的，就当挂彩票了，如果中了就起飞了。建议是提前准备申请材料，而且材料需要仔细准备一下。</p><h2 id="1-1-要求是什么？"><a href="#1-1-要求是什么？" class="headerlink" title="1.1 要求是什么？"></a>1.1 要求是什么？</h2><p>要求是以下几项：</p><ol><li>尚未居住在北领地</li><li>计划来北领地上学</li><li>获得了500学签</li><li>获得了COE</li><li>限定时间内入学</li></ol><p>这个要求基本来上学的都具备了，就是第一个条件容易被忽略，很多朋友到了北领地才想起来要申请，但是这就已经不符合第一个条件了。所以申请的话还是越早申请越好，越早申请个人是觉得概率越高，开学后还会在国际生迎新会上进行颁奖。</p><h2 id="1-2-如何申请？"><a href="#1-2-如何申请？" class="headerlink" title="1.2 如何申请？"></a>1.2 如何申请？</h2><p>申请路径还是见：<a href="https://grantsnt.nt.gov.au/welcome">https://grantsnt.nt.gov.au/welcome</a> 搜索Study in Australia’s Northern Territory Scholarship，如果开放了的话点进去就能填材料进行申请了。</p><p>申请需要哪些材料？</p><p>提交申请有两项个部分的内容需要准备：Student Details和Application Details</p><p>Student Details：这个部分就是填一些个人的信息，里面有一个很容易迷糊的地方。一个是要填手机号，但是这里只能填10位，但是大家如果还没来澳洲肯定没这边的手机号，其实这里不重要，把自己国内手机号去首位或者尾位填上去就行了，因为他们如果需要联系你是通过你的邮箱基本不会通过电话，所以邮箱没写错就行了。</p><p>Application Details：<br>这个部分就是主要的文书材料的环节了。<br>问题具体有以下几项：</p><ol><li>为什么要把奖给你以及你为什么来北领地上学 – <strong>写500字的说明</strong></li><li>CoE，到达北领地的时间以及到达的证明  –<strong>上传机票或行程单证明文件证明自己到达达尔文的日期</strong></li><li>需要两封支持信，支持为什么给奖给你   – <strong>一封雇主支持信，一封学校老师支持信（如果没有雇主的话就写两封老师的）</strong>。</li><li>其他证明文件 – <strong>主要是你文书里面提到的获得什么奖项的全部打包在这里</strong></li></ol><p>关于文书材料我根据自己申请成功的文书，整理了一份模板放这里供大家参考：<a href="https://pan.quark.cn/s/d7e39d7e06b3">https://pan.quark.cn/s/d7e39d7e06b3</a></p><img src="/2026/04/07/life/northern-territory-scholarship/certificates.jpg" class="">]]>
    </content>
    <id>https://studyflowblog.com/2026/04/07/life/northern-territory-scholarship/</id>
    <link href="https://studyflowblog.com/2026/04/07/life/northern-territory-scholarship/"/>
    <published>2026-04-07T10:30:00.000Z</published>
    <summary>北领地政府奖学金申请全流程分享，本科$4200、研究生$5200，附材料清单和避坑指南</summary>
    <title>我是如何拿到澳洲北领地政府奖学金的</title>
    <updated>2026-04-07T11:44:33.620Z</updated>
  </entry>
  <entry>
    <author>
      <name>Wednesday</name>
    </author>
    <category term="coding" scheme="https://studyflowblog.com/categories/coding/"/>
    <category term="编程" scheme="https://studyflowblog.com/tags/%E7%BC%96%E7%A8%8B/"/>
    <content>
      <![CDATA[<h2 id="学习教程"><a href="#学习教程" class="headerlink" title="学习教程"></a>学习教程</h2><ul><li>官方tutor教程</li><li><a href="https://www.openvim.com/tutorial.html">Interactive Vim tutorial (openvim.com)</a></li><li><a href="https://vim-adventures.com/">Learn VIM while playing a game - VIM Adventures</a></li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line">========= basic ==========</span><br><span class="line">left : h</span><br><span class="line">right : l</span><br><span class="line">up : k</span><br><span class="line">down : j</span><br><span class="line">undo the previous motion: u</span><br><span class="line">undo the previous motion of the line : U</span><br><span class="line">undo undo : ctrl+R</span><br><span class="line">word : w</span><br><span class="line">end : e</span><br><span class="line">insert : i</span><br><span class="line">append : a</span><br><span class="line">paste : p</span><br><span class="line">replace a char ：r</span><br><span class="line">replace a word : R</span><br><span class="line">copy a word: yw  (paste is p!!)</span><br><span class="line">copy some words : v+y</span><br><span class="line">show the numbers of line : set number</span><br><span class="line">to the line end : $</span><br><span class="line">command prompt : ctrl + d  / tab</span><br><span class="line">($ is means the end of the line)</span><br><span class="line">(w is means word)</span><br><span class="line">(e is means end) </span><br><span class="line">======== delete ========== d+motion</span><br><span class="line">delete a char: x</span><br><span class="line">delete to word : dw</span><br><span class="line">delete to end : de</span><br><span class="line">left delete : dj</span><br><span class="line">right delete : dl</span><br><span class="line">delete to line : d$</span><br><span class="line">delete line : dd</span><br><span class="line">======= number to execute =====</span><br><span class="line">move to second word : 2w</span><br><span class="line">move to second word end : 2e</span><br><span class="line">to the end of the line : 0</span><br><span class="line">delete 2 words : d2w</span><br><span class="line">delete 2 lines : 2dd</span><br><span class="line">======= the command of place =====</span><br><span class="line">delete a line and paste it below the current line : dd + p</span><br><span class="line">replace a word : r + word</span><br><span class="line">change a word to the end of the word : cw/ce</span><br><span class="line">change to the end of the row : c$</span><br><span class="line">change to second word : c2w</span><br><span class="line">======= location and file status =====</span><br><span class="line">go to the first line : gg</span><br><span class="line">go to the last line : G</span><br><span class="line">show file status : ctrl+g</span><br><span class="line">====== search command ==========</span><br><span class="line">search word in order :  /+word (n:next one ;N:the previous one)</span><br><span class="line">search word in reverse : ?+word</span><br><span class="line">find matched brackets(查找匹配的括号) : %</span><br><span class="line">replace word in this line : :/s/oldword/newword</span><br><span class="line">replace word all : :/s/oldword/newword/g</span><br><span class="line">replace word all and confirm everyone :  :/s/oldword/newword/gc</span><br><span class="line">replace word between line1 and line3 : :1,3/oldword/newword/g</span><br><span class="line">====== others =========</span><br><span class="line">use external instruction in vim : :!+commad(eg: :!ls)</span><br><span class="line">save file in another file : :w+filename(eg: :w test)</span><br><span class="line">merge other file in this file : :r + filename(insert to now location)           </span><br><span class="line">insert mode</span><br><span class="line">insert line under the line : o</span><br><span class="line">insert line upon the line : O</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://studyflowblog.com/2021/08/24/vim-tutor/</id>
    <link href="https://studyflowblog.com/2021/08/24/vim-tutor/"/>
    <published>2021-08-24T05:35:06.000Z</published>
    <summary>Vim入门学习笔记，结合官方tutor教程与VIM Adventures游戏整理的移动、编辑、查找替换等常用命令速查</summary>
    <title>vim tutor</title>
    <updated>2026-04-02T12:26:23.772Z</updated>
  </entry>
  <entry>
    <author>
      <name>Wednesday</name>
    </author>
    <category term="大数据" scheme="https://studyflowblog.com/categories/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    <category term="spark" scheme="https://studyflowblog.com/tags/spark/"/>
    <content>
      <![CDATA[<h1 id="1-问题描述"><a href="#1-问题描述" class="headerlink" title="1 问题描述"></a>1 问题描述</h1><p>前几天在跑一个程序的时候遇到了一个很诡异的事情，在dolphinSchedule上面上线了几天的一个程序突然挂掉了，查看问题原因总是报连接超时。无从下手，然后查看代码发现是同事写的代码有一个分区的bug，改掉了但是运行到程序的一个关联操作的地方还是连接超时，然后直接就死掉了。感觉很诡异，因为程序运行了几天没问题，说明代码可能是没问题的，然后计算的数据是一年的数据，猜想可能是数据量的变化出现的问题。</p><h1 id="2-问题详情"><a href="#2-问题详情" class="headerlink" title="2 问题详情"></a>2 问题详情</h1><h2 id="2-1-报错信息"><a href="#2-1-报错信息" class="headerlink" title="2.1 报错信息"></a>2.1 报错信息</h2><ul><li>具体信息</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">Traceback (most recent call last):</span><br><span class="line">  File &quot;LineLossRate.py&quot;, line 237, in &lt;module&gt;</span><br><span class="line">    dfjoin.show(5)</span><br><span class="line">  File &quot;/opt/cloudera/parcels/SPARK2-2.4.0.cloudera2-1.cdh5.13.3.p0.1041012/lib/spark2/python/lib/pyspark.zip/pyspark/sql/dataframe.py&quot;, line 378, in show</span><br><span class="line">  File &quot;/opt/cloudera/parcels/SPARK2-2.4.0.cloudera2-1.cdh5.13.3.p0.1041012/lib/spark2/python/lib/py4j-0.10.7-src.zip/py4j/java_gateway.py&quot;, line 1257, in __call__</span><br><span class="line">  File &quot;/opt/cloudera/parcels/SPARK2-2.4.0.cloudera2-1.cdh5.13.3.p0.1041012/lib/spark2/python/lib/pyspark.zip/pyspark/sql/utils.py&quot;, line 63, in deco</span><br><span class="line">  File &quot;/opt/cloudera/parcels/SPARK2-2.4.0.cloudera2-1.cdh5.13.3.p0.1041012/lib/spark2/python/lib/py4j-0.10.7-src.zip/py4j/protocol.py&quot;, line 328, in get_return_value</span><br><span class="line">py4j.protocol.Py4JJavaError: An error occurred while calling o409.showString.</span><br><span class="line">: org.apache.spark.SparkException: Could not execute broadcast in 1800 secs. You can increase the timeout for broadcasts via spark.sql.broadcastTimeout or disable broadcast join by setting spark.sql.autoBroadcastJoinThreshold to -1</span><br><span class="line">at org.apache.spark.sql.execution.exchange.BroadcastExchangeExec.doExecuteBroadcast(BroadcastExchangeExec.scala:150)</span><br><span class="line">at org.apache.spark.sql.execution.InputAdapter.doExecuteBroadcast(WholeStageCodegenExec.scala:387)</span><br><span class="line">at org.apache.spark.sql.execution.SparkPlan$$anonfun$executeBroadcast$1.apply(SparkPlan.scala:144)</span><br></pre></td></tr></table></figure><p>在其他节点上显示有一些重复连接超时的警告：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">21/07/02 19:13:42 WARN scheduler.TaskSetManager: Lost task 9.0 in stage 70.0 (TID 5771, cdh06.nari.com, executor 12): java.net.ConnectException: Connection timed out (Connection timed out)</span><br><span class="line">at java.net.PlainSocketImpl.socketConnect(Native Method)</span><br><span class="line">at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)</span><br><span class="line">at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)</span><br><span class="line">at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)</span><br><span class="line">at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)</span><br><span class="line">at java.net.Socket.connect(Socket.java:589)</span><br><span class="line">at java.net.Socket.connect(Socket.java:538)</span><br><span class="line">at java.net.Socket.&lt;init&gt;(Socket.java:434)</span><br><span class="line">at java.net.Socket.&lt;init&gt;(Socket.java:244)</span><br><span class="line">at org.apache.spark.api.python.PythonWorkerFact</span><br></pre></td></tr></table></figure><h2 id="2-2-报错代码"><a href="#2-2-报错代码" class="headerlink" title="2.2 报错代码"></a>2.2 报错代码</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">dfjoin=df365.join(resDf3,on=[&quot;xx&quot;]).\</span><br><span class="line">    withColumn(&quot;xx&quot;,F.lit(dateMyes)). \</span><br><span class="line">    withColumn(&quot;xx&quot;, F.expr(&quot;cast(`xx` as string)&quot;)).join(tg_id,on=[&quot;xx&quot;]).\</span><br><span class="line">    withColumnRenamed(&quot;xx&quot;,&quot;xx&quot;).\</span><br><span class="line">    select(&quot;xx&quot;,&quot;xx&quot;,&quot;xx&quot;&quot;xx&quot;&quot;xx&quot;&quot;xx&quot;&quot;xx&quot;).persist()</span><br><span class="line"></span><br><span class="line">dfjoin.show(5)</span><br><span class="line">dfjoin.coalesce(128).write.mode(&quot;append&quot;).partitionBy(&quot;XXX&quot;).format(&quot;parquet&quot;).saveAsTable(&quot;tableName&quot;)</span><br></pre></td></tr></table></figure><h1 id="3-问题刨析"><a href="#3-问题刨析" class="headerlink" title="3 问题刨析"></a>3 问题刨析</h1><p>从报错的信息中基本可以定位问题出现在数据join的位置，但是其中的两个dataframe，resDf3和df365数据量都在十几万也并不是很大。但是从这句话中可以看出也是join的问题，建议我们关闭BroadcastJoin。然后大致可以清楚了，BroadcastJoin是关联一个大表和一个小表的方法，但是这里关联的两个表数据量基本相同，如果使用BroadcastJoin的话就可能导致广播时间过长而连接超时，初步猜想问题的原因就在于此。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">You can increase the timeout for broadcasts via spark.sql.broadcastTimeout or disable broadcast join by setting spark.sql.autoBroadcastJoinThreshold to -1</span><br></pre></td></tr></table></figure><p>于是添加了spark.sql.autoBroadcastJoinThreshold参数为-1，程序正常运行，问题解决。</p><h1 id="4-扩展延伸：spark的三种join方式"><a href="#4-扩展延伸：spark的三种join方式" class="headerlink" title="4  扩展延伸：spark的三种join方式"></a>4  扩展延伸：spark的三种join方式</h1><p>文章参考链接：<a href="http://hbasefly.com/2017/03/19/sparksql-basic-join/">SparkSQL – 有必要坐下来聊聊Join – 有态度的HBase&#x2F;Spark&#x2F;BigData (hbasefly.com)</a></p><p>这里是对该文章内的信息转载并做一个简要的摘录：</p><p>当前SparkSQL支持三种Join算法－shuffle hash join、broadcast hash join以及sort merge join。其中前两者归根到底都属于hash join，只不过在hash join之前需要先shuffle还是先broadcast。</p><table><thead><tr><th>方法</th><th>所属类别</th><th>使用场景</th></tr></thead><tbody><tr><td>shuffle hash join</td><td>hash join</td><td>一张大表join一张小表</td></tr><tr><td>broadcast hash join</td><td>hash join</td><td>一张大表join一张极小表</td></tr><tr><td>sort merge join</td><td>merge join</td><td>两张大表join</td></tr></tbody></table><h2 id="4-1-Hash-Join"><a href="#4-1-Hash-Join" class="headerlink" title="4.1 Hash Join"></a>4.1 <strong>Hash Join</strong></h2><p>先来看看这样一条SQL语句：select * from order,item where item.id &#x3D; order.i_id，很简单一个Join节点，参与join的两张表是item和order，join key分别是item.id以及order.i_id。现在假设这个Join采用的是hash join算法，整个过程会经历三步：</p><ol><li><p>确定Build Table以及Probe Table：这个概念比较重要，Build Table使用join key构建Hash Table，而Probe Table使用join key进行探测，探测成功就可以join在一起。通常情况下，小表会作为Build Table，大表作为Probe Table。此事例中item为Build Table，order为Probe Table。</p></li><li><p>构建Hash Table：依次读取Build Table（item）的数据，对于每一行数据根据join key（item.id）进行hash，hash到对应的Bucket，生成hash table中的一条记录。数据缓存在内存中，如果内存放不下需要dump到外存。</p></li><li><p>探测：再依次扫描Probe Table（order）的数据，使用相同的hash函数映射Hash Table中的记录，映射成功之后再检查join条件（item.id &#x3D; order.i_id），如果匹配成功就可以将两者join在一起</p></li></ol><blockquote><p> 这里可能存在的疑问：</p><ol><li><p>hash join性能如何？hash join基本都只扫描两表一次，可以认为o(a+b)</p></li><li><p>为什么Build Table选择小表？因为构建的Hash Table最好能全部加载在内存，效率最高；这也决定了<strong>hash join算法只适合至少一个小表的join场景，对于两个大表的join场景并不适用</strong>；</p></li></ol></blockquote><h2 id="4-1-1-Broadcast-Hash-Join"><a href="#4-1-1-Broadcast-Hash-Join" class="headerlink" title="4.1.1 Broadcast Hash Join"></a>4.1.1 <strong>Broadcast Hash Join</strong></h2><p>Broadcast Hash Join一般分为两步：</p><ol><li><p>broadcast阶段：将小表广播分发到大表所在的所有主机。广播算法可以有很多，最简单的是先发给driver，driver再统一分发给所有executor；要不就是基于bittorrete的p2p思路；</p></li><li><p>hash join阶段：在每个executor上执行单机版hash join，小表映射，大表试探</p></li></ol><blockquote><p>SparkSQL规定broadcast hash join执行的基本条件为被广播小表必须小于参数spark.sql.autoBroadcastJoinThreshold，默认为10M</p></blockquote><h2 id="4-1-2-Shuffle-Hash-Join"><a href="#4-1-2-Shuffle-Hash-Join" class="headerlink" title="4.1.2 Shuffle Hash Join"></a>4.1.2 <strong>Shuffle Hash Join</strong></h2><p>在大数据条件下如果一张表很小，执行join操作最优的选择无疑是broadcast hash join，效率最高。但是一旦小表数据量增大，广播所需内存、带宽等资源必然就会太大，broadcast hash join就不再是最优方案。此时可以按照join key进行分区，根据key相同必然分区相同的原理，就可以将大表join分而治之，划分为很多小表的join，充分利用集群资源并行化。如下图所示，shuffle hash join也可以分为两步：</p><ol><li><p>shuffle阶段：分别将两个表按照join key进行分区，将相同join key的记录重分布到同一节点，两张表的数据会被重分布到集群中所有节点。这个过程称为shuffle</p></li><li><p>hash join阶段：每个分区节点上的数据单独执行单机hash join算法。</p></li></ol><h1 id="4-2-Sort-Merge-Join"><a href="#4-2-Sort-Merge-Join" class="headerlink" title="4.2 Sort-Merge Join"></a>4.2 <strong>Sort-Merge Join</strong></h1><p>sort merge join 一般分为三个步骤：</p><ol><li><p>shuffle阶段：将两张大表根据join key进行重新分区，两张表数据会分布到整个集群，以便分布式并行处理</p></li><li><p>sort阶段：对单个分区节点的两表数据，分别进行排序</p></li><li><p>merge阶段：对排好序的两张分区表数据执行join操作。join操作很简单，分别遍历两个有序序列，碰到相同join key就merge输出，否则取更小一边</p></li></ol><h1 id="5-再次回到问题"><a href="#5-再次回到问题" class="headerlink" title="5. 再次回到问题"></a>5. 再次回到问题</h1><p>从上面介绍的三种join的原理可以基本确定各自所对应的应用场景，这次遇到的问题就是由于spark默认使用了broadcast join方法，然后两个join的表的数据量基本相似，所以遇到了broadcast join乱广播导致程序超时的现象。所以手动禁止该方法解决了问题。</p>]]>
    </content>
    <id>https://studyflowblog.com/2021/07/08/spark/spark%E4%B8%89%E7%A7%8Djoin%E6%96%B9%E6%B3%95%E5%AF%B9%E6%AF%94%E5%8F%8A%E5%BA%94%E7%94%A8%E5%9C%BA%E6%99%AF/</id>
    <link href="https://studyflowblog.com/2021/07/08/spark/spark%E4%B8%89%E7%A7%8Djoin%E6%96%B9%E6%B3%95%E5%AF%B9%E6%AF%94%E5%8F%8A%E5%BA%94%E7%94%A8%E5%9C%BA%E6%99%AF/"/>
    <published>2021-07-08T00:22:48.000Z</published>
    <summary>从Spark广播超时报错出发，深入对比Broadcast Join、Shuffle Hash Join和Sort Merge Join的原理与适用场景</summary>
    <title>spark三种join方法对比及应用场景</title>
    <updated>2026-04-02T12:26:50.652Z</updated>
  </entry>
  <entry>
    <author>
      <name>Wednesday</name>
    </author>
    <category term="生活其他" scheme="https://studyflowblog.com/categories/%E7%94%9F%E6%B4%BB%E5%85%B6%E4%BB%96/"/>
    <category term="生活" scheme="https://studyflowblog.com/tags/%E7%94%9F%E6%B4%BB/"/>
    <category term="保险" scheme="https://studyflowblog.com/tags/%E4%BF%9D%E9%99%A9/"/>
    <content>
      <![CDATA[<h1 id="1-保险"><a href="#1-保险" class="headerlink" title="1. 保险"></a>1. 保险</h1><p>##1.1. 保险初步说明</p><p>###1.1.1 是什么</p><blockquote><p>一种对财务进行风险管理的工具</p><p>对风险进行转移或者降低</p></blockquote><h3 id="1-1-2-基本作用"><a href="#1-1-2-基本作用" class="headerlink" title="1.1.2 基本作用"></a>1.1.2 基本作用</h3><ul><li>风险来临时，帮助家庭度过难关，避免家庭陷入经济困境；</li><li>明确资金归属权的问题，明确资金的流向；</li><li>帮助我们做长期储蓄，实现养老和子女教育的问题。</li></ul><h2 id="1-2-医保"><a href="#1-2-医保" class="headerlink" title="1.2. 医保"></a>1.2. 医保</h2><p>社保，就是社会保险，<strong>属于社会福利性质的保险</strong>。它由政府主办，个人或单位负责交钱，政府承担保险公司的角色，财政给予补贴，并承担最终的保险责任。社保一共有 5 个项目，分别是<strong>养老保险、医疗保险、生育保险、工伤保险和失业保险</strong>，其中医疗保险就是我们常说的医保。而医保又分为职工医疗保险和城镇居民医疗保险两种。</p><p>###1.2.1 医保的优点</p><blockquote><p>医保门槛很低</p><p>广覆盖</p><p>价格实惠</p></blockquote><h3 id="1-2-2-医保的缺点"><a href="#1-2-2-医保的缺点" class="headerlink" title="1.2.2 医保的缺点"></a>1.2.2 医保的缺点</h3><blockquote><p>保障效果不足</p><p>受地域影响较大</p></blockquote><h2 id="1-3-普通人常见的几种商业保险"><a href="#1-3-普通人常见的几种商业保险" class="headerlink" title="1.3. 普通人常见的几种商业保险"></a>1.3. 普通人常见的几种商业保险</h2><h3 id="1-3-1-医疗险"><a href="#1-3-1-医疗险" class="headerlink" title="1.3.1 医疗险"></a>1.3.1 医疗险</h3><blockquote><p>弥补医保的不足，解决可能出现的高额医疗费用问题</p></blockquote><p>商业医疗险和社会医保之间的关系:<strong>两者关系的相互补充，而不是相互取代</strong></p><h3 id="1-3-2-重疾险"><a href="#1-3-2-重疾险" class="headerlink" title="1.3.2 重疾险"></a>1.3.2 重疾险</h3><blockquote><p>既能补充医疗费用，也能补偿因为大病导致的收入损失，贴补家用</p></blockquote><p>重疾险是「给付型」的保险，和当时购买时签订合同的保额有关，和生病花费多少无关。简单讲，保额是多少钱，生病了就赔多少钱。（打个比方生病花了10w，但是保额是20w，仍然是可以赔付20w）</p><p><strong>医疗险和重疾险组合</strong>，降低了理赔门槛，保障也更加全面，可以帮我们更好的对抗疾病风险</p><h3 id="1-3-3-意外险"><a href="#1-3-3-意外险" class="headerlink" title="1.3.3 意外险"></a>1.3.3 意外险</h3><blockquote><p>报销由意外带来的医疗，对意外导致的残疾和身故进行补偿；</p></blockquote><p>意外风险是由外因导致的，比如交通事故、磕伤碰伤、摔伤烫伤等等。不同的事故，导致的结果也不一样，有的可能只是皮外伤，也有可能出现骨折、留下残疾，甚至失去生命。</p><p>与之对应，意外险的主要保障内容有三类：<strong>意外医疗、意外身故和意外伤残</strong></p><h3 id="1-3-4-寿险"><a href="#1-3-4-寿险" class="headerlink" title="1.3.4 寿险"></a>1.3.4 寿险</h3><blockquote><p>补偿因身故导致的收入损失，避免家庭因经济支柱去世而陷入经济困难之中。</p></blockquote><p>它是一种以「身故」为赔付标准的保险，既包括疾病身故，也包括意外身故。</p><p>寿险可以让我们活着的时候是一台印钞机，倒下也是一堆人民币</p><h1 id="2-商业保险具体介绍"><a href="#2-商业保险具体介绍" class="headerlink" title="2. 商业保险具体介绍"></a>2. 商业保险具体介绍</h1><p>从省保费的角度看，应该先买重疾险，因为越早买便宜得越多；从决策难度的角度看，应该先买意外险，因为保费最便宜。如果一定要排个顺序，那我会建议大家先买医疗险中的<strong>百万医疗险</strong>。</p><p>##2.1 百万医疗险</p><h3 id="2-1-1-优点"><a href="#2-1-1-优点" class="headerlink" title="2.1.1 优点"></a>2.1.1 优点</h3><blockquote><p> 用到可能性大，保额高，价格便宜，决策简单。</p></blockquote><h3 id="2-1-2-缺点"><a href="#2-1-2-缺点" class="headerlink" title="2.1.2 缺点"></a>2.1.2 缺点</h3><blockquote><p>管大不管小（针对大病作用很大，但小病作用却很小）</p><p>续保不确定性大</p><p>无法补偿医疗费用之外的财务损失</p></blockquote><h3 id="2-1-3-总结"><a href="#2-1-3-总结" class="headerlink" title="2.1.3 总结"></a>2.1.3 总结</h3><ul><li>普通人选择医疗险的三个关键点：保额高、不限社保范围、价格能负担得起。 </li><li>百万医疗险的优点：高保额，都在 100 万以上；不限社保，100% 赔付，自费药进口药都能报销；价格便宜，年轻人只要两三百元一年，性价比很高。 </li><li>百万医疗险的不足：免赔额比较高；无法提供终身保障；无法补偿医疗费用意外的开销。 </li><li>购买医疗险时，注意仔细阅读健康告知和免责条款。</li></ul><h2 id="2-2-重疾险"><a href="#2-2-重疾险" class="headerlink" title="2.2 重疾险"></a>2.2 重疾险</h2><p>重疾险是给付型保险，而医疗险是补偿型保险。</p><p>**给付型，满足条件就给钱。<strong>只跟当初</strong>「保多少」<strong>有关，跟</strong>「治病花多少」**没关系</p><p>**补偿型，<strong>要看</strong>实际的花销，<strong>跟</strong>「保多少，花多少，花在哪儿了」**都有关系</p><h3 id="2-2-1-优点"><a href="#2-2-1-优点" class="headerlink" title="2.2.1 优点"></a>2.2.1 优点</h3><blockquote><p>产品较稳定</p><p>达到理赔标准，一次性赔付一笔钱，不限制用途；</p><p> 能够提供终身保障，不用担心保障中断；</p></blockquote><h3 id="2-2-2-缺点"><a href="#2-2-2-缺点" class="headerlink" title="2.2.2 缺点"></a>2.2.2 缺点</h3><blockquote><p>相比百万医疗险，理赔门槛较高。 </p><p>配置产品时，最好把百万医疗险和重疾险搭配起来，既能降低理赔门槛，也能加强特定高发重疾的保障。</p></blockquote><h2 id="2-3-重疾险的分类"><a href="#2-3-重疾险的分类" class="headerlink" title="2.3 重疾险的分类"></a>2.3 重疾险的分类</h2><ul><li>按保障时间长短，分定期重疾和终身重疾； </li><li>按赔付次数，分为单次重疾和多次重疾； </li><li>按疾病，分为分组重疾和不分组重疾.</li></ul><p>价格上： </p><ol><li>不分组的多次重疾要比分组的多次重疾更贵； </li><li>多次重疾比单次重疾更贵； </li><li>终身的重疾险也要比定期的重疾险更贵。</li></ol><h3 id="2-3-1-如何选择适合自己的重疾险"><a href="#2-3-1-如何选择适合自己的重疾险" class="headerlink" title="2.3.1 如何选择适合自己的重疾险"></a>2.3.1 如何选择适合自己的重疾险</h3><h4 id="2-3-1-1-第一步：确定预算"><a href="#2-3-1-1-第一步：确定预算" class="headerlink" title="2.3.1.1 第一步：确定预算"></a>2.3.1.1 第一步：确定预算</h4><blockquote><p>相对具有普适性的建议是：一个家庭，配置保障类产品的费用，也就是「重疾险 + 寿险 + 意外险 + 医疗险」这几类产品，每年的总保费占年收入的 5% 到 10% 是比较合理的，如果超过 10%，可能会影响日常生活的质量，缴费压力大一些；但如果低于 5%，保额、保障的全面性就势必受到影响。</p><p>而所有预算中，重疾险的保费会占到总预算的 70%-80%。</p></blockquote><h4 id="2-3-1-2-第二步：确定保额"><a href="#2-3-1-2-第二步：确定保额" class="headerlink" title="2.3.1.2 第二步：确定保额"></a>2.3.1.2 第二步：确定保额</h4><blockquote><p>考虑到目前的生活水平和医疗成本，重疾保额要达到 <strong>30-50 万</strong>，才会有明显的意义。如果是经济发达地区，收入和支出都较高、医疗费用水平也较高，保额目标做到 <strong>100 万以上</strong>也是合理的。</p><p>但是保额未必是一步到位的，可以后续根据自己能力逐渐补充提高。</p></blockquote><p>####2.3.1.3 第三步：选择产品类型</p><blockquote><p>在预算和保额的约束下，选择产品类型，实际上就是在保障责任的长度与深度之间做出取舍。长度也就是<strong>保障期限</strong>的长短，深度则是<strong>保障责任</strong>的多少，需要考虑的选择包含以下几项：</p><ul><li>多次重疾还是单次重疾？</li><li>如果是多次重疾，分组还是不分组？</li><li>终身重疾还是定期重疾？</li><li>是否要包含身故责任？</li></ul></blockquote><h4 id="2-3-1-4-第四步：选择具体产品"><a href="#2-3-1-4-第四步：选择具体产品" class="headerlink" title="2.3.1.4 第四步：选择具体产品"></a>2.3.1.4 第四步：选择具体产品</h4><blockquote><p>先把符合条件的产品的健康告知都认真地看一遍，需要智能核保的也都做一下得出核保结论，看哪些能保得上，哪些保不上，进一步缩小选择范围。</p><p>其次，看一些细节的区别，比如等待期的长短、中症和轻症的赔付比例、免责条款的范围、等等</p></blockquote><p>两个细节地方：</p><blockquote><p><strong>第一是，轻症和中症</strong>。在大多数重疾险中，轻症、中症都是和重疾绑定在一起的，不需要额外选择；个别产品是分开的，需要自己选择。我个人的建议是，<strong>尽量把轻症、中症都加上</strong>，毕竟它们能降低重疾险的理赔门槛，从保障的角度来说，是相当实用的。</p><p>**另一个是保费豁免。**在规定的缴费期内，如果达到某些特定情况，比如轻症、中症、重疾、全残或者身故，后续保费就不用再交了，保险合同依然有效，这相当于给自己要交的保费又买了一份保险。如果豁免的对象是被保险人，就叫做被保人豁免；如果针对的是投保人，那就叫投保人豁免。</p></blockquote><h4 id="2-3-1-5-可选：自己做产品组合"><a href="#2-3-1-5-可选：自己做产品组合" class="headerlink" title="2.3.1.5 可选：自己做产品组合"></a>2.3.1.5 可选：自己做产品组合</h4><blockquote><p>如果单一产品满足不了我们的需求，或者在不同产品之间过于纠结难以选择，也可以做组合。</p></blockquote><h2 id="2-4-低价格高保障的意外险"><a href="#2-4-低价格高保障的意外险" class="headerlink" title="2.4 低价格高保障的意外险"></a>2.4 低价格高保障的意外险</h2><h3 id="2-4-0-是什么"><a href="#2-4-0-是什么" class="headerlink" title="2.4.0 是什么"></a>2.4.0 是什么</h3><p>在意外险中，「意外」二字有专门的定义，它指的是外来的、突发的、非本意的、非疾病的使身体受到伤害的客观事件，并且这个客观事件是导致被保险人受伤或身故的直接原因、以及单独原因</p><h3 id="2-4-1-意外险的常见保障有哪些？"><a href="#2-4-1-意外险的常见保障有哪些？" class="headerlink" title="2.4.1 意外险的常见保障有哪些？"></a>2.4.1 意外险的常见保障有哪些？</h3><p>生活中的意外，按照严重程度的不同，导致的结果一般有 3 种：</p><ol><li>直接身故；</li><li>造成伤残，比如失明、肢体缺失；</li><li>身体损伤，需要进行治疗。</li></ol><p>与这 3 种情况相对应，意外险中最常见的保障责任也是3种，分别是意外身故责任、意外伤残责任和意外医疗责任。</p><h4 id="2-4-1-1-意外身故责任"><a href="#2-4-1-1-意外身故责任" class="headerlink" title="2.4.1.1 意外身故责任"></a>2.4.1.1 <strong>意外身故责任</strong></h4><p>这是意外险中最基础、最常见的保障，比较简单，如果被保险人因意外身故，意外险就会一次性赔付保额。和重疾险一样，意外身故也是给付型的，如果有多款意外险，意外身故责任可以叠加理赔。</p><h4 id="2-4-1-2-意外伤残责任"><a href="#2-4-1-2-意外伤残责任" class="headerlink" title="2.4.1.2 意外伤残责任"></a>2.4.1.2 意外伤残责任</h4><p>可以能提供完整分级的伤残保障，其他保险，要么不含伤残，要么只含比较严重的全残，覆盖范围都不如意外险。</p><p>根据《人身保险伤残评定标准》，意外伤残分为十级，以意外身故责任保额为基准，其中一级最重，可以赔付 100% 的保额，十级最轻，可以赔付 10% 的保额，中间每隔一级，赔付比例就相差 10%。</p><h4 id="2-4-1-3-意外医疗责任"><a href="#2-4-1-3-意外医疗责任" class="headerlink" title="2.4.1.3 意外医疗责任"></a>2.4.1.3 意外医疗责任</h4><p>意外医疗，也是一种医疗险，报销就医产生的费用，只不过相比普通的医疗险，它<strong>只保意外伤害带来的费用</strong>，疾病引起的医疗是不理赔的。</p><h3 id="2-4-2-意外险的常见分类"><a href="#2-4-2-意外险的常见分类" class="headerlink" title="2.4.2 意外险的常见分类"></a>2.4.2 意外险的常见分类</h3><p>根据事故原因的不同，意外险又可以分为综合意外险和专项意外险，并且两者可以叠加理赔，并不冲突。</p><h4 id="2-4-2-1-综合意外险"><a href="#2-4-2-1-综合意外险" class="headerlink" title="2.4.2.1 综合意外险"></a>2.4.2.1 综合意外险</h4><p>什么意外都能保的意外险，除了免责条款里的内容，不会特别限制意外的原因。</p><h4 id="2-4-2-2-专项意外险"><a href="#2-4-2-2-专项意外险" class="headerlink" title="2.4.2.2 专项意外险"></a>2.4.2.2 专项意外险</h4><p>只针对某种特定类型的意外提供保障。</p><p>比如普通的综合意外险，自驾发生事故是在理赔范围内的。但公共交通意外险，只有乘坐飞机、轮船、汽车、火车等公共交通工具，才能享受意外保障；范围再窄些，航空意外险，只有乘坐民营航班，才能享受意外保障。</p><h3 id="2-4-3-意外险的特点"><a href="#2-4-3-意外险的特点" class="headerlink" title="2.4.3 意外险的特点"></a>2.4.3 意外险的特点</h3><ol><li>保费低</li><li>杠杆高（低保费高赔付）</li><li>无等待期，即投即生效</li><li>无健康告知或少健康告知，相对十分宽松</li><li>根据职业存在风险分类，不同的职业选择不同的保险种类</li></ol><h2 id="2-5-寿险"><a href="#2-5-寿险" class="headerlink" title="2.5 寿险"></a>2.5 寿险</h2><p>寿险是指以「身故」为主要保障责任的保险，可以降低因被保险人去世而给家庭带来的财务缺口。分为终身寿险和定期寿险，终身寿险可以保障终身，什么时候都可以理赔。而定期寿险只保障一段时间，合同到期终止既不再理赔。前者适合普通家庭，后者适合高净值家庭。（一般定期寿险和终身寿险相差价格可达十倍左右）</p><h3 id="2-5-1-特点"><a href="#2-5-1-特点" class="headerlink" title="2.5.1 特点"></a>2.5.1 特点</h3><p>健康告知宽松，高保额，杠杆高，免责条款少</p><h2 id="2-6-慧民保"><a href="#2-6-慧民保" class="headerlink" title="2.6 慧民保"></a>2.6 慧民保</h2><p>当地政府和保险公司合作的另一种商业保险，价格便宜覆盖广，门槛低（低配版百万医疗险）</p><h3 id="2-6-1-优点"><a href="#2-6-1-优点" class="headerlink" title="2.6.1 优点"></a>2.6.1 优点</h3><ul><li>投保年龄范围大</li><li>职业限制少</li><li>价格便宜</li><li>既往症可以赔</li><li>几乎无健康告知</li></ul><h3 id="2-6-2-缺点"><a href="#2-6-2-缺点" class="headerlink" title="2.6.2 缺点"></a>2.6.2 缺点</h3><ul><li>免赔额高</li><li>保障范围&#x2F;力度不够</li><li>可持续不确定</li><li>必须购买当地医保</li></ul><h3 id="2-6-3-适合谁"><a href="#2-6-3-适合谁" class="headerlink" title="2.6.3 适合谁"></a>2.6.3 适合谁</h3><ul><li>买不到百万医疗险</li><li>有既往症</li><li>追求全面保障</li></ul><h2 id="2-7-团体险"><a href="#2-7-团体险" class="headerlink" title="2.7 团体险"></a>2.7 团体险</h2><p>一种企业为员工购买的保险，并不像社保一样要求企业为员工强制购买。</p><p>对于个人来说只能作为个人保险的补充，并不能当作个人保险配置的主体。</p><p>保额可能不足并且该保险与企业相绑定。</p><h2 id="2-8-相互宝"><a href="#2-8-相互宝" class="headerlink" title="2.8 相互宝"></a>2.8 相互宝</h2><p>个人感觉就像是非公类型的医保，每个人交钱放在一个池子里，然后生病了可以提交申请从里面拿出部分治疗费用。（一人生病众人分摊的概念）</p><p>优点：方便便宜，如果没有购买主体保险，可买入相护宝抵御部分风险。</p><p>缺点：依靠某些企业办理相护宝，可持续性存在隐患。并且丰富性较差，理赔纠纷仍存在灰色地带</p>]]>
    </content>
    <id>https://studyflowblog.com/2021/06/29/life/insurance/</id>
    <link href="https://studyflowblog.com/2021/06/29/life/insurance/"/>
    <published>2021-06-29T01:28:22.000Z</published>
    <summary>保险基础知识入门，梳理医保、重疾险、定期寿险等险种的作用、选购逻辑及社保与商业保险的搭配建议</summary>
    <title>保险相关基础知识</title>
    <updated>2026-04-02T12:26:54.473Z</updated>
  </entry>
  <entry>
    <author>
      <name>Wednesday</name>
    </author>
    <category term="生活其他" scheme="https://studyflowblog.com/categories/%E7%94%9F%E6%B4%BB%E5%85%B6%E4%BB%96/"/>
    <category term="生活" scheme="https://studyflowblog.com/tags/%E7%94%9F%E6%B4%BB/"/>
    <category term="维权" scheme="https://studyflowblog.com/tags/%E7%BB%B4%E6%9D%83/"/>
    <content>
      <![CDATA[<h1 id="美团吃到虫子并维权成功全记录"><a href="#美团吃到虫子并维权成功全记录" class="headerlink" title="美团吃到虫子并维权成功全记录"></a>美团吃到虫子并维权成功全记录</h1><h2 id="赔偿依据法律"><a href="#赔偿依据法律" class="headerlink" title="赔偿依据法律"></a>赔偿依据法律</h2><p>“根据《食品安全法》第一百四十八条第二款的规定，“生产不符合食品安全标准的食品或者经营明知是不符合食品安全标准的食品，消费者除要求赔偿损失外，还可以向生产者或者经营者要求支付价款十倍或者损失三倍的赔偿金；增加赔偿的金额不足一千元的，为一千元。”</p><h2 id="2021-5-23-17-47-事情发生"><a href="#2021-5-23-17-47-事情发生" class="headerlink" title="2021&#x2F;5&#x2F;23 17:47 事情发生"></a>2021&#x2F;5&#x2F;23 17:47 事情发生</h2><ul><li><p>美团点外卖吃到虫子</p></li><li><p>发生时间： 约2021年5月23日18点</p></li><li><p>店铺商家名称及地址：</p></li><li><ul><li>（美团）XXX大食堂</li></ul></li><li><ul><li>略</li></ul></li><li><p>食品已拍照并已留存</p><img src="/2021/05/25/life/rights-protection-record/%E5%BC%82%E7%89%A93.jpg" class=""><img src="/2021/05/25/life/rights-protection-record/%E5%BC%82%E7%89%A92.jpg" class=""></li></ul><p>##2021&#x2F;5&#x2F;23 17:56 尝试初步解决未果</p><blockquote><p>和商家协商希望<strong>五倍</strong>赔偿，商家表示对索要赔偿表示吃惊，说只退款。关于赔偿的事情<strong>很轻蔑的表示</strong>让我去申请美团索赔。</p></blockquote><p>但是在知乎上看了一些朋友和我遇到相同的问题但是在美团上申请了“放心吃”索赔，结果都是不太理想，为了避免不必要的麻烦遂决定绕过美团直接上12315，准备今晚先在12315app上提交投诉，然后明天打电话在投诉一遍。</p><h2 id="网友推荐解决方法（当无法和商家协商赔偿时）"><a href="#网友推荐解决方法（当无法和商家协商赔偿时）" class="headerlink" title="网友推荐解决方法（当无法和商家协商赔偿时）"></a>网友推荐解决方法（当无法和商家协商赔偿时）</h2><ul><li><p>联系当地工商局一起去找商家。</p></li><li><p>拨打+当地区号拨打12315，或者下载12315APP进行投诉，个人觉得打电话可能会快点，周末好像打不通，需要工作日拨打</p></li></ul><h2 id="2021-5-23-19-04-因为明天要上班所以想今天解决于是尝试联系美团客服"><a href="#2021-5-23-19-04-因为明天要上班所以想今天解决于是尝试联系美团客服" class="headerlink" title="2021&#x2F;5&#x2F;23 19:04 因为明天要上班所以想今天解决于是尝试联系美团客服"></a>2021&#x2F;5&#x2F;23 19:04 因为明天要上班所以想今天解决于是尝试联系美团客服</h2><p>网络人工客服排队99+，电话人工客服打了两次未接通人工客服。</p><p>于是选择等待app端的人工客服，大概等了十分钟终于有客服开始和我沟通了。</p><h2 id="2021-5-23-19-33-和app端人工客服开始沟通"><a href="#2021-5-23-19-33-和app端人工客服开始沟通" class="headerlink" title="2021&#x2F;5&#x2F;23 19:33 和app端人工客服开始沟通"></a>2021&#x2F;5&#x2F;23 19:33 和app端人工客服开始沟通</h2><img src="/2021/05/25/life/rights-protection-record/%E8%81%94%E7%B3%BB%E5%AE%A2%E6%9C%8D-1.jpg" class=""><h2 id="2021-5-23-19-40-客服沟通完表示退款并返10元红包，表示不接受。"><a href="#2021-5-23-19-40-客服沟通完表示退款并返10元红包，表示不接受。" class="headerlink" title="2021&#x2F;5&#x2F;23 19:40 客服沟通完表示退款并返10元红包，表示不接受。"></a>2021&#x2F;5&#x2F;23 19:40 客服沟通完表示退款并返10元红包，表示不接受。</h2><img src="/2021/05/25/life/rights-protection-record/10%E5%85%83.jpg" class=""><ul><li>客服让我稍等并咨询管理解决。</li></ul><h2 id="2021-5-23-19-44-客服表示给我一张30并且可提现的充值卡，表示拒绝"><a href="#2021-5-23-19-44-客服表示给我一张30并且可提现的充值卡，表示拒绝" class="headerlink" title="2021&#x2F;5&#x2F;23 19:44 客服表示给我一张30并且可提现的充值卡，表示拒绝"></a>2021&#x2F;5&#x2F;23 19:44 客服表示给我一张30并且可提现的充值卡，表示拒绝</h2><ul><li>表示30也拒绝后客服提升到了50元，搁这跟我<strong>钓鱼</strong>呢</li></ul><img src="/2021/05/25/life/rights-protection-record/30%E5%85%83.jpg" class=""><h2 id="2021-5-23-19-46-客服表示给我一张50并且可提现的充值卡，表示拒绝。将转交专员客服处理，预计24小时解决"><a href="#2021-5-23-19-46-客服表示给我一张50并且可提现的充值卡，表示拒绝。将转交专员客服处理，预计24小时解决" class="headerlink" title="2021&#x2F;5&#x2F;23 19:46 客服表示给我一张50并且可提现的充值卡，表示拒绝。将转交专员客服处理，预计24小时解决"></a>2021&#x2F;5&#x2F;23 19:46 客服表示给我一张50并且可提现的充值卡，表示拒绝。将转交专员客服处理，预计24小时解决</h2><img src="/2021/05/25/life/rights-protection-record/50%E5%85%83.jpg" class=""><p>客服说我的问题需要提交到专员处理并一个工作日内回电，我表示明天如果不解决周二直接提到12315上。等待明天的解决方案。</p><img src="/2021/05/25/life/rights-protection-record/%E7%94%B3%E8%AF%B7%E4%B8%93%E5%91%98%E5%A4%84%E7%90%86.jpg" class=""><h2 id="2021-5-24-一天无事发生，未得到美团专员客服联系"><a href="#2021-5-24-一天无事发生，未得到美团专员客服联系" class="headerlink" title="2021&#x2F;5&#x2F;24 一天无事发生，未得到美团专员客服联系"></a>2021&#x2F;5&#x2F;24 一天无事发生，未得到美团专员客服联系</h2><h2 id="2021-5-24-22-20-12315-APP进行投诉"><a href="#2021-5-24-22-20-12315-APP进行投诉" class="headerlink" title="2021&#x2F;5&#x2F;24 22:20 12315 APP进行投诉"></a>2021&#x2F;5&#x2F;24 22:20 12315 APP进行投诉</h2><ul><li>投诉内容（详细时间，地点，事情经过，基本诉求）</li></ul><p>本人于2021年5月23日17:12分在尚食客大食堂定了一份外卖（包含一份炒豆芽，一份鸡块和一份米饭），外卖于17:50分左右送达并打开准备食用，发现鸡块上粘着异物（初步判断为苍蝇），个人认为改商家“生产了不符合食品安全标准的食品”遂进行投诉：个人诉求为：希望商家对我的商品进行退款并对我支付价款十倍的赔偿金，商品订单实付价格为12.76元，但是由于使用了5元的会员红包且该红包为本人花钱购买，即总共实际付款为17.76，商家应退款17.76元并应赔偿177.6元。</p><img src="/2021/05/25/life/rights-protection-record/%E6%8A%95%E8%AF%89.png" class=""><img src="/2021/05/25/life/rights-protection-record/%E6%8A%95%E8%AF%892.png" class=""><h2 id="2021-5-25-13-44-美团客服处理状态更新显示未打通我的电话，但是并未接到电话"><a href="#2021-5-25-13-44-美团客服处理状态更新显示未打通我的电话，但是并未接到电话" class="headerlink" title="2021&#x2F;5&#x2F;25 13:44 美团客服处理状态更新显示未打通我的电话，但是并未接到电话"></a>2021&#x2F;5&#x2F;25 13:44 美团客服处理状态更新显示未打通我的电话，但是并未接到电话</h2><img src="/2021/05/25/life/rights-protection-record/%E4%B8%93%E5%91%98%E7%94%B5%E8%AF%9D%E6%9C%AA%E5%91%BC%E9%80%9A.jpg" class=""><h2 id="2021-5-25-14-54-接到专员客服电话，客服表示已对该店铺进行监管并对我退款进行十倍支付款赔偿"><a href="#2021-5-25-14-54-接到专员客服电话，客服表示已对该店铺进行监管并对我退款进行十倍支付款赔偿" class="headerlink" title="2021&#x2F;5&#x2F;25 14:54 接到专员客服电话，客服表示已对该店铺进行监管并对我退款进行十倍支付款赔偿"></a>2021&#x2F;5&#x2F;25 14:54 接到专员客服电话，客服表示已对该店铺进行监管并对我退款进行十倍支付款赔偿</h2><ul><li>本来算的实付款应该为17.76的，其中包含一张会员红包，但是这个会员红包是我花了3.3开的，所以一张会员券并不值什么钱，所以实付就算12.76算了。赔偿金额为127.6元。</li></ul><img src="/2021/05/25/life/rights-protection-record/%E8%B5%94%E5%81%BF%E8%AE%B0%E5%BD%95.jpg" class=""><h2 id="2021-5-25-14-55-问题解决"><a href="#2021-5-25-14-55-问题解决" class="headerlink" title="2021&#x2F;5&#x2F;25 14:55 问题解决"></a>2021&#x2F;5&#x2F;25 14:55 问题解决</h2><h2 id="2021-5-25-14-56-撤销12315投诉"><a href="#2021-5-25-14-56-撤销12315投诉" class="headerlink" title="2021&#x2F;5&#x2F;25 14:56 撤销12315投诉"></a>2021&#x2F;5&#x2F;25 14:56 撤销12315投诉</h2>]]>
    </content>
    <id>https://studyflowblog.com/2021/05/25/life/rights-protection-record/</id>
    <link href="https://studyflowblog.com/2021/05/25/life/rights-protection-record/"/>
    <published>2021-05-25T01:32:24.000Z</published>
    <summary>外卖吃到异物后的完整维权流程记录，依据《食品安全法》第148条成功索赔的全过程</summary>
    <title>美团吃到虫子并维权成功全记录</title>
    <updated>2026-04-02T12:51:19.972Z</updated>
  </entry>
  <entry>
    <author>
      <name>Wednesday</name>
    </author>
    <category term="工作" scheme="https://studyflowblog.com/categories/%E5%B7%A5%E4%BD%9C/"/>
    <category term="spark" scheme="https://studyflowblog.com/tags/spark/"/>
    <content>
      <![CDATA[<h1 id="1-基础概念"><a href="#1-基础概念" class="headerlink" title="1 基础概念"></a>1 基础概念</h1><h2 id="1-1-核心组件"><a href="#1-1-核心组件" class="headerlink" title="1.1 核心组件"></a>1.1 核心组件</h2><ul><li>Driver</li></ul><blockquote><p>将用户程序转化成任务（job）</p><p>在Executor之间执行任务调度</p><p>跟踪Executor的执行情况</p><p>通过UI站视查询运行情况</p></blockquote><ul><li>Executor：</li></ul><blockquote><p>执行任务并返回给Driver</p><p>通过自身的Block Manager为用户程序中需要缓存的RDD提供内存式存储。RDD是直接存储在Executor中的进程内的，因此任务在运行时可以充分利用缓存数据加速运算。</p></blockquote><ul><li>Master</li></ul><blockquote><p> 资源调度和分配，集群的监控</p></blockquote><ul><li>Worker</li></ul><blockquote><p>由Master分配任务并进行计算</p></blockquote><ul><li>ApplicantsMaster</li></ul><blockquote><p>对Driver与Master的直接通信进行解耦合，Driver通过ApplicantsMaster与Master进行通信，监控任务的执行等任务</p></blockquote><h2 id="1-2-核心概念"><a href="#1-2-核心概念" class="headerlink" title="1.2 核心概念"></a>1.2 核心概念</h2><ul><li>Executor和Core</li></ul><blockquote><p>Executor为提供计算的节点个数，Core为核数</p></blockquote><ul><li>并行度</li></ul><blockquote><p>整个集群并行执行任务的数量</p></blockquote><h2 id="1-3-其他相关"><a href="#1-3-其他相关" class="headerlink" title="1.3 其他相关"></a>1.3 其他相关</h2><ul><li><strong>序列化</strong>（serialization）在<a href="https://zh.wikipedia.org/wiki/%E8%A8%88%E7%AE%97%E6%A9%9F%E7%A7%91%E5%AD%B8">计算机科学</a>的资料处理中，是指将<a href="https://zh.wikipedia.org/wiki/%E8%B3%87%E6%96%99%E7%B5%90%E6%A7%8B">数据结构</a>或对象状态转换成可取用格式（例如存成文件，存于缓冲，或经由网络中发送），以留待后续在相同或另一台计算机环境中，能恢复原先状态的过程。</li></ul><h1 id="2-RDD"><a href="#2-RDD" class="headerlink" title="2 RDD"></a>2 RDD</h1><p>RDD数据处理方式：类似IO流，包含装饰器设计模式。</p><p>通过每一个操作方法的输入与输出来确定数据的格式。</p><h2 id="2-1-RDD的创建"><a href="#2-1-RDD的创建" class="headerlink" title="2.1 RDD的创建"></a>2.1 RDD的创建</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">//TODO 通过内存中创建</span><br><span class="line">val ints: Seq[Int] = Seq(1, 2, 3, 4)</span><br><span class="line">//val value: RDD[Int] = sc.parallelize(ints)</span><br><span class="line">//makeRdd方法就是parallelize方法的包装</span><br><span class="line">val value: RDD[Int] = sc.makeRDD(ints)</span><br><span class="line"></span><br><span class="line">//TODO 通过文件创建rdd</span><br><span class="line">//1. 通过文件路径创建</span><br><span class="line">val value: RDD[String] = sc.textFile(&quot;datas/1.txt&quot;)</span><br><span class="line">//2. 通过文件夹创建</span><br><span class="line">//val value: RDD[String] = sc.textFile(&quot;datas.txt&quot;)</span><br><span class="line">//3. 通过文件路径通配符创建</span><br><span class="line">//val value: RDD[String] = sc.textFile(&quot;datas/1*.txt&quot;)</span><br><span class="line">//4. 通过hdfs文件系统路径创建</span><br><span class="line">//val value: RDD[String] = sc.textFile(&quot;hdfs://linux1:8080/tmp/file.txt&quot;)     </span><br></pre></td></tr></table></figure><h2 id="2-1-RDD方法-RDD算子"><a href="#2-1-RDD方法-RDD算子" class="headerlink" title="2.1 RDD方法&#x3D;&gt;RDD算子"></a>2.1 RDD方法&#x3D;&gt;RDD算子</h2><ul><li>转换方法</li></ul><p>功能的转换和封装，将旧的RDD包装成新的RDD(flatMap,map)</p><ul><li>行动方法（操作方法）</li></ul><p>触发任务的调度和作业的执行(collect)</p><p>惰性执行方法</p><h3 id="2-1-1-RDD转换算子"><a href="#2-1-1-RDD转换算子" class="headerlink" title="2.1.1 RDD转换算子"></a>2.1.1 RDD转换算子</h3><h4 id="2-1-1-1-Map"><a href="#2-1-1-1-Map" class="headerlink" title="2.1.1.1 Map"></a>2.1.1.1 Map</h4><ul><li>Map方法</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">scala&gt; val rdd = sc.makeRDD(List(1,2,3))</span><br><span class="line">rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[0] at makeRDD at &lt;console&gt;:24</span><br><span class="line">scala&gt; rdd.map&#123;(v)=&gt;(v*2)&#125;.collect()</span><br><span class="line">scala&gt; rdd.map((num:Int)=&gt;&#123;num*2&#125;).collect()</span><br><span class="line">scala&gt; rdd.map((num:Int)=&gt;(num*2)).collect()</span><br><span class="line">scala&gt; rdd.map((num:Int)=&gt;num*2).collect()</span><br><span class="line"># 最简单的方法</span><br><span class="line">scala&gt; rdd.map&#123;_*2&#125;.collect()</span><br><span class="line">scala&gt; rdd.map(_*2).collect()</span><br><span class="line">res: Array[Int] = Array(2, 4, 6)</span><br></pre></td></tr></table></figure><ul><li>Map的并行计算顺序</li><li><ul><li>单个分区内的数据是一个一个执行的，只有前面一个数据的所有逻辑都执行完才能进行下一个数据的执行逻辑，分区内数据的执行是有序的</li><li>而多个分区的数据计算是无序的</li></ul></li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"># 单个分区</span><br><span class="line">val value: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4),**1**)</span><br><span class="line">  val mapRDD: RDD[Int] = value.map(</span><br><span class="line">    num =&gt; &#123;</span><br><span class="line">      println(&quot;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&quot; + num)</span><br><span class="line">      num</span><br><span class="line">    &#125;</span><br><span class="line">  )</span><br><span class="line">  val mapRDD1: RDD[Int] = mapRDD.map(</span><br><span class="line">    num =&gt; &#123;</span><br><span class="line">      println(&quot;@@@@@@@@&quot; + num)</span><br><span class="line">      num</span><br><span class="line">    &#125;</span><br><span class="line">  )</span><br><span class="line">  mapRDD1.collect()</span><br><span class="line">  # 输出结果</span><br><span class="line">  &gt;&gt;&gt;&gt;&gt;&gt;&gt;1</span><br><span class="line">  @@@@@@@@1</span><br><span class="line">  &gt;&gt;&gt;&gt;&gt;&gt;&gt;2</span><br><span class="line">  @@@@@@@@2</span><br><span class="line">  &gt;&gt;&gt;&gt;&gt;&gt;&gt;3</span><br><span class="line">  @@@@@@@@3</span><br><span class="line">  &gt;&gt;&gt;&gt;&gt;&gt;&gt;4</span><br><span class="line">  @@@@@@@@4</span><br><span class="line">  # 多个分区</span><br><span class="line">  val value: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4),2)</span><br><span class="line">  。。。。。。。</span><br><span class="line">  &gt;&gt;&gt;&gt;&gt;&gt;&gt;3</span><br><span class="line">  @@@@@@@@3</span><br><span class="line">  &gt;&gt;&gt;&gt;&gt;&gt;&gt;4</span><br><span class="line">  @@@@@@@@4</span><br><span class="line">  &gt;&gt;&gt;&gt;&gt;&gt;&gt;1</span><br><span class="line">  @@@@@@@@1</span><br><span class="line">  &gt;&gt;&gt;&gt;&gt;&gt;&gt;2</span><br><span class="line">  @@@@@@@@2</span><br></pre></td></tr></table></figure><h4 id="2-1-1-2-MapPratitions"><a href="#2-1-1-2-MapPratitions" class="headerlink" title="2.1.1.2 MapPratitions"></a>2.1.1.2 MapPratitions</h4><p>MapPratitions是对分区的数据进行操作</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"># 求分区的最大值</span><br><span class="line">val value: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4),2)</span><br><span class="line"># mapPartitions传入的是迭代器，传出的也是迭代器</span><br><span class="line">   val value1: RDD[Int] = value.mapPartitions (</span><br><span class="line">     iter =&gt; List(iter.max).iterator</span><br><span class="line">   )</span><br><span class="line">   value1.collect().foreach(println)</span><br><span class="line">   # 输出</span><br><span class="line">   2</span><br><span class="line">   4</span><br></pre></td></tr></table></figure><h4 id="2-1-1-3-Map和Mappartitions的对比"><a href="#2-1-1-3-Map和Mappartitions的对比" class="headerlink" title="2.1.1.3 Map和Mappartitions的对比"></a>2.1.1.3 Map和Mappartitions的对比</h4><p>map是对所有的数据串行进行操作，输入与输出个数相同</p><p>而mappartitions是分块对分区内的数据进行操作，输入与输出数据个书可以不同。</p><p>map速度慢，占用内存较少</p><p>mappartitions速度快，会长时间占用内存。</p><h4 id="2-1-1-4-glom-方法"><a href="#2-1-1-4-glom-方法" class="headerlink" title="2.1.1.4 glom()方法"></a>2.1.1.4 glom()方法</h4><p>将同一个分区的数据直接转换为相同类型的内存数组进行处理，分区不变</p><h4 id="2-1-1-5-groupby"><a href="#2-1-1-5-groupby" class="headerlink" title="2.1.1.5 groupby()"></a>2.1.1.5 groupby()</h4><p>groupby方法会将数据打乱并重新组合，也称之为shuffle</p><p>分区数不变</p><h4 id="2-1-1-6-coalsce-numPartitions-num；shuffle-boolen"><a href="#2-1-1-6-coalsce-numPartitions-num；shuffle-boolen" class="headerlink" title="2.1.1.6 coalsce(numPartitions&#x3D;num；shuffle&#x3D;boolen)"></a>2.1.1.6 coalsce(numPartitions&#x3D;num；shuffle&#x3D;boolen)</h4><p>当大批量数据经过处理之后，需要<strong>缩小</strong>分区数可以使用coalsce()方法修改分区数。分区数修改后，分区内的数据不会被coalsce方法打散重新组合（并且同一个分区的数据也不会被分开）。</p><p>随意使用coalsce方法可能会产生数据倾斜，如果希望使数据均衡，可以使用shuffle（使shuffle&#x3D;True）。</p><p>如果需要<strong>扩大分区</strong>的话使用repartition，它是coalsce的上层实现（coalsce(numPartitions&#x3D;bignum；shuffle&#x3D;True)）</p><h4 id="2-1-1-7-groupByKey和reduceByKey的区别"><a href="#2-1-1-7-groupByKey和reduceByKey的区别" class="headerlink" title="2.1.1.7 groupByKey和reduceByKey的区别"></a>2.1.1.7 groupByKey和reduceByKey的区别</h4><ul><li>groupByKey只分组，如果需要聚合需使用map</li><li>reduceByKey包含分组和聚合两个功能</li><li>如果只需要分组则只能使用groupByKey</li></ul><p><strong>groupByKey</strong>导致数据打乱重组，数据处理的时候存在shuffle操作，数据量大的话可能会导致数据倾斜然后内存溢出。</p><p>所以在spark中shuffle操作必须落盘处理（保存在硬盘中），不能在内存中进行等待。所以shuffle操作的性能十分低。</p><p><strong>reduceByKey</strong>可以预先在分区内将相同key的数据进行聚合，然后进行落盘再shuffle。<strong>提升了很大的性能</strong></p><p>而groupByKey只能先打乱所有的数据再落盘聚合。</p><h4 id="2-1-1-8-foreach"><a href="#2-1-1-8-foreach" class="headerlink" title="2.1.1.8 foreach"></a>2.1.1.8 foreach</h4><p>foreach是在每个exector上执行的，而collect()算子是将所有的分区数据集合在一起在执行的</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">val rdd: RDD[Int] = sc.makeRDD(List(1,2,3,4),2)</span><br><span class="line">// 收集后打印</span><br><span class="line">rdd.map(num=&gt;num).collect().foreach(println)</span><br><span class="line">&gt; 1 2 3 4</span><br><span class="line">// 分布式打印</span><br><span class="line">rdd.foreach(println)</span><br><span class="line">&gt; 3 4 1 2 </span><br></pre></td></tr></table></figure><h4 id="2-1-1-9-宽依赖和窄依赖"><a href="#2-1-1-9-宽依赖和窄依赖" class="headerlink" title="2.1.1.9 宽依赖和窄依赖"></a>2.1.1.9 宽依赖和窄依赖</h4><p>宽依赖：父rdd对一个子rdd（一对一&#x2F;独生子女)</p><p>窄依赖：父rdd对多个子rdd（一对多&#x2F;多生子女）</p><h3 id="2-1-2-闭包检查"><a href="#2-1-2-闭包检查" class="headerlink" title="2.1.2 闭包检查"></a>2.1.2 闭包检查</h3><blockquote><p>闭包的理解：使用到了外部（超出其作用域）的变量的函数称为闭包。</p></blockquote><p>从计算的角度<strong>算子以外的代码都是在driver端运行，算子里的代码都是在excutor里面运行</strong>，在scala编程中经常会遇到算子内会用到算子外的数据，这样就形成了闭包的效果。如果内部需要用到外部的数据，则需要检查内部的对象能否序列化，这样的过程称为<strong>闭包检测</strong>。（如果算子内部的对象可以进行序列化，则可以对外部引用数据进行序列化传给excutor端进行计算）</p><h2 id="3-SparkSql"><a href="#3-SparkSql" class="headerlink" title="3 SparkSql"></a>3 SparkSql</h2><h3 id="3-1-DataSet和DataFrame"><a href="#3-1-DataSet和DataFrame" class="headerlink" title="3.1 DataSet和DataFrame"></a>3.1 DataSet和DataFrame</h3><p>强类型：DataSet（每一行的类型都可能不相同，当相同时就是DateFrame）</p><p>弱类型：DataFrame（每一行的类型都相同，由第一行标识出）</p>]]>
    </content>
    <id>https://studyflowblog.com/2021/03/03/spark/spark%E5%AD%A6%E4%B9%A0/</id>
    <link href="https://studyflowblog.com/2021/03/03/spark/spark%E5%AD%A6%E4%B9%A0/"/>
    <published>2021-03-03T06:31:58.000Z</published>
    <summary>Spark核心组件Driver、Executor、Master架构原理，RDD与DataFrame操作及任务调度机制学习笔记</summary>
    <title>Spark核心架构与RDD编程入门</title>
    <updated>2026-04-02T12:38:04.641Z</updated>
  </entry>
  <entry>
    <author>
      <name>Wednesday</name>
    </author>
    <category term="工作相关" scheme="https://studyflowblog.com/categories/%E5%B7%A5%E4%BD%9C%E7%9B%B8%E5%85%B3/"/>
    <category term="python" scheme="https://studyflowblog.com/tags/python/"/>
    <category term="pyspark" scheme="https://studyflowblog.com/tags/pyspark/"/>
    <content>
      <![CDATA[<h1 id="0-需求说明"><a href="#0-需求说明" class="headerlink" title="0. 需求说明"></a>0. 需求说明</h1><p>需求是对变压器的负荷进行预测，每一个变压器当作一个独立的个体。需要训练的个体数过多，所以尝试使用pyspark进行训练。原始数据保存在hbase中，程序的整体设计流程为：从hbase中读取数据 –&gt; 数据预处理 –&gt; 使用pyspark训练模型并进行预测 </p><h2 id="1-读取hbase"><a href="#1-读取hbase" class="headerlink" title="1. 读取hbase"></a>1. 读取hbase</h2><blockquote><p>因为是在自己的本地windows进行测试，电脑内存较小就没上虚拟机了。</p></blockquote><h3 id="1-1-pyspark读取hbase需要配置相关包"><a href="#1-1-pyspark读取hbase需要配置相关包" class="headerlink" title="1.1 pyspark读取hbase需要配置相关包"></a>1.1 pyspark读取hbase需要配置相关包</h3><p>将HBase的lib目录下的如下包复制到spark的目录下（因为放在其他目录中需要添加该目录的环境变量，所以为了省事就全部放在spark的默认jar目录中了，在spark启动的时候会全部加载进去，在<a href="http://127.0.0.1:4040/environment/%E6%9C%80%E4%B8%8B%E9%9D%A2%E7%9A%84**Resource**%E4%B8%AD%E5%8F%AF%E4%BB%A5%E6%9F%A5%E7%9C%8B%E5%88%B0%EF%BC%89">http://127.0.0.1:4040/environment/最下面的**Resource**中可以查看到）</a></p><ul><li><p>所有hbase开头的jar文件</p></li><li><p>guava-12.0.1.jar</p></li><li><p>htrace-core-3.1.0-incubating.jar</p></li><li><p>protobuf-java-2.5.0.jar</p></li><li><p><a href="https://mvnrepository.com/artifact/org.apache.spark/spark-examples_2.11/1.6.0-typesafe-001">spark-example-1.6.0.jar</a>（把hbase的数据转换python可读取的jar包）</p></li><li><p><strong>metrics-core-2.2.0.jar</strong></p></li></ul><h3 id="1-2-读取数据"><a href="#1-2-读取数据" class="headerlink" title="1.2 读取数据"></a>1.2 读取数据</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 在pyspark中打开</span></span><br><span class="line">host = <span class="string">&#x27;172.16.221.102&#x27;</span></span><br><span class="line">table = <span class="string">&#x27;PSSC:HT_E_MP_CURVE_RELA&#x27;</span></span><br><span class="line">conf = &#123;<span class="string">&quot;hbase.zookeeper.quorum&quot;</span>: host, <span class="string">&quot;hbase.mapreduce.inputtable&quot;</span>: table&#125;</span><br><span class="line">keyConv = <span class="string">&quot;org.apache.spark.examples.pythonconverters.ImmutableBytesWritableToStringConverter&quot;</span></span><br><span class="line">valueConv = <span class="string">&quot;org.apache.spark.examples.pythonconverters.HBaseResultToStringConverter&quot;</span></span><br><span class="line">hbase_rdd = sc.newAPIHadoopRDD(<span class="string">&quot;org.apache.hadoop.hbase.mapreduce.TableInputFormat&quot;</span>,<span class="string">&quot;org.apache.hadoop.hbase.io.ImmutableBytesWritable&quot;</span>,<span class="string">&quot;org.apache.hadoop.hbase.client.Result&quot;</span>,keyConverter=keyConv,valueConverter=valueConv,conf=conf)</span><br><span class="line">count = hbase_rdd.count()</span><br><span class="line">hbase_rdd.cache()</span><br><span class="line">output = hbase_rdd.collect()</span><br><span class="line"><span class="keyword">for</span> (k, v) <span class="keyword">in</span> output:</span><br><span class="line">        <span class="built_in">print</span> (k, v)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 显示部分结果如下</span></span><br><span class="line">&#123;<span class="string">&quot;qualifier&quot;</span> : <span class="string">&quot;IC85&quot;</span>, <span class="string">&quot;timestamp&quot;</span> : <span class="string">&quot;1614130918336&quot;</span>, <span class="string">&quot;columnFamily&quot;</span> : <span class="string">&quot;C&quot;</span>, <span class="string">&quot;row&quot;</span> : <span class="string">&quot;20201115&quot;</span>, <span class="string">&quot;type&quot;</span> : <span class="string">&quot;Put&quot;</span>, <span class="string">&quot;value&quot;</span> : <span class="string">&quot;0.0000&quot;</span>&#125;</span><br><span class="line">&#123;<span class="string">&quot;qualifier&quot;</span> : <span class="string">&quot;IC86&quot;</span>, <span class="string">&quot;timestamp&quot;</span> : <span class="string">&quot;1614130918336&quot;</span>, <span class="string">&quot;columnFamily&quot;</span> : <span class="string">&quot;C&quot;</span>, <span class="string">&quot;row&quot;</span> : <span class="string">&quot;20201115&quot;</span>, <span class="string">&quot;type&quot;</span> : <span class="string">&quot;Put&quot;</span>, <span class="string">&quot;value&quot;</span> : <span class="string">&quot;0.0000&quot;</span>&#125;</span><br><span class="line">&#123;<span class="string">&quot;qualifier&quot;</span> : <span class="string">&quot;IC87&quot;</span>, <span class="string">&quot;timestamp&quot;</span> : <span class="string">&quot;1614130918336&quot;</span>, <span class="string">&quot;columnFamily&quot;</span> : <span class="string">&quot;C&quot;</span>, <span class="string">&quot;row&quot;</span> : <span class="string">&quot;20201115&quot;</span>, <span class="string">&quot;type&quot;</span> : <span class="string">&quot;Put&quot;</span>, <span class="string">&quot;value&quot;</span> : <span class="string">&quot;0.0000&quot;</span>&#125;</span><br><span class="line">&#123;<span class="string">&quot;qualifier&quot;</span> : <span class="string">&quot;IC88&quot;</span>, <span class="string">&quot;timestamp&quot;</span> : <span class="string">&quot;1614130918336&quot;</span>, <span class="string">&quot;columnFamily&quot;</span> : <span class="string">&quot;C&quot;</span>, <span class="string">&quot;row&quot;</span> : <span class="string">&quot;20201115&quot;</span>, <span class="string">&quot;type&quot;</span> : <span class="string">&quot;Put&quot;</span>, <span class="string">&quot;value&quot;</span> : <span class="string">&quot;0.0000&quot;</span>&#125;</span><br><span class="line">&#123;<span class="string">&quot;qualifier&quot;</span> : <span class="string">&quot;IC89&quot;</span>, <span class="string">&quot;timestamp&quot;</span> : <span class="string">&quot;1614130918336&quot;</span>, <span class="string">&quot;columnFamily&quot;</span> : <span class="string">&quot;C&quot;</span>, <span class="string">&quot;row&quot;</span> : <span class="string">&quot;20201115&quot;</span>, <span class="string">&quot;type&quot;</span> : <span class="string">&quot;Put&quot;</span>, <span class="string">&quot;value&quot;</span> : <span class="string">&quot;0.0000&quot;</span>&#125;</span><br></pre></td></tr></table></figure><h3 id="1-3-遇到问题"><a href="#1-3-遇到问题" class="headerlink" title="1.3 遇到问题"></a>1.3 遇到问题</h3><h3 id="1-3-1-java-io-IOException-com-google-protobuf-ServiceException-java-lang-NoClassDefFoundError"><a href="#1-3-1-java-io-IOException-com-google-protobuf-ServiceException-java-lang-NoClassDefFoundError" class="headerlink" title="1.3.1 java.io.IOException: com.google.protobuf.ServiceException: java.lang.NoClassDefFoundError:"></a>1.3.1 java.io.IOException: com.google.protobuf.ServiceException: java.lang.NoClassDefFoundError:</h3><p>遇到问题要自己思考出现问题的原因是什么，以及找到问题的解决方法之后要思考底层是如何运行的，为什么这个方法能够解决这个问题。</p><ul><li>问题复现：</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"># 包放好后启动报如下错误（第一部分）</span><br><span class="line">An error occurred while calling z:org.apache.spark.api.python.PythonRDD.newAPIHadoopRDD.</span><br><span class="line">: org.apache.hadoop.hbase.client.RetriesExhaustedException: Failed after attempts=32, exceptions:</span><br><span class="line">Mon Mar 01 14:28:51 CST 2021, null, java.net.SocketTimeoutException: callTimeout=60000, callDuration=68422: row &#x27;PSSC:HT_E_MP_CURVE_RELA,,00000000000000&#x27; on table &#x27;hbase:meta&#x27; at region=hbase:meta,,1.1588230740, hostname=worker01,60020,1613617191977, seqNum=0</span><br><span class="line">......</span><br><span class="line"># 并且下面会有这样一句：（第二部分）</span><br><span class="line">Caused by: java.io.IOException: com.google.protobuf.ServiceException: java.lang.NoClassDefFoundError: com/yammer/metrics/core/Gauge</span><br></pre></td></tr></table></figure><ul><li>解决：</li></ul><p>在google了很多之后以上第一部分的问题之后，都是在说一些hosts映射的解决方法。但是我的hosts是没有问题的，启动之前就已经添加过主机名的映射了，未解决我的问题。</p><p>然后仔细将所有的报错提示从头到尾看了一遍，发现了上面第二部分的问题，然后在想是不是因为第二部分的问题导致了第一部分的错误，然后去网上搜该方法，显示”com&#x2F;yammer&#x2F;metrics&#x2F;core&#x2F;Gauge”是<strong>metrics-core-2.2.0.jar</strong>包里面的方法，然后在hbase的lib文件夹下面找到了该包，于是导入后遂解决了问题。</p><h3 id="1-3-2-pyspark默认版本的问题"><a href="#1-3-2-pyspark默认版本的问题" class="headerlink" title="1.3.2 pyspark默认版本的问题"></a>1.3.2 pyspark默认版本的问题</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Exception: Python in worker has different version 3.5 than that in driver 3.6, PySpark cannot run with different minor versions.Please check environment variables PYSPARK_PYTHON and PYSPARK_DRIVER_PYTHON are correctly set.</span><br></pre></td></tr></table></figure><ul><li><p>问题解析：</p><p>因为自己的的电脑上有三个版本的python（3.5&#x2F;3.6&#x2F;3.8），出现这个问题是因为环境变量冲突，默认的环境变量是anaconda的3.5，然后在pycharm中使用的python版本为3.6，所以产生了冲突。只需要在spark的配置文件中指定某一环境变量即可    </p></li><li><p>解决方法：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"># 在spark-3.0.2-bin-hadoop3.2\conf\spark-env.sh中添加环境变量</span><br><span class="line"># 如果不存在的话将spark-env.sh.template修改为spark-env.sh并添加</span><br><span class="line">export PYSPARK_PYTHON=D:\Anaconda3\envs\python36</span><br><span class="line">export PYSPARK_DRIVER_PYTHON=D:\Anaconda3\envs\python36</span><br><span class="line">export SPARK_HOME=D:\spark-3.0.2-bin-hadoop3.2</span><br></pre></td></tr></table></figure></li></ul><p>##2. 大批量跑模型</p>]]>
    </content>
    <id>https://studyflowblog.com/2021/03/01/python/pyspark%E8%AF%BB%E5%8F%96hbase%E8%B7%91%E6%A8%A1%E5%9E%8B%E5%B0%9D%E8%AF%95/</id>
    <link href="https://studyflowblog.com/2021/03/01/python/pyspark%E8%AF%BB%E5%8F%96hbase%E8%B7%91%E6%A8%A1%E5%9E%8B%E5%B0%9D%E8%AF%95/"/>
    <published>2021-03-01T00:58:59.000Z</published>
    <summary>记录使用PySpark读取HBase数据、进行预处理并训练变压器负荷预测模型的完整流程及环境配置踩坑</summary>
    <title>PySpark读取HBase数据训练预测模型实践</title>
    <updated>2026-04-02T12:38:07.523Z</updated>
  </entry>
  <entry>
    <author>
      <name>Wednesday</name>
    </author>
    <category term="编程语言特性相关" scheme="https://studyflowblog.com/categories/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%E7%89%B9%E6%80%A7%E7%9B%B8%E5%85%B3/"/>
    <category term="python" scheme="https://studyflowblog.com/tags/python/"/>
    <content>
      <![CDATA[<h1 id="1-线程池ThreadPoolExecutor"><a href="#1-线程池ThreadPoolExecutor" class="headerlink" title="1.线程池ThreadPoolExecutor"></a>1.线程池ThreadPoolExecutor</h1><p><a href="https://www.jianshu.com/p/120b61aa4cee">https://www.jianshu.com/p/120b61aa4cee</a></p><h1 id="2-装饰器"><a href="#2-装饰器" class="headerlink" title="2. 装饰器"></a>2. 装饰器</h1><p><strong>装饰器本质上是一个 Python 函数或类，它可以让其他函数或类在不需要做任何代码修改的前提下增加额外功能，装饰器的返回值也是一个函数&#x2F;类对象。它经常用于有切面需求的场景，比如：插入日志、性能测试、事务处理、缓存、权限校验等场景，装饰器是解决这类问题的绝佳设计。有了装饰器，我们就可以抽离出大量与函数功能本身无关的雷同代码到装饰器中并继续重用。概括的讲，装饰器的作用就是为已经存在的对象添加额外的功能</strong>。<br>先来看一个简单例子，如果你要对多个函数进行统计运行时间，不使用装饰器会是这样的：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">from time import time, sleep</span><br><span class="line"></span><br><span class="line">def fun_one():</span><br><span class="line">    start = time()</span><br><span class="line">    sleep(1)</span><br><span class="line">    end = time()</span><br><span class="line">    cost_time = end - start</span><br><span class="line">    print(&quot;func one run time &#123;&#125;&quot;.format(cost_time))</span><br><span class="line">    </span><br><span class="line">def fun_two():</span><br><span class="line">    start = time()</span><br><span class="line">    sleep(1)</span><br><span class="line">    end = time()</span><br><span class="line">    cost_time = end - start</span><br><span class="line">    print(&quot;func two run time &#123;&#125;&quot;.format(cost_time))</span><br></pre></td></tr></table></figure><p>在每个函数里都需要获取开始时间start、结束时间end、计算耗费时间cost_time、加上一个输出语句。<br>使用装饰器的方法是这样的</p><p>简单装饰器</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">def run_time(func):</span><br><span class="line">    def wrapper():</span><br><span class="line">        start = time()</span><br><span class="line">        func()                  # **函数在这里运行**</span><br><span class="line">        end = time()</span><br><span class="line">        cost_time = end - start</span><br><span class="line">        print(&quot;func three run time &#123;&#125;&quot;.format(cost_time))</span><br><span class="line">    return wrapper</span><br><span class="line"></span><br><span class="line">@run_time</span><br><span class="line">def fun_one():</span><br><span class="line">    sleep(1)</span><br><span class="line">    </span><br><span class="line">@run_time</span><br><span class="line">def fun_two():</span><br><span class="line">    sleep(1)</span><br></pre></td></tr></table></figure><h1 id="3-anconda升级python版本"><a href="#3-anconda升级python版本" class="headerlink" title="3.anconda升级python版本"></a>3.anconda升级python版本</h1><ul><li>先查看本地镜像源，清华镜像源从2019年已经停用了，建议使用中科大的镜像源</li><li>然后直接命令指定升级python版本即可，如果不升级镜像源的话可能报404或者下载速度慢</li></ul><h2 id="3-1-修改镜像源"><a href="#3-1-修改镜像源" class="headerlink" title="3.1 修改镜像源"></a>3.1 修改镜像源</h2><p>先查看已经安装过的镜像源，cmd窗口执行命令：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">conda config --show</span><br></pre></td></tr></table></figure><p>查看配置项channels，如果显示带有tsinghua，则说明已安装过清华镜像。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">channels:</span><br><span class="line">- https://mirrors.tuna.tsinghua.edu.cn/tensorflow/linux/cpu/</span><br><span class="line">- https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/msys2/</span><br><span class="line">- https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/</span><br><span class="line">- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/</span><br><span class="line">- https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/</span><br></pre></td></tr></table></figure><p>下一步，使用<code>conda config --remove channels url地址</code>删除清华镜像，如下命令删除第一个。然后，依次删除所有镜像源</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">conda config --remove channels https://mirrors.tuna.tsinghua.edu.cn/tensorflow/linux/cpu/</span><br></pre></td></tr></table></figure><p>添加目前可用的中科大镜像源：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">conda config --add channels https://mirrors.ustc.edu.cn/anaconda/pkgs/free/</span><br></pre></td></tr></table></figure><p>并设置搜索时显示通道地址：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">conda config --set show_channel_urls yes</span><br></pre></td></tr></table></figure><p>确认是否安装镜像源成功，执行<code>conda config --show</code>，找到<code>channels</code>值为如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">channels:</span><br><span class="line">  - https://mirrors.ustc.edu.cn/anaconda/pkgs/free/</span><br><span class="line">  - defaults</span><br></pre></td></tr></table></figure><h2 id="3-2-升级python版本"><a href="#3-2-升级python版本" class="headerlink" title="3.2 升级python版本"></a>3.2 升级python版本</h2><p>打开Anaconda Prompt，<br>输入</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">conda create -n python36 python=3.6 anaconda</span><br></pre></td></tr></table></figure><h1 id="4-文件io"><a href="#4-文件io" class="headerlink" title="4.文件io"></a>4.文件io</h1><h2 id="4-1-文件打开方式，r-w-a-的说明"><a href="#4-1-文件打开方式，r-w-a-的说明" class="headerlink" title="4.1 文件打开方式，r&#x2F;w&#x2F;a&#x2F;+的说明"></a>4.1 文件打开方式，r&#x2F;w&#x2F;a&#x2F;+的说明</h2><ul><li>r只读，r+读写(覆盖写)，不创建</li><li>w新建只写，w+新建读写，二者都会将文件内容清零</li><li>w+与r+区别：</li><li><blockquote><p>r+：可读可写，若文件不存在，报错；w+: 可读可写，若文件不存在，创建</p></blockquote></li><li>a：附加写方式打开，不可读；</li><li>a+: 附加读写方式打开</li></ul><p>#5. 代码规范性测试</p><p>pylint代码规范测试</p><h1 id="6-fileinput-懒惰行迭代"><a href="#6-fileinput-懒惰行迭代" class="headerlink" title="6. fileinput 懒惰行迭代"></a>6. fileinput 懒惰行迭代</h1><p>只读取实际需要的文件部分</p><h1 id="7-编码规范"><a href="#7-编码规范" class="headerlink" title="7. 编码规范"></a>7. 编码规范</h1><ol><li>多次使用的常量使用全大写命名，并考虑设置为全局变量</li><li>创建单独的配置文件模块，保存一些配置的参数<blockquote><p>简单配置或者使用configparser模块</p></blockquote></li><li>在需要的时候使用日志记录（logging模块）</li></ol><h1 id="8-魔法方法理解"><a href="#8-魔法方法理解" class="headerlink" title="8.魔法方法理解"></a>8.魔法方法理解</h1><p>魔法方法是python内置方法，不需要主动调用，存在的目的是为了给python的解释器进行调用，几乎每个魔法方法都有一个对应的内置函数，或者运算符，当我们对这个对象使用这些函数或者运算符时就会调用类中的对应魔法方法，可以理解为重写内置函数</p><p>实际上, 当我们调用x &#x3D; SomeClass()的时候调用,__init__并不是第一个执行的, __new__才是。所以准确来说,是__new__和__init__共同构成了”构造函数”.</p><p><strong>new</strong>() 是一种负责创建类实例的静态方法，它无需使用 staticmethod 装饰器修饰，且该方法会优先 <strong>init</strong>() 初始化方法被调用</p><h1 id="9-正则表达式"><a href="#9-正则表达式" class="headerlink" title="9.正则表达式"></a>9.正则表达式</h1><p>##9.1 match()和search()比较</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">m = re.match(&#x27;foo&#x27;, &#x27;seafood&#x27;) </span><br><span class="line">m.group()</span><br><span class="line">&gt;&gt;&gt; None    # 匹配失败</span><br><span class="line"></span><br><span class="line">m = re.search(&#x27;foo&#x27;, &#x27;seafood&#x27;) </span><br><span class="line">m.group()</span><br><span class="line">&gt;&gt;&gt; &#x27;foo&#x27; # 搜索成功， 但是之前匹配失败</span><br></pre></td></tr></table></figure><p>match是从字符串的起始部分开始匹配模式，把字符串当作一个整体来匹配，而search表示字符串中任意位置出现符合匹配模式的字符串，都将其提取出来。<br>##9.2  中括号（[cr][23][dp][o2]）和或运算符（r2d2|c3po）的区别</p><ul><li>[cr][23][dp][o2]<blockquote><p>上述表示匹配由四个字符组成的类似‘ABCD’字符串，每一个字符匹配一个中括号内的两个字符中的一个</p></blockquote></li><li>r2d2|c3po<blockquote><p>上述表示匹配“r2d2”或“c3po”</p></blockquote></li></ul><p>##9.3 使用group()或groups()访问匹配组</p><p>pattern中每一个括号为一个匹配模式，返回的结果保存在group()中，而groups()可以查看所有的子组匹配结果（如果pattern中无括号分组模式则groups为空）。</p><blockquote><blockquote><p>m &#x3D; re.match(‘(\w\w\w)-(\d\d\d)’, ‘abc-123’)<br>m.group() # 完整匹配<br>‘abc-123’<br>m.group(1) # 子组 1<br>‘abc’<br>m.group(2) # 子组 2<br>‘123’<br>m.groups() # 全部子组<br>(‘abc’, ‘123’)</p></blockquote></blockquote><h2 id="9-4-findall和search方法的区别"><a href="#9-4-findall和search方法的区别" class="headerlink" title="9.4 findall和search方法的区别"></a>9.4 findall和search方法的区别</h2><p>findall()查询字符串中某个正则表达式模式全部的非重复出现情况，和search类似，但是findall总是返回一个列表，<strong>当匹配失败时列表为空；当匹配成功时返回所有成功的匹配部分</strong>。</p><h2 id="9-5-sub-和subn-，表示替换"><a href="#9-5-sub-和subn-，表示替换" class="headerlink" title="9.5 sub()和subn()，表示替换"></a>9.5 sub()和subn()，表示替换</h2><p>sub和subn基本一样，但是subn还返回了替换的次数。</p><h1 id="10-python多线程与多进程"><a href="#10-python多线程与多进程" class="headerlink" title="10 python多线程与多进程"></a>10 python多线程与多进程</h1><p>参考<a href="https://www.cnblogs.com/kaituorensheng/p/4465768.html">python进程池：multiprocessing.pool - jihite - 博客园 (cnblogs.com)</a></p><h2 id="10-1-总结"><a href="#10-1-总结" class="headerlink" title="10.1 总结"></a>10.1 总结</h2><p><strong>i&#x2F;o密集型任务用多线程，多任务计算使用多进程</strong></p><h2 id="10-2-使用进程池"><a href="#10-2-使用进程池" class="headerlink" title="10.2 使用进程池"></a>10.2 使用进程池</h2><h3 id="10-2-1-样例"><a href="#10-2-1-样例" class="headerlink" title="10.2.1 样例"></a>10.2.1 样例</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">#coding: utf-8</span><br><span class="line">import multiprocessing</span><br><span class="line">import time</span><br><span class="line"></span><br><span class="line">def func(msg):</span><br><span class="line">    print &quot;msg:&quot;, msg</span><br><span class="line">    time.sleep(3)</span><br><span class="line">    print &quot;end&quot;</span><br><span class="line"></span><br><span class="line">if __name__ == &quot;__main__&quot;:</span><br><span class="line">    pool = multiprocessing.Pool(processes = 3)</span><br><span class="line">    for i in xrange(4):</span><br><span class="line">        msg = &quot;hello %d&quot; %(i)</span><br><span class="line">        pool.apply_async(func, (msg, ))   #维持**同时**执行的进程总数为processes，当一个进程执行完毕后会添加新的进程进去（多通道）</span><br><span class="line">   #pool.apply(func, (msg, )) # 而使用apply方法的时候是通道阻塞的，一次只有一个进程在运行，当一个进程执行完毕后再添加新的进程（单通道）</span><br><span class="line">    print &quot;Mark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~&quot;</span><br><span class="line">    pool.close()</span><br><span class="line">    pool.join()   #调用join之前，先调用close函数，否则会出错。执行完close后不会有新的进程加入到pool,join函数等待所有子进程结束</span><br><span class="line">    print &quot;Sub-process(es) done.&quot;</span><br></pre></td></tr></table></figure><h3 id="10-2-2-结果："><a href="#10-2-2-结果：" class="headerlink" title="10.2.2 结果："></a>10.2.2 结果：</h3><p>apply_async方法，多通道</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">mMsg: hark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~ello 0</span><br><span class="line"> </span><br><span class="line">msg: hello 1</span><br><span class="line">msg: hello 2</span><br><span class="line">end</span><br><span class="line">msg: hello 3</span><br><span class="line">end</span><br><span class="line">end</span><br><span class="line">end</span><br><span class="line">Sub-process(es) done.</span><br></pre></td></tr></table></figure><p>apply方法，单通道</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">msg: hello 0</span><br><span class="line">end</span><br><span class="line">msg: hello 1</span><br><span class="line">end</span><br><span class="line">msg: hello 2</span><br><span class="line">end</span><br><span class="line">msg: hello 3</span><br><span class="line">end</span><br><span class="line">Mark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~</span><br><span class="line">Sub-process(es) done.　</span><br></pre></td></tr></table></figure><h1 id="11-python-集合set中-add与update的区别"><a href="#11-python-集合set中-add与update的区别" class="headerlink" title="11 python 集合set中 add与update的区别"></a>11 python 集合set中 add与update的区别</h1><p>集合set是一个无序不重复元素的集</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">set</span>([<span class="string">&#x27;hello&#x27;</span>,<span class="string">&#x27;hello&#x27;</span>,<span class="string">&#x27;hi&#x27;</span>])</span><br><span class="line"><span class="comment"># &#123;&#x27;hello&#x27;, &#x27;hi&#x27;&#125;</span></span><br><span class="line"><span class="built_in">set</span>(<span class="string">&#x27;hello hello hi&#x27;</span>)</span><br><span class="line"><span class="comment"># &#123;&#x27; &#x27;, &#x27;e&#x27;, &#x27;h&#x27;, &#x27;i&#x27;, &#x27;l&#x27;, &#x27;o&#x27;&#125;</span></span><br></pre></td></tr></table></figure><p><code>set.add()</code> 与<code>set.update()</code>的区别</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">myset1 = <span class="built_in">set</span>()</span><br><span class="line">myset1.add(<span class="string">&#x27;hello&#x27;</span>)</span><br><span class="line"><span class="comment">#&#123;&#x27;hello&#x27;&#125;</span></span><br><span class="line">myset1.update(<span class="string">&#x27;world&#x27;</span>)</span><br><span class="line"><span class="comment">#&#123;&#x27;d&#x27;, &#x27;hello&#x27;, &#x27;l&#x27;, &#x27;o&#x27;, &#x27;r&#x27;, &#x27;w&#x27;&#125;</span></span><br><span class="line"></span><br><span class="line">myset2 = <span class="built_in">set</span>()</span><br><span class="line">myset2.add(<span class="string">&#x27;123&#x27;</span>)</span><br><span class="line">myset2.update(<span class="string">&#x27;123&#x27;</span>)</span><br><span class="line"><span class="comment">#&#123;&#x27;1&#x27;, &#x27;123&#x27;, &#x27;2&#x27;, &#x27;3&#x27;&#125;</span></span><br></pre></td></tr></table></figure><h1 id="12-python离线环境迁移"><a href="#12-python离线环境迁移" class="headerlink" title="12. python离线环境迁移"></a>12. python离线环境迁移</h1><h2 id="12-1-依赖文件requirement-txt生成"><a href="#12-1-依赖文件requirement-txt生成" class="headerlink" title="12.1 依赖文件requirement.txt生成"></a>12.1 依赖文件requirement.txt生成</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"># 在当前目录生成该目录下项目所需的所有依赖文件：注意生成包之后需要手动核对一下版本最好，会有部分版本生成错误的问题</span><br><span class="line">pipreqs ./ --encoding=utf8</span><br><span class="line"># 与pip freeze的区别</span><br><span class="line"># freeze 是生成当前python环境的所有包</span><br></pre></td></tr></table></figure><h2 id="12-2-然后根据requirement-txt批量下载包文件"><a href="#12-2-然后根据requirement-txt批量下载包文件" class="headerlink" title="12.2 然后根据requirement.txt批量下载包文件"></a>12.2 然后根据requirement.txt批量下载包文件</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">pip wheel --wheel-dir=.pip wheel --wheel-dir=./packages -r requirements.txt</span><br><span class="line"># 最后一起迁移到离线环境中并进行安装</span><br><span class="line">#在新服务器的site-packages目录下执行：</span><br><span class="line">pip install --no-index --find-links=/xxx/xxx/packages -r /xxx/xxx/packages/requirements.txt</span><br></pre></td></tr></table></figure><h2 id="12-3-出现问题"><a href="#12-3-出现问题" class="headerlink" title="12.3 出现问题"></a>12.3 出现问题</h2><p>使用pipreqs生成的requirement.txt无法完整安装所有的包</p><p>使用<code>python -m pip freeze &gt; req.txt</code>生成依赖文件然后替换用该文件安装</p><h1 id="13-collection方法详解"><a href="#13-collection方法详解" class="headerlink" title="13. collection方法详解"></a>13. collection方法详解</h1><h2 id="13-1-命名元组-namedtuple"><a href="#13-1-命名元组-namedtuple" class="headerlink" title="13.1 命名元组 namedtuple"></a>13.1 命名元组 namedtuple</h2><p>使用namedtuple可以对元组中的每一个值进行命名</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"># 使用命名元组解析csv</span><br><span class="line">from collections import *</span><br><span class="line">import csv</span><br><span class="line"></span><br><span class="line">EmployeeRecord = namedtuple(&#x27;EmployeeRecord&#x27;,&#x27;name, age, title, department, paygrade&#x27;)</span><br><span class="line">for emp in map(EmployeeRecord._make,csv.reader(open(&quot;employee.csv&quot;,&quot;rb&quot;))):</span><br><span class="line">    print (emp.name,emp.title)</span><br></pre></td></tr></table></figure><h2 id="13-2-defaultdict"><a href="#13-2-defaultdict" class="headerlink" title="13.2 defaultdict"></a>13.2 defaultdict</h2><p>功能和dict差不多，区别是会对未存在的key值赋予一个默认值而不报异常</p><p>并且 defaultdict()的default_factory参数可以传入list，int，tuple等类型</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">&gt;&gt;&gt; from collections import *</span><br><span class="line">&gt;&gt;&gt; &gt;&gt;&gt; s = [(&#x27;yellow&#x27;,1),(&#x27;blue&#x27;,2),(&#x27;yellow&#x27;,3),(&#x27;blue&#x27;,4),(&#x27;red&#x27;,5)]</span><br><span class="line">&gt;&gt;&gt; d = defaultdict(list)</span><br><span class="line">&gt;&gt;&gt; for k,v in s: </span><br><span class="line">... d[k].append(v)</span><br><span class="line">&gt;&gt;&gt; d.items()</span><br><span class="line">[(&#x27;blue&#x27;, [2, 4]), (&#x27;red&#x27;, [5]), (&#x27;yellow&#x27;, [1, 3])]</span><br></pre></td></tr></table></figure><h2 id="13-3-Counter"><a href="#13-3-Counter" class="headerlink" title="13.3 Counter"></a>13.3 Counter</h2><p>counter可以支持方便、快速的计数</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&gt;&gt;&gt; from collections import *</span><br><span class="line"></span><br><span class="line">&gt;&gt;&gt; cnt = Counter(&#x27;aabbcc&#x27;)</span><br><span class="line">&gt;&gt;&gt; cnt</span><br><span class="line">Out[14]: Counter(&#123;&#x27;a&#x27;: 2, &#x27;b&#x27;: 2, &#x27;c&#x27;: 2&#125;)</span><br></pre></td></tr></table></figure><h2 id="13-4-deque"><a href="#13-4-deque" class="headerlink" title="13.4 deque"></a>13.4 deque</h2><p>deque是栈和队列的一种广义实现，deque是”double-end queue”的简称；deque支持线程安全、有效内存地以近似O(1)的性能在deque的两端插入和删除元素，尽管list也支持相似的操作，但是它主要在固定长度操作上的优化，从而在pop(0)和insert(0,v)（会改变数据的位置和大小）上有O(n)的时间复杂度。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">&gt;&gt;&gt; from collections import deque</span><br><span class="line">&gt;&gt;&gt; dq = deque(range(10), maxlen=10) </span><br><span class="line">&gt;&gt;&gt; dq</span><br><span class="line">deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)</span><br><span class="line"># rotate 将循环队列向右移三次</span><br><span class="line">&gt;&gt;&gt; dq.rotate(3) </span><br><span class="line">&gt;&gt;&gt; dq</span><br><span class="line">deque([7, 8, 9, 0, 1, 2, 3, 4, 5, 6], maxlen=10)</span><br></pre></td></tr></table></figure><h1 id="————其他—————————"><a href="#————其他—————————" class="headerlink" title="————其他—————————"></a>————其他—————————</h1><ul><li>mysql&#x2F;oracle</li></ul><blockquote><p>内连接是保证两个表中所有的行都要满足连接条件，而外连接则不然。在外连接中，某些不满条件的列也会显示出来，也就是说，只限制其中一个表的行，而不限制另一个表的行。分左连接、右连接、全连接三种。</p></blockquote><ul><li>Django&#x2F;Flask</li><li>git</li></ul><h1 id="14-多线程，多进程，协程"><a href="#14-多线程，多进程，协程" class="headerlink" title="14 多线程，多进程，协程"></a>14 多线程，多进程，协程</h1><p>程序是由多个进程组成的，然后每个进程可能有多个线程（线程更加底层）</p><p>而python由于GIL（全局解释器锁）的存在，使得多线程无法充分使用多核的优势</p><p>如果使用爬虫这种i&#x2F;o密集型程序，多线程操作还是很明显的。</p><h1 id="15-pandas操作"><a href="#15-pandas操作" class="headerlink" title="15 pandas操作"></a>15 pandas操作</h1><h1 id="16-scrapy相关"><a href="#16-scrapy相关" class="headerlink" title="16 scrapy相关"></a>16 scrapy相关</h1><h1 id="17-restful-api"><a href="#17-restful-api" class="headerlink" title="17 restful api"></a>17 restful api</h1><p>遵循 REST 架构规范的应用编程接口</p><blockquote><p>api：</p><p>客户端和服务器端信息的传递者</p></blockquote><blockquote><p>传输格式：</p><p>JSON（Javascript 对象表示法）、HTML、XLT、Python、PHP 或纯文本</p></blockquote>]]>
    </content>
    <id>https://studyflowblog.com/2021/01/18/python/python%E7%9B%B8%E5%85%B3/</id>
    <link href="https://studyflowblog.com/2021/01/18/python/python%E7%9B%B8%E5%85%B3/"/>
    <published>2021-01-18T05:27:47.000Z</published>
    <summary>Python实用特性整理，涵盖ThreadPoolExecutor线程池用法、装饰器原理及AOP切面编程的常见应用场景</summary>
    <title>Python实用特性：线程池与装饰器详解</title>
    <updated>2026-04-02T12:38:05.629Z</updated>
  </entry>
  <entry>
    <author>
      <name>Wednesday</name>
    </author>
    <category term="python" scheme="https://studyflowblog.com/categories/python/"/>
    <category term="python" scheme="https://studyflowblog.com/tags/python/"/>
    <content>
      <![CDATA[<p>#Python 下划线、双下划线</p><p>一般常见的下划线为以下几种：</p><ul><li>前置单下划线：<code>_var</code></li><li>后置单下划线：<code>var_</code></li><li>前置双下划线：<code>__var</code></li><li>前后双下划线：<code>__var__</code></li><li>单下划线：<code>_</code></li></ul><p>###1. 前置单下划线变量和单下划线方法的区别：</p><blockquote><p>前置单下划线表示在方法内部使用，单下划线变量可以从外部引用，而单下划线方法在使用通配符导入包时（from package import *）不可以，除非使用import package，然后使用”package._model”进行使用。</p></blockquote><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">class Test:</span><br><span class="line">    def __init__(self):</span><br><span class="line">        self.foo = 11</span><br><span class="line">        self._bar = 23</span><br><span class="line">&gt;&gt;&gt; t = Test()</span><br><span class="line">&gt;&gt;&gt; t.foo</span><br><span class="line">11</span><br><span class="line">&gt;&gt;&gt; t._bar</span><br><span class="line">23</span><br><span class="line"></span><br><span class="line"># my_module.py：</span><br><span class="line">def external_func():</span><br><span class="line">    return 23</span><br><span class="line">def _internal_func():</span><br><span class="line">    return 42</span><br><span class="line">&gt;&gt;&gt; from my_module import *</span><br><span class="line">&gt;&gt;&gt; external_func()</span><br><span class="line">23</span><br><span class="line">&gt;&gt;&gt; _internal_func()</span><br><span class="line">NameError: &quot;name &#x27;_internal_func&#x27; is not defined&quot;</span><br><span class="line"></span><br><span class="line">&gt;&gt;&gt; import my_module</span><br><span class="line">&gt;&gt;&gt; my_module.external_func()</span><br><span class="line">23</span><br><span class="line">&gt;&gt;&gt; my_module._internal_func()</span><br><span class="line">42</span><br></pre></td></tr></table></figure><h3 id="2-后置单下划线"><a href="#2-后置单下划线" class="headerlink" title="2. 后置单下划线"></a>2. 后置单下划线</h3><p>当需要使用的最合适的变量名与关键字所重复，可以使用后置单下划线来绕过命名冲突</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&gt;&gt;&gt; def make_object(name, class):</span><br><span class="line">SyntaxError: &quot;invalid syntax&quot;</span><br><span class="line"></span><br><span class="line">&gt;&gt;&gt; def make_object(name, class_):</span><br><span class="line">... pass</span><br></pre></td></tr></table></figure><h3 id="3-前置双下划线"><a href="#3-前置双下划线" class="headerlink" title="3. 前置双下划线"></a>3. 前置双下划线</h3><p>前置双下划线会被解释器进行命名改写。这个例子先声明<code>_MangledGlobal__mangled</code>为全局变量，然后在名为<code>MangledGlobal</code>的类环境中访问变量。由于名称改写，类中的<code>test()</code>方法仅用<code>__mangled</code>就能引用<code>_MangledGlobal__mangled</code>全局变量。<code>__mangled</code>以双下划线开头，因此Python解释器自动将名称扩展为<code>_MangledGlobal__mangled</code>。这表明名称改写不专门与类属性绑定，而是能够应用于类环境中所有以双下划线开头的名称</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">_MangledGlobal__mangled = 23</span><br><span class="line"></span><br><span class="line">class MangledGlobal:</span><br><span class="line">    def test(self):</span><br><span class="line">        return __mangled</span><br><span class="line"></span><br><span class="line">&gt;&gt;&gt; MangledGlobal().test()</span><br><span class="line">23</span><br></pre></td></tr></table></figure><p>###4. 单独单下划线</p><p>单下划线有时用作名称，来表示变量是临时的或无关紧要的</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">例如下面的循环中并不需要访问运行的索引，那么可以使用_来表示它只是一个临时值：</span><br><span class="line">&gt;&gt;&gt; for _ in range(32):</span><br><span class="line">...     print(&#x27;Hello, World.&#x27;)</span><br></pre></td></tr></table></figure><h3 id="5-总结"><a href="#5-总结" class="headerlink" title="5.总结"></a>5.总结</h3><ul><li><strong>前置单下划线</strong><code>_var</code>：命名约定，用来表示该名称仅在内部使用。一般对Python解释器没有特殊含义（通配符导入除外），只能作为对程序员的提示。</li><li><strong>后置单下划线</strong><code>var_</code>：命名约定，用于避免与Python关键字发生命名冲突。</li><li><strong>前置双下划线</strong><code>__var</code>：在类环境中使用时会触发名称改写，对Python解释器有特殊含义。</li><li><strong>前后双下划线</strong><code>__var__</code>：表示由Python语言定义的特殊方法。在自定义的属性中要避免使用这种命名方式。</li><li><strong>单下划线</strong><code>_</code>：有时用作临时或无意义变量的名称（“不关心”）。此外还能表示Python REPL会话中上一个表达式的结果。</li></ul>]]>
    </content>
    <id>https://studyflowblog.com/2020/12/23/python/python%E4%B8%AD%E6%96%B9%E6%B3%95%E5%8F%8A%E5%8F%98%E9%87%8F%E7%9A%84%E4%B8%8B%E5%88%92%E7%BA%BF%E5%8F%8C%E4%B8%8B%E5%88%92%E7%BA%BF%E7%AD%89%E5%90%AB%E4%B9%89/</id>
    <link href="https://studyflowblog.com/2020/12/23/python/python%E4%B8%AD%E6%96%B9%E6%B3%95%E5%8F%8A%E5%8F%98%E9%87%8F%E7%9A%84%E4%B8%8B%E5%88%92%E7%BA%BF%E5%8F%8C%E4%B8%8B%E5%88%92%E7%BA%BF%E7%AD%89%E5%90%AB%E4%B9%89/"/>
    <published>2020-12-23T07:02:35.000Z</published>
    <summary>详解Python中单下划线、双下划线、前后双下划线等五种命名约定的含义、访问控制行为与使用场景</summary>
    <title>python中方法及变量的下划线双下划线等含义</title>
    <updated>2026-04-02T12:26:46.046Z</updated>
  </entry>
  <entry>
    <author>
      <name>Wednesday</name>
    </author>
    <category term="工作相关" scheme="https://studyflowblog.com/categories/%E5%B7%A5%E4%BD%9C%E7%9B%B8%E5%85%B3/"/>
    <category term="python" scheme="https://studyflowblog.com/tags/python/"/>
    <category term="Flask" scheme="https://studyflowblog.com/tags/Flask/"/>
    <content>
      <![CDATA[<h1 id="1-什么是缓慢的http拒绝服务攻击漏洞"><a href="#1-什么是缓慢的http拒绝服务攻击漏洞" class="headerlink" title="1.什么是缓慢的http拒绝服务攻击漏洞"></a>1.什么是缓慢的http拒绝服务攻击漏洞</h1><p>缓慢的http拒绝服务攻击是一种专门针对于Web的应用层拒绝服务攻击，攻击者操纵网络上的肉鸡，对目标Web服务器进行海量http request攻击，直到服务器带宽被打满，造成了拒绝服务。</p><p>慢速HTTP拒绝服务攻击经过不断的演变和发展，主要有三种攻击类型，分别是Slow headers、Slow body、Slow read。</p><ul><li><p><code>slowloris</code>：完整的http请求是以 <code>\r\n\r\n</code> 结尾，攻击时仅发送 <code>\r\n</code>，少发送一个 <code>\r\n</code>，服务器认为请求还未发完，就会一直等待直至超时</p></li><li><p><code>slow post</code>：通过声明一个较大的content-length后，body缓慢发送，导致服务器一直等待</p><ul><li><code>slow read</code>：向服务器发送一个正常合法的read请求，请求一个很大的文件，但把TCP滑动窗口设置得很小，服务器就会以滑动窗口的大小切割文件，然后发送，这时文件会长期存放在内存中，消耗资源</li></ul></li></ul><h1 id="2-Flask应对措施"><a href="#2-Flask应对措施" class="headerlink" title="2. Flask应对措施"></a>2. Flask应对措施</h1><ul><li><p>限制每次请求数据的大小</p></li><li><p>限制单个HTTP请求头的最大许可时间</p></li><li><p>限制单个ip请求数量</p></li><li><p>。。。。。。</p></li></ul><p>#3. 使用slowhttptest测试漏洞</p><h2 id="3-1-slowhttptest的安装"><a href="#3-1-slowhttptest的安装" class="headerlink" title="3.1 slowhttptest的安装"></a>3.1 slowhttptest的安装</h2><blockquote><p> 安装说明:<a href="https://github.com/shekyan/slowhttptest/wiki/InstallationAndUsage1">https://github.com/shekyan/slowhttptest/wiki/InstallationAndUsage1</a></p></blockquote><ol><li><p>下载压缩包：<a href="https://github.com/shekyan/slowhttptest/releases">https://github.com/shekyan/slowhttptest/releases</a></p></li><li><p>安装libssl-dev<br><code>$ yum install openssl openssl-devel</code></p></li><li><p>安装C++编译器<br><code>$ yum install gcc-c++</code></p></li><li><p>解压</p><p><code>$ tar -xzvf slowhttptest-x.x.tar.gz</code> </p></li><li><p>编译安装</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ cd slowhttptest-x.x</span><br><span class="line">$ ./configure --prefix=PREFIX</span><br><span class="line">$ make</span><br><span class="line">$ sudo make install</span><br></pre></td></tr></table></figure></li></ol><h2 id="3-2-参数说明及使用测试"><a href="#3-2-参数说明及使用测试" class="headerlink" title="3.2 参数说明及使用测试"></a>3.2 参数说明及使用测试</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">-g      在测试完成后，以时间戳为名生成一个CVS和HTML文件的统计数据</span><br><span class="line">-H      SlowLoris模式</span><br><span class="line">-B      Slow POST模式</span><br><span class="line">-R      Range Header模式</span><br><span class="line">-X      Slow Read模式</span><br><span class="line">-c      number of connections 测试时建立的连接数</span><br><span class="line">-d      HTTP proxy host:port  为所有连接指定代理</span><br><span class="line">-e      HTTP proxy host:port  为探测连接指定代理</span><br><span class="line">-i      seconds 在slowrois和Slow POST模式中，指定发送数据间的间隔。</span><br><span class="line">-l      seconds 测试维持时间</span><br><span class="line">-n      seconds 在Slow Read模式下，指定每次操作的时间间隔。</span><br><span class="line">-o      file name 使用-g参数时，可以使用此参数指定输出文件名</span><br><span class="line">-p      seconds 指定等待时间来确认DoS攻击已经成功</span><br><span class="line">-r      connections per second 每秒连接个数</span><br><span class="line">-s      bytes 声明Content-Length header的值</span><br><span class="line">-t      HTTP verb 在请求时使用什么操作，默认GET</span><br><span class="line">-u      URL  指定目标url</span><br><span class="line">-v      level 日志等级（详细度）</span><br><span class="line">-w      bytes slow read模式中指定tcp窗口范围下限</span><br><span class="line">-x      bytes 在slowloris and Slow POST tests模式中，指定发送的最大数据长度</span><br><span class="line">-y      bytes slow read模式中指定tcp窗口范围上限</span><br><span class="line">-z      bytes 在每次的read()中，从buffer中读取数据量</span><br></pre></td></tr></table></figure><h2 id="3-2-1参考实例："><a href="#3-2-1参考实例：" class="headerlink" title="3.2.1参考实例："></a>3.2.1参考实例：</h2><p>slowloris模式：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">slowhttptest -c 1000 -H -g -o my_header_stats -i 10 -r 200 -t GET -u https://xxxxxx.xxxxx.xx -x 24 -p 3</span><br></pre></td></tr></table></figure><p>slow post模式：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ slowhttptest -c 3000 -B -g -o my_body_stats -i 110 -r 200 -s 8192 -t FAKEVERB -u http://xxx.xxx.xxx -x 10 -p 3</span><br></pre></td></tr></table></figure><p>slow read模式：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ slowhttptest -c 8000 -X -r 200 -w 512 -y 1024 -n 5 -z 32 -k 3 -u https://xxx.xxx.xxx -p 3</span><br></pre></td></tr></table></figure><h1 id="4-使用说明"><a href="#4-使用说明" class="headerlink" title="4.使用说明"></a>4.使用说明</h1><p>以下是分别使用四种方式对自己的服务进行测试的结果，先开始不太懂每个曲线图的意义。这里说明一下，上面的参数可以设置连接数量以及持续请求时间等参数。然后下面展现的是曲线图，主要看在测试程序运行时间内Connected的数量是否被释放，是否通过服务的设置不符合条件的请求被服务端关闭，Service是否正常运行（服务是否存在被漏洞攻击程序给冲宕机了）。</p><p>##4.1 RANGE HEADERS</p><img src="/2020/11/06/python/Flask%E4%B9%8B%E7%BC%93%E6%85%A2%E7%9A%84http%E6%8B%92%E7%BB%9D%E6%9C%8D%E5%8A%A1%E6%94%BB%E5%87%BB%E6%BC%8F%E6%B4%9E%E8%A7%A3%E5%86%B3/image-20201115151719480.png" class=""><p>##4.2 SLOW HEADERS(SlowLoris)</p><img src="/2020/11/06/python/Flask%E4%B9%8B%E7%BC%93%E6%85%A2%E7%9A%84http%E6%8B%92%E7%BB%9D%E6%9C%8D%E5%8A%A1%E6%94%BB%E5%87%BB%E6%BC%8F%E6%B4%9E%E8%A7%A3%E5%86%B3/image-20201115151939829.png" class=""><p>##4.3 SLOW BODY(SLOW POST)</p><img src="/2020/11/06/python/Flask%E4%B9%8B%E7%BC%93%E6%85%A2%E7%9A%84http%E6%8B%92%E7%BB%9D%E6%9C%8D%E5%8A%A1%E6%94%BB%E5%87%BB%E6%BC%8F%E6%B4%9E%E8%A7%A3%E5%86%B3/image-20201115154404536.png" class=""><h2 id="4-4-SLOW-READ"><a href="#4-4-SLOW-READ" class="headerlink" title="4.4  SLOW READ"></a>4.4  SLOW READ</h2><img src="/2020/11/06/python/Flask%E4%B9%8B%E7%BC%93%E6%85%A2%E7%9A%84http%E6%8B%92%E7%BB%9D%E6%9C%8D%E5%8A%A1%E6%94%BB%E5%87%BB%E6%BC%8F%E6%B4%9E%E8%A7%A3%E5%86%B3/image-20201115152038635.png" class="">]]>
    </content>
    <id>https://studyflowblog.com/2020/11/06/python/Flask%E4%B9%8B%E7%BC%93%E6%85%A2%E7%9A%84http%E6%8B%92%E7%BB%9D%E6%9C%8D%E5%8A%A1%E6%94%BB%E5%87%BB%E6%BC%8F%E6%B4%9E%E8%A7%A3%E5%86%B3/</id>
    <link href="https://studyflowblog.com/2020/11/06/python/Flask%E4%B9%8B%E7%BC%93%E6%85%A2%E7%9A%84http%E6%8B%92%E7%BB%9D%E6%9C%8D%E5%8A%A1%E6%94%BB%E5%87%BB%E6%BC%8F%E6%B4%9E%E8%A7%A3%E5%86%B3/"/>
    <published>2020-11-06T01:32:24.000Z</published>
    <summary>详解Slowloris、Slow POST、Slow Read三种缓慢HTTP拒绝服务攻击原理，及在Flask应用中的防御配置方案</summary>
    <title>Flask之缓慢的http拒绝服务攻击漏洞解决</title>
    <updated>2026-04-02T12:51:30.624Z</updated>
  </entry>
  <entry>
    <author>
      <name>Wednesday</name>
    </author>
    <category term="工作相关" scheme="https://studyflowblog.com/categories/%E5%B7%A5%E4%BD%9C%E7%9B%B8%E5%85%B3/"/>
    <category term="数据分析" scheme="https://studyflowblog.com/tags/%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90/"/>
    <content>
      <![CDATA[<h1 id="1-熵权法说明"><a href="#1-熵权法说明" class="headerlink" title="1.熵权法说明"></a>1.熵权法说明</h1><p>通过信息熵的大小判断某项数据在整体数据评测中所占的比重大小。</p><blockquote><p>一般来说，若某个指标的<strong>信息熵Ej越小</strong>，表明指标值得变异程度<strong>越大</strong>，提供的信息量越多，在综合评价中所能起到的作用也越大，其权重也就越大。相反，某个指标的<strong>信息熵越大</strong>，表明指标值得变异程度<strong>越小</strong>，提供的信息量也越少，在综合评价中所起到的作用也越小，其权重也就越小</p></blockquote><p>#2.计算步骤</p><h2 id="2-1-数据标准化"><a href="#2-1-数据标准化" class="headerlink" title="2.1 数据标准化"></a>2.1 数据标准化</h2><p>假设给定了<em>k</em>个指标（类别）$X_1$,$X_2$，……，$X_i$&#x3D;{$x_1$,$x_2$,……},</p><p>假设对各指标数据标准化后的值为 : $Y_1$,$Y_2$….$Y_k$</p><p>则$X_{ij}$表示第$i$个指标下的第$j$个值</p><p>那么标准化的$Y_{ij}$ &#x3D;$\frac{X_{ij}-min(X_{ij})}{max(X_i) - min(X_i)}$</p><h2 id="2-2-计算各指标的信息熵"><a href="#2-2-计算各指标的信息熵" class="headerlink" title="2.2 计算各指标的信息熵"></a>2.2 计算各指标的信息熵</h2><p>信息熵   $E_j &#x3D; -\frac{1}{lnn}∑^n_{i&#x3D;1}p_{ij}lnp_{ij}$</p><p>其中   $p_{ij} &#x3D; \frac{Y_{ij}}{\stackrel{n}{\stackrel{∑{Y_{ij}}}{i&#x3D;1}}}$</p><p>如果： $p_{ij}&#x3D;0$</p><p>则：$\stackrel{lim}{p_{ij-0}} p_{ij}lnp_{ij}&#x3D;0$</p><h2 id="2-3-确定各指标权重"><a href="#2-3-确定各指标权重" class="headerlink" title="2.3 确定各指标权重"></a>2.3 确定各指标权重</h2><p>根据信息熵$E_j$的计算公式计算信息熵分别为$E_1,E_2,……E_k$</p><p>然后计算权重:</p><p>$W_i &#x3D; \frac{1-E_i}{k-∑E_i}(i &#x3D; 1,2,3….k)$</p><blockquote><p>这里的分母$k-∑E_i$通过上面计算出所有的信息熵E之后为一个固定的常数。</p></blockquote><h2 id="2-4-根据权重打分"><a href="#2-4-根据权重打分" class="headerlink" title="2.4 根据权重打分"></a>2.4 根据权重打分</h2><p>根据计算后：$X_1$,$X_2$，……，$X_i$&#x3D;{$x_1$,$x_2$,……}每一项指标分别对应的权重为$W_1,W_2,….W_i$</p><p>得到每个个体的得分计算为  </p><p>$R_1 &#x3D; X_{11}W_1+X_{21}W_2+….+X_{i1}W_i$ </p>]]>
    </content>
    <id>https://studyflowblog.com/2020/10/15/entropy-weight-method/</id>
    <link href="https://studyflowblog.com/2020/10/15/entropy-weight-method/"/>
    <published>2020-10-15T05:00:06.000Z</published>
    <summary>基于信息熵理论的权重计算方法详解，含数据标准化、信息熵计算及权重公式推导的完整步骤</summary>
    <title>熵权法计算权重</title>
    <updated>2026-04-02T12:26:06.920Z</updated>
  </entry>
  <entry>
    <author>
      <name>Wednesday</name>
    </author>
    <category term="娱乐" scheme="https://studyflowblog.com/categories/%E5%A8%B1%E4%B9%90/"/>
    <category term="python" scheme="https://studyflowblog.com/tags/python/"/>
    <category term="娱乐" scheme="https://studyflowblog.com/tags/%E5%A8%B1%E4%B9%90/"/>
    <content>
      <![CDATA[<h1 id="0-摘要"><a href="#0-摘要" class="headerlink" title="0. 摘要"></a>0. 摘要</h1><ul><li><p>因为本机安装过anaconda，python，tensorflow等环境，所以直接按照版本安装部分依赖即可。</p></li><li><p>后来发现谷歌的colab有免费的gpu可以白嫖，并且colab内置了tensorflow和图形计算框架，所以省去了搭建环境的烦恼。可以直接使用，也很方便。弊端就是不氪金每天能分配的gpu资源不定并且容易断联，session断开需要重新配置，不过图一乐还是可以的。</p></li></ul><h1 id="1-本地手动安装"><a href="#1-本地手动安装" class="headerlink" title="1. 本地手动安装"></a>1. 本地手动安装</h1><p>全程安装按照官方指南，见 <a href="https://github.com/deepfakes/faceswap/blob/master/INSTALL.md%E3%80%82">https://github.com/deepfakes/faceswap/blob/master/INSTALL.md。</a></p><h1 id="2-问题记录"><a href="#2-问题记录" class="headerlink" title="2. 问题记录"></a>2. 问题记录</h1><h2 id="2-1-问题1"><a href="#2-1-问题1" class="headerlink" title="2.1 问题1"></a>2.1 问题1</h2><ul><li><p>问题：import win32console  # pylint: disable&#x3D;import-errorImportError: DLL load failed while importing win32console: 找不到指定的模块</p></li><li><p>解决：进入“Anaconda3\Scripts”下找到“pywin32_postinstall.py”文件，并执行“python3 pywin32_postinstall.py -install”</p></li></ul><h2 id="2-2-问题2"><a href="#2-2-问题2" class="headerlink" title="2.2 问题2"></a>2.2 问题2</h2><ul><li>问题：在使用pip一键安装requirements_base.txt及requirements_nvidia.txt中的依赖的时候速度慢，并且切换到anaconda的新建虚拟环境中windows下无法执行其中的git命令</li><li>解决：速度慢修改anaconda的镜像。git无法访问：手动下载git包并将“requirements_base”中的git相关命令注释掉（git慢的话修改git的代理端口）</li></ul><h2 id="2-3-问题3"><a href="#2-3-问题3" class="headerlink" title="2.3 问题3"></a>2.3 问题3</h2><ul><li>问题：File “D:\faceswap\lib\gui\menu.py”, line 302, in _get_branchesretcode, stdout.decode(‘utf-8’).strip().replace(“\n”, “ - “)) UnicodeDecodeError: ‘utf-8’ codec can’t decode byte 0xb2 in position 6: invalid start byte</li><li>解决： 查看源码发现该句只是写入日志的内容，直接注掉，成功运行</li></ul><h2 id="2-4-问题4"><a href="#2-4-问题4" class="headerlink" title="2.4 问题4"></a>2.4 问题4</h2><ul><li><p>问题：一直无法启动gpu进行运算，猜想是缺少插件CUDA toolkit（英伟达计算架构框架）和cudnn（英伟达架构算法）</p></li><li><p>证明猜想：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">#测试tfGPU是否能够使用：</span><br><span class="line">import tensorflow as tf</span><br><span class="line">import os</span><br><span class="line">os.environ[&#x27;TF_CPP_MIN_LOG_LEVEL&#x27;] = &#x27;2&#x27; </span><br><span class="line">print(&#x27;GPU&#x27;, tf.test.is_gpu_available())</span><br><span class="line">a = tf.constant(2.0)</span><br><span class="line">b = tf.constant(4.0)</span><br><span class="line">print(a + b)</span><br><span class="line"></span><br><span class="line">#结果为如下表示可以使用gpu</span><br><span class="line">GPU True</span><br><span class="line">tf.Tensor(6.0, shape=(), dtype=float32)</span><br><span class="line"></span><br><span class="line">#但是报错</span><br><span class="line">“Could not load dynamic library &#x27;cusparse64_10.dll&#x27;; dlerror: cusparse64_10.dll not found”等dll文件不存在</span><br><span class="line"></span><br><span class="line">#需要安装CUDA和cudnn</span><br><span class="line">CUDA：https://developer.nvidia.com/cuda-downloads </span><br><span class="line">CUDNN: https://developer.nvidia.com/cudnn</span><br></pre></td></tr></table></figure></li></ul><h3 id="2-4-1-安装cuda和cudnn后查看gpu是否启动报错"><a href="#2-4-1-安装cuda和cudnn后查看gpu是否启动报错" class="headerlink" title="2.4.1 安装cuda和cudnn后查看gpu是否启动报错"></a>2.4.1 安装cuda和cudnn后查看gpu是否启动报错</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">问题：Could not load dynamic library &#x27;cudnn64_7.dll&#x27;; dlerror: cudnn64_7.dll not found</span><br><span class="line"></span><br><span class="line">解决方法：Copy &lt;installpath&gt;\cuda\bin\cudnn64_7.dll to C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0\bin.</span><br><span class="line">Copy &lt;installpath&gt;\cuda\ include\cudnn.h to C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0\include.</span><br><span class="line">Copy &lt;installpath&gt;\cuda\lib\x64\cudnn.lib to C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0\lib\x64</span><br></pre></td></tr></table></figure><h1 id="3-colab安装"><a href="#3-colab安装" class="headerlink" title="3. colab安装"></a>3. colab安装</h1><p>参考：<code>https://github.com/RakaMaru/Faceswap_Google_Colab_Master/blob/master/Faceswap_Google_Colab_Master.ipynb</code>，因为colab的python版本问题又不想修改python版本，进行了部分修改。以下均在colab的笔记本中按顺序执行(前置需要配置一些文件的目录参考上述链接)</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line">#@title Use this to check the assigned GPU</span><br><span class="line">!cat /etc/os-release</span><br><span class="line"></span><br><span class="line">def install_dependencies():</span><br><span class="line">  !ln -sf /opt/bin/nvidia-smi /usr/bin/nvidia-smi;</span><br><span class="line">  !pip install gputil;</span><br><span class="line">  !pip install psutil;</span><br><span class="line">  !pip install humanize;%%capture</span><br><span class="line"></span><br><span class="line">def printm():</span><br><span class="line"> GPUs = GPU.getGPUs()</span><br><span class="line"></span><br><span class="line"> if len(GPUs) == 0:</span><br><span class="line">  print(&quot;No GPU available.&quot;)</span><br><span class="line">  return</span><br><span class="line"></span><br><span class="line"> gpu = GPUs[0]</span><br><span class="line"> process = psutil.Process(os.getpid())</span><br><span class="line"> print(&quot;Gen RAM Free: &quot; + humanize.naturalsize( psutil.virtual_memory().available ), &quot; | Proc size: &quot; + humanize.naturalsize( process.memory_info().rss))</span><br><span class="line"> print(&quot;GPU RAM Free: &#123;0:.0f&#125;MB | Used: &#123;1:.0f&#125;MB | Util &#123;2:3.0f&#125;% | Total &#123;3:.0f&#125;MB&quot;.format(gpu.memoryFree, gpu.memoryUsed, gpu.memoryUtil*100, gpu.memoryTotal))</span><br><span class="line"></span><br><span class="line">from IPython.utils import io</span><br><span class="line">from google.colab import drive</span><br><span class="line">import psutil</span><br><span class="line">import humanize</span><br><span class="line">import os</span><br><span class="line"> </span><br><span class="line">with io.capture_output() as captured:</span><br><span class="line">  install_dependencies()</span><br><span class="line">print(&quot;Dependencies installed.&quot;)</span><br><span class="line"></span><br><span class="line">import GPUtil as GPU</span><br><span class="line">printm()</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">#@title Set Time Zone</span><br><span class="line">!rm /etc/localtime</span><br><span class="line">!ln -s /usr/share/zoneinfo/HST /etc/localtime</span><br><span class="line">!date</span><br><span class="line"></span><br><span class="line">#above is for HST, you can find yours in</span><br><span class="line">#/usr/share/zoneinfo</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">! rm -rf ./drive</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">#@title Mount Google Drive</span><br><span class="line"></span><br><span class="line">from google.colab import drive</span><br><span class="line">drive.mount(&#x27;/content/drive&#x27;, force_remount=True)</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">#@title Cleanup folders if needed</span><br><span class="line"></span><br><span class="line">!rm -r face_a</span><br><span class="line">!rm -r face_b</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">#@title Download training data</span><br><span class="line">!cp &quot;/content/drive/My Drive/colab_files/faceswap/faces/face_a.zip&quot; .</span><br><span class="line">!cp &quot;/content/drive/My Drive/colab_files/faceswap/faces/face_b.zip&quot; .</span><br><span class="line"></span><br><span class="line">!unzip face_a.zip -d face_a</span><br><span class="line">!unzip face_b.zip -d face_b</span><br><span class="line"></span><br><span class="line">!rm face_a.zip</span><br><span class="line">!rm face_b.zip</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">#@title Grab the latest Faceswap</span><br><span class="line">!git clone --single-branch --branch r1.0 https://github.com/deepfakes/faceswap.git</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">#@title Copy configuration files</span><br><span class="line">!cp &quot;/content/drive/My Drive/colab_files/faceswap/config/train.ini&quot; faceswap/config/</span><br><span class="line">!ls -lA faceswap/config/</span><br><span class="line">!cat faceswap/config/train.ini</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">#@title 删除自带的tensorflow2.3安装1.15.0</span><br><span class="line">! pip install tensorflow==1.15.0</span><br><span class="line">! pip install gast==0.2.2</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">#@title Install Tensorflow</span><br><span class="line">!pip install -r faceswap/requirements_nvidia.txt</span><br><span class="line">#@ 这里会报错“ERROR: albumentations 0.1.12 has requirement imgaug&lt;0.2.7,&gt;=0.2.5, but you&#x27;ll have imgaug 0.2.9 which is incompatible.”  但没关系</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">#@title 开始训练，参数根据需要修改</span><br><span class="line">num_iterations = &quot;100000&quot;</span><br><span class="line">save_every = &quot;360&quot;</span><br><span class="line">save_model_every = &quot;25000&quot;</span><br><span class="line">batch_num = &quot;8&quot;</span><br><span class="line">num_gpus = &quot;1&quot;</span><br><span class="line"></span><br><span class="line">trainer_type = &quot;dlight&quot;</span><br><span class="line"></span><br><span class="line">model_dir = &quot;/content/drive/My Drive/colab_files/faceswap/models/model&quot;</span><br><span class="line">alignments_file_a = &quot;face_a/alignments.fsa&quot;</span><br><span class="line">alignments_file_b = &quot;face_b/alignments.fsa&quot;</span><br><span class="line">timelapse_dir = &quot;/content/drive/My Drive/colab_files/faceswap/output/timelapse&quot;</span><br><span class="line">#@title set variables end</span><br><span class="line"></span><br><span class="line">!python3 faceswap/faceswap.py train \</span><br><span class="line">  -A &#x27;./face_a/faceA&#x27; -ala &#x27;&#123;alignments_file_a&#125;&#x27; \</span><br><span class="line">  -B &#x27;./face_b/faceB&#x27; -alb &#x27;&#123;alignments_file_b&#125;&#x27; \</span><br><span class="line">  -m &#x27;&#123;model_dir&#125;&#x27; \</span><br><span class="line">  -t &#x27;&#123;trainer_type&#125;&#x27; \</span><br><span class="line">  -bs &#x27;&#123;batch_num&#125;&#x27; \</span><br><span class="line">  -it &#x27;&#123;num_iterations&#125;&#x27; \</span><br><span class="line">  -g &#x27;&#123;num_gpus&#125;&#x27; \</span><br><span class="line">  -s &#x27;&#123;save_every&#125;&#x27; \</span><br><span class="line">  -ss &#x27;&#123;save_model_every&#125;&#x27; \</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">#@title 查看结果：</span><br><span class="line">Setting Faceswap backend to NVIDIA</span><br><span class="line">09/26/2020 16:14:02 INFO     Log level set to: INFO</span><br><span class="line">Using TensorFlow backend.</span><br><span class="line">09/26/2020 16:14:04 INFO     Model A Directory: /content/face_a/faceA</span><br><span class="line">09/26/2020 16:14:04 INFO     Model B Directory: /content/face_b/faceB</span><br><span class="line">09/26/2020 16:14:04 INFO     Training data directory: /content/drive/My Drive/colab_files/faceswap/models/model</span><br><span class="line">09/26/2020 16:14:04 INFO     ===================================================</span><br><span class="line">09/26/2020 16:14:04 INFO       Starting</span><br><span class="line">09/26/2020 16:14:04 INFO       Press &#x27;ENTER&#x27; to save and quit</span><br><span class="line">09/26/2020 16:14:04 INFO       Press &#x27;S&#x27; to save model weights immediately</span><br><span class="line">09/26/2020 16:14:04 INFO     ===================================================</span><br><span class="line">09/26/2020 16:14:05 INFO     Loading data, this may take a while...</span><br><span class="line">09/26/2020 16:14:05 INFO     Loading Model from Dlight plugin...</span><br><span class="line">09/26/2020 16:14:05 INFO     Using configuration saved in state file</span><br><span class="line">09/26/2020 16:14:10 INFO     Loaded model from disk: &#x27;/content/drive/My Drive/colab_files/faceswap/models/model&#x27;</span><br><span class="line">09/26/2020 16:14:10 INFO     Loading Trainer from Original plugin...</span><br><span class="line">09/26/2020 16:14:12 INFO     Enabled TensorBoard Logging</span><br><span class="line">[16:14:27] [#04386] Loss A: 0.05719, Loss B: 0.05088</span><br><span class="line">09/26/2020 16:14:32 INFO     [Saved models] - Average since last save: face_loss_A: 0.05719, face_loss_B: 0.05088</span><br><span class="line">[16:19:42] [#04746] Loss A: 0.04279, Loss B: 0.03795</span><br><span class="line">09/26/2020 16:19:46 INFO     [Saved models] - Average since last save: face_loss_A: 0.04535, face_loss_B: 0.04580</span><br><span class="line">[16:25:01] [#05106] Loss A: 0.04427, Loss B: 0.04492</span><br><span class="line">09/26/2020 16:25:06 INFO     [Saved models] - Average since last save: face_loss_A: 0.04406, face_loss_B: 0.04483</span><br><span class="line">[16:25:19] [#05120] Loss A: 0.04438, Loss B: 0.03987</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="3-1-总结"><a href="#3-1-总结" class="headerlink" title="3.1 总结"></a>3.1 总结</h2><p>根据官方指导上面的损失函数lossA和lossB分别训练到0.02和0.01就基本差不多了，最后把训练的模型下载到本地上，然后跑一下就行了。虽然是在云端训练的，但是本地似乎还是需要安装相关的环境，因为模型下载下来了本地使用还是需要环境。</p><p>云端的好处就是不用占用本机的内存什么的，还有gpu用方便很多速度也蛮快的。</p>]]>
    </content>
    <id>https://studyflowblog.com/2020/09/23/faceswap%E5%AE%89%E8%A3%85%E4%B8%8E%E8%B8%A9%E5%9D%91/</id>
    <link href="https://studyflowblog.com/2020/09/23/faceswap%E5%AE%89%E8%A3%85%E4%B8%8E%E8%B8%A9%E5%9D%91/"/>
    <published>2020-09-23T05:04:43.000Z</published>
    <summary>本地手动安装与Google Colab免费GPU两种方式搭建FaceSwap换脸环境的对比及踩坑记录</summary>
    <title>本地安装faceswap与一键使用google的colab搭建faceswap两种方式</title>
    <updated>2026-04-02T12:26:14.471Z</updated>
  </entry>
  <entry>
    <author>
      <name>Wednesday</name>
    </author>
    <category term="编程语言特性相关" scheme="https://studyflowblog.com/categories/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%E7%89%B9%E6%80%A7%E7%9B%B8%E5%85%B3/"/>
    <category term="python" scheme="https://studyflowblog.com/tags/python/"/>
    <category term="pyqt5" scheme="https://studyflowblog.com/tags/pyqt5/"/>
    <content>
      <![CDATA[<h1 id="1-pyqt5及qtdesigner的安装"><a href="#1-pyqt5及qtdesigner的安装" class="headerlink" title="1. pyqt5及qtdesigner的安装"></a>1. pyqt5及qtdesigner的安装</h1><h2 id="1-1-安装"><a href="#1-1-安装" class="headerlink" title="1.1 安装"></a>1.1 安装</h2><p>安装步骤网上一堆自行谷歌</p><h3 id="1-2-使用pyqt5生成ui文件后无法转py，报错"><a href="#1-2-使用pyqt5生成ui文件后无法转py，报错" class="headerlink" title="1.2 使用pyqt5生成ui文件后无法转py，报错"></a>1.2 使用pyqt5生成ui文件后无法转py，报错</h3><p>错误如下:</p><blockquote><p>Fatal error in launcher: Unable to create process using ‘“‘</p></blockquote><p>原因：</p><blockquote><p>执行语句错误，完整应为</p><p>“python -m PyQt5.uic.pyuic untitled.ui -o untitled.py”</p></blockquote><blockquote><p>注意在pycharm中的tools中的pyuic的settings—External Tools—(External Tools)Pyuic内Tools settings设置的正确，正确如下</p></blockquote><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Program: D:\Anaconda3\envs\python36\python.exe</span><br><span class="line">Arguments: -m PyQt5.uic.pyuic $FileName$ -o $FileNameWithoutExtension$.py</span><br><span class="line">Working directory: $FileDir$</span><br></pre></td></tr></table></figure><h1 id="2-使用"><a href="#2-使用" class="headerlink" title="2. 使用"></a>2. 使用</h1><p>功能比较简单，就是一个根据时间和工作日&#x2F;休息日的不同计算出差补助。</p><p>主要是因为之前没用过pyqt5，所以突发奇想尝试一下，发现也是蛮简单的。</p><img src="/2020/09/17/python/pyqt5%E4%BD%BF%E7%94%A8%E5%8F%8A%E5%87%BA%E7%8E%B0%E9%97%AE%E9%A2%98/2.png" class=""><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br></pre></td><td class="code"><pre><span class="line"># -*- coding: utf-8 -*-</span><br><span class="line"></span><br><span class="line"># Form implementation generated from reading ui file &#x27;untitled.ui&#x27;</span><br><span class="line">#</span><br><span class="line"># Created by: PyQt5 UI code generator 5.15.0</span><br><span class="line">#</span><br><span class="line"># WARNING: Any manual changes made to this file will be lost when pyuic5 is</span><br><span class="line"># run again.  Do not edit this file unless you know what you are doing.</span><br><span class="line"></span><br><span class="line">from PyQt5.QtWidgets import *</span><br><span class="line">from PyQt5 import QtCore, QtWidgets</span><br><span class="line">from datetime import datetime</span><br><span class="line">import datetime as datetime0</span><br><span class="line"># 添加这一行可以在程序捕获异常的时候pyqt不崩溃</span><br><span class="line">import cgitb</span><br><span class="line">cgitb.enable( format = &#x27;text&#x27;)</span><br><span class="line"></span><br><span class="line">class Ui_Form(object):</span><br><span class="line">    def setupUi(self, Form):</span><br><span class="line">        Form.setObjectName(&quot;Form&quot;)</span><br><span class="line">        Form.resize(586, 401)</span><br><span class="line">        self.textEdit = QtWidgets.QTextEdit(Form)</span><br><span class="line">        self.textEdit.setGeometry(QtCore.QRect(130, 100, 141, 41))</span><br><span class="line">        self.textEdit.setObjectName(&quot;textEdit&quot;)</span><br><span class="line">        self.textEdit_2 = QtWidgets.QTextEdit(Form)</span><br><span class="line">        self.textEdit_2.setGeometry(QtCore.QRect(420, 100, 121, 41))</span><br><span class="line">        self.textEdit_2.setObjectName(&quot;textEdit_2&quot;)</span><br><span class="line">        self.textEdit_4 = QtWidgets.QTextEdit(Form)</span><br><span class="line">        self.textEdit_4.setGeometry(QtCore.QRect(420, 190, 121, 41))</span><br><span class="line">        self.textEdit_4.setObjectName(&quot;textEdit_4&quot;)</span><br><span class="line">        self.textEdit_5 = QtWidgets.QTextEdit(Form)</span><br><span class="line">        self.textEdit_5.setGeometry(QtCore.QRect(210, 270, 281, 91))</span><br><span class="line">        self.textEdit_5.setObjectName(&quot;textEdit_5&quot;)</span><br><span class="line">        self.textEdit_5.setReadOnly(True)</span><br><span class="line">        self.textEdit_6 = QtWidgets.QTextEdit(Form)</span><br><span class="line">        self.textEdit_6.setGeometry(QtCore.QRect(130, 190, 141, 41))</span><br><span class="line">        self.textEdit_6.setObjectName(&quot;textEdit_6&quot;)</span><br><span class="line"></span><br><span class="line">        self.pushButton = QtWidgets.QPushButton(Form)</span><br><span class="line">        self.pushButton.setGeometry(QtCore.QRect(110, 290, 61, 41))</span><br><span class="line">        self.pushButton.setObjectName(&quot;pushButton&quot;)</span><br><span class="line">        self.label = QtWidgets.QLabel(Form)</span><br><span class="line">        self.label.setGeometry(QtCore.QRect(40, 100, 71, 31))</span><br><span class="line">        self.label.setObjectName(&quot;label&quot;)</span><br><span class="line">        self.label_2 = QtWidgets.QLabel(Form)</span><br><span class="line">        self.label_2.setGeometry(QtCore.QRect(320, 100, 81, 31))</span><br><span class="line">        self.label_2.setObjectName(&quot;label_2&quot;)</span><br><span class="line">        self.label_3 = QtWidgets.QLabel(Form)</span><br><span class="line">        self.label_3.setGeometry(QtCore.QRect(30, 190, 91, 31))</span><br><span class="line">        self.label_3.setObjectName(&quot;label_3&quot;)</span><br><span class="line">        self.label_4 = QtWidgets.QLabel(Form)</span><br><span class="line">        self.label_4.setGeometry(QtCore.QRect(320, 200, 91, 16))</span><br><span class="line">        self.label_4.setObjectName(&quot;label_4&quot;)</span><br><span class="line">        self.label_5 = QtWidgets.QLabel(Form)</span><br><span class="line">        self.label_5.setGeometry(QtCore.QRect(40, 40, 281, 41))</span><br><span class="line">        self.label_5.setObjectName(&quot;label_5&quot;)</span><br><span class="line"></span><br><span class="line">        self.retranslateUi(Form)</span><br><span class="line">        QtCore.QMetaObject.connectSlotsByName(Form)</span><br><span class="line">        Form.show()</span><br><span class="line"></span><br><span class="line">    def retranslateUi(self, Form):</span><br><span class="line">        _translate = QtCore.QCoreApplication.translate</span><br><span class="line">        Form.setWindowTitle(_translate(&quot;Form&quot;, &quot;Form&quot;))</span><br><span class="line">        self.textEdit.setHtml(_translate(&quot;Form&quot;,</span><br><span class="line">                                         &quot;&lt;!DOCTYPE HTML PUBLIC \&quot;-//W3C//DTD HTML 4.0//EN\&quot; \&quot;http://www.w3.org/TR/REC-html40/strict.dtd\&quot;&gt;\n&quot;</span><br><span class="line">                                         &quot;&lt;html&gt;&lt;head&gt;&lt;meta name=\&quot;qrichtext\&quot; content=\&quot;1\&quot; /&gt;&lt;style type=\&quot;text/css\&quot;&gt;\n&quot;</span><br><span class="line">                                         &quot;p, li &#123; white-space: pre-wrap; &#125;\n&quot;</span><br><span class="line">                                         &quot;&lt;/style&gt;&lt;/head&gt;&lt;body style=\&quot; font-family:\&#x27;SimSun\&#x27;; font-size:9pt; font-weight:400; font-style:normal;\&quot;&gt;\n&quot;</span><br><span class="line">                                         &quot;&lt;p style=\&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;&quot;))</span><br><span class="line">        self.pushButton.setText(_translate(&quot;Form&quot;, &quot;计算&quot;))</span><br><span class="line">        self.label.setText(_translate(&quot;Form&quot;, &quot;起始时间：&quot;))</span><br><span class="line">        self.label_2.setText(_translate(&quot;Form&quot;, &quot;结束时间：&quot;))</span><br><span class="line">        self.label_3.setText(_translate(&quot;Form&quot;, &quot;工作日价格：&quot;))</span><br><span class="line">        self.label_4.setText(_translate(&quot;Form&quot;, &quot;休息日价格：&quot;))</span><br><span class="line">        self.label_5.setText(_translate(&quot;Form&quot;, &quot;时间输入格式：20200101&quot;))</span><br><span class="line">        self.textEdit_2.setHtml(_translate(&quot;Form&quot;,</span><br><span class="line">                                           &quot;&lt;!DOCTYPE HTML PUBLIC \&quot;-//W3C//DTD HTML 4.0//EN\&quot; \&quot;http://www.w3.org/TR/REC-html40/strict.dtd\&quot;&gt;\n&quot;</span><br><span class="line">                                           &quot;&lt;html&gt;&lt;head&gt;&lt;meta name=\&quot;qrichtext\&quot; content=\&quot;1\&quot; /&gt;&lt;style type=\&quot;text/css\&quot;&gt;\n&quot;</span><br><span class="line">                                           &quot;p, li &#123; white-space: pre-wrap; &#125;\n&quot;</span><br><span class="line">                                           &quot;&lt;/style&gt;&lt;/head&gt;&lt;body style=\&quot; font-family:\&#x27;SimSun\&#x27;; font-size:9pt; font-weight:400; font-style:normal;\&quot;&gt;\n&quot;</span><br><span class="line">                                           &quot;&lt;p style=\&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;&quot;))</span><br><span class="line">        self.textEdit_4.setHtml(_translate(&quot;Form&quot;,</span><br><span class="line">                                           &quot;&lt;!DOCTYPE HTML PUBLIC \&quot;-//W3C//DTD HTML 4.0//EN\&quot; \&quot;http://www.w3.org/TR/REC-html40/strict.dtd\&quot;&gt;\n&quot;</span><br><span class="line">                                           &quot;&lt;html&gt;&lt;head&gt;&lt;meta name=\&quot;qrichtext\&quot; content=\&quot;1\&quot; /&gt;&lt;style type=\&quot;text/css\&quot;&gt;\n&quot;</span><br><span class="line">                                           &quot;p, li &#123; white-space: pre-wrap; &#125;\n&quot;</span><br><span class="line">                                           &quot;&lt;/style&gt;&lt;/head&gt;&lt;body style=\&quot; font-family:\&#x27;SimSun\&#x27;; font-size:9pt; font-weight:400; font-style:normal;\&quot;&gt;\n&quot;</span><br><span class="line">                                           &quot;&lt;p style=\&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;&quot;))</span><br><span class="line">        self.textEdit_6.setHtml(_translate(&quot;Form&quot;,</span><br><span class="line">                                           &quot;&lt;!DOCTYPE HTML PUBLIC \&quot;-//W3C//DTD HTML 4.0//EN\&quot; \&quot;http://www.w3.org/TR/REC-html40/strict.dtd\&quot;&gt;\n&quot;</span><br><span class="line">                                           &quot;&lt;html&gt;&lt;head&gt;&lt;meta name=\&quot;qrichtext\&quot; content=\&quot;1\&quot; /&gt;&lt;style type=\&quot;text/css\&quot;&gt;\n&quot;</span><br><span class="line">                                           &quot;p, li &#123; white-space: pre-wrap; &#125;\n&quot;</span><br><span class="line">                                           &quot;&lt;/style&gt;&lt;/head&gt;&lt;body style=\&quot; font-family:\&#x27;SimSun\&#x27;; font-size:9pt; font-weight:400; font-style:normal;\&quot;&gt;\n&quot;</span><br><span class="line">                                           &quot;&lt;p style=\&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;&quot;))</span><br><span class="line"></span><br><span class="line">        self.pushButton.clicked.connect(self.calc)</span><br><span class="line"></span><br><span class="line">    def calc(self):</span><br><span class="line"></span><br><span class="line">        try:</span><br><span class="line">            startTime = self.textEdit.toPlainText()</span><br><span class="line">            stopTime = self.textEdit_2.toPlainText()</span><br><span class="line">            holiday_price = self.textEdit_4.toPlainText()</span><br><span class="line">            workday_price = self.textEdit_6.toPlainText()</span><br><span class="line">            # today = datetime.now().weekday() + 1</span><br><span class="line">            工作日 = 0</span><br><span class="line">            休息日 = 0</span><br><span class="line">            begin = datetime0.date(int(startTime[0:4]), int(startTime[4:6]), int(startTime[6:8]))</span><br><span class="line">            end = datetime0.date(int(stopTime[0:4]), int(stopTime[4:6]), int(stopTime[6:8]))</span><br><span class="line">            for eachDay in range((end - begin).days + 1):</span><br><span class="line">                eachDay = str(begin + datetime0.timedelta(days=eachDay)).split(&quot;-&quot;)</span><br><span class="line"></span><br><span class="line">                week = datetime.strptime(eachDay[0] + eachDay[1] + eachDay[2], &quot;%Y%m%d&quot;).weekday() + 1</span><br><span class="line">                if week &gt; 0 and week &lt; 6:</span><br><span class="line">                    工作日 += 1</span><br><span class="line">                else:</span><br><span class="line">                    休息日 += 1</span><br><span class="line">            result = &quot;一共有工作日&#123;&#125;天，休息日&#123;&#125;天\n&quot;.format(工作日, 休息日) +\</span><br><span class="line">            &quot;出差补贴一共为&#123;&#125;元&quot;.format(工作日 * int(workday_price) + 休息日 * int(holiday_price))</span><br><span class="line">            self.textEdit_5.setText(result)</span><br><span class="line">        except Exception :</span><br><span class="line">            # 输入框制空并显示重新输入</span><br><span class="line">            self.textEdit_5.setText(&quot;输入格式有误\n请检查并重新输入&quot;)</span><br><span class="line">            # self.textEdit.setText()</span><br><span class="line">            # self.textEdit_2.setText()</span><br><span class="line">            # self.textEdit_5.setText()</span><br><span class="line">            # self.textEdit_6.setText()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">if __name__ == &quot;__main__&quot;:</span><br><span class="line">    import sys</span><br><span class="line">    app = QApplication(sys.argv)</span><br><span class="line">    widget = QWidget(None)</span><br><span class="line">    ui = Ui_Form()</span><br><span class="line">    ui.setupUi(widget)</span><br><span class="line">    sys.exit(app.exec_())</span><br><span class="line">    pass</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://studyflowblog.com/2020/09/17/python/pyqt5%E4%BD%BF%E7%94%A8%E5%8F%8A%E5%87%BA%E7%8E%B0%E9%97%AE%E9%A2%98/</id>
    <link href="https://studyflowblog.com/2020/09/17/python/pyqt5%E4%BD%BF%E7%94%A8%E5%8F%8A%E5%87%BA%E7%8E%B0%E9%97%AE%E9%A2%98/"/>
    <published>2020-09-17T07:43:07.000Z</published>
    <summary>使用PyQt5和Qt Designer为Python小工具开发GUI界面的入门实践，含PyUIC配置错误等常见问题解决</summary>
    <title>PyQt5入门：为Python小工具开发GUI界面</title>
    <updated>2026-04-02T12:51:32.156Z</updated>
  </entry>
  <entry>
    <author>
      <name>Wednesday</name>
    </author>
    <category term="图数据库" scheme="https://studyflowblog.com/categories/%E5%9B%BE%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
    <category term="neo4j" scheme="https://studyflowblog.com/tags/neo4j/"/>
    <category term="图数据库" scheme="https://studyflowblog.com/tags/%E5%9B%BE%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
    <content>
      <![CDATA[<h1 id="1-neo4j集群安装"><a href="#1-neo4j集群安装" class="headerlink" title="1. neo4j集群安装"></a>1. neo4j集群安装</h1><ol start="0"><li>文件说明：</li></ol><ul><li>neo4j-enterprise-3.4.18-unix.tar.gz为软件压缩包</li><li>如下其余的都是第三方包，后面只需要移动到后面指定的文件夹中即可</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">apoc-3.4.0.4-all.jar                hive-service-1.1.0-cdh5.15.2.jar</span><br><span class="line">graph-algorithms-algo-3.4.12.7.jar  httpclient-4.2.5.jar</span><br><span class="line">hive-exec-1.1.0-cdh5.15.2.jar       httpcore-4.2.5.jar</span><br><span class="line">hive-jdbc-1.1.0-cdh5.15.2.jar       libfb303-0.9.3.jar</span><br><span class="line">hive-metastore-1.1.0-cdh5.15.2.jar  libthrift-0.9.3.jar</span><br></pre></td></tr></table></figure><ol><li>说明<blockquote><p>安装目录这里假设为根目录下面的opt文件夹<code>/opt</code>,具体视现场情况而定。配置集群方式可以通过先修改一份配置文件，然后将整个文件包发送到另外两个节点上。最后再修改neo4j的id即可。以下为此种安装方式的步骤</p></blockquote></li></ol><blockquote><p>测试主机配置ip分别为：</p></blockquote><blockquote><p>xxx.xx.xxx.100&#x2F;xxx.xx.xxx.102&#x2F;xxx.xx.xxx.9，</p></blockquote><blockquote><p>对应的hostname分别为master&#x2F;slave01&#x2F;slave02，</p></blockquote><blockquote><p>对应的neo4j集群id分别为1&#x2F;2&#x2F;3<br>（具体视现场情况而定,neo4j集群id只是一个标识）</p></blockquote><ol start="2"><li>获取软件包之后然后在安装目录下直接解压缩，命令如下</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tar -vxf neo4j-enterprise-3.4.18-unix.tar.gz</span><br></pre></td></tr></table></figure><ol start="3"><li>开启http页面服务外部访问</li></ol><ul><li>修改neo4j-enterprise-3.4.18&#x2F;conf&#x2F;neo4j.conf文件</li></ul><p>删除</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"># dbms.connectors.default_listen_address=0.0.0.0</span><br></pre></td></tr></table></figure><p>行前面的井号</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"># vim中打开文件然后输入“/+字符串”然后回车可以进行字符串搜索，使用n键调整上下匹配</span><br></pre></td></tr></table></figure><ol start="4"><li>配置ip地址及页面缓存大小</li></ol><ul><li>修改neo4j-enterprise-3.4.18&#x2F;conf&#x2F;neo4j.conf文件</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"># 修改文件中的不带井号行的数据为如下值</span><br><span class="line"></span><br><span class="line"># Unique server id for this Neo4j instance</span><br><span class="line"># can not be negative id and must be unique</span><br><span class="line">ha.server_id=1 </span><br><span class="line"># List of other known instances in this cluster</span><br><span class="line"># Alternatively, use IP addresses:</span><br><span class="line">ha.initial_hosts=xxx.xx.xxx.100:5001,xxx.xx.xxx.102:5001,xxx.xx.xxx.9:5001(这里根据实际id进行修改，端口默认不变)</span><br><span class="line"># HA - High Availability</span><br><span class="line"># SINGLE - Single mode, default.</span><br><span class="line">dbms.mode=HA</span><br><span class="line"># HTTP Connector</span><br><span class="line">dbms.connector.http.enabled=true</span><br><span class="line">dbms.connector.http.listen_address=:7474</span><br><span class="line"># 设置堆内存和页面缓存大小</span><br><span class="line">dbms.memory.heap.initial_size=10g</span><br><span class="line">dbms.memory.heap.max_size=10g</span><br><span class="line">dbms.memory.pagecache.size=10g</span><br></pre></td></tr></table></figure><ol start="5"><li><p>将第0步里面所述的所有第三方包放到<code>/opt/neo4j-enterprise-3.4.18/plugins</code>目录下</p></li><li><p>将整个neo4j文件夹<code>opt/neo4j-enterprise-3.4.18</code>复制到要安装的从节点对应的目录下</p></li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">scp -r ./opt/neo4j-enterprise-3.4.18/ root@xxx.xx.xxx.102:/opt/</span><br><span class="line">scp -r ./opt/neo4j-enterprise-3.4.18/ root@xxx.xx.xxx.9:/opt/</span><br></pre></td></tr></table></figure><ol start="7"><li>修改从节点的neo4jID</li></ol><ul><li>修改从节点的neo4j-enterprise-3.4.18&#x2F;conf&#x2F;neo4j.conf文件</li><li>找到neo4j.conf文件中的如下部分，将两个从节点中的ha.server_id分别修改为2和3</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"># Unique server id for this Neo4j instance</span><br><span class="line"># can not be negative id and must be unique</span><br><span class="line">ha.server_id=1 </span><br></pre></td></tr></table></figure><ol start="8"><li>然后分别<strong>启动三个集群</strong>，主从节点启动顺序不定，<strong>集群配置必须三个节点都启动服务才能启动成功。单独启动一个节点是无法启动成功的</strong>。</li></ol><p>在<code>/opt/neo4j-enterprise-3.4.18/bin</code>目录下分别输入</p><figure class="highlight plaintext"><figcaption><span>start``` ，并等待数秒即可</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">&gt; 重启和关闭的命令将上面的start改成restart或者stop即可</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">9. web客户端查看是否启动成功</span><br><span class="line"></span><br><span class="line">客户端页面为“http://xxx.xx.xxx.100:7474/browser/”,</span><br><span class="line">第一次登陆的时候会强制修改密码,</span><br><span class="line">默认账号密码为:neo4j/neo4j。</span><br><span class="line">点击页面上的System information框内的monitor键如下显示表示成功启动。</span><br><span class="line">（id为1，2，3的节点状态都是alive）</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>Cluster<br>IdAliveAvailableIs Master<br>1truetrueyes<br>2truetrue-<br>3truetrue</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line"># 2.启动可能遇到问题:</span><br><span class="line"></span><br><span class="line">## 2.1  ERROR Failed to start Neo4j</span><br><span class="line">&gt;  ERROR Failed to start Neo4j: Starting Neo4j failed: Component &#x27;org.neo4j.server.database.LifecycleManagingDatabase@1e141e42&#x27; was successfully initialized, but failed to start. Please see the attached cause exception &quot;Conversation-response mapping:</span><br><span class="line">&gt;  &#123;1/13#=ResponseFuture&#123;conversationId=&#x27;1/13#&#x27;, initiatedByMessageType=join, response=null&#125;&#125;&quot;. Starting Neo4j failed: Component </span><br><span class="line"></span><br><span class="line">如上错误说明三台机器之间访问出现了问题，不能相互感知，不能加入集群，还需要增加下面配置</span><br></pre></td></tr></table></figure><h1 id="在neo4j-node1-neo4j-conf中添加"><a href="#在neo4j-node1-neo4j-conf中添加" class="headerlink" title="在neo4j-node1 neo4j.conf中添加"></a>在neo4j-node1 neo4j.conf中添加</h1><p>dbms.connectors.default_listen_address&#x3D;xxx.xx.xxx.100<br>dbms.connector.bolt.enabled&#x3D;true<br>dbms.connector.bolt.listen_address&#x3D;:7687</p><h1 id="在neo4j-node2-neo4j-conf中添加"><a href="#在neo4j-node2-neo4j-conf中添加" class="headerlink" title="在neo4j-node2 neo4j.conf中添加"></a>在neo4j-node2 neo4j.conf中添加</h1><p>dbms.connectors.default_listen_address&#x3D;xxx.xx.xxx.102<br>dbms.connector.bolt.enabled&#x3D;true<br>dbms.connector.bolt.listen_address&#x3D;:7687</p><h1 id="在neo4j-node3-neo4j-conf中添加"><a href="#在neo4j-node3-neo4j-conf中添加" class="headerlink" title="在neo4j-node3 neo4j.conf中添加"></a>在neo4j-node3 neo4j.conf中添加</h1><p>dbms.connectors.default_listen_address&#x3D;xxx.xx.xxx.9<br>dbms.connector.bolt.enabled&#x3D;true<br>dbms.connector.bolt.listen_address&#x3D;:7687</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">然后三台集群分别启动即可，无启动顺序。等待启动完成查看neo4j状态或者看日志都可以看到启动已经成功。</span><br><span class="line"></span><br><span class="line">## 2.2 Neo4j-shell 报错Connection refused</span><br></pre></td></tr></table></figure><p>cd &#x2F;etc&#x2F;neo4j<br>vim neo4j.conf<br>去掉 #dbms.shell.enabled&#x3D;true 前面的#</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">更改后重启neo4j即可</span><br><span class="line"></span><br><span class="line"># 3. 部分需要脚本</span><br><span class="line">**下面脚本都需要在文件解压后在“/opt/neo4j-enterprise-3.4.18/bin”（解压文件的bin目录下）目录下手动建立相关的文件**</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">## 3.1 清空库脚本</span><br><span class="line">- 路径：</span><br><span class="line">&gt; /opt/neo4j-enterprise-3.4.18/bin/dropDataBase.sh</span><br><span class="line"></span><br><span class="line">- 内容：</span><br></pre></td></tr></table></figure><p>#!&#x2F;bin&#x2F;bash</p><h1 id="Neo4j删除边和顶点的脚本"><a href="#Neo4j删除边和顶点的脚本" class="headerlink" title="Neo4j删除边和顶点的脚本"></a>Neo4j删除边和顶点的脚本</h1><h1 id="删除边"><a href="#删除边" class="headerlink" title="删除边"></a>删除边</h1><p>&#x2F;opt&#x2F;neo4j-enterprise-3.4.18&#x2F;bin&#x2F;cypher-shell  “call apoc.periodic.iterate(‘match ()-[r]-() return r’,’delete r’,{batchsize:’100000’,iterateList:true,paraller:true,concurrency:8})”</p><h1 id="删除点"><a href="#删除点" class="headerlink" title="删除点"></a>删除点</h1><p>&#x2F;opt&#x2F;neo4j-enterprise-3.4.18&#x2F;bin&#x2F;cypher-shell  “call apoc.periodic.iterate(‘match (n) return n’,’delete n’,{batchsize:’100000’,iterateList:true,paraller:true,concurrency:8})”</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">- 说明：</span><br><span class="line">&gt; 该脚本为删库脚本，需要删库的时候**先执行删除边**脚本**再执行删除点**脚本</span><br><span class="line"></span><br><span class="line">## 3.2 服务崩溃自动启动脚本</span><br><span class="line">- 路径：</span><br><span class="line">&gt; /opt/neo4j-enterprise-3.4.18/bin/selfStartUp.sh</span><br><span class="line"></span><br><span class="line">- 内容：</span><br></pre></td></tr></table></figure><p>check_point(){<br>    echo “端口检测”<br>    netstat -tlpn grep “\b$1\b”<br>}<br>while true<br>do<br>if check_point 7474<br>then<br>    echo “已存在”<br>else<br>    echo “不在”<br>    .&#x2F;neo4j start<br>fi<br>  sleep 10m<br>done</p><pre><code>- 说明：&gt; 该脚本需要在**集群的三台机器上**常驻执行，neo4j安装后执行```nohup  selfStartUp.sh  &amp; ```启动</code></pre>]]>
    </content>
    <id>https://studyflowblog.com/2020/08/13/neo4j%E9%9B%86%E7%BE%A4%E5%AE%89%E8%A3%85/</id>
    <link href="https://studyflowblog.com/2020/08/13/neo4j%E9%9B%86%E7%BE%A4%E5%AE%89%E8%A3%85/"/>
    <published>2020-08-13T07:27:32.000Z</published>
    <summary>Neo4j企业版三节点集群完整安装配置步骤，含第三方依赖包清单、配置文件修改及集群节点ID分配</summary>
    <title>neo4j集群安装步骤</title>
    <updated>2026-04-02T12:26:25.200Z</updated>
  </entry>
  <entry>
    <author>
      <name>Wednesday</name>
    </author>
    <category term="机器学习笔记" scheme="https://studyflowblog.com/categories/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    <category term="machine learning" scheme="https://studyflowblog.com/tags/machine-learning/"/>
    <content>
      <![CDATA[<h1 id="0-概述"><a href="#0-概述" class="headerlink" title="0.概述"></a>0.概述</h1><p>部分摘录</p><h1 id="1-线性回归"><a href="#1-线性回归" class="headerlink" title="1. 线性回归"></a>1. 线性回归</h1><h2 id="1-1-特征缩放（归一化）"><a href="#1-1-特征缩放（归一化）" class="headerlink" title="1.1 特征缩放（归一化）"></a>1.1 特征缩放（归一化）</h2><h3 id="1-1-1-Standardization"><a href="#1-1-1-Standardization" class="headerlink" title="1.1.1 Standardization"></a>1.1.1 Standardization</h3><p>Standardization又称为 Z-score normalization，量化后的特征将服从标准正态分布：</p><p>z &#x3D; （X<sub>i</sub>-μ）&#x2F;δ</p><p>其中， μ ， δ  分别为对应特征  x<sub>i</sub>  的均值和标准差。量化后的特征将分布在  [−1,1]  区间</p><!-- more --><h3 id="1-1-2-Min-Max-Scaling"><a href="#1-1-2-Min-Max-Scaling" class="headerlink" title="1.1.2 Min-Max Scaling"></a>1.1.2 Min-Max Scaling</h3><p>Min-Max Scaling 又称为 normalization，特征量化的公式为：</p><p>z &#x3D; x<sub>i</sub>−min(x<sub>i</sub>)  &#x2F;  max(x<sub>i</sub>)−min(x<sub>i</sub>)</p><p>量化后的特征将分布在[0,1]区间。</p><p>大多数机器学习算法中，会选择 Standardization 来进行特征缩放，但是，Min-Max Scaling 也并非会被弃置一地。在数字图像处理中，像素强度通常就会被量化到[0,1] 区间，在一般的神经网络算法中，也会要求特征被量化到  [0,1]  区间。</p><h2 id="1-2-学习率的调节"><a href="#1-2-学习率的调节" class="headerlink" title="1.2 学习率的调节"></a>1.2 学习率的调节</h2><h3 id="1-2-1-梯度下降"><a href="#1-2-1-梯度下降" class="headerlink" title="1.2.1 梯度下降"></a>1.2.1 梯度下降</h3><ul><li>批量梯度下降</li><li>随机梯度下降</li></ul><h3 id="1-2-2-正规方程"><a href="#1-2-2-正规方程" class="headerlink" title="1.2.2 正规方程"></a>1.2.2 正规方程</h3><p>我们通过梯度下降法来求得J(θ)  的最小值，但是对于学习率α  的调节有时候使得我们非常恼火。为此，我们可通过正规方程来最小化  J(θ) ：</p><p>θ&#x3D;(X^T^X)^−1^X^T^y</p><p>其中， X  为输入向量矩阵，第0  个特征表示偏置（x0&#x3D;1）， y  为目标向量，仅从该表达式形式上看，我们也脱离了学习率α 的束缚。</p><h2 id="1-3-欠拟合与过拟合"><a href="#1-3-欠拟合与过拟合" class="headerlink" title="1.3 欠拟合与过拟合"></a>1.3 欠拟合与过拟合</h2><h3 id="1-3-1-局部加权线性回归（LWR）"><a href="#1-3-1-局部加权线性回归（LWR）" class="headerlink" title="1.3.1 局部加权线性回归（LWR）"></a>1.3.1 局部加权线性回归（LWR）</h3><p>为了解决欠拟合和过拟合问题，引入了<strong>局部加权线性回归。</strong></p><p>LWR 补充自机器学习实战一书，后续章节中我们知道，更一般地，我们使用<strong>正规化</strong>来解决过拟合问题。</p><h1 id="2-逻辑回归"><a href="#2-逻辑回归" class="headerlink" title="2. 逻辑回归"></a>2. 逻辑回归</h1><ul><li>逻辑回归就是解决分类问题，求决策边界</li></ul><h2 id="2-1-0-1-分类问题"><a href="#2-1-0-1-分类问题" class="headerlink" title="2.1 0&#x2F;1 分类问题"></a>2.1 0&#x2F;1 分类问题</h2><p>简单的根据y值大小（类别）进行分类</p><h2 id="2-2-逻辑回归"><a href="#2-2-逻辑回归" class="headerlink" title="2.2 逻辑回归"></a>2.2 逻辑回归</h2><p>上一节我们知道，使用线性回归来处理 0&#x2F;1 分类问题总是困难重重的，因此，人们定义了逻辑回归来完成 0&#x2F;1 分类问题，逻辑一词也代表了是（1）和非（0）。</p><h3 id="2-2-1-Sigmoid预测函数"><a href="#2-2-1-Sigmoid预测函数" class="headerlink" title="2.2.1 Sigmoid预测函数"></a>2.2.1 Sigmoid预测函数</h3><p>在逻辑回归中，定义预测函数为：<br>h<sub>θ</sub>(x)&#x3D;g(z)</p><p>其中， z&#x3D;θ^T^x  是<strong>分类边界</strong>（分类边界曲线&#x2F;线），且g(z)&#x3D;1 &#x2F; 1+e^−z^</p><p>g(z)  称之为 Sigmoid Function，亦称 Logic Function</p><h2 id="2-3-决策边界"><a href="#2-3-决策边界" class="headerlink" title="2.3 决策边界"></a>2.3 决策边界</h2><p>决策边界，顾名思义，就是用来划清界限的边界，边界的形态可以不定，可以是点，可以是线，也可以是平面。Andrew Ng 在公开课中强调：“<strong>决策边界是预测函数  hθ(x) 的属性，而不是训练集属性</strong>”，这是因为能作出“划清”类间界限的只有h<sub>θ</sub>(x) ，而训练集只是用来训练和调节参数的。</p><h2 id="2-4-利用正规化解决过拟合问题"><a href="#2-4-利用正规化解决过拟合问题" class="headerlink" title="2.4 利用正规化解决过拟合问题"></a>2.4 利用正规化解决过拟合问题</h2><p>在之前的文章中，我们认识了过拟合问题,通常，我们有如下策略来解决过拟合问题：</p><p>减少特征数，显然这只是权宜之计，因为特征意味着信息，放弃特征也就等同于丢弃信息，要知道，特征的获取往往也是艰苦卓绝的。</p><p>不放弃特征，而是拉伸曲线使之更加平滑以解决过拟合问题，为了拉伸曲线，也就<strong>要弱化一些高阶项（曲线曲折的罪魁祸首</strong>）。由于高阶项中的特征 x 无法更改，因此特征是无法弱化的，我们能弱化的只有高阶项中的系数  θ<sub>i</sub> 。我们把这种弱化称之为是对参数  θ 的惩罚（penalize）。Regularization（正规化）正是完成这样一种惩罚的“侩子手”</p><h2 id="2-5-多分类问题"><a href="#2-5-多分类问题" class="headerlink" title="2.5 多分类问题"></a>2.5 多分类问题</h2><p>通常采用 <strong>One-vs-All</strong>，亦称 One-vs-the Rest 方法来实现多分类，其将多分类问题转化为了多次二分类问题。假定完成K个分类，One-vs-All 的执行过程如下：</p><ul><li>轮流选中某一类型i，将其视为正样本，即 “1” 分类，剩下样本都看做是负样本，即 “0” 分类。</li><li>训练逻辑回归模型得到参数  θ^(1)^,θ^(2)^,…,θ^(K)^ ，即总共获得了K−1个决策边界。</li></ul><p>给定输入x，为确定其分类，需要分别计算  h<sub>θ</sub>^(k)^(x),k&#x3D;1,…,K , h<sub>θ</sub>^(k)^(x)越趋近于 1，x  越接近是第k类。</p><p><strong>总结</strong>：ABC三类，将A当作一类，BC当作一类，得到决策边界h<sub>1</sub>；然后将B当一类，AC当一类，得到决策边界h2，以此类推。然后将目标x<sub>θ</sub>代入h<sub>1</sub>,h<sub>2</sub>,h<sub>3</sub>中求解。哪个值越<strong>接近1</strong>，即为x<sub>0</sub>的类别。</p><p><strong>这里为什么条件是接近1</strong>：因为逻辑回归的归一化函数比如说上面的sigmod函数归一化的值区间为[0,1],如果h<sub>3</sub>(x0)值为0.8表示x<sub>0</sub>有0.8的概率为h3类别，并不是h<sub>3</sub>(x<sub>0</sub>)的值越大表示它的类别一定为h<sub>3</sub></p><h1 id="3-神经网络"><a href="#3-神经网络" class="headerlink" title="3. 神经网络"></a>3. 神经网络</h1><h2 id="3-1-再论-0-1-分类问题"><a href="#3-1-再论-0-1-分类问题" class="headerlink" title="3.1 再论 0&#x2F;1 分类问题"></a>3.1 再论 0&#x2F;1 分类问题</h2><p>在逻辑回归中，通过对特征进行多项式展开，可以让逻辑回归支持非线性的分类问题。<br>但是当数据的特征维度比较高的时候，多项式展开扩展后的特征个数是特别高的，对计算机的性能提出了很大的挑战。</p><p>比如说在计算机视觉（CV）领域，图像的特征往往都是高维的</p><p>因此，就需要考虑用新的机器学习模型来处理高维特征的非线性分类问题，<strong>神经网络是典型的不需要增加特征数目就能完成非线性分类问题的模型</strong>。</p><h2 id="3-2-神经网络概述"><a href="#3-2-神经网络概述" class="headerlink" title="3.2 神经网络概述"></a>3.2 神经网络概述</h2><h3 id="3-2-1-卷积层"><a href="#3-2-1-卷积层" class="headerlink" title="3.2.1 卷积层"></a>3.2.1 卷积层</h3><p>作用：提取图像特征</p><p>关于卷积核的选取</p><h3 id="3-2-2-池化层"><a href="#3-2-2-池化层" class="headerlink" title="3.2.2 池化层"></a>3.2.2 池化层</h3><p>特征降维</p><h3 id="3-2-3-全连接层"><a href="#3-2-3-全连接层" class="headerlink" title="3.2.3 全连接层"></a>3.2.3 全连接层</h3><p>全连接的核心操作就是矩阵向量乘积：y&#x3D;Wx</p><p>本质就是由一个特征空间线性变换到另一个特征空间。目标空间的任一维——也就是隐层的一个 cell——都认为会受到源空间的每一维的影响。不考虑严谨，可以说，目标向量是源向量的加权和。</p><p>在 CNN 中，全连接常出现在最后几层，<strong>用于对前面设计的特征做加权和</strong>。比如 mnist，前面的卷积和池化相当于做特征工程，后面的全连接相当于做特征加权。（卷积相当于全连接的有意弱化，按照局部视野的启发，把局部之外的弱影响直接抹为零影响；还做了一点强制，不同的局部所使用的参数居然一致。弱化使参数变少，节省计算量，又专攻局部不贪多求全；强制进一步减少参数。少即是多）</p><p>在 RNN 中，全连接用来把 embedding 空间拉到隐层空间，把隐层空间转回 label 空间等</p><h1 id="4-算法分析与优化"><a href="#4-算法分析与优化" class="headerlink" title="4. 算法分析与优化"></a>4. 算法分析与优化</h1><h2 id="4-1-调优方法"><a href="#4-1-调优方法" class="headerlink" title="4.1 调优方法"></a>4.1 调优方法</h2><p>在线性回归中，我们使用了如下的代价函数来评估预测误差：<br>J(θ)&#x3D;1 &#x2F; 2m（costFunction + 正则化表达式）</p><p>想要降低预测误差，即提高预测精度，我们往往会采用这些手段：</p><table><thead><tr><th>手段</th><th>优&#x2F;劣</th></tr></thead><tbody><tr><td>采集更多的样本</td><td>我们认为见多识广会让人变得聪明，但是也会让人变得优柔寡断，或者聪明反被聪明误。</td></tr><tr><td>降低特征维度</td><td>也许被降掉的维度会是非常有用的知识。</td></tr><tr><td>采集更多的特征</td><td>增加了计算负担，也可能导致过拟合。</td></tr><tr><td>进行高次多项式回归</td><td>可能造成过拟合。</td></tr><tr><td>调试正规化参数λ</td><td>这个调节策略缺乏指导，只能是猜测性调节。</td></tr></tbody></table><h2 id="4-2-数据集划分"><a href="#4-2-数据集划分" class="headerlink" title="4.2 数据集划分"></a>4.2 数据集划分</h2><p>训练集：70%<br>测试集：30%</p><p>在对数据集进行训练集和测试集的划分前，最好先对数据集进行乱序，防止类似样本聚到一起</p><h3 id="4-2-1-交叉验证集"><a href="#4-2-1-交叉验证集" class="headerlink" title="4.2.1  交叉验证集"></a>4.2.1  交叉验证集</h3><p>在多项式回归中,我们总是尝试不同的多项式次数（degree）,形成了不同的预测模型:</p><p>y1&#x3D; θ<sub>0</sub>X</p><p>y2 &#x3D; θ<sub>0</sub>X  + θ<sub>1</sub>X^2^ </p><p>y3 &#x3D; θ<sub>0</sub>X + θ<sub>1</sub>X^2^ + θ<sub>2</sub>X^3^</p><p>假设y<sub>3</sub>的测试误差最小，我们选取y<sub>3</sub>中的θ作为模型的参数。<br>这时参数已经有了，如何<strong>评估模型的泛化能力</strong>呢？</p><p>这时就要使用交叉验证集对模型的泛化能力进行验证。通常情况下：</p><ul><li>训练集：60%，确定参数  θ </li><li>交叉验证集：20%，进行模型选择。</li><li>测试集：20%，评价模型预测能力。</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">graph LR</span><br><span class="line">使用训练集得到参数θ--&gt;使用交叉验证集对模型泛化能力进行验证可能会修改超参数θ的值</span><br><span class="line">使用交叉验证集对模型泛化能力进行验证可能会修改超参数θ的值--&gt; 使用测试集评价模型预测能力</span><br></pre></td></tr></table></figure><h2 id="4-3偏差与方差"><a href="#4-3偏差与方差" class="headerlink" title="4.3偏差与方差"></a>4.3偏差与方差</h2><p>在机器学习中，<strong>偏差</strong>（bias）反映了模型无法描述数据规律，而<strong>方差</strong>（variance）反映了模型对训练集过度敏感，而丢失了数据规律，高偏差和高方差都会造成新数据到来时，模型给出错误的预测。</p><p>通过诊断（Diagnose）模型是出现了高偏差问题还是高方差问题，我们能对症下药，采取不同的解决策略</p><table><thead><tr><th>方法</th><th>使用场景</th></tr></thead><tbody><tr><td>采集更多的样本</td><td>高方差</td></tr><tr><td>降低特征维度</td><td>高方差</td></tr><tr><td>采集更多的特征</td><td>高偏差</td></tr><tr><td>进行高次多项式回归</td><td>高偏差</td></tr><tr><td>降低参数  λ</td><td>高方差</td></tr><tr><td>增大参数  λ</td><td>高偏差</td></tr></tbody></table><h2 id="4-4-问题分析方法"><a href="#4-4-问题分析方法" class="headerlink" title="4.4 问题分析方法"></a>4.4 问题分析方法</h2><p>对于机器学习问题，吴恩达给出了一些 tips：</p><ul><li>在一开始，尽量不要将问题复杂化（不要提前优化），先快速实现一个简单算法，然后通过交叉验证集评估模型。这就好比在软件工程中，不会做提前优化，而是先迭代功能。</li><li>通过绘制学习曲线（learning curve），确定面临的问题是高偏差还是高方差，来决定是添加更多训练样本，还是添加更多特征。</li><li>甚至可以手动检查交叉验证集中误差较大的样本，确定错误的来源和解决策略。</li><li></li></ul><h3 id="4-4-1-举个例子"><a href="#4-4-1-举个例子" class="headerlink" title="4.4.1 举个例子"></a>4.4.1 举个例子</h3><p>假定交叉验证集有 500 个样本，即  mcv&#x3D;500mcv&#x3D;500 ，我们的模型错分了其中 100 个样本，那么我们会通过下述手段进行错误分析：</p><ul><li>需要知道哪些邮件被错分了，是假冒伪劣的推销邮件？医药邮件？还是钓鱼邮件？</li><li>需要知道提供什么线索（特征）能帮助模型区分出这些邮件？</li></ul><p>例如，在这 100 个错分样本中，我们发现有 53 个样本是钓鱼邮件，因此，我们就需要考虑为模型注入识别的钓鱼邮件的能力。继续观察，我们发现，在这 53 封钓鱼邮件中，故意使用错误拼写的邮件有 5 封，来源可疑（发送人可疑）的邮件有 16 封，使用了大量煽动性标点符号的邮件有 32 封。因此，对于识别钓鱼邮件来说，我们更适合将煽动性标点符号添加为特征，而不用再考虑去识别错误拼写。</p><h1 id="5-SVM"><a href="#5-SVM" class="headerlink" title="5. SVM"></a>5. SVM</h1><p>常用来处理多维度不规则分类问题</p><h2 id="5-1-大间距分类器"><a href="#5-1-大间距分类器" class="headerlink" title="5.1 大间距分类器"></a>5.1 大间距分类器</h2><p>SVM 是典型的大间距分类器：寻找一个直线（面）将数据分类，而这个线（面）到所有点的<strong>距离之和最大</strong>。</p><h2 id="5-2-核函数"><a href="#5-2-核函数" class="headerlink" title="5.2 核函数"></a>5.2 核函数</h2><p>在逻辑回归中，我们会通过多项式扩展来处理非线性分类问题：<br>h<sub>0</sub>(x)&#x3D;θ<sub>0</sub>+θ<sub>1</sub>x<sub>1</sub>+θ<sub>2</sub>x<sub>2</sub>+θ<sub>3</sub>x<sub>1</sub>x<sub>2</sub>+θ<sub>4</sub>x<sub>1</sub>^2^+θ<sub>5</sub>x<sub>2</sub>^2^+⋯</p><p>假设我们令：</p><p>f<sub>1</sub>&#x3D;x<sub>1</sub>,</p><p>f<sub>2</sub>&#x3D;x<sub>2</sub>,</p><p>f<sub>3</sub>&#x3D;x<sub>1</sub>x<sub>2</sub>,</p><p>f<sub>4</sub>&#x3D;x<sub>1</sub>^2^,</p><p>f<sub>5</sub>&#x3D;x<sub>2</sub>^2^</p><p>则预测函数为：</p><p>h<sub>θ</sub>(x)&#x3D;θ<sub>0</sub>+θ<sub>1</sub>f<sub>1</sub>+θ<sub>2</sub>f<sub>2</sub>+θ<sub>3</sub>f<sub>3</sub>+⋯</p><p>但多项式回归所带来的高阶项不一定作用明显，针对这一问题，SVM 不会引入高阶项来作为新的特征，而是会选择一些标记点（landmark），并将样本x与标记点l^(i)^的相似程度作为<strong>新的训练特征f<sub>i</sub>（将f<sub>i</sub>作为数据集的一个属性进行训练）</strong> ：</p><p>距离度量的方式就称之为核函数（Kernel），最常见的核函数是高斯核函数（Gaussian Kernel）</p><h2 id="5-3-svm的使用说明"><a href="#5-3-svm的使用说明" class="headerlink" title="5.3 svm的使用说明"></a>5.3 svm的使用说明</h2><h3 id="5-3-1-参数和核函数"><a href="#5-3-1-参数和核函数" class="headerlink" title="5.3.1 参数和核函数"></a>5.3.1 参数和核函数</h3><p>使用这些库时，我们通常需要声明 SVM 需要的两个关键部分：</p><ul><li><p>参数  C </p><blockquote><p>由于  C  可以看做与正规化参数  λ  作用相反，则对于  C  的调节：</p><p>低偏差，高方差，即遇到了过拟合时：减小  C  值。<br>高偏差，低方差，即遇到了欠拟合时：增大  C  值。</p></blockquote></li><li><p>核函数（Kernel）</p><blockquote><p>而对于核函数的选择有这么一些 tips：</p><p>当特征维度n较高，而样本规模m较小时，不宜使用核函数，否则容易引起过拟合。</p><p>当特征维度n较低，而样本规模m足够大时，考虑使用高斯核函数。不过在使用高斯核函数前，需要进行特征缩放（feature scaling）。另外，当核函数的参数δ较大时，特征f<sub>i</sub>  较为平缓，即各个样本的特征差异变小，此时会造成欠拟合（高偏差，低方差）：</p></blockquote></li></ul><h3 id="5-3-2-分类模型的选择"><a href="#5-3-2-分类模型的选择" class="headerlink" title="5.3.2 分类模型的选择"></a>5.3.2 分类模型的选择</h3><p>目前，我们学到的分类模型有：</p><blockquote><p>（1）逻辑回归<br>（2）神经网络；<br>（3）SVM。</p></blockquote><p>怎么选择在这三者中做出选择呢？我们考虑特征维度n及样本规模m ：</p><ul><li>如果n相对于m非常大，例如例如  n&#x3D;10000,m∈(10,10000) :此时选用用逻辑回归或者无核的 SVM。</li><li>如果n较小，m适中，如 n∈(1,1000) ，而 m∈(10,10000) ：此时选用核函数为高斯核函数的 SVM。</li><li>如果n较小，m 较大，如 n∈(1,1000) ，而  m&gt;50000 ：此时，需要创建更多的特征（比如通过多项式扩展），再使用逻辑回归或者无核的 SVM。</li></ul><p>神经网络对于上述情形都有不错的适应性，但是计算性能上较慢。</p><h1 id="6-k-means"><a href="#6-k-means" class="headerlink" title="6. k-means"></a>6. k-means</h1><ul><li>聚类算法，其中的k为簇的种类数</li><li>簇中心的选取：遍历随机初始化选取</li><li>确定聚类数：肘部法则</li></ul><h2 id="6-1-二分k-means"><a href="#6-1-二分k-means" class="headerlink" title="6.1 二分k-means"></a>6.1 二分k-means</h2><ul><li>常规的 K-Means 算法的误差通常只能收敛到局部最小，在此，引入一种称为二分 K-Means（bisecting kmeans）的算法，相较于常规的 K-Means，二分 K-Means 不急于一来就随机K个聚类中心，而是首先把所有点归为一个簇，然后将该簇一分为二。计算各个所得簇的失真函数（即误差），选择误差最大的簇再进行划分（即最大程度地减少误差），重复该过程直至达到期望的簇数目</li><li>虽然二分 K-Means 能带来全局最优解，但是我们也可以看到，该算法是一个贪心算法，因此计算量不小。</li></ul><h1 id="7-特征降维"><a href="#7-特征降维" class="headerlink" title="7. 特征降维"></a>7. 特征降维</h1><ul><li>特征降维的一般手段就是将高维特征投影到低维空间</li></ul><h2 id="7-1-PCA（主成分分析）"><a href="#7-1-PCA（主成分分析）" class="headerlink" title="7.1 PCA（主成分分析）"></a>7.1 PCA（主成分分析）</h2><p>PCA，Principle Component Analysis，即主成分分析法，是<strong>特征降维</strong>的最常用手段。顾名思义，PCA 能从<strong>冗余特征中提取主要成分，在不太损失模型质量的情况下，提升了模型训练速度</strong></p><h1 id="8-异常检测"><a href="#8-异常检测" class="headerlink" title="8. 异常检测"></a>8. 异常检测</h1><h2 id="8-1-高斯分布模型"><a href="#8-1-高斯分布模型" class="headerlink" title="8.1 高斯分布模型"></a>8.1 高斯分布模型</h2><p>异常检测的核心就在于找到一个概率模型，帮助我们知道一个样本落入正常样本中的概率，从而帮助我们区分正常和异常样本。高斯分布（Gaussian Distribution）模型就是异常检测算法最常使用的概率分布模型。</p><h1 id="9-推荐系统"><a href="#9-推荐系统" class="headerlink" title="9. 推荐系统"></a>9. 推荐系统</h1><h2 id="9-1-协同过滤"><a href="#9-1-协同过滤" class="headerlink" title="9.1 协同过滤"></a>9.1 协同过滤</h2><ul><li><p>基于用户的协同过滤推荐(User-based Collaborative Filtering Recommendation)</p><blockquote><p>基于用户的协同过滤推荐算法先使用统计技术寻找与目标用户有相同喜好的邻居，然后根据目标用户的邻居的喜好产生向目标用户的推荐。基本原理就是利用用户访问行为的相似性来互相推荐用户可能感兴趣的资源</p></blockquote></li><li><p>基于项目的协同过滤推荐(Item-based Collaborative Filtering Recommendation)</p><blockquote><p>根据所有用户对物品或者信息的评价，发现物品和物品之间的相似度，然后根据用户的历史偏好信息将类似的物品推荐给该用户</p></blockquote></li><li><p>基于模型的协同过滤推荐(Model-based Collaborative Filtering Recommendation)</p></li></ul><blockquote><p>基模型的协同过滤推荐就是基于样本的用户喜好信息，训练一个推荐模型，然后根据实时的用户喜好的信息进行预测推荐</p></blockquote>]]>
    </content>
    <id>https://studyflowblog.com/2020/05/20/machine-learning-notes/</id>
    <link href="https://studyflowblog.com/2020/05/20/machine-learning-notes/"/>
    <published>2020-05-20T01:44:56.000Z</published>
    <summary>机器学习基础笔记，涵盖特征缩放（Standardization/Min-Max）、正则化、逻辑回归等核心概念整理</summary>
    <title>机器学习基础笔记：线性回归与特征缩放</title>
    <updated>2026-04-02T12:38:02.920Z</updated>
  </entry>
  <entry>
    <author>
      <name>Wednesday</name>
    </author>
    <category term="大数据" scheme="https://studyflowblog.com/categories/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    <category term="Hbase" scheme="https://studyflowblog.com/tags/Hbase/"/>
    <content>
      <![CDATA[<h1 id="1-HBase的存储形式"><a href="#1-HBase的存储形式" class="headerlink" title="1. HBase的存储形式"></a>1. HBase的存储形式</h1><p>hbase的内部使用KeyValue的形式存储，其key是rowKey：family：column：logTime，value是其存储的内容。</p><p>其在region内大多以升序的形式排列，唯一的时logTime是以降序的形式进行排列。</p><p>所以，rowKey里越靠近左边的信息越容易被检索到。其设计时，要考虑把重要的信息放左边，不重要的信息放到右边。这样可以提高查询数据的速度。最重要的提高索引速度的就是设计合适的rowKey。</p><p>在做RowKey设计时，请先考虑业务是读比写多，还是读比写少，HBase本身是为写优化的，即便是这样，也可能会出现热点问题，而如果我们读比较多的话，除了考虑以上RowKey设计原则外，还可以考虑HBase的Coprocessor甚至elasticSearch结合的方法，无论哪种方式，都建议做实际业务场景下数据的压力测试以得到最优结果。</p><h2 id="1-1-Hbase的存储结构"><a href="#1-1-Hbase的存储结构" class="headerlink" title="1.1 Hbase的存储结构"></a>1.1 Hbase的存储结构</h2><h3 id="1-1-1-逻辑存储结构"><a href="#1-1-1-逻辑存储结构" class="headerlink" title="1.1.1 逻辑存储结构"></a>1.1.1 逻辑存储结构</h3><table><thead><tr><th>-</th><th>-</th><th>列族：info</th><th>列族：area</th></tr></thead><tbody><tr><td>行键</td><td>时间戳</td><td>name &#x2F;  age</td><td>country &#x2F; city</td></tr><tr><td>Row1</td><td>t1</td><td>张三 &#x2F; 11</td><td>中国 &#x2F; 上海</td></tr><tr><td>Row2</td><td>t2</td><td>王二 &#x2F; 20</td><td></td></tr></tbody></table><p>在本图中，列簇（Column Family）对应的值就是 info 和 area ，列（ Column 或者称为 Qualifier ）对应的就是 name 、 age 、 country 和 city ，Row key 对应的就是 Row 1 和 Row 2，Cell 对应的就是具体的值。</p><ul><li>Row key ：表的主键，按照字典序排序。</li><li>列簇：在 HBase 中，列簇将表进行横向切割。</li><li>列：属于某一个列簇，在 HBase 中可以进行动态的添加。</li><li>Cell : 是指具体的 Value 。</li><li>Version ：在这张图里面没有显示出来，这个是指版本号，用时间戳（TimeStamp ）来表示。</li></ul><p>在 HBase 中的 KEY 组成是这样的：</p><table><thead><tr><th>Row key</th><th>CF（列簇）</th><th>cloumn</th><th>TimeStamp</th><th>value</th><th></th></tr></thead><tbody><tr><td>row 1</td><td>info</td><td>name</td><td>t1</td><td>张三</td><td></td></tr></tbody></table><p>KEY 的组成是以 Row key 、CF(Column Family) 、Column 和 TimeStamp 组成的。</p><p>TimeStamp 在 HBase 中充当的作用就是版本号，因为在 HBase 中有着数据多版本的特性，所以同一个 KEY 可以有多个版本的 Value 值（可以通过配置来设置多少个版本）。查询的话是默认取回最新版本的那条数据，但是也可以进行查询多个版本号的数据</p><h3 id="1-1-2-Region-Server-和-Region-的关系"><a href="#1-1-2-Region-Server-和-Region-的关系" class="headerlink" title="1.1.2 Region Server 和 Region 的关系"></a>1.1.2 Region Server 和 Region 的关系</h3><table><thead><tr><th>Region Server</th><th>Region Server</th><th>Region Server</th><th>Region Server</th></tr></thead><tbody><tr><td><strong>RegionA</strong></td><td><strong>RegionA</strong></td><td><strong>RegionB</strong></td><td><strong>RegionB</strong></td></tr><tr><td>CFA</td><td>CFB</td><td>CFC</td><td>CFD</td></tr></tbody></table><ul><li>一个 Region Server 就是一个机器节点(服务器)</li><li>一个 Region Server 包含着多个 Region</li><li>一个 Region 包含着多个列簇 (CF)</li><li>一个 Region Server 中可以有多张 Table，一张 Table 可以有多个 Region</li></ul><h3 id="1-1-3-Hbase读取数据的过程"><a href="#1-1-3-Hbase读取数据的过程" class="headerlink" title="1.1.3 Hbase读取数据的过程"></a>1.1.3 Hbase读取数据的过程</h3><p>Client 请求读取数据时，先转发到 ZK 集群，在 ZK 集群中寻找到相对应的 Region Server，再找到对应的 Region，先是查 MemStore，如果在 MemStore 中获取到数据，那么就会直接返回，否则就是再由 Region 找到对应的 Store File，从而查到具体的数据。</p><p>在整个架构中，HMaster 和 HRegion Server 可以是同一个节点上，可以有多个 HMaster 存在，但是只有一个 HMaster 在活跃。</p><p>在 Client 端会进行 rowkey-&gt; HRegion 映射关系的缓存，降低下次寻址的压力。</p><h2 id="1-2-HBase的存储机制"><a href="#1-2-HBase的存储机制" class="headerlink" title="1.2 HBase的存储机制"></a>1.2 HBase的存储机制</h2><p>HBase是一个面向列的数据库，在表中它由行排序。表模式定义只能列族，也就是键值对。一个表有多个列族以及每一个列族可以有任意数量的列。后续列的值连续存储在磁盘上。表中的每个单元格值都具有时间戳。总之，在一个HBase：</p><ul><li>表是行的集合。</li><li>行是列族的集合。</li><li>列族是列的集合。</li><li>列是键值对的集合。</li></ul><p>这里的列式存储或者说面向列，其实说的是列族存储，HBase是根据列族来存储数据的。列族下面可以有非常多的列，列族在创建表的时候就必须指定。</p><h1 id="2-HBase中建立分区（表）"><a href="#2-HBase中建立分区（表）" class="headerlink" title="2.HBase中建立分区（表）"></a>2.HBase中建立分区（表）</h1><h2 id="2-1-为什么要建立分区表-分区表存在的意义是什么"><a href="#2-1-为什么要建立分区表-分区表存在的意义是什么" class="headerlink" title="2.1 为什么要建立分区表&#x2F;分区表存在的意义是什么:"></a>2.1 为什么要建立分区表&#x2F;分区表存在的意义是什么:</h2><ul><li>将大数据表的增加由无序变为有序</li></ul><h2 id="2-2-原理"><a href="#2-2-原理" class="headerlink" title="2.2 原理"></a>2.2 原理</h2><blockquote><p>在HBase中，每个表默认时都由一个Region存储原数据，但是若当数据慢慢增加时，就会将一个Region分裂成其他若干个不同的region。但是这种无序的分裂，会让后期的表扫描，表过滤操作很不方便（特别耗时）。正是着眼于未来表的改变，才有了为表创建分区的操作。<br>提前创建分区表为有序</p></blockquote>]]>
    </content>
    <id>https://studyflowblog.com/2020/05/11/HBase%E5%9F%BA%E7%A1%80/</id>
    <link href="https://studyflowblog.com/2020/05/11/HBase%E5%9F%BA%E7%A1%80/"/>
    <published>2020-05-11T05:03:36.000Z</published>
    <summary>介绍HBase的KeyValue存储结构、RowKey设计原则及读写优化策略，兼顾Coprocessor与ElasticSearch结合方案</summary>
    <title>HBase基础</title>
    <updated>2026-04-02T12:25:50.326Z</updated>
  </entry>
  <entry>
    <author>
      <name>Wednesday</name>
    </author>
    <category term="工作相关" scheme="https://studyflowblog.com/categories/%E5%B7%A5%E4%BD%9C%E7%9B%B8%E5%85%B3/"/>
    <category term="图数据库" scheme="https://studyflowblog.com/tags/%E5%9B%BE%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
    <category term="nebula-graph" scheme="https://studyflowblog.com/tags/nebula-graph/"/>
    <content>
      <![CDATA[<h1 id="1-nebula的安装"><a href="#1-nebula的安装" class="headerlink" title="1. nebula的安装"></a>1. nebula的安装</h1><h2 id="1-1-下载nebula的rpm包"><a href="#1-1-下载nebula的rpm包" class="headerlink" title="1.1 下载nebula的rpm包"></a>1.1 下载nebula的rpm包</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">bash $ wget https://nebula-graph.oss-cn-hangzhou.aliyuncs.com/package/1.0.0-rc2/nebula-1.0.0-rc2.el7-5.x86_64.rpm</span><br><span class="line"></span><br><span class="line"># centos6的链接格式  </span><br><span class="line"># package/$&#123;release_version&#125;/nebula-$&#123;release_version&#125;.el6-5.x86_64.rpm</span><br><span class="line"># centos7的链接格式</span><br><span class="line"># package/$&#123;release_version&#125;/nebula-$&#123;release_version&#125;.el7-5.x86_64.rpm</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="1-2-安装"><a href="#1-2-安装" class="headerlink" title="1.2 安装"></a>1.2 安装</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ sudo rpm -ivh nebula-2019.12.23-nightly.el6-5.x86_64.rpm</span><br></pre></td></tr></table></figure><h2 id="1-3-启动"><a href="#1-3-启动" class="headerlink" title="1.3 启动"></a>1.3 启动</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ sudo /usr/local/nebula/scripts/nebula.service start all</span><br></pre></td></tr></table></figure><h2 id="1-4-查看-Nebula-Graph-服务"><a href="#1-4-查看-Nebula-Graph-服务" class="headerlink" title="1.4 查看 Nebula Graph 服务"></a>1.4 查看 Nebula Graph 服务</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ sudo /usr/local/nebula/scripts/nebula.service status all</span><br></pre></td></tr></table></figure><h2 id="1-5-连接-Nebula-Graph-服务"><a href="#1-5-连接-Nebula-Graph-服务" class="headerlink" title="1.5 连接 Nebula Graph 服务"></a>1.5 连接 Nebula Graph 服务</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ sudo /usr/local/nebula/bin/nebula -u user -p password</span><br></pre></td></tr></table></figure><h2 id="1-6-停止-Nebula-Graph-服务"><a href="#1-6-停止-Nebula-Graph-服务" class="headerlink" title="1.6 停止 Nebula Graph 服务"></a>1.6 停止 Nebula Graph 服务</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ sudo /usr/local/nebula/scripts/nebula.service stop all</span><br></pre></td></tr></table></figure><h1 id="2-集群部署"><a href="#2-集群部署" class="headerlink" title="2. 集群部署"></a>2. 集群部署</h1><h2 id="2-1集群部署ip需要修改的步骤如下："><a href="#2-1集群部署ip需要修改的步骤如下：" class="headerlink" title="2.1集群部署ip需要修改的步骤如下："></a>2.1集群部署ip需要修改的步骤如下：</h2><ol><li>“nebula&#x2F;etc&#x2F;”文件夹里面的配置文件“nebula-metad.conf”里面的相关ip地址</li><li>本地“etc&#x2F;hosts”里面的hostsip地址</li><li>“nebula&#x2F;etc&#x2F;nebula-graphd.conf”里面的hostname</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">etc/nebula-graphd.conf:26:--meta_server_addrs=ng1:45500</span><br><span class="line">etc/nebula-graphd.conf:28:--local_ip=ng1</span><br><span class="line">etc/nebula-graphd.conf:48:--ws_ip=ng1</span><br></pre></td></tr></table></figure><ol start="4"><li>添加节点服务器地址</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"># 以下几个文件中的地址都要添加节点地址，用逗号隔开</span><br><span class="line">etc/nebula-storaged.conf:19:--meta_server_addrs=127.0.0.1:45500</span><br><span class="line">etc/nebula-metad.conf:20:--meta_server_addrs=192.168.111.133:45500</span><br><span class="line">etc/nebula-graphd.conf:26:--meta_server_addrs=ng2:45500</span><br></pre></td></tr></table></figure><ol start="5"><li>修改nebula的host文件</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">[root@ng2 scripts]# vi graph.hosts </span><br><span class="line">[root@ng2 scripts]# vi meta.hosts </span><br><span class="line">[root@ng2 scripts]# vi storage.hosts </span><br><span class="line"># 添加如下：</span><br><span class="line">ng1</span><br><span class="line">ng2</span><br></pre></td></tr></table></figure><ol start="6"><li>这个时候应该已经可以登陆了，如下，但是登录从节点每次都需要输入密码。为了方便，下一步需要手动设置ssh免密登录</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">[root@ng1 nebula]# ./jiqunRestart.sh </span><br><span class="line">Processing Meta Service ...</span><br><span class="line">start ng1</span><br><span class="line">The authenticity of host &#x27;ng1 (::1)&#x27; can&#x27;t be established.</span><br><span class="line">ECDSA key fingerprint is SHA256:A1I43wcavqxvxEqTh2XYzqdYlXZZVbavUpmoQffE26Y.</span><br><span class="line">ECDSA key fingerprint is MD5:5d:9c:99:d7:70:51:5f:f0:e5:c7:cc:0d:54:e5:b6:f9.</span><br><span class="line">Are you sure you want to continue connecting (yes/no)? yes</span><br><span class="line">Warning: Permanently added &#x27;ng1&#x27; (ECDSA) to the list of known hosts.</span><br><span class="line">root@ng1&#x27;s password: </span><br><span class="line">start ng2</span><br><span class="line">The authenticity of host &#x27;ng2 (192.168.111.133)&#x27; can&#x27;t be established.</span><br><span class="line">ECDSA key fingerprint is SHA256:A1I43wcavqxvxEqTh2XYzqdYlXZZVbavUpmoQffE26Y.</span><br><span class="line">ECDSA key fingerprint is MD5:5d:9c:99:d7:70:51:5f:f0:e5:c7:cc:0d:54:e5:b6:f9.</span><br><span class="line">Are you sure you want to continue connecting (yes/no)? yes</span><br><span class="line">Warning: Permanently added &#x27;ng2,192.168.111.133&#x27; (ECDSA) to the list of known hosts.</span><br><span class="line">root@ng2&#x27;s password: </span><br><span class="line">Processing Storage Service ...</span><br><span class="line">start ng1</span><br><span class="line">root@ng1&#x27;s password: </span><br><span class="line">start ng2</span><br><span class="line">root@ng2&#x27;s password: </span><br><span class="line">Processing Graph Service ...</span><br><span class="line">start ng1</span><br><span class="line">root@ng1&#x27;s password: </span><br><span class="line">start ng2</span><br><span class="line">root@ng2&#x27;s password: </span><br></pre></td></tr></table></figure><ol start="7"><li>配置免密登录</li></ol><ul><li>因为nebula自身是没有从属关系的，这里把ng1当作Master节点，ng2当作slave节点</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">首先生成 Master 节点的公匙，在Master 节点的终端中执行（因为改过主机名，所以还需要删掉原有的再重新生成一次）：</span><br><span class="line">cd ~/.ssh               # 如果没有该目录，先执行一次ssh localhost</span><br><span class="line">rm ./id_rsa*            # 删除之前生成的公匙（如果有）</span><br><span class="line">ssh-keygen -t rsa       # 一直按回车就可以</span><br><span class="line"></span><br><span class="line">让Master节点需能无密码SSH 本机，在 Master节点上执行： </span><br><span class="line">cat ./id_rsa.pub &gt;&gt; ./authorized_keys</span><br><span class="line"></span><br><span class="line">完成后可执行 ssh ng1 验证一下（可能需要输入 yes，成功后执行 exit 返回原来的终端）。接着在 Master 节点将上公匙传输到 ng2 节点：</span><br><span class="line">scp ~/.ssh/id_rsa.pub leox@ng2:/home/hadoop/</span><br><span class="line"></span><br><span class="line">接着在 ng2 节点上，将 ssh 公匙加入授权：</span><br><span class="line">mkdir ~/.ssh       # 如果不存在该文件夹需先创建，若已存在则忽略</span><br><span class="line">cat ~/id_rsa.pub &gt;&gt; ~/.ssh/authorized_keys</span><br><span class="line">rm ~/id_rsa.pub    # 用完就可以删掉了</span><br><span class="line"></span><br><span class="line">如果有其他 Slave 节点，也要执行将 Master公匙传输到 Slave节点、在 Slave 节点上加入授权这两步。</span><br><span class="line">这样，在Master节点上就可以无密码 SSH 到各个 Slave节点了</span><br></pre></td></tr></table></figure><ul><li>配置成功后再次启动结果如下</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">[root@ng1 nebula]# ./jiqunRestart.sh </span><br><span class="line">Processing Meta Service ...</span><br><span class="line">start ng1</span><br><span class="line">start ng2</span><br><span class="line">Processing Storage Service ...</span><br><span class="line">start ng1</span><br><span class="line">start ng2</span><br><span class="line">Processing Graph Service ...</span><br><span class="line">start ng1</span><br><span class="line">start ng2</span><br><span class="line"># 进入命令行</span><br><span class="line">[root@ng1 nebula]# ./login.sh </span><br><span class="line">Welcome to Nebula Graph (Version 1.0.0-rc4)</span><br><span class="line">(user@127.0.0.1:3699) [(none)]&gt; </span><br></pre></td></tr></table></figure><h1 id="3-可视化安装"><a href="#3-可视化安装" class="headerlink" title="3.可视化安装"></a>3.可视化安装</h1><h2 id="3-1-因为可视化组件需要使用docker安装，所以需要先安装docker"><a href="#3-1-因为可视化组件需要使用docker安装，所以需要先安装docker" class="headerlink" title="3.1 因为可视化组件需要使用docker安装，所以需要先安装docker"></a>3.1 因为可视化组件需要使用docker安装，所以需要先安装docker</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"># 见docker官方安装文档：</span><br><span class="line">https://docs.docker.com/engine/install/centos/#install-using-the-repository</span><br></pre></td></tr></table></figure><h2 id="3-2-安装docker-compose"><a href="#3-2-安装docker-compose" class="headerlink" title="3.2 安装docker-compose"></a>3.2 安装docker-compose</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo curl -L &quot;https://github.com/docker/compose/releases/download/1.25.5/docker-compose-$(uname -s)-$(uname -m)&quot; -o /usr/local/bin/docker-compose</span><br></pre></td></tr></table></figure><h2 id="3-3-安装并启动-Nebula-Graph-Studio"><a href="#3-3-安装并启动-Nebula-Graph-Studio" class="headerlink" title="3.3 安装并启动 Nebula Graph Studio"></a>3.3 安装并启动 Nebula Graph Studio</h2><ul><li>在命令行中输入以下命令，下载 Nebula Graph Studio 安装包。</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git clone https://github.com/vesoft-inc/nebula-web-docker</span><br></pre></td></tr></table></figure><ul><li>在命令行中，进入到安装文件夹 nebula-web-docker(当前放在根目录下面)。</li><li>输入 systemctl start docker 启动docker</li><li>输入 docker-compose pull &amp;&amp; docker-compose up 启动 Nebula Graph Studio 服务。</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Creating docker_importer_1 ... done</span><br><span class="line">Creating docker_client_1   ... done</span><br><span class="line">Creating docker_web_1      ... done</span><br><span class="line">Creating docker_nginx_1    ... done</span><br></pre></td></tr></table></figure><blockquote><p>这一步可能会出现“ERROR: Couldn’t connect to Docker daemon at http+docker:&#x2F;&#x2F;localhost - is it running?<br>If it’s at a non-standard location, specify the URL with the DOCKER_HOST environment variable.的问题”，是因为需要给用户登录，直接su给root权限启动就可以了</p></blockquote><ul><li>启动成功，访问: <a href="http://0.0.0.0:7001（本机为192.168.111.135:7001）">http://0.0.0.0:7001（本机为192.168.111.135:7001）</a></li><li>配置数据库的相关信息</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Host :192.168.111.135:3699</span><br><span class="line">用户名:user</span><br><span class="line">密码:password</span><br></pre></td></tr></table></figure><h1 id="4-遇到问题及解决"><a href="#4-遇到问题及解决" class="headerlink" title="4. 遇到问题及解决"></a>4. 遇到问题及解决</h1><h2 id="0-1-某个服务无法启动，查看日志中发现”failed-to-set-SO-REUSEPORT-on-async-server-socket-Protocol-not-available”问题。"><a href="#0-1-某个服务无法启动，查看日志中发现”failed-to-set-SO-REUSEPORT-on-async-server-socket-Protocol-not-available”问题。" class="headerlink" title="0.1  某个服务无法启动，查看日志中发现”failed to set SO_REUSEPORT on async server socket Protocol not available”问题。"></a>0.1  某个服务无法启动，查看日志中发现”failed to set SO_REUSEPORT on async server socket Protocol not available”问题。</h2><ul><li>问题原因<blockquote><p> 使用的版本为centos6.5， 内核版本是小于3.9<br> SO_REUSEPORT 在linux 3.9 及以上才支持的</p></blockquote></li><li>解决方法</li></ul><blockquote><p>修改系统版本为centos7.5 内核版本为3.10即可解决</p></blockquote>]]>
    </content>
    <id>https://studyflowblog.com/2020/04/16/nebula-graph-installation-and-cluster-deployment/</id>
    <link href="https://studyflowblog.com/2020/04/16/nebula-graph-installation-and-cluster-deployment/"/>
    <published>2020-04-16T05:43:04.000Z</published>
    <summary>国产图数据库Nebula Graph的安装部署与集群配置实践，记录踩坑经历及最终弃坑转向Neo4j的原因</summary>
    <title>图数据库nebula-graph的安装与集群部署</title>
    <updated>2026-04-02T12:26:02.835Z</updated>
  </entry>
  <entry>
    <author>
      <name>Wednesday</name>
    </author>
    <category term="工作相关" scheme="https://studyflowblog.com/categories/%E5%B7%A5%E4%BD%9C%E7%9B%B8%E5%85%B3/"/>
    <category term="python" scheme="https://studyflowblog.com/tags/python/"/>
    <content>
      <![CDATA[<h1 id="1-出现问题及解决"><a href="#1-出现问题及解决" class="headerlink" title="1.出现问题及解决"></a>1.出现问题及解决</h1><h2 id="1-1-python打包访问oracle数据库无instantclient相关问题"><a href="#1-1-python打包访问oracle数据库无instantclient相关问题" class="headerlink" title="1.1 python打包访问oracle数据库无instantclient相关问题"></a>1.1 python打包访问oracle数据库无instantclient相关问题</h2><h3 id="1-1-1-问题1：cx-Oracle-DatabaseError-DPI-1047-64-bit-Oracle-Client-library-cannot-be-loaded解决方法”"><a href="#1-1-1-问题1：cx-Oracle-DatabaseError-DPI-1047-64-bit-Oracle-Client-library-cannot-be-loaded解决方法”" class="headerlink" title="1.1.1 问题1：cx_Oracle.DatabaseError: DPI-1047: 64-bit Oracle Client library cannot be loaded解决方法”"></a>1.1.1 问题1：cx_Oracle.DatabaseError: DPI-1047: 64-bit Oracle Client library cannot be loaded解决方法”</h3><blockquote><p>解决方法：本机安装instantclient，安装步骤如下(参考：<a href="http://www.360doc.com/content/12/1103/21/8827884_245559524.shtml">http://www.360doc.com/content/12/1103/21/8827884_245559524.shtml</a>)</p></blockquote><ul><li>第一步，先安装Oracle客户端，结合你的情况自己找合适的版本，我下载的是 oracle-instantclient11.2-basic-11.2.0.3.0-1.x86_64.rpm<br>下载地址为（<a href="http://www.oracle.com/technetwork/topics/linuxx86-64soft-092277.html%EF%BC%89">http://www.oracle.com/technetwork/topics/linuxx86-64soft-092277.html）</a></li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rpm -ivh oracle-instantclient11.2-basic-11.2.0.3.0-1.x86_64.rpm</span><br></pre></td></tr></table></figure><ul><li><p>第二步，找出你的ORACLE_HOME，我安装之后在这里：&#x2F;usr&#x2F;lib&#x2F;oracle&#x2F;11.2&#x2F;client64&#x2F;lib&#x2F;</p></li><li><p>第三步，下载oracle-instantclient11.2-sdk-11.2.0.1.0-1.x86_64.zip 下载地址。解压，把里面那个叫sdk的文件夹复制到 &#x2F;usr&#x2F;lib&#x2F;oracle&#x2F;11.2&#x2F;client64&#x2F;lib&#x2F;</p></li><li><p>第四步，做一个软链：</p></li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ cd /usr/lib/oracle/11.2/client64/lib/</span><br><span class="line">$ ln -s libclntsh.so.11.1 libclntsh.so</span><br></pre></td></tr></table></figure><ul><li>第五步，安装cx_Oracle:</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ export ORACLE_HOME=/usr/lib/oracle/11.2/client64/lib</span><br><span class="line">$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$ORACLE_HOME</span><br><span class="line">$ easy_install cx_Oracle</span><br></pre></td></tr></table></figure><ul><li>试一下：</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ python</span><br><span class="line">$ import cx_Oracle</span><br></pre></td></tr></table></figure><pre><code>done.</code></pre><ul><li>不过还没配环境变量，一退再进去就不行了，在.bashrc里加入刚才安装之前的那两句：</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$cd vim ~/.bashrc</span><br><span class="line">export ORACLE_HOME=/usr/lib/oracle/11.2/client64/lib</span><br><span class="line">export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$ORACLE_HOME</span><br><span class="line"></span><br><span class="line">source ~/.bashrc</span><br></pre></td></tr></table></figure><h3 id="1-1-2-问题2：上述问题解决后出现“cx-Oracle-DatabaseError-Error-while-trying-to-retrieve-text-for-error-ORA-01804”问题"><a href="#1-1-2-问题2：上述问题解决后出现“cx-Oracle-DatabaseError-Error-while-trying-to-retrieve-text-for-error-ORA-01804”问题" class="headerlink" title="1.1.2 问题2：上述问题解决后出现“cx_Oracle.DatabaseError: Error while trying to retrieve text for error ORA-01804”问题"></a>1.1.2 问题2：上述问题解决后出现“cx_Oracle.DatabaseError: Error while trying to retrieve text for error ORA-01804”问题</h3><blockquote><p>解决方法（参考“<a href="https://blog.csdn.net/zklth/article/details/7184032%EF%BC%89%E2%80%9D%EF%BC%9A">https://blog.csdn.net/zklth/article/details/7184032）”：</a></p></blockquote><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">注释掉</span><br><span class="line">os.environ[&quot;LD_LIBRARY_PATH&quot;] = &#x27;$LD_LIBRARY_PATH:$ORACLE_HOME:$ORACLE_HOME&#x27;</span><br></pre></td></tr></table></figure><blockquote><p>出现新问题 “cx_Oracle连接数据库错误ORA-21561: 生成 OID 失败”</p></blockquote><blockquote><p>解决方法:参考“<a href="https://www.rookiefly.cn/detail/161%E2%80%9D">https://www.rookiefly.cn/detail/161”</a></p></blockquote><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">修改本机hostname名称，然后终于成功</span><br><span class="line">cat /etc/hosts  </span><br><span class="line">127.0.0.1   localhost redhat6</span><br><span class="line">::1         localhost redhat6</span><br></pre></td></tr></table></figure><h2 id="1-1-3-问题3：数据库可以访问后出现-“from-numpy-testing-import-nosetester-ImportError-cannot-import-name-‘nosetester’”问题"><a href="#1-1-3-问题3：数据库可以访问后出现-“from-numpy-testing-import-nosetester-ImportError-cannot-import-name-‘nosetester’”问题" class="headerlink" title="1.1.3 问题3：数据库可以访问后出现 “from numpy.testing import nosetester  ImportError: cannot import name ‘nosetester’”问题"></a>1.1.3 问题3：数据库可以访问后出现 “from numpy.testing import nosetester  ImportError: cannot import name ‘nosetester’”问题</h2><blockquote><p>问题原因：当前安装在python3.5.2的numpy版本和pandas版本冲突，修改版本<br>可用版本: </p></blockquote><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">numpy (1.11.1)</span><br><span class="line">pandas (0.18.1)</span><br></pre></td></tr></table></figure><h2 id="1-1-4-问题4：打包报错“-File-“sklearn-metrics-pairwise-fast-pyx”-line-1-in-init-sklearn-metrics-pairwise-fast-ImportError-No-module-named-‘sklearn-utils-cython-blas’”"><a href="#1-1-4-问题4：打包报错“-File-“sklearn-metrics-pairwise-fast-pyx”-line-1-in-init-sklearn-metrics-pairwise-fast-ImportError-No-module-named-‘sklearn-utils-cython-blas’”" class="headerlink" title="1.1.4 问题4：打包报错“ File “sklearn&#x2F;metrics&#x2F;pairwise_fast.pyx”, line 1, in init sklearn.metrics.pairwise_fast ImportError: No module named ‘sklearn.utils._cython_blas’”"></a>1.1.4 问题4：打包报错“ File “sklearn&#x2F;metrics&#x2F;pairwise_fast.pyx”, line 1, in init sklearn.metrics.pairwise_fast ImportError: No module named ‘sklearn.utils._cython_blas’”</h2><blockquote><p>问题原因：打包的时候无法自动加入sklearn包，手动添加即可</p></blockquote><blockquote><p>解决方法：在main.spec中的hiddenimports属性中手动添加</p></blockquote><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">hiddenimports=[&#x27;cython&#x27;,  &#x27;sklearn&#x27;, &#x27;sklearn.utils._cython_blas&#x27;,&#x27;sklearn.neighbors.typedefs&#x27;,</span><br><span class="line">           &#x27;sklearn.neighbors.quad_tree&#x27;,&#x27;sklearn.tree&#x27;,&#x27;sklearn.tree._utils&#x27;],</span><br></pre></td></tr></table></figure><h2 id="1-1-5-问题5：打包后运行然后又报问题2，“cx-Oracle-DatabaseError-Error-while-trying-to-retrieve-text-for-error-ORA-01804”"><a href="#1-1-5-问题5：打包后运行然后又报问题2，“cx-Oracle-DatabaseError-Error-while-trying-to-retrieve-text-for-error-ORA-01804”" class="headerlink" title="1.1.5 问题5：打包后运行然后又报问题2，“cx_Oracle.DatabaseError: Error while trying to retrieve text for error ORA-01804”"></a>1.1.5 问题5：打包后运行然后又报问题2，“cx_Oracle.DatabaseError: Error while trying to retrieve text for error ORA-01804”</h2><blockquote><p>问题原因：缺少相关的oracle包</p></blockquote><blockquote><p>解决方法: 在main.spec中添加：</p></blockquote><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">a.binaries = a.binaries + [(&#x27;libclntsh.so&#x27;, &#x27;/usr/lib/oracle/11.2/client64/lib/libclntsh.so.11.1&#x27;,&#x27;BINARY&#x27;)]</span><br><span class="line">a.binaries = a.binaries + [(&#x27;libnnz11.so&#x27;, &#x27;/usr/lib/oracle/11.2/client64/lib/libnnz11.so&#x27;,&#x27;BINARY&#x27;)]</span><br><span class="line">a.binaries = a.binaries + [(&#x27;libocci.so&#x27;, &#x27;/usr/lib/oracle/11.2/client64/lib/libocci.so.11.1&#x27;,&#x27;BINARY&#x27;)]</span><br><span class="line">a.binaries = a.binaries + [(&#x27;libociicus.so&#x27;, &#x27;/usr/lib/oracle/11.2/client64/lib/libociicus.so&#x27;,&#x27;BINARY&#x27;)]</span><br></pre></td></tr></table></figure><h2 id="1-1-6-问题6：添加上述包之后出现“Oracle-returned-an-error-ORA-12737-Instant-Client-Light-unsupported-server-character-set-SIMPLIFIED-CHINESE-CHINA-UTF8-”"><a href="#1-1-6-问题6：添加上述包之后出现“Oracle-returned-an-error-ORA-12737-Instant-Client-Light-unsupported-server-character-set-SIMPLIFIED-CHINESE-CHINA-UTF8-”" class="headerlink" title="1.1.6 问题6：添加上述包之后出现“Oracle returned an error. ORA-12737: Instant Client Light: unsupported server character set SIMPLIFIED CHINESE_CHINA.UTF8 ”"></a>1.1.6 问题6：添加上述包之后出现“Oracle returned an error. ORA-12737: Instant Client Light: unsupported server character set SIMPLIFIED CHINESE_CHINA.UTF8 ”</h2><blockquote><p>问题原因： 见（<a href="https://thwack.solarwinds.com/t5/SAM-Discussions/Oracle-returned-an-error-ORA-12737-Instant-Client-Light/td-p/356034%EF%BC%89">https://thwack.solarwinds.com/t5/SAM-Discussions/Oracle-returned-an-error-ORA-12737-Instant-Client-Light/td-p/356034）</a><br>或<a href="https://www.cnblogs.com/chenjianhong/p/4144399.html">https://www.cnblogs.com/chenjianhong/p/4144399.html</a></p></blockquote><p>在Instant Client Light中，语言只能是美国语言，地区可以是任何受支持的地区，字符集可以是以下任意一项：</p><p>＆＃9632; 单字节</p><p>US7ASCII<br>WE8DEC<br>WE8MSWIN1252<br>WE8ISO8859P1<br>＆＃9632; 统一码</p><p>UTF8<br>AL16UTF16<br>AL32UTF8<br>指定除列出为客户端或服务器字符集的字符集或国家字符集以外的字符集或国家字符集，或者在客户端上以NLS_LANG设置语言时，将引发以下错误之一：</p><ul><li>＆＃9632; ORA-12734</li><li>＆＃9632; ORA-12735</li><li>＆＃9632; ORA-12736</li><li>＆＃9632; ORA-12737</li></ul><p>使用Instant Client Light，获得的错误消息仅是英文的。因此，NLS_LANG设置的有效值的类型为：American_territory.characterset ，其中，region可以是任何有效且受支持的领域，并且characterset可以</p><p>可以是前面列出的任何字符集。Instant Client Light可以与在OCI_UTF16模式下创建的OCI环境句柄一起使用。</p><blockquote><p>解决方法：<br>修改文字编码：</p></blockquote><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">将os.environ[&#x27;NLS_LANG&#x27;] = &#x27;SIMPLIFIED CHINESE_CHINA.UTF8&#x27;  修改为os.environ[&#x27;NLS_LANG&#x27;] = &#x27;AMERICAN_AMERICA.AL32UTF8&#x27;</span><br></pre></td></tr></table></figure><h1 id="2-最终打包成功样例"><a href="#2-最终打包成功样例" class="headerlink" title="2.最终打包成功样例"></a>2.最终打包成功样例</h1><h2 id="步骤0：下载oracle客户端驱动文件"><a href="#步骤0：下载oracle客户端驱动文件" class="headerlink" title="步骤0：下载oracle客户端驱动文件"></a>步骤0：下载oracle客户端驱动文件</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">instantclient-sdk-linux.x64-11.2.0.4.0_2</span><br><span class="line">instantclient-basic-linux.x64-11.2.0.4.0</span><br><span class="line"># basic中要包含libclntsh.so，libnnz11.so，libocci.so，libociicus.so这四个文件</span><br><span class="line"># 如果没有从instantclient-basiclite-linux.x64-11.2.0.4.0包中找</span><br><span class="line"># 官方下载链接“https://www.oracle.com/database/technologies/instant-client/downloads.html”</span><br></pre></td></tr></table></figure><h2 id="步骤1-添加依赖链接在main-spec中"><a href="#步骤1-添加依赖链接在main-spec中" class="headerlink" title="步骤1:添加依赖链接在main.spec中"></a>步骤1:添加依赖链接在main.spec中</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line">main.spec： pyinstaller打包的说明文件，里面编写一些打包的时候手动添加的文件</span><br><span class="line"></span><br><span class="line"># -*- mode: python ; coding: utf-8 -*-</span><br><span class="line"></span><br><span class="line">block_cipher = None</span><br><span class="line"></span><br><span class="line">a = Analysis([&#x27;main.py&#x27;],</span><br><span class="line">             pathex=[&#x27;/home/redhat/Desktop/monthPredict&#x27;],</span><br><span class="line">             datas=[],</span><br><span class="line">             hiddenimports=[&#x27;cython&#x27;,  &#x27;sklearn&#x27;, &#x27;sklearn.utils._cython_blas&#x27;,&#x27;sklearn.neighbors.typedefs&#x27;,</span><br><span class="line">             &#x27;sklearn.neighbors.quad_tree&#x27;,&#x27;sklearn.tree&#x27;,&#x27;sklearn.tree._utils&#x27;],</span><br><span class="line">             hookspath=[],</span><br><span class="line">             runtime_hooks=[],</span><br><span class="line">             excludes=[],</span><br><span class="line">             win_no_prefer_redirects=False,</span><br><span class="line">             win_private_assemblies=False,</span><br><span class="line">             cipher=block_cipher,</span><br><span class="line">             noarchive=False)</span><br><span class="line"></span><br><span class="line">a.binaries = a.binaries + [(&#x27;libclntsh.so&#x27;, &#x27;/usr/lib/oracle/11.2/client64/lib/libclntsh.so.11.1&#x27;,&#x27;BINARY&#x27;)]</span><br><span class="line">a.binaries = a.binaries + [(&#x27;libnnz11.so&#x27;, &#x27;/usr/lib/oracle/11.2/client64/lib/libnnz11.so&#x27;,&#x27;BINARY&#x27;)]</span><br><span class="line">a.binaries = a.binaries + [(&#x27;libocci.so&#x27;, &#x27;/usr/lib/oracle/11.2/client64/lib/libocci.so.11.1&#x27;,&#x27;BINARY&#x27;)]</span><br><span class="line">a.binaries = a.binaries + [(&#x27;libociicus.so&#x27;, &#x27;/usr/lib/oracle/11.2/client64/lib/libociicus.so&#x27;,&#x27;BINARY&#x27;)]</span><br><span class="line"></span><br><span class="line">pyz = PYZ(a.pure, a.zipped_data,</span><br><span class="line">             cipher=block_cipher)</span><br><span class="line">exe = EXE(pyz,</span><br><span class="line">          a.scripts,</span><br><span class="line">          a.binaries,</span><br><span class="line">          a.zipfiles,</span><br><span class="line">          a.datas,</span><br><span class="line">          [],</span><br><span class="line">          name=&#x27;main&#x27;,</span><br><span class="line">          debug=False,</span><br><span class="line">          bootloader_ignore_signals=False,</span><br><span class="line">          strip=False,</span><br><span class="line">          upx=True,</span><br><span class="line">          upx_exclude=[],</span><br><span class="line">          runtime_tmpdir=None,</span><br><span class="line">          console=True )</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="步骤2：打包"><a href="#步骤2：打包" class="headerlink" title="步骤2：打包"></a>步骤2：打包</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">使用 pyinstaller  main.spec 命令打包</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://studyflowblog.com/2020/04/13/python/python%E6%89%93%E5%8C%85%E7%A8%8B%E5%BA%8F%E7%A7%BB%E6%A4%8D%E5%88%B0%E7%94%9F%E4%BA%A7%E7%8E%AF%E5%A2%83redhat%E5%87%BA%E7%8E%B0%E7%9A%84%E9%97%AE%E9%A2%98/</id>
    <link href="https://studyflowblog.com/2020/04/13/python/python%E6%89%93%E5%8C%85%E7%A8%8B%E5%BA%8F%E7%A7%BB%E6%A4%8D%E5%88%B0%E7%94%9F%E4%BA%A7%E7%8E%AF%E5%A2%83redhat%E5%87%BA%E7%8E%B0%E7%9A%84%E9%97%AE%E9%A2%98/"/>
    <published>2020-04-13T05:49:22.000Z</published>
    <summary>Python程序打包后移植到RedHat生产环境时遭遇的Oracle客户端、cx_Oracle依赖等问题及完整解决方案</summary>
    <title>python打包程序移植到生产环境redhat出现的问题</title>
    <updated>2026-04-02T12:26:58.470Z</updated>
  </entry>
  <entry>
    <author>
      <name>Wednesday</name>
    </author>
    <category term="机器学习笔记" scheme="https://studyflowblog.com/categories/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    <category term="machine learning" scheme="https://studyflowblog.com/tags/machine-learning/"/>
    <content>
      <![CDATA[<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>随机森林和GBDT部分对比</p><h2 id="一：随机森林-bagging思想"><a href="#一：随机森林-bagging思想" class="headerlink" title="一：随机森林(bagging思想)"></a>一：随机森林(bagging思想)</h2><ul><li>通过构建决策树然后进行分类，最后选择种类比较多的类别</li><li>随机：随机选取固定的样本和固定的特征</li><li>集成学习：投票选举（民主集中制）</li></ul><h2 id="二：GBDT-boosting思想"><a href="#二：GBDT-boosting思想" class="headerlink" title="二：GBDT(boosting思想)"></a>二：GBDT(boosting思想)</h2><ul><li>GBDT是回归树不是分类树</li><li>核心在于累加所有树的结果最为最终结果</li><li>利用损失函数的负梯度去模拟残差，使用残差构建决策树</li><li><strong>为什么使用梯度决策树而不是回归算法</strong></li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&gt; 回归算法寻找参数和目标值之间存在的某种曲线关系，但是当某些特征与目标值之间无线性关系可使用梯度决策树。</span><br><span class="line">&gt; 梯度决策树是讲特征拟化成数值分类回归最佳结果。</span><br></pre></td></tr></table></figure><!-- more --><h3 id="2-1-参数说明"><a href="#2-1-参数说明" class="headerlink" title="2.1 参数说明"></a>2.1 参数说明</h3><p><code>class sklearn.ensemble.GradientBoostingRegressor(loss=&#39;ls&#39;, learning_rate=0.1, n_estimators=100, subsample=1.0, min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_depth=3, init=None, random_state=None, max_features=None, alpha=0.9, verbose=0, max_leaf_nodes=None, warm_start=False, presort=&#39;auto&#39;)</code></p><ul><li><p><strong>n_estimators</strong>：指GBR使用的学习算法的数量。通常，如果你的设备性能更好，可以把n_estimators设置的更大，效果也会更好。</p></li><li><p><strong>max_depth</strong>：应该在优化其他参数之前先调整max_depth参数。因为每个学习算法都是一颗决策树，max_depth决定了树生成的节点数。选择合适的节点数量可以更好的拟合数据，而更多的节点数可能造成拟合过度。</p></li><li><p><strong>loss</strong>:loss参数决定损失函数，也直接影响误差。默认值为ls，表示最小二乘法（least squares）。还有最小绝对值差值，Huber损失和分位数损失（quantiles）等等。</p></li><li><p><strong>learning_rate</strong>:学习率</p></li></ul><h4 id="2-1-1-参数调整"><a href="#2-1-1-参数调整" class="headerlink" title="2.1.1 参数调整"></a>2.1.1 参数调整</h4><p><strong>Grid Search</strong>来选择性能表现最好的超参数</p><p><a href="https://zhuanlan.zhihu.com/p/55524425">https://zhuanlan.zhihu.com/p/55524425</a></p><p>在Gradient Boosting Regressor 模型中，有一些独立的参数最好是手动调整。</p><p>超参数主要使用了：</p><ul><li>n_estimators&#x3D;2000,</li><li>learning_rate&#x3D;0.01,</li><li>max_depth&#x3D;15,</li><li>max_features&#x3D;’sqrt’,</li><li>min_samples_leaf&#x3D;10,</li><li>min_samples_split&#x3D;10,</li><li>loss&#x3D;’ls’,</li><li>random_state &#x3D;42)</li></ul><h3 id="2-2-负荷预测程序说明记录"><a href="#2-2-负荷预测程序说明记录" class="headerlink" title="2.2 负荷预测程序说明记录"></a>2.2 负荷预测程序说明记录</h3><h4 id="1-误差偏大的处理方法"><a href="#1-误差偏大的处理方法" class="headerlink" title="1. 误差偏大的处理方法"></a>1. 误差偏大的处理方法</h4><h5 id="误差偏大的原因：缺失值处理不当-0或空值设置为均值"><a href="#误差偏大的原因：缺失值处理不当-0或空值设置为均值" class="headerlink" title="误差偏大的原因：缺失值处理不当(0或空值设置为均值)"></a>误差偏大的原因：缺失值处理不当(0或空值设置为均值)</h5><blockquote><p>因为数据波动较大，设置为历史均值是否存在较大的参考价值。其次数据量教少直接删除也不太可取。尝试设置为近一年的均值进行测试。</p></blockquote>]]>
    </content>
    <id>https://studyflowblog.com/2020/03/27/random-forest-and-gbdt/</id>
    <link href="https://studyflowblog.com/2020/03/27/random-forest-and-gbdt/"/>
    <published>2020-03-27T05:43:24.000Z</published>
    <summary>对比随机森林（Bagging）与GBDT（Boosting）的核心思想、残差拟合原理及各自适用的业务场景</summary>
    <title>随机森林和梯度决策树（GBDT）</title>
    <updated>2026-04-02T12:26:20.855Z</updated>
  </entry>
  <entry>
    <author>
      <name>Wednesday</name>
    </author>
    <category term="大数据" scheme="https://studyflowblog.com/categories/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    <category term="大数据" scheme="https://studyflowblog.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    <content>
      <![CDATA[<h1 id="1-感想"><a href="#1-感想" class="headerlink" title="1.感想"></a>1.感想</h1><p>融会贯通</p><h2 id="2-1-章节摘录"><a href="#2-1-章节摘录" class="headerlink" title="2.1 章节摘录"></a>2.1 章节摘录</h2><h3 id="2-1-1-07-为什么说MapReduce既是编程模型又是计算框架？"><a href="#2-1-1-07-为什么说MapReduce既是编程模型又是计算框架？" class="headerlink" title="2.1.1 07 | 为什么说MapReduce既是编程模型又是计算框架？"></a>2.1.1 07 | 为什么说MapReduce既是编程模型又是计算框架？</h3><blockquote><p>模型是人们对一类事物的概括与抽象，可以帮助我们更好地理解事物的本质，更方便地解决问题。比如，数学公式是我们对物理与数学规律的抽象，地图和沙盘是我们对地理空间的抽象，软件架构图是软件工程师对软件系统的抽象。</p></blockquote><blockquote><p>通过抽象，我们更容易把握事物的内在规律，而不是被纷繁复杂的事物表象所迷惑，更进一步深刻地认识这个世界。通过抽象，伽利略发现力是改变物体运动的原因，而不是使物体运动的原因，为全人类打开了现代科学的大门。</p></blockquote><blockquote><p>这些年，我自己认识了很多优秀的人，他们各有所长、各有特点，但是无一例外都有个共同的特征，就是对事物的洞察力。他们能够穿透事物的层层迷雾，直指问题的核心和要害，不会犹豫和迷茫，轻松出手就搞定了其他人看起来无比艰难的事情。有时候光是看他们做事就能感受到一种美感，让人意醉神迷。</p></blockquote><blockquote><p>这种洞察力就是来源于他们对事物的抽象能力，虽然我不知道这种能力缘何而来，但是见识了这种能力以后，我也非常渴望拥有对事物的抽象能力。所以在遇到问题的时候，我就会停下来思考：这个问题为什么会出现，它揭示出来背后的规律是什么，我应该如何做。甚至有时候会把这些优秀的人带入进思考：如果是戴老师、如果是潘大侠，他会如何看待、如何解决这个问题。通过这种不断地训练，虽然和那些最优秀的人相比还是有巨大的差距，但是仍然能够感受到自己的进步，这些小小的进步也会让自己产生大大的快乐，一种不荒废光阴、没有虚度此生的感觉。</p></blockquote><blockquote><p>我希望你也能够不断训练自己，遇到问题的时候，停下来思考一下：这些现象背后的规律是什么。有时候并不需要多么艰深的思考，仅仅就是停一下，就会让你察觉到以前不曾注意到的一些情况，进而发现事物的深层规律。这就是洞察力。</p></blockquote><h3 id="2-1-2-06-新技术层出不穷，HDFS依然是存储的王者"><a href="#2-1-2-06-新技术层出不穷，HDFS依然是存储的王者" class="headerlink" title="2.1.2 06 | 新技术层出不穷，HDFS依然是存储的王者"></a>2.1.2 06 | 新技术层出不穷，HDFS依然是存储的王者</h3><ul><li>1.如何设计分布式文件系统<blockquote><p>DataNode 负责文件数据的存储和读写操作，HDFS 将文件数据分割成若干数据块（Block），每个 DataNode 存储一部分数据块，这样文件就分布存储在整个 HDFS 服务器集群中.<br>NameNode 负责整个分布式文件系统的元数据（MetaData）管理，也就是文件路径名、数据块的 ID 以及存储位置等信息，相当于操作系统中文件分配表（FAT）的角色。</p></blockquote></li></ul><blockquote><p>NameNode 负责整个分布式文件系统的元数据（MetaData）管理，也就是文件路径名、数据块的 ID 以及存储位置等信息，相当于操作系统中文件分配表（FAT）的角色</p></blockquote><ul><li>2.HDFS的高可用设计</li></ul><ol><li>数据存储故障容错<blockquote><p>磁盘介质在存储过程中受环境或者老化影响，其存储的数据可能会出现错乱。HDFS 的应对措施是，对于存储在 DataNode 上的数据块，计算并存储校验和（CheckSum）。在读取数据的时候，重新计算读取出来的数据的校验和，如果校验不正确就抛出异常，应用程序捕获异常后就到其他 DataNode 上读取备份数据。</p></blockquote></li><li>磁盘故障容错<blockquote><p>如果 DataNode 监测到本机的某块磁盘损坏，就将该块磁盘上存储的所有 BlockID 报告给 NameNode，NameNode 检查这些数据块还在哪些 DataNode 上有备份，通知相应的 DataNode 服务器将对应的数据块复制到其他服务器上，以保证数据块的备份数满足要求。</p></blockquote></li><li>DataNode 故障容错<blockquote><p>DataNode 会通过心跳和 NameNode 保持通信，如果 DataNode 超时未发送心跳，NameNode 就会认为这个 DataNode 已经宕机失效，立即查找这个 DataNode 上存储的数据块有哪些，以及这些数据块还存储在哪些服务器上，随后通知这些服务器再复制一份数据块到其他服务器上，保证 HDFS 存储的数据块备份数符合用户设置的数目，即使再出现服务器宕机，也不会丢失数据。</p></blockquote></li><li>NameNode 故障容错<blockquote><p>NameNode 是整个 HDFS 的核心，记录着 HDFS 文件分配表信息，所有的文件路径和数据块存储信息都保存在 NameNode，如果 NameNode 故障，整个 HDFS 系统集群都无法使用；如果 NameNode 上记录的数据丢失，整个集群所有 DataNode 存储的数据也就没用了。</p></blockquote></li></ol><h3 id="2-1-3-13-同样的本质，为何Spark可以更高效？"><a href="#2-1-3-13-同样的本质，为何Spark可以更高效？" class="headerlink" title="2.1.3 13 | 同样的本质，为何Spark可以更高效？"></a>2.1.3 13 | 同样的本质，为何Spark可以更高效？</h3><ol><li>spark的计算阶段<blockquote><p>MapReduce 一个应用一次只运行一个 map 和一个 reduce 不同，Spark 可以根据应用的复杂程度，分割成更多的计算阶段（stage），这些计算阶段组成一个有向无环图 DAG，Spark 任务调度器可以根据 DAG 的依赖关系执行计算阶段。</p></blockquote></li></ol><h3 id="2-1-4-18-如何自己开发一个大数据SQL引擎？"><a href="#2-1-4-18-如何自己开发一个大数据SQL引擎？" class="headerlink" title="2.1.4 18 | 如何自己开发一个大数据SQL引擎？"></a>2.1.4 18 | 如何自己开发一个大数据SQL引擎？</h3><blockquote><p>如果只是作为学习者，被动接受总是困难的。但如果从开发者的视角看，很多东西就豁然开朗了，明白了原理，有时甚至不需要学习，顺着原理就可以推导出各种实现细节。</p></blockquote><blockquote><p>各种知识从表象上看，总是杂乱无章的，如果只是学习这些繁杂的知识点，固然自己的知识面是有限的，并且遇到问题的应变能力也很难提高。所以有些高手看起来似乎无所不知，不论谈论起什么技术，都能头头是道，其实并不是他们学习、掌握了所有技术，而是他们是在谈到这个问题的时候，才开始进行推导，并迅速得出结论。</p></blockquote><blockquote><p>我在 Intel 的时候，面试过一个交大的实习生，她大概只学过一点 MapReduce 的基本知识，我问她如何用 MapReduce 实现数据库的 join 操作，可以明显看出她没学习过这部分知识。她说：我想一下，然后盯着桌子看了两三秒的时间，就开始回答，基本跟 Hive 的实现机制一样。从她的回答就能看出这个女生就是一个高手，高手不一定要很资深、经验丰富，把握住了技术的核心本质，掌握了快速分析推导的能力，能够迅速将自己的知识技能推进到陌生的领域，就是高手。</p></blockquote><h2 id="2-1-4-23-大数据基准测试可以带来什么好处？"><a href="#2-1-4-23-大数据基准测试可以带来什么好处？" class="headerlink" title="2.1.4 23 | 大数据基准测试可以带来什么好处？"></a>2.1.4 23 | 大数据基准测试可以带来什么好处？</h2><p>大数据测试工具：HiBeach<br>HiBench 内置了若干主要的大数据计算程序作为基准测试的负载（workload）。</p><h2 id="2-1-5-24-从大数据性能测试工具Dew看如何快速开发大数据系统"><a href="#2-1-5-24-从大数据性能测试工具Dew看如何快速开发大数据系统" class="headerlink" title="2.1.5 24 | 从大数据性能测试工具Dew看如何快速开发大数据系统"></a>2.1.5 24 | 从大数据性能测试工具Dew看如何快速开发大数据系统</h2><h3 id="1-Akka-原理与应用"><a href="#1-Akka-原理与应用" class="headerlink" title="1. Akka 原理与应用"></a>1. Akka 原理与应用</h3><blockquote><p>Akka 使用一种叫 Actor 的编程模型，Actor 编程模型是和面向对象编程模型平行的一种编程模型。面向对象认为一切都是对象，对象之间通过消息传递，也就是方法调用实现复杂的功能。</p></blockquote><blockquote><p>而 Actor 编程模型认为一切都是 Actor，Actor 之间也是通过消息传递实现复杂的功能，但是这里的消息是真正意义上的消息。不同于面向对象编程时，方法调用是同步阻塞的，也就是被调用者在处理完成之前，调用者必须阻塞等待；给 Actor 发送消息不需要等待 Actor 处理，消息发送完就不用管了，也就是说，消息是<strong>异步</strong>的。</p></blockquote><h3 id="2-主要原理"><a href="#2-主要原理" class="headerlink" title="2. 主要原理"></a>2. 主要原理</h3><blockquote><p>Akka 实现异步消息的主要原理是，Actor 之间的消息传输是通过一个收件箱 Mailbox 完成的，发送者 Actor 的消息发到接收者 Actor 的收件箱，接收者 Actor 一个接一个地串行从收件箱取消息调用自己的 receive 方法进行处理。如下图<br><a href="https://static001.geekbang.org/resource/image/26/13/269b28c63c69444dd9dcb0c3124e0713.png">https://static001.geekbang.org/resource/image/26/13/269b28c63c69444dd9dcb0c3124e0713.png</a></p></blockquote><blockquote><p>发送者通过调用一个 Actor 的引用 ActorRef 来发送消息，ActorRef 将消息放到 Actor 的 Mailbox 里就返回了，发送者不需要阻塞等待消息被处理，这是和传统的面向对象编程最大的不同，对象一定要等到被调用者返回结果才继续向下执行。</p></blockquote><blockquote><p>通过这种异步消息方式，Akka 也顺便实现了并发编程：消息同时异步发送给多个 Actor，这些 Actor 看起来就是在同时执行，即并发执行。</p></blockquote><h2 id="2-1-6-25-模块答疑：我能从大厂的大数据开发实践中学到什么？"><a href="#2-1-6-25-模块答疑：我能从大厂的大数据开发实践中学到什么？" class="headerlink" title="2.1.6 25 | 模块答疑：我能从大厂的大数据开发实践中学到什么？"></a>2.1.6 25 | 模块答疑：我能从大厂的大数据开发实践中学到什么？</h2><h3 id="1-学习方法"><a href="#1-学习方法" class="headerlink" title="1. 学习方法"></a>1. 学习方法</h3><p>学习一样新技术的时候，不会到处乱找资料，而是直接读原始论文。通过原始论文掌握核心设计原理以后，如果需要进一步学习，就去官网看官方文档；如果还需要再进一步参与开发，就去读源代码。</p><p>我刚开始读论文时感觉很费劲，但是后面习惯以后，发现读论文真的是最快的学习方法，因为最核心的东西就在其中，一旦看懂，就真的懂了，而且可以触类旁通，整个软件从使用到开发，很多细节通过脑补就可以猜个八九不离十。而且越是优秀的产品，越是厉害的作者，论文反而越是容易读懂，可能是因为这些作者是真的高手，自己理得越清楚，写出来的论文越是脉络清晰、结构合理、逻辑严谨。</p><h3 id="2-典型的互联网大数据平台的架构。"><a href="#2-典型的互联网大数据平台的架构。" class="headerlink" title="2. 典型的互联网大数据平台的架构。"></a>2. 典型的互联网大数据平台的架构。</h3><p>如图<br><a href="https://static001.geekbang.org/resource/image/5f/1f/5f0515ad5740575ff79ac8c68990071f.png">https://static001.geekbang.org/resource/image/5f/1f/5f0515ad5740575ff79ac8c68990071f.png</a></p><h2 id="2-1-7-35-如何利用大数据成为“增长黑客”？"><a href="#2-1-7-35-如何利用大数据成为“增长黑客”？" class="headerlink" title="2.1.7 35 | 如何利用大数据成为“增长黑客”？"></a>2.1.7 35 | 如何利用大数据成为“增长黑客”？</h2><h3 id="1-AARRR-用户增长模型"><a href="#1-AARRR-用户增长模型" class="headerlink" title="1. AARRR 用户增长模型"></a>1. AARRR 用户增长模型</h3><blockquote><p>关于用户增长有一个著名的 AARRR 模型，它描述了用户增长的 5 个关键环节，分别是：获取用户（Acquisition）、提高活跃度（Activation）、提高留存率（Retention）、获取收入（Revenue）和自传播（Refer）。<br>获取用户：通过各种推广手段，使产品触达用户并吸引用户，让用户访问我们的产品。</p></blockquote><blockquote><p><strong>提高活跃度</strong>：用户访问我们的产品后，如果发现没意思、体验差，就很难再次打开，产品的价值也就无法实现。因此需要结合产品内容、运营活动各种手段吸引用户，提升产品的活跃度。</p></blockquote><blockquote><p><strong>提高留存率</strong>：留住一个老用户的成本远低于获取一个新用户，而真正为产品带来营收利润的通常是老用户，因此需要提高留存率。提高留存率的常用手段有：针对老用户推出各种优惠和活动；建立会员等级体系，注册时间越长等级越高；对于一段时间没有访问的疑似流失用户进行消息短信推送以实现用户挽回等。</p></blockquote><blockquote><p><strong>获取收</strong>入：做企业不是做慈善，开发、运营互联网产品的最终目的还是为了赚钱，即获取收入。互联网产品收入主要有用户付费和广告收入，有些互联网产品看起来是用户付费，但其实主要营收是广告收入，比如淘宝。<br>自传播：让用户利用利用自己的社交网络进行产品推广就是自传播，几乎所有的互联网产品都有“分享到”这样一个功能按钮，促进用户社交传播。有些产品还会利用“帮我砍价”“帮我抢票”等产品功能推动用户进行分享，实现产品的裂变式传播、病毒式营销。</p></blockquote><h3 id="2-拼多多如何利用-AARRR-模型实现用户快速增长。"><a href="#2-拼多多如何利用-AARRR-模型实现用户快速增长。" class="headerlink" title="2. 拼多多如何利用 AARRR 模型实现用户快速增长。"></a>2. 拼多多如何利用 AARRR 模型实现用户快速增长。</h3><blockquote><p>拼多多是近几年互联网产品中将自传播发挥到极致的一个产品。拼多多用户群体主要为三四线以下城市人群，社交成本比较低，愿意为了砍几块钱发动自己的各种社交资源，因此拼多多就利用“帮好友砍价”这一功能实现产品的快速裂变传播。事实上，拼多多非常准确地抓住了这一群体的社交痛点：交往不多的朋友，与其尬聊维持友谊，不如帮我砍价来的更实惠更亲密。</p></blockquote><blockquote><p><strong>自传播</strong>也是拼多多主要获取用户的手段。比如帮好友砍价时，拼多多会提示“下载 App 可以帮好友砍更多价”，于是用户量呈指数级增长。<br>拼多多为了让新来的用户快速上手、增加活跃度，用户第一次使用拼多多的时候，并不需要注册登录，直接就可以挑选商品和购买，在后面订单环节再让用户注册，这时用户已经产生购买冲动，进行注册也更容易被接受。<br>拼多多通过各种消息推送促使用户打开 App（或者微信小程序），并在开屏页面的优惠信息给用户制造惊喜，达到留存用户的目的。</p></blockquote><blockquote><p>拼多多的主要交易模式为<strong>拼团</strong>，拼团属于冲动型购买，拼多多为了<strong>减少用户的思考时间、维持购买冲动，将购买路径设计得尽可能短，使用户可以尽快完成付费，企业获取收入。</strong></p></blockquote>]]>
    </content>
    <id>https://studyflowblog.com/2019/12/25/learning-big-data-from-scratch/</id>
    <link href="https://studyflowblog.com/2019/12/25/learning-big-data-from-scratch/"/>
    <published>2019-12-25T04:48:47.000Z</published>
    <summary>极客时间《从0开始学大数据》学习笔记，涵盖MapReduce编程模型、HDFS分布式存储架构设计及核心知识摘录</summary>
    <title>从0开始学大数据笔记</title>
    <updated>2026-04-02T12:26:00.041Z</updated>
  </entry>
</feed>
