<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="/rss/rss2.xsl"?>
<rss version="2.0"
  xmlns:atom="http://www.w3.org/2005/Atom"
  xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>RF极客日志</title>
    <link>https://blog.itrf.cn/</link>
    
    <atom:link href="https://blog.itrf.cn/rss.xml" rel="self" type="application/rss+xml"/>
    
    <description>沉淀编程学习干货、技术笔记与项目实操经验，分享实用技术及应用心得，顺带记录旅行、读书等生活碎片，定格成长轨迹。</description>
    <pubDate>Sun, 15 Mar 2026 14:33:51 GMT</pubDate>
    <generator>http://hexo.io/</generator>
    
    <item>
      <title>OpenClaw-QQBot 插件安装失败（npm install failed）完整解决方案</title>
      <link>https://blog.itrf.cn/posts/29b5/</link>
      <guid>https://blog.itrf.cn/posts/29b5/</guid>
      <pubDate>2026-03-15T14:28:26.000Z</pubDate>
      
      <description>本文针对 openclaw plugins install @tencent-connect/openclaw-qqbot 执行时出现的 npm install failed 报错，提供更清晰、更健壮、更易操作的解决方案，覆盖问题定位、环境修复、手动安装、权限配置全流程。</description>
      
      
      <enclosure url="https://t.alcy.cc/moez?_r_=ff9c0f8b-8566-fcf8-04fa-47754e72c5d3" type="image"/>
      
      
      <content:encoded><![CDATA[<p>今天安装openclaw对接qq机器人遇到了一个问题，直接运行</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">openclaw plugins install @tencent-connect/openclaw-qqbot@latest</span><br></pre></td></tr></table></figure><p>这个脚本，会出现下面这个提示</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">npm install failed:</span><br></pre></td></tr></table></figure><p>npm 安装依赖失败，尝试了很多次都会提示这个(之前运行这个脚本就没遇到这个情况，很玄学) 。</p><p>经过了一天的折腾，找到了问题所在和解决方案</p><h2 id="问题核心原因"><a href="#问题核心原因" class="headerlink" title="问题核心原因"></a>问题核心原因</h2><h4 id="1-OpenClaw-内置安装机制的「过度限制」"><a href="#1-OpenClaw-内置安装机制的「过度限制」" class="headerlink" title="1. OpenClaw 内置安装机制的「过度限制」"></a>1. OpenClaw 内置安装机制的「过度限制」</h4><p>OpenClaw 的 <code>plugins install</code> 命令不是简单调用 <code>npm install</code>，而是加了多层校验：</p><ul><li>检测插件代码中的 <code>child_process</code>（Shell 执行）、环境变量读取等行为，即使是插件正常功能，也会触发「安全拦截」；</li><li>内置的 npm 执行环境被限制了权限 / 超时时间，一旦依赖下载稍慢或编译耗时，就会直接判定「npm install failed」；</li><li>临时目录（<code>/tmp/openclaw-plugin-*</code>）的权限 / 生命周期管控严格，手动操作稍慢就会被自动清理。</li></ul><h4 id="2-服务器基础环境不匹配"><a href="#2-服务器基础环境不匹配" class="headerlink" title="2. 服务器基础环境不匹配"></a>2. 服务器基础环境不匹配</h4><ul><li>Node.js 版本：插件要求 <code>node &gt;=24</code>，openclaw安装脚本安装的默认版本是 v22.22.1，部分依赖编译会出现问题；</li><li>编译依赖缺失：插件中的音频转换、系统调用类依赖需要 <code>gcc/g++/python</code>，即使安装了基础编译环境，低版本系统仍可能编译失败；</li><li>资源不足：服务器内存 / CPU 不足时，<code>npm install</code> 会因资源抢占卡住，表现为「无报错但一直卡」。</li></ul><h4 id="3-网络层的隐性问题"><a href="#3-网络层的隐性问题" class="headerlink" title="3. 网络层的隐性问题"></a>3. 网络层的隐性问题</h4><ul><li>npm 源的「安全审计接口」在国内镜像（如淘宝源）未实现，触发 404 警告，OpenClaw 会误判为安装失败；</li><li>依赖包分片下载时，服务器网络波动导致超时，npm 无重试机制，直接终止安装；</li><li>部分依赖包（如二进制预编译包）的 CDN 节点在境外，下载速度极慢甚至超时。</li></ul><h2 id="分步解决方案"><a href="#分步解决方案" class="headerlink" title="分步解决方案"></a>分步解决方案</h2><h4 id="1-升级node版本"><a href="#1-升级node版本" class="headerlink" title="1.升级node版本"></a>1.升级node版本</h4><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 全局安装 n 工具</span></span><br><span class="line">npm install -g n</span><br><span class="line"></span><br><span class="line"><span class="comment"># 如果提示权限不足（Permission denied），加 sudo 执行</span></span><br><span class="line"><span class="built_in">sudo</span> npm install -g n</span><br><span class="line"></span><br><span class="line"><span class="comment"># 安装并切换到 v24 最新稳定版</span></span><br><span class="line"><span class="built_in">sudo</span> n 24</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看 Node.js 版本（输出 v24.x.x 即为成功）</span></span><br><span class="line">node -v</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看 npm 版本（同步升级，无需单独操作）</span></span><br><span class="line">npm -v</span><br></pre></td></tr></table></figure><h4 id="2-将npm源改成官方源"><a href="#2-将npm源改成官方源" class="headerlink" title="2.将npm源改成官方源"></a>2.将npm源改成官方源</h4><p>国内镜像会出现更新不同步的问题</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">npm config <span class="built_in">set</span> registry https://registry.npmjs.org/</span><br></pre></td></tr></table></figure><h4 id="3-直接通过npm安装插件"><a href="#3-直接通过npm安装插件" class="headerlink" title="3.直接通过npm安装插件"></a>3.直接通过npm安装插件</h4><p> 绕过OpenClaw 插件安装机制的限制</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">npm install -g @tencent-connect/openclaw-qqbot@latest</span><br></pre></td></tr></table></figure><p>等待安装好之后把 npm 安装好的包迁移到 OpenClaw 插件目录</p><h4 id="4-定位插件安装路径"><a href="#4-定位插件安装路径" class="headerlink" title="4.定位插件安装路径"></a>4.定位插件安装路径</h4><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 1. 自动定位 npm 全局安装的插件路径（兼容不同系统）</span></span><br><span class="line">PLUGIN_PATH=$(npm root -g)/@tencent-connect/openclaw-qqbot</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 验证路径是否存在（不存在则提示错误）</span></span><br><span class="line"><span class="keyword">if</span> [ ! -d <span class="string">&quot;<span class="variable">$PLUGIN_PATH</span>&quot;</span> ]; <span class="keyword">then</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;❌ 插件安装失败，请检查 npm install 命令是否执行成功&quot;</span></span><br><span class="line">  <span class="built_in">exit</span> 1</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;✅ 插件安装路径：<span class="variable">$PLUGIN_PATH</span>&quot;</span></span><br><span class="line"><span class="keyword">fi</span></span><br></pre></td></tr></table></figure><h4 id="5-迁移到-OpenClaw-插件目录"><a href="#5-迁移到-OpenClaw-插件目录" class="headerlink" title="5.迁移到 OpenClaw 插件目录"></a>5.迁移到 OpenClaw 插件目录</h4><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 1. 删除 OpenClaw 旧的未完成目录</span></span><br><span class="line"><span class="built_in">rm</span> -rf /root/.openclaw/extensions/openclaw-qqbot</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 将 npm 安装好的插件完整复制过去</span></span><br><span class="line"><span class="built_in">cp</span> -r <span class="variable">$PLUGIN_PATH</span> /root/.openclaw/extensions/openclaw-qqbot</span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 赋予正确权限</span></span><br><span class="line"><span class="built_in">chown</span> -R <span class="variable">$USER</span>:<span class="variable">$USER</span> /root/.openclaw/extensions/openclaw-qqbot</span><br><span class="line"><span class="built_in">chmod</span> -R 755 /root/.openclaw/extensions/openclaw-qqbot</span><br></pre></td></tr></table></figure><h4 id="6-配置信任-amp-验证插件"><a href="#6-配置信任-amp-验证插件" class="headerlink" title="6.配置信任 &amp; 验证插件"></a>6.配置信任 &amp; 验证插件</h4><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 1. 配置 plugins.allow 消除安全提示</span></span><br><span class="line"><span class="built_in">cat</span> &gt; /root/.openclaw/config.json &lt;&lt; <span class="string">EOF</span></span><br><span class="line"><span class="string">&#123;</span></span><br><span class="line"><span class="string">  &quot;plugins&quot;: &#123;</span></span><br><span class="line"><span class="string">    &quot;allow&quot;: [&quot;@tencent-connect/openclaw-qqbot&quot;, &quot;wecom-app&quot;]</span></span><br><span class="line"><span class="string">  &#125;</span></span><br><span class="line"><span class="string">&#125;</span></span><br><span class="line"><span class="string">EOF</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 2.重启OpenClaw服务</span></span><br><span class="line">openclaw gateway restart</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 最终验证（看到插件名称即为成功）</span></span><br><span class="line">openclaw plugins list | grep openclaw-qqbot</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="7-配置-QQ-机器人"><a href="#7-配置-QQ-机器人" class="headerlink" title="7.配置 QQ 机器人"></a>7.配置 QQ 机器人</h4><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 1.配置绑定当前QQ机器人</span></span><br><span class="line">openclaw channels add --channel qqbot --token <span class="string">&quot;190xxxxx:iT9xxxxxx&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 2.重启OpenClaw服务</span></span><br><span class="line">openclaw gateway restart</span><br></pre></td></tr></table></figure><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><ol><li>核心解决思路：<strong>绕过 OpenClaw 内置安装机制</strong>，手动用 npm 安装插件后迁移到指定目录；</li><li>关键前置：升级 Node.js 到 v24+、安装编译依赖、切换 npm 官方源，解决底层环境问题；</li><li>验证要点：插件迁移后需配置信任列表，重启服务并通过 <code>openclaw plugins list</code> 确认加载成功。</li></ol>]]></content:encoded>
      
      
      <category domain="https://blog.itrf.cn/tags/%E9%9B%B6%E7%A2%8E%E7%9F%A5%E8%AF%86/">零碎知识</category>
      
      <category domain="https://blog.itrf.cn/tags/openclaw/">openclaw</category>
      
    </item>
    
    <item>
      <title>一文读懂比特币工作原理</title>
      <link>https://blog.itrf.cn/posts/34af/</link>
      <guid>https://blog.itrf.cn/posts/34af/</guid>
      <pubDate>2026-02-28T18:55:17.000Z</pubDate>
      
      <description>Bitcoin is a decentralized digital currency that can be used without a centralized authority or bank. It uses peer-to-peer technology to allow users to send and receive money without the need for a financial institution.</description>
      
      
      <enclosure url="https://t.alcy.cc/ycy?_r_=a420b844-3932-a522-dc2e-6060d040ec43" type="image"/>
      
      
      <content:encoded><![CDATA[<h2 id="一、比特币的诞生"><a href="#一、比特币的诞生" class="headerlink" title="一、比特币的诞生"></a>一、比特币的诞生</h2><p>2008 年，一位化名中本聪的神秘人物（或组织）发表了比特币白皮书，提出一种去中心化的数字货币构想，旨在实现无需传统金融机构的交易体系。次年，即 2009 年，中本聪基于白皮书中的理念，开发出比特币钱包软件，并公开供用户下载使用，比特币网络由此正式启动。</p><h2 id="二、比特币网络搭建"><a href="#二、比特币网络搭建" class="headerlink" title="二、比特币网络搭建"></a>二、比特币网络搭建</h2><p>当新用户下载并运行比特币钱包软件时，软件会自动连接内置的种子节点。这些种子节点如同网络的 “联络站”，会向新用户节点分享其已连接的其他节点信息。新用户节点借此与众多对等节点建立起 P2P（点对点）连接。在这个过程中，各个节点按照既定规则相互交换信息，逐渐构建起一个没有中心控制的比特币网络。网络中的所有节点地位平等，共同维护着网络的正常运转。这种 P2P 网络结构确保了比特币系统的去中心化特性，没有任何一个节点能够单独掌控整个网络。</p><h2 id="三、钱包地址生成"><a href="#三、钱包地址生成" class="headerlink" title="三、钱包地址生成"></a>三、钱包地址生成</h2><p>每个用户都可以通过本地的比特币钱包软件生成独一无二的钱包地址。钱包地址类似于现实生活中的银行账号，是用户在比特币网络中接收和存储比特币的标识。它由钱包软件依据特定算法生成，整个生成过程无需依赖第三方机构，极大地保障了用户的自主性和隐私性。这意味着用户可以完全掌控自己的钱包地址，无需担心第三方机构的干预或泄露风险。</p><h2 id="四、内存池功能"><a href="#四、内存池功能" class="headerlink" title="四、内存池功能"></a>四、内存池功能</h2><p>钱包软件会在用户电脑里开辟一块默认大小为 300M 的内存空间，这就是内存池。它就像一个 “交易等待区”，主要任务是临时存放转账记录。当用户发起交易时，相关的转账信息会先被暂存到内存池中，等待被打包进区块，成为区块链账本的一部分。内存池中的交易处于待确认状态，它们在这里排队等待被矿工打包处理。</p><h2 id="五、创世区块诞生"><a href="#五、创世区块诞生" class="headerlink" title="五、创世区块诞生"></a>五、创世区块诞生</h2><ol><li><strong>铸币交易</strong>：比特币网络的首个钱包用户开始创建区块 0 。在这个过程中，该用户通过一种特殊的 coinbase 铸币交易，凭空给自己的钱包地址转入 50 枚比特币。这 50 枚比特币是比特币网络给予打包区块者的初始奖励，此后每四年奖励减半。这种奖励机制旨在激励更多节点参与到比特币网络的维护和新区块的创建中来，为比特币网络的发展提供动力。</li><li><strong>提取记录</strong>：完成铸币交易后，由于此时网络刚刚起步，内存池中仅有这笔铸币交易，并无其他交易，用户便从内存池中提取该转账记录，随后进入打包区块头环节。</li></ol><h2 id="六、打包区块头流程"><a href="#六、打包区块头流程" class="headerlink" title="六、打包区块头流程"></a>六、打包区块头流程</h2><ol><li><p><strong>确定交易记录</strong>：将从内存池提取的铸币交易记录确定为该区块要记录的交易内容，这是构成区块的核心数据。</p></li><li><p><strong>交易哈希运算</strong>：对确定好的铸币交易进行哈希运算。哈希算法具有三个重要特性：</p><ul><li><strong>唯一性</strong>：无论何时何地，对相同的数据进行哈希运算，得到的结果始终固定且唯一。例如，无论谁对字符 “a” 进行 SHA256 哈希运算，都会得到相同的哈希值。这保证了每个交易在经过哈希运算后都有独一无二的标识。</li><li><strong>不可逆性</strong>：无法通过哈希值反向推导出原始数据内容。比如将一篇文章进行哈希运算后，他人无法从哈希值还原出文章原文。这为交易信息提供了保密性。</li><li><strong>雪崩效应</strong>：原始数据哪怕只有极其微小的改动，都会导致哈希运算结果截然不同。通过对交易记录进行哈希运算得到的数值，会被填入区块头。这使得任何对交易的篡改都会导致哈希值的明显变化，从而易于被发现。</li></ul></li></ol><ol><li><p><strong>添加相关信息</strong>：</p><ul><li><strong>上一区块哈希值</strong>：因为这是首个区块，不存在上一个区块，所以在区块头此位置填 0 。后续区块则会在此处填入上一区块的哈希值，通过这种方式将各个区块依次连接起来，形成区块链结构。这种链式结构保证了区块链数据的连贯性和可追溯性，每个区块都依赖于前一个区块的哈希值，一旦前面的区块被篡改，后续区块的哈希值就会不匹配。</li><li><strong>时间戳</strong>：记录区块创建的时间，为区块链提供时间维度信息，用于确定交易顺序和区块先后关系，帮助节点验证数据的真实性和顺序性。时间戳使得区块链上的交易和区块按照时间顺序排列，便于追踪和验证。</li><li><strong>规定难度</strong>：区块头设定当前难度值，如 0X1D00FFFF ，该值决定挖矿难度。难度值会根据全网算力的变化而动态调整，以确保比特币网络平均每 10 分钟产生一个新区块。难度值的调整是比特币网络自我调节的重要机制，保证了出块速度的稳定性。</li><li><strong>随机数</strong>：初始值设为 0 ，在挖矿过程中，通过不断改变随机数重新计算哈希值，以寻找符合难度要求的结果。随机数是挖矿过程中不断尝试的变量，通过调整它来改变哈希运算结果，是挖矿找到有效区块的关键因素之一。</li></ul></li></ol><h2 id="七、挖矿过程解析"><a href="#七、挖矿过程解析" class="headerlink" title="七、挖矿过程解析"></a>七、挖矿过程解析</h2><ol><li><strong>哈希计算与比较</strong>：对打包好的区块头进行哈希计算，会得到一个很大的数值。该数值需与当前设定的难度值（bits）进行比较。若计算值小于 bits ，则挖矿成功；若大于 bits ，需改变随机数重新计算哈希值，反复尝试直至找到小于 bits 的数值。这个过程就像是在海量数字中碰运气，每秒计算哈希值的次数越多（即算力越高），挖到矿的概率就越大。可以想象成在一个巨大的数字迷宫中寻找特定的出口，算力就是寻找出口的速度。</li><li><strong>难度调整与时间控制</strong>：比特币网络设定大约每 10 分钟产生一个区块。为实现这一目标，会根据全网算力的变化动态调整难度值。每挖出 2016 个区块（大约两周时间），系统会检查挖矿平均速度。若平均出块时间快于 10 分钟，表明全网算力提升，将增加挖矿难度（减小 bits 值）；若慢于 10 分钟，说明全网算力降低，将降低挖矿难度（增大 bits 值）。通过这种自动调整机制，确保比特币网络维持相对稳定的出块节奏，使得比特币的发行和交易能够有序进行。</li><li><strong>新区块传播与验证</strong>：当某节点成功挖到矿，会立即向其邻居节点发送新区块哈希值。邻居节点发现自身无此区块，便向该节点索要完整区块数据。收到后，邻居节点对区块头进行哈希计算，验证哈希值与接收到的一致且小于 bits 规定难度，确认区块有效后，将其保存至自身电脑的区块链数据库，并继续向其他节点传播。首个挖出的区块即创世区块，创世区块诞生后，比特币网络随即开启第二个区块的挖掘。新区块的传播和验证过程确保了整个比特币网络数据的一致性和准确性。</li></ol><h2 id="八、后续区块挖掘与交易处理"><a href="#八、后续区块挖掘与交易处理" class="headerlink" title="八、后续区块挖掘与交易处理"></a>八、后续区块挖掘与交易处理</h2><ol><li><strong>第二个区块挖掘</strong>：第二个区块挖掘流程与首个类似。节点先创建铸币交易，奖励自己 50 枚比特币，接着从内存池提取交易记录（此时内存池可能已有其他交易，如转账交易）放入区块。随后打包区块头，填入所有交易哈希值及上一区块（创世区块）的哈希值，添加时间戳、目标难度、随机数等信息，通过改变随机数计算哈希值，竞争挖掘新区块。不同节点同时竞争，谁先找到符合难度要求的哈希值，谁就成功挖到第二个区块。在这个过程中，各个节点都在努力尝试找到合适的随机数，以满足难度要求，就像一场紧张的竞赛。</li><li><strong>交易处理</strong>：例如，某用户（假设为 A）要向另一用户（假设为 B）转账比特币。A 先确认自己拥有足够且未花费的比特币，如在创世区块获得的 50 枚。A 创建交易，给 B 转 9 枚比特币，同时给自己转回剩余比特币（假设转回 40 枚，遵循比特币需花完之前交易金额的规则），剩余 1 枚作为矿工手续费。A 将交易信息广播给邻居节点，其他节点验证 A 是否有足够比特币。通过查之前区块确认后，接受交易并暂存于内存池。此时 B 的钱包显示收到 9 枚比特币，但处于未确认状态。当某节点成功挖到包含该交易的新区块并广播，其他节点验证有效后加入区块链数据库，同时从内存池删除该交易，B 才真正收到比特币。随着新区块不断生成，区块链不断增长，交易记录被永久记录，确保交易不可篡改与可追溯。这个过程保证了比特币交易的安全性和可靠性，每一笔交易都被准确记录在区块链上。</li></ol><h2 id="九、挖矿意义"><a href="#九、挖矿意义" class="headerlink" title="九、挖矿意义"></a>九、挖矿意义</h2><p>挖矿在比特币网络中起着核心作用。由于每个节点都能构建区块，为确定使用哪个区块添加到区块链，设计了挖矿机制。挖矿基于 PoW（工作量证明），是一种竞争性记账机制。只有率先找到符合条件哈希值（小于规定难度值）的节点，其构建的区块才能被添加到区块链，并获得挖矿奖励。每个区块头包含上一区块哈希值，形成链式结构。若有人篡改某区块内容，该区块哈希值改变，后续区块哈希值对应不上，链会断开。比特币网络只认可最长区块链，篡改无效。理论上，若个体或组织拥有超全网 51% 的算力，可从创世区块重新挖矿改写历史，即 “51% 算力攻击”。但现实中，要超越比特币全网目前 900EH/s 的算力几乎不可能，保障了比特币网络的安全与稳定。挖矿不仅是产生新比特币的方式，更是维护比特币网络安全和一致性的重要手段。</p>]]></content:encoded>
      
      
      <category domain="https://blog.itrf.cn/tags/btc/">btc</category>
      
      <category domain="https://blog.itrf.cn/tags/%E8%99%9A%E6%8B%9F%E8%B4%A7%E5%B8%81/">虚拟货币</category>
      
      <category domain="https://blog.itrf.cn/tags/%E6%AF%94%E7%89%B9%E5%B8%81/">比特币</category>
      
      <category domain="https://blog.itrf.cn/tags/%E5%8C%BA%E5%9D%97%E9%93%BE/">区块链</category>
      
    </item>
    
    <item>
      <title>HTTPS页面请求HTTP接口失败？一文讲透Mixed Content</title>
      <link>https://blog.itrf.cn/posts/ceca/</link>
      <guid>https://blog.itrf.cn/posts/ceca/</guid>
      <pubDate>2026-02-13T15:15:57.000Z</pubDate>
      
      <description>前端开发中，有一个高频踩坑场景：前端绑定域名后，HTTP 协议下请求后端 IP 接口一切正常，可一旦开启 SSL 切换为 HTTPS，接口就直接请求失败，控制台报出红色错误 —— 这不是代码 Bug，而是浏览器的安全限制，核心原因就是「Mixed Content（混合内容）」。</description>
      
      
      <enclosure url="https://t.alcy.cc/ycy?_r_=4c261424-2c88-1737-cbfc-acdab78e5fd3" type="image"/>
      
      
      <content:encoded><![CDATA[<h1 id="HTTPS页面请求HTTP接口失败？一文讲透Mixed-Content"><a href="#HTTPS页面请求HTTP接口失败？一文讲透Mixed-Content" class="headerlink" title="HTTPS页面请求HTTP接口失败？一文讲透Mixed Content"></a>HTTPS页面请求HTTP接口失败？一文讲透Mixed Content</h1><blockquote><p>前端开发中，有一个高频踩坑场景：前端绑定域名后，HTTP 协议下请求后端 IP 接口一切正常，可一旦开启 SSL 切换为 HTTPS，接口就直接请求失败，控制台报出红色错误 —— 这不是代码 Bug，而是浏览器的安全限制，核心原因就是「Mixed Content（混合内容）」。</p></blockquote><h2 id="一、先明确核心概念：Mixed-Content（混合内容）"><a href="#一、先明确核心概念：Mixed-Content（混合内容）" class="headerlink" title="一、先明确核心概念：Mixed Content（混合内容）"></a>一、先明确核心概念：Mixed Content（混合内容）</h2><p>首先厘清两个基础认知，避免混淆：</p><ol><li><p>HTTPS 的本质</p><p>HTTPS = HTTP + TLS（Transport Layer Security，传输层安全协议），相当于给 HTTP 传输的数据加了一层加密外套，防止数据被窃听、篡改，保障访问安全；而 HTTP 是明文传输，无任何加密保护。我们常说的「开启 SSL」，本质就是启用 HTTPS 加密。</p></li><li><p>Mixed Content（混合内容）的定义</p><p>当 HTTPS 加密页面 中，请求了 HTTP 明文资源 时，就属于 Mixed Content（混合内容）。</p><p>浏览器的安全逻辑：HTTPS 页面代表用户处于安全的加密环境，若此时请求 HTTP 明文接口，传输的数据会暴露在风险中，可能被抓包、篡改。因此，浏览器会强制拦截此类请求，这是无法通过后端配置绕过的硬规则。</p></li></ol><p>浏览器控制台的标准报错：</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="title class_">Mixed</span> <span class="title class_">Content</span>: <span class="title class_">The</span> page at <span class="string">&#x27;https://yourdomain.com&#x27;</span> was loaded over <span class="variable constant_">HTTPS</span>, but requested an insecure <span class="title class_">XMLHttpRequest</span> endpoint <span class="string">&#x27;http://your-server-ip:port/api&#x27;</span>. <span class="title class_">This</span> request has been blocked; the content must be served over <span class="variable constant_">HTTPS</span>.</span><br></pre></td></tr></table></figure><h2 id="二、重点解答：HTTPS-页面能否请求-HTTP-接口？"><a href="#二、重点解答：HTTPS-页面能否请求-HTTP-接口？" class="headerlink" title="二、重点解答：HTTPS 页面能否请求 HTTP 接口？"></a>二、重点解答：HTTPS 页面能否请求 HTTP 接口？</h2><p>结论：对于接口请求（fetch/axios/XHR），现代浏览器 100% 拦截，无生产可用的例外场景。</p><p>浏览器将 Mixed Content 分为两类，仅被动内容有极特殊情况：</p><ul><li>被动内容（Passive Mixed Content）：如图片、CSS、字体，部分浏览器可能放行，但会标红警告，无实际生产意义；</li><li>主动内容（Active Mixed Content）：如接口请求、JS 脚本、XHR/fetch，所有现代浏览器（Chrome/Edge/Firefox 等）一律强制拦截，这也是我们踩坑的核心场景。</li></ul><p>补充：手动关闭浏览器混合内容拦截（仅测试用），无法推广给普通用户，生产环境完全不可用。</p><h2 id="三、最简单、一劳永逸的解决方案（Nginx-代理）"><a href="#三、最简单、一劳永逸的解决方案（Nginx-代理）" class="headerlink" title="三、最简单、一劳永逸的解决方案（Nginx 代理）"></a>三、最简单、一劳永逸的解决方案（Nginx 代理）</h2><p>核心思路：让前端和接口「同域名、同 HTTPS」，彻底规避 Mixed Content 和跨域，无需修改后端代码。</p><h3 id="最终实现效果"><a href="#最终实现效果" class="headerlink" title="最终实现效果"></a>最终实现效果</h3><p>前端：<a href="https://yourdomain.com">https://yourdomain.com</a> （HTTPS 加密，原域名）；</p><p>接口：<a href="https://yourdomain.com/api">https://yourdomain.com/api</a> （同域名、同 HTTPS，通过 Nginx 代理到后端）。</p><h3 id="Nginx-核心配置（复制即用，替换实际信息）"><a href="#Nginx-核心配置（复制即用，替换实际信息）" class="headerlink" title="Nginx 核心配置（复制即用，替换实际信息）"></a>Nginx 核心配置（复制即用，替换实际信息）</h3><figure class="highlight nginx"><table><tr><td class="code"><pre><span class="line"><span class="section">server</span> &#123;</span><br><span class="line"></span><br><span class="line"><span class="attribute">listen</span> <span class="number">443</span> ssl;</span><br><span class="line"></span><br><span class="line"><span class="attribute">server_name</span> [yourdomain.com](https://yourdomain.com);  <span class="comment"># 你的前端域名</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># SSL 证书配置（复用前端证书，无需额外申请）</span></span><br><span class="line"></span><br><span class="line"><span class="attribute">ssl_certificate</span> /your-ssl-path/yourdomain.pem;</span><br><span class="line"></span><br><span class="line"><span class="attribute">ssl_certificate_key</span> /your-ssl-path/yourdomain.key;</span><br><span class="line">    </span><br><span class="line"><span class="comment"># 前端静态资源配置（无需部署前端可删除）</span></span><br><span class="line"></span><br><span class="line"><span class="section">location</span> / &#123;</span><br><span class="line"></span><br><span class="line"><span class="attribute">root</span> /your-frontend-path;</span><br><span class="line"></span><br><span class="line"><span class="attribute">index</span> index.html;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line">    </span><br><span class="line"><span class="comment">#核心：接口代理，转发 /api 请求到后端 HTTP 服务</span></span><br><span class="line">    </span><br><span class="line"><span class="section">location</span> /api &#123;</span><br><span class="line"></span><br><span class="line"><span class="attribute">proxy_pass</span> http://your-server-ip:your-server-port;  <span class="comment"># 后端 IP + 端口</span></span><br><span class="line"></span><br><span class="line"><span class="attribute">proxy_set_header</span> Host <span class="variable">$host</span>;</span><br><span class="line"></span><br><span class="line"><span class="attribute">proxy_set_header</span> X-Real-IP <span class="variable">$remote_addr</span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 可选：80 端口自动跳转 HTTPS，优化用户体验</span></span><br><span class="line"></span><br><span class="line"><span class="section">server</span> &#123;</span><br><span class="line"></span><br><span class="line"><span class="attribute">listen</span> <span class="number">80</span>;</span><br><span class="line"></span><br><span class="line"><span class="attribute">server_name</span> [yourdomain.com](https://yourdomain.com);</span><br><span class="line"></span><br><span class="line"><span class="attribute">return</span> <span class="number">301</span> https://hostrequest_uri;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="前端代码修改（1-行搞定）"><a href="#前端代码修改（1-行搞定）" class="headerlink" title="前端代码修改（1 行搞定）"></a>前端代码修改（1 行搞定）</h3><p>原写法（硬编码 HTTP 协议和 IP 地址，触发 Mixed Content 拦截）：</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="title function_">fetch</span>(<span class="string">&#x27;http://your-server-ip:your-server-port/api/xxx&#x27;</span>)</span><br></pre></td></tr></table></figure><p>修改后（使用相对路径，自动继承页面的 HTTPS 协议和域名，规避混合内容）：</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="title function_">fetch</span>(<span class="string">&#x27;/api/xxx&#x27;</span>)</span><br></pre></td></tr></table></figure><h2 id="四、终极避坑口诀（记牢不踩坑）"><a href="#四、终极避坑口诀（记牢不踩坑）" class="headerlink" title="四、终极避坑口诀（记牢不踩坑）"></a>四、终极避坑口诀（记牢不踩坑）</h2><ol><li>HTTPS 页面只能请求 HTTPS 接口，否则触发 Mixed Content 拦截；</li><li>HTTP 页面可请求 HTTP/HTTPS 接口，无 Mixed Content 限制；</li><li>跨域可通过后端 CORS 解决，Mixed Content 只能统一 HTTPS 协议；</li><li>接口类主动内容，HTTPS 页面请求 HTTP 接口必失败，无例外。</li></ol>]]></content:encoded>
      
      
      <category domain="https://blog.itrf.cn/tags/%E9%9B%B6%E7%A2%8E%E7%9F%A5%E8%AF%86/">零碎知识</category>
      
    </item>
    
    <item>
      <title>阿里云大模型工程师ACA认证学习笔记</title>
      <link>https://blog.itrf.cn/posts/5f08/</link>
      <guid>https://blog.itrf.cn/posts/5f08/</guid>
      <pubDate>2026-01-27T09:14:17.000Z</pubDate>
      
      <description>阿里云大模型工程师ACA认证学习笔记</description>
      
      
      <enclosure url="https://t.alcy.cc/ycy?_r_=4618cabf-f5bf-9c00-b775-c2224fcf5d57" type="image"/>
      
      
      <content:encoded><![CDATA[<h1 id="阿里云大模型工程师ACA认证学习笔记"><a href="#阿里云大模型工程师ACA认证学习笔记" class="headerlink" title="阿里云大模型工程师ACA认证学习笔记"></a>阿里云大模型工程师ACA认证学习笔记</h1><h2 id="背景知识"><a href="#背景知识" class="headerlink" title="背景知识"></a>背景知识</h2><p>从2022年底ChatGPT的一鸣惊人，再到持续进行的”百模大战”，”大模型”已经逐渐成为了技术和公众领域的热点。</p><p>大模型是人工智能领域的一个重要里程碑，它推动了人工智能技术的发展，并为人类的未来带来新的可能性。有人曾经类比，大模型的发明相当于人类文明的哪个节点？一个浪漫的答案可能是：人类学会使用火的时刻。</p><h2 id="人工智能"><a href="#人工智能" class="headerlink" title="人工智能"></a>人工智能</h2><p>定义：人工智能（AI）是一门使机器模拟人类<strong>智能过程</strong>的学科，其中具体包括学习、推理、自我修正、感知和处理语言等功能。人工智能涉及计算机科学、数据分析、统计学、机器工程、语言学、神经科学、哲学和心理学等多个学科的领域，旨在研究、设计、构建具备智能、学习、推理和行动能力的计算机和机器。</p><h3 id="分类"><a href="#分类" class="headerlink" title="分类"></a>分类</h3><ul><li><p>机器学习(ML)：研究计算机如何在没有明确编程的情况下，通过对数据的分析、学习，自动改进其行为或做出预测的学科。旨在使计算机系统具备从经验中学习的能力，以适应新情况、结局问题或完成特定任务。</p><p>根据工作模式分类：</p><ul><li>监督学习：学习为人提供的数据，对具备某种特征的数据进行人为标记。</li><li>无监督学习：根据数据特征进行相似归类。</li><li>强化学习：奖励机制，猜对了奖励，猜错了不给奖励。</li></ul></li><li><p>深度学习(DL)：是机器学习的一个分支，主要使用<strong>神经网络模型</strong>对数据进行学习和表示。深度学习算法试图模拟人类大脑的工作方式，通过对大量数据的学习，自动提取出数据的高层次特征和模式，从而实现图像识别、语音识别、自然语言处理等任务。</p><p>神经网络的分类：</p><ul><li>卷积神经网络 CNNs</li><li>循环神经网络 RNNs</li><li>Transformer网络</li></ul></li><li><p>生成式人工智能(GenerativeAI)：深度学习中快速增长的子集，使用大模型提供支持，在大量原始、未标记的数据基础上对深度学习模型进行预训练，使得机器能够“理解”语言甚至图像，并能够根据需要自动生成内容。</p></li></ul><h2 id="大模型"><a href="#大模型" class="headerlink" title="大模型"></a>大模型</h2><p>提出：2021年，斯坦福大学的研究员团队发表了一篇论文，提出了<a href="https://arxiv.org/abs/2108.07258">Foundational Models</a>（基础模型，即大模型）的概念。</p><p>定义：它是一类具有大量参数（通常在十亿以上），能在极为广泛的数据上进行训练，并适用于多种任务和应用的预训练深度学习模型。</p><h3 id="大模型的训练的三个阶段"><a href="#大模型的训练的三个阶段" class="headerlink" title="大模型的训练的三个阶段"></a>大模型的训练的三个阶段</h3><ul><li><p>预训练(Pre-training)：在这个阶段大模型会从海量的互联网文本中学习，掌握语言的统计规律、事实性知识以及上下文关联能力。但它本质上只是学会了“预测下一个词”或“补全句子”，并没有真正理解人类提问的意图。它虽然“知道很多”，却还不懂得“如何回应人的请求”。因此，我们需要通过监督微调（SFT）来教会它遵循指令。</p></li><li><p>监督微调(SFT)：在这个阶段大模型会学习大量由人类精心构造的“指令-回答”样本，涵盖日常对话、逻辑推理、专业问答等多种场景。通过这些示范，模型学会将用户的输入理解为明确的指令，并给出有针对性的回应。经过SFT，模型已经具备了基本的指令遵循能力和任务完成能力。但它仍可能输出不恰当的内容——比如包含偏见、虚假信息、不安全言论等，因为它只学会了“怎么答”，还没学会“什么该答、什么不该答”。</p></li><li><p>对齐偏好(Preference Alignment)：在这个阶段大模型会针对同一个问题生成多个不同的回答，由人类评估者根据有用性、安全性、礼貌性、事实准确性等标准进行比较和选择。模型通过学习这些“人类更喜欢哪个回答”的信号，逐渐调整自己的输出方式，使得回答不仅正确，而且更符合人类的价值观和交流习惯。  </p><p>实现对齐的方式：</p><ul><li>基于人类反馈的强化学习（RLHF，Reinforcement Learning from Human Feedback），先用人类标注的数据训练一个“评分老师”（奖励模型），再让这个“老师”指导大模型改进回答。</li><li>DPO（Direct Preference Optimization），跳过训练“老师”的中间步骤，直接根据人类偏好数据优化模型本身。这个方法简化了训练流程，效果也很好</li></ul></li></ul><h3 id="大模型的特点"><a href="#大模型的特点" class="headerlink" title="大模型的特点"></a>大模型的特点</h3><ul><li>规模和参数量大</li><li>适应性和灵活性强</li><li>广泛数据集的预训练</li><li>计算资源需求大</li></ul><h3 id="大模型分类"><a href="#大模型分类" class="headerlink" title="大模型分类"></a>大模型分类</h3><ul><li><p>大语言模型(LLM)：这类大模型专注于自然语言处理（NLP），旨在处理语言、文章、对话等自然语言文本。它们通常基于深度学习架构（如Transformer模型），经过大规模文本数据集训练而成，能够捕捉语言的复杂性，包括语法、语义、语境以及蕴含的文化和社会知识。语言大模型典型应用包括文本生成、问答系统、文本分类、机器翻译、对话系统等。</p></li><li><p>多模态模型：多模态大模型能够同时处理和理解来自不同感知通道（如文本、图像、音频、视频等）的数据，并在这些模态之间建立关联和交互。它们能够整合不同类型的输入信息，进行跨模态推理、生成和理解任务。多模态大模型的应用涵盖视觉问答、图像描述生成、跨模态检索、多媒体内容理解等领域。</p><p>模态组合：</p><ul><li>视觉+文本：包括VQA视觉问答，图像字幕，图文检索、文生图等应用。</li><li>音频+文本：包括语音生成、语音摘要、语音识别等应用。</li><li>音频+视觉：包括音生图、演讲人脸生成等应用。</li></ul></li></ul><h3 id="大模型工作原理"><a href="#大模型工作原理" class="headerlink" title="大模型工作原理"></a>大模型工作原理</h3><p>用户在可以使用自然语言与大模型交流，用户的文本就是“提示词”。提示词越清晰，模型的回答就越符合预期。</p><p>大模型处理提示词的工作流程可以分为两部分，第一部分是分词化与词表映射，第二部分为生成文本。</p><h4 id="分词化"><a href="#分词化" class="headerlink" title="分词化"></a>分词化</h4><p>分词化（Tokenization）是自然语言处理（NLP）中的重要概念，它是将段落和句子分割成更小的分词（token）的过程。</p><p>分词化的粒度分类：</p><ul><li>词粒度（Word-Level Tokenization）分词化，适用于大多数西方语言，如英语。</li><li>字符粒度（Character-Level）分词化，是中文最直接的分词方法，以单个汉字为单位进行分词化。</li><li>子词粒度（Subword-Level）分词化，它将单词分解成更小的单位，比如词根、词缀等。这种方法对于处理新词（比如专有名词、网络用语等）特别有效，因为即使是新词，它的组成部分（子词）很可能已经存在于词表中了。</li></ul><h4 id="词表映射"><a href="#词表映射" class="headerlink" title="词表映射"></a>词表映射</h4><p>分出的词就是token，每一个token都会通过预先设置好的词表，映射为一个 token id，这是token 的“身份证”，一句话最终会被表示为一个元素为token id的列表，供计算机进行下一步处理。</p><h4 id="生成文本"><a href="#生成文本" class="headerlink" title="生成文本"></a>生成文本</h4><p>大语言模型的工作概括来说是根据给定的文本预测下一个token。对我们来说，看似像在对大模型提问，但实际上是给了大模型一串提示文本，让它可以对后续的文本进行推理。</p><p>大模型的推理过程不是一步到位的，当大模型进行推理时，它会基于现有的token，根据概率最大原则预测出下一个最有可能的token，然后将该预测的token加入到输入序列中，并将更新后的输入序列继续输入大模型预测下一个token，这个过程叫做<strong>自回归</strong>。直到输出特殊token（如<EOS>，end of sentence，专门用来控制推理何时结束）或输出长度达到阈值。</p><h3 id="大模型的经典应用场景"><a href="#大模型的经典应用场景" class="headerlink" title="大模型的经典应用场景"></a>大模型的经典应用场景</h3><ul><li>文本生成</li><li>文本编辑</li><li>推理分析</li><li>总结概要</li></ul><h2 id="提示词"><a href="#提示词" class="headerlink" title="提示词"></a>提示词</h2><p><strong>提示词（Prompt）是用户发送给大语言模型的问题、指令或请求，</strong>来明确地告诉模型用户想要解决的问题或完成的任务，是大语言模型理解用户需求并据此生成相关、准确回答或内容的基础。对于大语言模型来说，提示词就是用户输入给大语言模型的文本信息。</p><p>提示词Prompt—-&gt;大语言模型LLM—-&gt;返回结果Completion</p><h3 id="提示词工程"><a href="#提示词工程" class="headerlink" title="提示词工程"></a>提示词工程</h3><p><strong>提示词工程（Prompt Engineering）</strong>就是研究如何构建和调整提示词，从而让大语言模型实现各种符合用户预期的任务的过程。</p><p>提示词工程包括以下关键步骤：</p><ol><li>理解任务需求</li><li>构建有效提示词</li><li>评估提示词效果</li><li>持续迭代改进</li></ol><h3 id="提示词技巧"><a href="#提示词技巧" class="headerlink" title="提示词技巧"></a>提示词技巧</h3><h4 id="直接提问"><a href="#直接提问" class="headerlink" title="直接提问"></a>直接提问</h4><p>直接提问，也称为<strong>零样本提示（Zero-Shot Prompting），</strong>即不给大语言模型提供案例，完全依靠 LLM 理解和处理能力完成任务。适用于<strong>目标明确、问题简单、答案确定且唯一</strong>等场景。</p><p>直接提问时，可遵循以下原则：</p><ul><li><strong>简洁</strong>：尽量用最简短的方式表达问题。过于冗长的问题可能包含多余的信息，导致模型理解错误或答非所问。</li><li><strong>具体</strong>：避免抽象的问题，确保问题是具体的，不含糊。</li><li><strong>详细上下文</strong>：如果问题涉及特定上下文或背景信息，要提供足够的详情以帮助模型理解，即使是直接提问也不例外。</li><li><strong>避免歧义</strong>：如果一个词或短语可能有多重含义，要么明确其含义，要么重新表述以消除歧义。</li><li><strong>逻辑清晰</strong>：问题应逻辑连贯，避免出现逻辑上的混淆或矛盾，这样才能促使模型提供有意义的回答。</li></ul><h4 id="增加示例"><a href="#增加示例" class="headerlink" title="增加示例"></a>增加示例</h4><p>在提示词中提供少量（通常几个或几十个）示例，也称为<strong>少样本提示（Few-Shot Prompting）</strong>，以帮助模型更好地理解任务要求和期望输出。比如：</p><ul><li>让 LLM 跟随我们所要求的规范、格式、概念、文法、语气进行输出。</li><li>提供进一步推理的参考，比如让大模型学会数学运算或按照示例方式进行逻辑推理。</li></ul><p>增加示例时可参考以下技巧：</p><ul><li><strong>精选代表性样本</strong>：选择具有代表性的示例，覆盖任务的各种情况，同时避免使用可能引起模型混淆的极端或边缘案例，确保模型能从有限的数据中学习到任务的核心特征。</li><li><strong>保证示例的多样性</strong>：尽可能覆盖任务的各种角度和情境，确保模型能从有限的数据中学习到任务的核心特征。</li><li><strong>使用相似的格式和结构</strong>：在所有示例中使用相似的提示格式和结构，使模型能够清晰识别输入与输出的关系。</li><li><strong>让大语言模型生成示例</strong>：实践时，我们还可以先让 LLM 按照提示生成一些示例，再进行筛选或人工调整，以提高示例质量和针对性。</li></ul><h4 id="分配角色"><a href="#分配角色" class="headerlink" title="分配角色"></a>分配角色</h4><p>赋予模型一个具体的角色或身份，来引导模型在特定角色视角下生成回答。</p><p>分配角色适用以下场景：</p><ul><li>需要专业知识或特定视角的问题解答。例如，模拟老师、医生、律师等回答相关领域的问题。</li><li>模拟特定人物或角色的语言风格。例如，模仿某个著名人物（如历史人物或文学角色）语言风格的文本时。</li><li>进行角色扮演游戏或创作。在创意写作或角色扮演游戏中扮演指定的角色，与使用者进行互动。</li><li>在特定行业内进行决策模拟。例如，模拟一个管理咨询师来分析商业案例或提供商业建议。</li></ul><p>分配角色引导模型生成符合特定情境和对话逻辑的内容，可遵循以下技巧：</p><ul><li><strong>明确角色身份与特性</strong><ul><li>确定角色的基本属性，如年龄、性别、职业、性格、技能、价值观等。</li><li>赋予角色相关领域的专业知识或特殊背景，如专家、学者、历史人物、虚构角色等。</li></ul></li><li><strong>设定角色目标与动机</strong><ul><li>为角色设定对话的目标，如寻求信息、说服他人、解决问题、分享观点等。</li><li>揭示角色的内在动机，如个人利益、道德信念、情感需求等，有助于塑造角色的真实性和深度。</li></ul></li><li><strong>设定角色语言风格</strong>：<ul><li>根据角色性格、教育水平、文化背景等设定其语言习惯、用词选择、句式结构、口头禅等。</li><li>规定角色在对话中的情绪状态，如冷静理智、激动愤怒、悲伤失落、幽默风趣等，影响其表达方式。</li></ul></li><li><strong>设定角色规则约束</strong>：规定角色在对话中的行为约束，如不得人身攻击、保持礼貌尊重、遵守讨论主题等。</li><li><strong>动态调整角色设定</strong>：<ul><li>随着对话深入，适时调整角色设定以适应新的情境和话题，如角色态度转变、关系演变、目标更新等。</li><li>向模型反馈角色表现，如偏离设定、缺乏个性、对话僵化等，及时修正角色设定并引导模型调整。</li></ul></li></ul><h4 id="限定输出风格-格式"><a href="#限定输出风格-格式" class="headerlink" title="限定输出风格/格式"></a>限定输出风格/格式</h4><p>为了更好的限定“<strong>风格</strong>”，准确引导模型写出符合需求的内容，下面我们介绍一些推荐的技巧：</p><ol><li><strong>明确指出所需的内容类型</strong>，如“论文/散文/诗歌/新闻报道/剧本/日记”等。</li><li><strong>用形容词限定风格</strong>，如“严谨客观”、“感性抒情”、“幽默诙谐”、“庄重典雅”等。</li><li><strong>列举风格（代表人物/作品）示例</strong>，如“仿照鲁迅先生的笔触描述社会现象”或“以J.K.罗琳的叙述风格撰写一段奇幻冒险故事”。</li><li><strong>设定语境与情感色彩</strong>：为模型设定故事背景、情感基调或角色视角，影响其语言表达和修辞选择，从而形成特定风格。如“以一名二战老兵的视角，深情回忆战场经历”。</li><li><strong>规定语言与句式特点</strong>：要求使用特定词汇、短语、成语、俚语、古语等，或强调长句、短句、排比、反问、比喻等修辞手法的运用，以契合特定风格。</li></ol><p>还可以进一步对<strong>内容的输出格式</strong>进行限定：</p><ol><li><strong>明确输出长度</strong>：如“撰写一篇关于全球气候变化的新闻报道，标题需简洁明快，概括主题，不超过300字”。</li><li><strong>段落结构</strong>：规定正文的段落数量、每段的大致内容与逻辑关系，如“文章分为引言、主体（分三点论述）和结论三部分”。</li><li><strong>列表与编号</strong>：要求使用项目符号、数字编号等形式列出要点或步骤，如“通过数字编号列出五种有效的时间管理方法，并简要解释”。</li><li><strong>引用与注释</strong>：指示何时使用引号、脚注、尾注、参考文献等格式引用他人观点或资料，如“在论述中适当引用至少两篇相关学术论文，并按照APA格式添加引用和参考文献”。</li><li><strong>格式标记</strong>：如对齐方式（左对齐、居中、右对齐）、字体样式（加粗、斜体、下划线）、缩进、行距、页眉页脚等进行详细说明，甚至可以要求大模型按照JSON格式输出。</li><li><strong>特殊要求</strong>：针对特定场景，如邮件、信函、通知、海报、简历等，规定相应的格式标准，如“按照商务电子邮件的标准格式撰写邀请函，包括收件人、抄送人、主题、问候语、正文、结束语、签名档等部分”。</li></ol><h4 id="拆解复杂任务"><a href="#拆解复杂任务" class="headerlink" title="拆解复杂任务"></a>拆解复杂任务</h4><p>把一个复杂的任务，拆解成多个稍微简单的任务，让大语言模型<strong>分步</strong>来思考问题，称为<strong>思维链（Chain-of-Thought Prompting, CoT）</strong>提示，这种方式可让大语言模型像人类一样逐步解释或进行推理，从而极大地提升 LLM 的能力。</p><p>思维链分类：</p><ul><li><p>零样本思维链（Zero-shot CoT）：没有给 LLM 提供问题解析示例。</p></li><li><p>少样本思维链（Few-shot Cot）：给 LLM 提供问题解析示例。</p><p><strong>有时 LLM 通过</strong>零样本思维链可能会得到错误的答案，可以通过增加示例的方式，即少样本思维链，帮助 LLM 理解相关任务并正确执行。</p></li></ul><h3 id="提示词框架"><a href="#提示词框架" class="headerlink" title="提示词框架"></a>提示词框架</h3><p>提示词可以包含的要素：</p><ul><li><p><strong>指令 Instruction</strong>：需要模型去做什么，如回答某个问题、撰写某种类型的文章或按照特定格式进行总结。指令应该简洁、明确，确保 LLM 能够理解任务的目标和要求。</p></li><li><p><strong>背景信息 Context</strong>：背景信息可以包括任务的背景、目的相关的各类信息，还可以为 LLM 设置角色背景、观点立场等信息，LLM 将在此背景下进行回应或生成文本。</p></li><li><p><strong>参考样本 Examples</strong>：与解决用户问题相关的示例，比如通过少样本提示的方式帮助 LLM 更好理解如何处理指令。</p></li><li><p><strong>输入数据 Input Data</strong>：用户输入指令和要求，比如用什么语气，生成多少字的内容。</p></li><li><p><strong>输出指示 Output Indicator</strong>：指定输出的类型或格式，我们可以给出限定关键词、限制条件或要求的输出格式/方式（如表格），也可以避免无关或不期望的信息出现。</p></li></ul><p>结合上述要素，我们可以根据任务的复杂度来设计提示词，具体有以下几种情况：</p><ol><li><strong>纯指令型</strong>：最直接的互动方式，仅通过简明指令向模型提出需求，适合于寻求快速、基本答案的场景。</li><li><strong>背景</strong>+<strong>指令</strong>：在指令基础上融入背景信息，为模型创造一个理解和响应任务的框架，尤其适用于需要考虑特定情境或角色定位的任务。</li><li><strong>指令</strong>/<strong>背景</strong>+<strong>输出指示</strong>：在基础指令或背景信息之上，加入输出指示，精确指导模型如何组织和呈现答案，以满足特定的格式或风格要求。</li><li><strong>综合型提示</strong>：结合指令、背景信息、输入数据与输出指示，形成一个全方位的引导体系。这种复合型提示尤为强大，能够在复杂任务中提高模型输出的针对性与质量，尤其是在模型需要从示例中学习并模仿特定风格或结构时。</li></ol><p>常见提示词框架及场景</p><div class="table-container"><table><thead><tr><th><strong>框架</strong></th><th><strong>具体应用领域（举例子）</strong></th><th><strong>内容含义</strong></th></tr></thead><tbody><tr><td><strong>CRISPE</strong></td><td>项目管理、团队协作、客户服务</td><td>能力 (Capabilities)角色 (Roles)洞察 (Insights)陈述 (Statement)个性 (Personality)实验 (Experiment)</td></tr><tr><td><strong>ROSES</strong></td><td>软件开发、产品设计、市场营销策略规划</td><td>角色 (Role)目标 (Objective)场景 (Setting)预期解决方案 (Expected Solution)步骤 (Steps)</td></tr><tr><td><strong>TRACE</strong></td><td>市场研究、业务分析、教学设计</td><td>任务 (Task)请求 (Request)操作 (Action)上下文 (Context)示例 (Examples)</td></tr></tbody></table></div><h2 id="推理模型"><a href="#推理模型" class="headerlink" title="推理模型"></a>推理模型</h2><p>推理模型通常指专门优化用于逻辑推理、问题解决、多步推断等任务的模型，它们通常在数学解题、代码生成、逻辑分析等方面有更好的表现。与通用模型相比，推理模型拥有更强的逻辑能力，因为它经过了大量的专项训练，能够更好地理解复杂的问题，并且在解答时更有条理。但并不是说就一定比通用模型更好，两种模型都有各自的应用场景，下表从一些典型维度对这两类模型进行了对比：</p><div class="table-container"><table><thead><tr><th><strong>维度</strong></th><th><strong>推理模型</strong></th><th><strong>通用模型</strong></th></tr></thead><tbody><tr><td>设计目标</td><td>专注于<strong>逻辑推理</strong>、<strong>多步问题求解</strong>、<strong>数学计算</strong>等需要深度分析的任务</td><td>面向<strong>通用对话、知识问答、文本生成</strong>等广泛场景</td></tr><tr><td>训练数据侧重</td><td>大量<strong>数学题解</strong>、<strong>代码逻辑</strong>、<strong>科学推理</strong>数据集增强推理能力</td><td>覆盖<strong>百科、文学、对话</strong>等多领域海量数据</td></tr><tr><td>典型输出特征</td><td>输出包含<strong>完整推导</strong>步骤，注重逻辑链条的完整性</td><td>输出<strong>简洁直接</strong>，侧重结果的自然语言表达</td></tr><tr><td>响应速度</td><td>复杂推理任务<strong>响应较慢</strong>（需多步计算）</td><td>常规任务<strong>响应更快</strong>（单步生成为主）</td></tr></tbody></table></div><p>推理模型还是通用模型？如何选择？以下是一些推荐：</p><ul><li><strong>明确的通用任务</strong>：对于明确定义的问题，<strong>通用模型</strong>一般能够很好地处理。</li><li><strong>复杂任务</strong>：对于非常复杂的任务，且需要给出相对<strong>更精确和可靠</strong>的答案，推荐使用<strong>推理模型</strong>。这些任务可能有：<ul><li>模糊的任务：任务相关信息很少，你无法提供模型相对明确的指引。</li><li>大海捞针：传递大量非结构化数据，提取最相关的信息或寻找关联/差别。</li><li>调试和改进代码：需要审查并进一步调试、改进大量代码。</li></ul></li><li><strong>速度和成本</strong>：一般来说推理模型的推理时间较长，如果你对于时间和成本敏感，且任务复杂度不高，<strong>通用模型</strong>可能是更好的选择。</li></ul><p>当然你还可以在你的应用中结合使用两种模型：使用推理模型完成Agent的规划和决策，使用通用模型完成任务执行。</p><p>推理模型在面对相对模糊的任务也能给出详尽且格式良好的响应。因此，使用一些提示词技巧（如指示模型“逐步思考”）可能反而限制了模型的推理。下面列举了一些适用于推理模型的提示技巧：</p><ul><li><strong>直接提问</strong>：保持提示<strong>简洁、清晰</strong>，且明确任务限制。</li><li><strong>避免思维链提示</strong>：你无需提示推理模型“逐步思考”或“解释你的推理”，它们本身会进行深入的思考。</li><li><strong>根据模型响应调整提示词</strong>：直接提问推理模型通常能够产生良好的响应，但如果你有更复杂精细的要求，可以在提示词中明确，比如有<strong>明确的输入信息和输出要求</strong>时，你可以通过<strong>增加示例</strong>明确这些信息，还可以通过<strong>分隔符</strong>帮助推理模型区分不同的信息模块。这个过程可以是重复多次的，不断尝试调整提示，让模型不断推理迭代，直到符合你的要求。</li></ul><h2 id="插件"><a href="#插件" class="headerlink" title="插件"></a>插件</h2><p>定义：大模型插件是一种软件组件，它们设计用于增强和扩展基础大模型的功能。如网络搜索、视觉生成、语音合成等。</p><p>典型的插件包括：</p><ul><li>基础工具，如计算器、时钟、天气信息、股票行情</li><li>可以让用户获得实时性消息的插件，比如体育赛事报道、实时、热点新闻</li><li>可以为模型拓展更多能力的插件，比如图片生成、语音合成、代码解释器</li></ul><h3 id="工作原理"><a href="#工作原理" class="headerlink" title="工作原理"></a>工作原理</h3><p>首先接收输入，判断是否需要使用工具及选择合适的工具，然后使用工具并获得返回结果进行后续推理生成。</p><p>用户提问-&gt; 程序根据用户问题调用大模型-&gt;大模型判断是否需要调用工具-&gt;大模型输出工具名称解析工具入参-&gt;程序根据工具名称、入参调用工具-&gt;工具输出运行结果-&gt;程序根据工具结果、用户提问重新调用大模型-&gt;大模型给出回复-&gt;程序展示结果给用户</p><h2 id="RAG"><a href="#RAG" class="headerlink" title="RAG"></a>RAG</h2><p>RAG（Retrieval-Augmented Generation，检索增强生成） 是一种结合了信息检索技术与语言生成模型的人工智能技术。该技术通过从外部知识库中检索相关信息，并将其作为提示（Prompt）输入给大型语言模型（LLMs），以增强模型处理知识密集型任务的能力，如问答、文本摘要、内容生成等。</p><ul><li>内置知识库</li><li>用户定制知识库</li></ul><h3 id="实现原理"><a href="#实现原理" class="headerlink" title="实现原理"></a>实现原理</h3><p><strong>检索增强生成</strong>包括三个步骤，建立<strong>索引</strong>、<strong>检索</strong>、<strong>生成</strong>。</p><p><strong>建立索引：</strong>首先要清洗和提取原始数据，将 PDF、Docx等不同格式的文件解析为<strong>纯文本</strong>数据；然后将文本数据分割成更小的片段（chunk）；最后将这些片段经过嵌入模型转换成向量数据（此过程叫做embedding），并将原始语料块和嵌入向量以键值对形式存储到向量数据库中，以便进行后续快速且频繁的搜索。这就是建立索引的过程。</p><p><strong>检索生成</strong>：系统会获取到用户输入，随后计算出用户的问题与向量数据库中的文档块之间的相似度，选择相似度最高的K个文档块（K值可以自己设置）作为回答当前问题的知识。知识与问题会合并到提示词模板中提交给大模型，大模型给出回复。这就是检索生成的过程。</p><blockquote><p>提示词模板类似于：请阅读：{知识文档块}，请问：{用户的问题}</p></blockquote><h3 id="RAG-存在的问题"><a href="#RAG-存在的问题" class="headerlink" title="RAG 存在的问题"></a>RAG 存在的问题</h3><ul><li>问题比较抽象或者概念比较模糊，导致大模型没有准确理解使用者的问题。</li><li>知识库没有检索到问题的答案</li><li>缺少对答案做兜底验证的机制</li></ul><h3 id="RAG评测标准"><a href="#RAG评测标准" class="headerlink" title="RAG评测标准"></a>RAG评测标准</h3><ul><li>考察<strong>检索</strong>模块的准确性，如准确率、召回率、F1值等等；</li><li>考察<strong>生成</strong>模块生成答案的价值，如相关性、真实性等等。</li></ul><p>参考<a href="https://docs.ragas.io/en/stable/concepts/metrics/index.html#"><strong>Ragas</strong></a>提及的评测矩阵指南，可以建立一些自己的评测指标。</p><h3 id="改造方案"><a href="#改造方案" class="headerlink" title="改造方案"></a>改造方案</h3><h4 id="提升索引准确度"><a href="#提升索引准确度" class="headerlink" title="提升索引准确度"></a>提升索引准确度</h4><ul><li><p>优化文本解析过程，正确的从文档中提取有效语料。</p></li><li><p>优化chunk切分模式</p><ul><li>利用领域专有知识进行精准切分</li><li>基于固定大小切分</li><li>上下文感知，切分时考虑前后文关系避免信息断裂</li></ul></li></ul><ul><li><p>句子滑动窗口检索，通过设置window_size（窗口大小）来调整提取句子的数量，当用户的问题匹配到一个chunk语料块时，通过窗函数提取目标语料块的上下文，而不仅仅是语料块本身，这样来获得更完整的语料上下文信息，提升RAG生成质量。</p></li><li><p>自动合并检索，将文档分块，建成一棵语料块的树，比如1024切分、512切分、128切分，并构造出一棵文档结构树。当应用作搜索时，如果同一个父节点的多个叶子节点被选中，则返回整个父节点对应的语料块。</p></li><li><p>选择更适合业务的Embedding模型</p><p>把chunk语料块由原来的文本内容转换为机器可以用于比对计算的一组数字，即变为Embedding向量。</p></li><li><p>选择更合适业务的ReRank模型，好的ReRank模型会让更贴近用户问题的chunks的排名更靠前。</p></li><li><p>Raptor用聚类为文档块建立索引，这就像通过文档的内容为文档自动建立目录的过程。</p></li></ul><h4 id="让问题更好理解"><a href="#让问题更好理解" class="headerlink" title="让问题更好理解"></a>让问题更好理解</h4><ul><li><p>Enrich完善用户问题</p><ul><li><p>多轮对话补全需求</p></li><li><p>大模型转述用户问题</p></li><li><p>让用户补全信息</p></li></ul></li></ul><ul><li><p>Multi-Query 多路召回，先一次性改写出多种用户问题，让大模型根据用户提出的问题，从多种不同角度去生成有一定提问角度或提问内容上存在差异的问题。让这些存在差异的问题作为大模型对用户真实需求的猜测，然后再把每个问题分别生成答案，并总结出最终答案。</p><p>提示词模板</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">You are an AI language model assistant. Your task is to generate five </span><br><span class="line">different versions of the given user question to retrieve relevant documents from a vector database. By generating multiple perspectives on the user question, your goal is to help the user overcome some of the limitations of the distance-based similarity search. </span><br><span class="line">Provide these alternative questions separated by newlines. Original question: &#123;question&#125;</span><br></pre></td></tr></table></figure></li><li><p>RAG-Fusion过滤融合，在经过多路召回获取了各种语料块之后，并不是将所有检索到的语料块都交给大模型，而是先进行一轮筛选，给检索到的语料块进行去重操作，然后按照与原问题的相关性进行排序，再将语料块打包喂给大模型来生成答案。</p></li><li><p>Step Back 问题摘要，让大模型先对问题进行一轮抽象，从大体上去把握用户的问题，获得一层高级思考下的语料块。</p></li><li><p>Decomposition问题分解，用户的问题拆成一个一个小问题来理解，或者可以说是RAG中的CoT。</p><p>提示词</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">You are a helpful assistant that generates multiple sub-questions related to an input question. \n</span><br><span class="line">The goal is to break down the input into a set of sub-problems / sub-questions that can be answers in isolation. \n</span><br><span class="line">Generate multiple search queries related to: &#123;question&#125; \n</span><br><span class="line">Output (3 queries):</span><br></pre></td></tr></table></figure><ul><li>串行执行</li><li>并行执行</li></ul></li><li><p>HyDE假设答案，让大模型先来根据用户的问题生成一段假设答案，然后用这段假设的答案作为新的问题去文档库里匹配新的文档块，再进行总结，生成最终答案。</p><p>用户提问-&gt;生成答案-&gt;信息抽离-&gt;生成答案</p></li></ul><h4 id="改造信息抽取途径"><a href="#改造信息抽取途径" class="headerlink" title="改造信息抽取途径"></a>改造信息抽取途径</h4><p>   <strong>Corrective Retrieval Augmented Generation (CRAG)</strong>是一种改善提取信息质量的策略：如果通过知识库检索得到的信息与用户问题相关性太低，我们就主动搜索互联网，将网络搜索到的信息与知识库搜索到的信息合并，再让大模型进行整理给出最终答案。</p><p>   两种实现方式</p><ul><li><strong>向量相似度</strong>，我们用检索信息得到的向量相似度分来判断。判断每个语料块与用户问题的相似度评分，是否高过某个阈值，如果搜索到的语料块与用户问题的相似度都比较低，就代表知识库中的信息与用户问题不太相关；</li></ul><ul><li><strong>直接问大模型</strong>，我们可以先将知识库检索到的信息交给大模型，让大模型自主判断，这些资料是否能回答用户的问题。</li></ul><h4 id="回答前反复思考"><a href="#回答前反复思考" class="headerlink" title="回答前反复思考"></a>回答前反复思考</h4><p>Self-RAG，也称为self-reflection，是一种通过在应用中设计<strong>反馈路径</strong>实现<strong>自我反思</strong>的策略。<br>   基于这个思想，我们可以让应用问自己三个问题：</p><ul><li><p><strong>相关性</strong>：我获取的这些材料和问题相关吗？ 在生成答案前判断，不相关就改写问题。</p></li><li><p><strong>无幻觉</strong>：我的答案是不是按照材料写的来讲，还是我自己编造的？在生成答案后判断，出现幻觉就重新生成答案。</p></li><li><p><strong>已解答</strong>：我的答案是不是解答了问题？在输出答案前判断，没有解答问题就改写问题。</p></li></ul><h4 id="从多个数据源中获取资料"><a href="#从多个数据源中获取资料" class="headerlink" title="从多个数据源中获取资料"></a>从多个数据源中获取资料</h4><ul><li>从数据库中获取统计指标，大模型可以将用户问题转化为SQL语句去数据库中检索相关信息，这个能力就是NL2SQL（Natural Language to SQL）。</li><li>从知识图谱中获取数据，Neo4j是一款图数据库引擎，可以为我们提供知识图谱构建和计算服务。在知识图谱上，我们调用各种图分析算法，如标签传播、关键节点发现等等，可以快速检索多度关联关系，挖掘隐藏关系。</li></ul><h2 id="微调"><a href="#微调" class="headerlink" title="微调"></a>微调</h2><p>核心思想：在预训练的基础上，使用特定领域的数据对模型进行进一步的训练，从而让模型更擅长处理你想要解决的问题，也就是说，<strong>让大模型更懂你</strong>。</p><h3 id="微调的特点"><a href="#微调的特点" class="headerlink" title="微调的特点"></a>微调的特点</h3><ol><li>数据规模小</li><li>训练时间短</li></ol><h3 id="微调可以实现"><a href="#微调可以实现" class="headerlink" title="微调可以实现"></a>微调可以实现</h3><ul><li>风格化，角色扮演</li><li>格式化，系统对接</li></ul><h3 id="微调的作用"><a href="#微调的作用" class="headerlink" title="微调的作用"></a>微调的作用</h3><p>提高效率降低成本</p><h3 id="微调的关键"><a href="#微调的关键" class="headerlink" title="微调的关键"></a>微调的关键</h3><ul><li>需要特定领域的高质量数据</li><li>需要配置合适的参数才能达到想要的微调效果</li></ul><h3 id="微调的使用场景"><a href="#微调的使用场景" class="headerlink" title="微调的使用场景"></a>微调的使用场景</h3><p>如果你的部署环境里无法提供知识检索服务，或者你单纯希望大模型记住这些重要的信息。你可以考虑使用微调，为大模型注入专有领域的知识库，确保大模型的回答会更准确、更高效。</p><h3 id="业务决策"><a href="#业务决策" class="headerlink" title="业务决策"></a>业务决策</h3><ul><li><p>业务需求匹配度</p><p>首先明确业务的具体需求，明确要通过大模型微调解决的具体业务问题和应用场景。可以先问自己以下问题：</p><ul><li>你的任务是否需要复杂的语言理解或生成能力？例如，复杂的自然语言生成、意图识别等任务可能受益于大模型微调。</li><li>你的任务是否需要高度特定于某个领域或任务的语言能力？例如，法律文书分析、医学文本理解等领域任务。</li><li>当前的模型是否已能满足大部分需求？如果能满足，则可能不需要微调。</li><li>是否有具体的业务指标来衡量微调前后效果对比？比如微调后的大模型推送给客户的信息更加准确，从而降低投诉率。</li></ul></li><li><p>数据可用性与质量</p><p>微调需要足够的高质量领域特定数据。需要评估当前业务系统中是否能够提取出足够的标注数据用于训练，以及数据的质量、代表性是否满足要求。</p></li><li><p>合规与隐私</p><p>业务工作者需要确保使用的数据符合法律法规要求，处理个人数据时遵循隐私保护原则，尤其是GDPR等国际和地区隐私法规。此外，还要关注模型偏见、过拟合、泛化能力不足等潜在风险，以及这些风险对业务可能造成的影响。</p></li><li><p>资源和技术可行性</p><p>微调过程需要计算资源和时间成本，包括GPU资源、存储空间以及可能的专家人力成本。需要评估项目预算和资源是否允许进行有效微调。</p><p>团队是否具备微调大模型所需的技术能力和经验，或者是否有合适的合作伙伴提供技术支持。</p></li></ul><h3 id="微调流程"><a href="#微调流程" class="headerlink" title="微调流程"></a>微调流程</h3><ol><li>准备数据<ul><li>数据收集：收集用于微调模型的数据，例如之前的交互记录、常见问题及回答等。</li><li>数据清洗：清洗这些数据，去除敏感信息，保证数据的质量。</li></ul></li><li>模型选择<ul><li>选择适合的预训练大模型。准备一个或多个有使用权限的通用大模型。</li></ul></li><li>模型微调<ul><li>使用你收集的数据来微调所选模型。</li><li>标注数据：如果需要，对数据进行标注以支持监督学习。</li><li>微调模型：运行微调程序，调整模型的参数使其更适应你的业务数据。</li></ul></li><li>模型评估<ul><li>你可以在准备训练集的同时，准备一份与训练集格式一致的评测集，该评测集用来评测微调后模型的效果。如果评测集数据条目较少，你可以直接去观察微调后模型的输出，并与评测集的<strong>output</strong>进行比较。</li></ul></li><li>模型集成<ul><li>编写代码将模型调用集成到现有的业务流程中去。</li><li>设定合理的调用逻辑和流程，如何处理模型的输出，如何处理API调用错误等。</li><li>确保API调用遵守数据保护法规。</li><li>实施适当的身份验证和授权控制。</li></ul></li><li>测试与优化<ul><li>在开发和测试环境中测试API调用和模型集成。</li><li>根据测试结果优化模型性能。</li></ul></li><li>模型部署<ul><li>在生产环境中部署微调后的模型。</li></ul></li><li>监控与维护<ul><li>监控模型的性能和API的健康状况。</li><li>定期重新评估和优化模型以适应新数据和业务变化。</li></ul></li><li>持续迭代<ul><li>根据用户反馈和业务需求的变化，继续改进模型和API。</li></ul></li></ol><p>在进行这一切工作时，保持良好的文档习惯也至关重要，这样团队成员可以轻松跟踪项目的进展，新加入的成员也能够快速上手。此外，确保所有与微调和部署模型相关的关键决策都得到适当记录和备份。</p><h3 id="微调的方式"><a href="#微调的方式" class="headerlink" title="微调的方式"></a>微调的方式</h3><p>大模型由多个子模块组成</p><p><strong>全参微调（Full Fine Tuning）</strong>是在预训练模型的基础上进行<strong>全量参数</strong>微调的模型优化方法，只要有参数，就会被调整。该方法避免消耗重新开始训练模型所有参数所需的大量计算资源，又能避免部分参数未被微调导致模型性能下降。但是，大模型训练成本高昂，需要庞大的计算资源和大量的数据，即使是全参数微调，往往也需要较高的训练成本。</p><p><strong>高效微调 PEFT</strong>（Parameter-Efficient Fine-Tuning）旨在降低微调参数的数量和计算复杂度的同时提高预训练模型在新任务上的性能，缓解微调大型预训练模型的训练成本，只需要调整某几层网络的参数即可。当前比较主流的 PEFT 方法包括 Adapter Tuning、Prompt Tuning、LoRA 等等。</p><ul><li>LoRA 的全称是 “Low-Rank Adaptation”（低秩适应）。它不对原模型做微调，而是在原始模型旁边增加一个旁路，通过学习小参数的低秩矩阵来近似模型权重矩阵的参数更新，训练时只优化低秩矩阵参数，通过降维度再升维度的操作来大量压缩需要训练的参数。</li><li>Adapter Tuning，该方法会在原有的模型架构上，在某些位置之间插入Adapter层，微调训练时只训练这些Adapter层，而原先的参数不会参与训练。该方法可以在只额外增加3.6%参数的情况下达到与全参数微调相似的效果。</li><li>Prefix Tuning，该方法不改变原有模型的结构，而是给文本数据的token之前构造一段固定长度任务相关的虚拟token作为前缀Prefix。训练的时候只更新这个虚拟token部分的参数，而其他部分参数固定。这些前缀向量可以视为对模型进行引导的“指令”，帮助模型理解即将处理的任务。由于只需学习远少于模型全部参数数量的前缀向量，这种方法极大地减少了内存占用和计算成本。</li><li>Prompt Tuning，一种轻量高效的微调方法，其原理与Prefix Tuning类似，但仅在输入层加入可训练的prompt tokens向量（而非像Prefix Tuning那样作用于每层Transformer）来引导模型输出。这些提示词向量并不一定放在问题的前边，也可以针对不同的任务设计不同的提示词向量。微调训练只对这些前缀提示词向量进行更新，从而<strong>不修改大模型本身的参数</strong>，大幅减少训练参数。</li></ul><h3 id="RAG-v-s-微调"><a href="#RAG-v-s-微调" class="headerlink" title="RAG v.s. 微调"></a>RAG v.s. 微调</h3><div class="table-container"><table><thead><tr><th><strong>对比项</strong></th><th><strong>RAG</strong></th><th><strong>微调</strong></th></tr></thead><tbody><tr><td>应用场景</td><td>解决垂直领域知识问答的“幻觉”问题</td><td>解决风格化生成和垂域系统指令生成问题</td></tr><tr><td>技术能力要求</td><td>需要了解如何使用向量数据库</td><td>需要有较强的数据预处理能力需要熟悉微调的参数体系和调整策略</td></tr><tr><td>成本比较</td><td>部署和运维线上推理服务与向量数据库</td><td>微调训练成本，部署运维成本，需要专业技术人员操作</td></tr><tr><td>数据集准备</td><td>准备一份知识库文档即可</td><td>需要在知识库文档里提取高质量问答对</td></tr><tr><td>最终输入大模型的token个数</td><td>需要将知识与问题拼接起来，输入大模型的tokens个数较多</td><td>只需输入问题即可，输入大模型的tokens个数很少</td></tr><tr><td>推理速度</td><td>经过检索、生成两个步骤，且向大模型输入较多token，推理速度相对较慢</td><td>直接推理，速度较快</td></tr><tr><td>知识更新的及时性</td><td>可以做到准实时，只需要及时地更新知识库中的文档</td><td>比RAG慢，微调训练需要花费较多时间</td></tr></tbody></table></div><h2 id="Agent"><a href="#Agent" class="headerlink" title="Agent"></a>Agent</h2><p>定义：从软件工程的角度来看，大模型Agent是指基于大语言模型的，能使用工具与外部世界进行交互的计算机程序。</p><h3 id="Agent的工作流程"><a href="#Agent的工作流程" class="headerlink" title="Agent的工作流程"></a>Agent的工作流程</h3><ol><li>感知</li><li>思考</li><li>行动</li></ol><h3 id="Agent的能力"><a href="#Agent的能力" class="headerlink" title="Agent的能力"></a>Agent的能力</h3><ul><li><p>记忆能力</p><ul><li>短期记忆，对应着用户与大模型Agent之间的历史对话、提示词、搜索工具反馈的语料块等等，但这些信息仅与当前用户与大模型的对话内容有关。一旦用户关闭应用或离开聊天环境，这类信息就消失了。</li><li>长期记忆，对应着系统持久化的信息，如业务历史记录、知识库等，通常存储在外部向量数据库和文档库中，Agent会利用长期记忆来回答用户私有知识或专业领域相关的问题。</li></ul></li><li><p>规划能力</p><ul><li><p>任务分解</p><ul><li>思维链</li><li>思维树</li><li>思维图</li></ul></li><li><p>推理与反思，让大模型先审视自己的结果，然后再输出结果。</p><ul><li><p>ReAct，现原理是把思考、推理、观察整合到提示词模版中，让大模型在具体工作中既可以基于环境反馈选择使用何种工具来完成工作，又可以基于环境反馈推理是否可以给出任务的答案。</p><p>大模型在ReAct提示词的引导下完成推理、行动、再推理、再行动，直至生成最终答案。</p><ul><li>当大模型接收到用户请求的时候，大模型先判断能不能解答用户的问题。</li><li>当大模型不能解答用户的问题时，大模型会根据可用工具列表的介绍，选择一个合适的工具。</li><li>大模型根据工具的使用说明生成调用指令，并在指令中插入需要的参数。</li><li>Agent主体程序会根据大模型生成的指令来调用对应的工具服务</li><li>Agent主体程序等待工具执行完成并收集执行结果。</li><li>带着用户问题和执行结果，大模型会再次考虑是否已经解决了问题。如果没有解决，就继续选择一个工具来执行。如果解决了，大模型会生成最终答案，并结束任务。</li></ul><p>提示词模板</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">input_variables=[&#x27;agent_scratchpad&#x27;, &#x27;input&#x27;, &#x27;tool_names&#x27;, &#x27;tools&#x27;] </span><br><span class="line">template=&#x27;Answer the following questions as best you can. You have access to the following tools:</span><br><span class="line"></span><br><span class="line">&#123;tools&#125;</span><br><span class="line"></span><br><span class="line">Use the following format:</span><br><span class="line"></span><br><span class="line">Question: the input question you must answer</span><br><span class="line">Thought: you should always think about what to do</span><br><span class="line">Action: the action to take, should be one of [&#123;tool_names&#125;]</span><br><span class="line">Action Input: the input to the action</span><br><span class="line">Observation: the result of the action</span><br><span class="line">... (this Thought/Action/Action Input/Observation can repeat N times)</span><br><span class="line">Thought: I now know the final answer</span><br><span class="line">Final Answer: the final answer to the original input question</span><br><span class="line"></span><br><span class="line">Begin!</span><br><span class="line"></span><br><span class="line">Question: &#123;input&#125;</span><br><span class="line">Thought:&#123;agent_scratchpad&#125;&#x27;</span><br></pre></td></tr></table></figure></li></ul></li></ul></li></ul><h3 id="Agent与大模型的区别"><a href="#Agent与大模型的区别" class="headerlink" title="Agent与大模型的区别"></a>Agent与大模型的区别</h3><ol><li>它不局限于输出回答，还能通过插件（工具）<strong>与外部世界交互</strong>，论上只要是计算机程序能做的事情，它都能做到。</li><li>它不再是被动式地接受多轮提问，而是能<strong>自主地推理</strong>（拆解任务、选择最优路径）、<strong>主动纠错、自主完成任务</strong>。你可以让它每完成一个或多个步骤就给你同步进展，和你确认下一步的动作，也可以授权它自主地完成所有步骤。</li><li>它不仅可以完成简单的事情，还能<strong>完成复杂的任务</strong>。</li><li>它可以自我迭代，<strong>吸取历史经验</strong>，不断成长，因为它不仅能记住这次会话里你对它的指导，还能记住以前的会话里你给它提过的要求。</li><li>它不仅能完成通用的任务，还能<strong>完成特定领域的任务</strong>，因为它可以接入特定领域的外部知识库和工具。</li></ol><h3 id="Multi-Agent"><a href="#Multi-Agent" class="headerlink" title="Multi-Agent"></a>Multi-Agent</h3><p>通过给每个Agent制定明确且专业的角色名称和职责描述，不仅可以提升它们的专业性，还能帮助它们更好地理解和配合各自的工作。</p><p>Agent之间可以互相指派任务，也可以是一个主Agent带着一系列从Agent协同工作。</p><h2 id="多模态大模型"><a href="#多模态大模型" class="headerlink" title="多模态大模型"></a>多模态大模型</h2><p>每一种信息的来源或者形式，都可以称为一种模态（Modality）。</p><p>在大模型领域，模态指的是数据或信息的类型或表达形式，文本、图像、视频、音频等都是不同的模态。多模态，顾名思义，就是指结合两种及以上的模态，进行更综合的数据处理和分析。</p><h3 id="混合专家模型（MoE）"><a href="#混合专家模型（MoE）" class="headerlink" title="混合专家模型（MoE）"></a>混合专家模型（MoE）</h3><p><strong>混合专家模型的核心思想就是</strong>术有专攻，类似“专家会诊”，由多个不同领域的模型（即“专家”）组合成一个模型，分别去解决不同领域的问题。</p><ul><li>Experts（专家网络）：每个专家网络是用来处理某一特定类型问题或数据子集的独立模型，一般都在他们各自的专长上受过训练，推理时只有部分专家网络参与计算。</li><li>GateNet（门控网络）：类似于“交通指挥官”，负责评估输入数据，并决定由哪些专家参与处理当前的问题，尽可能地利用每个专家的专业知识来提供最准确的预测或决策输出。</li></ul><p>局限性：</p><ul><li><strong>计算资源需求高</strong>：MoE模型中的每个专家都是一个独立的模型，当专家数量增加时，模型的总参数量也相应增加，导致部署模型可能需要更多的GPU显存资源。</li><li><strong>过拟合风险</strong>：由于参数量大和复杂性，可能比简单模型更容易过拟合训练数据，特别是当数据量不足时。</li></ul><h3 id="大小模型云端协同"><a href="#大小模型云端协同" class="headerlink" title="大小模型云端协同"></a>大小模型云端协同</h3><p><strong>将大模型部署在云端，向边、端的小模型输出模型能力，小模型负责实时数据处理和初步推理，并向大模型反馈算法与执行成效，</strong>这样既能在云端充分发挥大模型的推理训练能力，又能调动边、端的小模型的敏捷性，可实现合理分配计算资源、提高响应速度。</p><h3 id="Agent与多模态"><a href="#Agent与多模态" class="headerlink" title="Agent与多模态"></a>Agent与多模态</h3><ul><li><strong>Agent</strong>概念通常指的是可以通过组件或工具与外界环境进行交互的大模型应用。它能够扩展模型的功能，比如增加特定领域的知识、优化交互流程或实现定制化逻辑，使模型更加灵活高效地应用于实际场景。</li><li><strong>多模态</strong>指的是模型能够处理和整合多种类型的数据，如文本、图像、声音等。在大模型领域，多模态技术的融合旨在让模型具备跨媒体理解和生成能力，这对于提升模型在复杂应用场景中的表现至关重要，如图文并茂的问答系统、视频内容分析等。</li></ul>]]></content:encoded>
      
      
      <category domain="https://blog.itrf.cn/tags/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</category>
      
      <category domain="https://blog.itrf.cn/tags/ACA%E8%AE%A4%E8%AF%81/">ACA认证</category>
      
      <category domain="https://blog.itrf.cn/tags/%E5%A4%A7%E6%A8%A1%E5%9E%8B/">大模型</category>
      
    </item>
    
    <item>
      <title>学习NAT类型与实战各类型打洞可行性</title>
      <link>https://blog.itrf.cn/posts/f77d/</link>
      <guid>https://blog.itrf.cn/posts/f77d/</guid>
      <pubDate>2026-01-02T07:03:23.000Z</pubDate>
      
      <description>本文介绍了各NAT类型打洞可行性，以及实际操作步骤。</description>
      
      
      <enclosure url="https://t.alcy.cc/ycy?_r_=aa36be44-ebbe-2341-93b8-53db6a64e890" type="image"/>
      
      
      <content:encoded><![CDATA[<p>参考：<a href="https://tailscale.com/blog/how-nat-traversal-works">https://tailscale.com/blog/how-nat-traversal-works</a></p><h2 id="一、相关工具（含使用场景）"><a href="#一、相关工具（含使用场景）" class="headerlink" title="一、相关工具（含使用场景）"></a>一、相关工具（含使用场景）</h2><ul><li>在线检测NAT类型：<a href="https://natchecker.com">NAT Checker</a>（核心功能：检测映射/过滤行为，精准判定NAT1-9类型，建议打洞前必测）</li><li>通用打洞工具：<a href="https://tailscale.com/">Tailscale</a>（适配EasyNAT全场景，HardNAT需依赖UPnP，操作简单，适合新手）</li><li>复杂NAT适配工具：<a href="https://easytier.cn/">Easytier</a>（支持端口扫描/预测，可尝试HardNAT组合，适合进阶用户）</li><li>辅助排查工具：Wireshark（抓包分析流量走向，定位打洞失败原因，如端口过滤、映射端口变化）</li></ul><h2 id="二、必备知识（打洞原理基础）"><a href="#二、必备知识（打洞原理基础）" class="headerlink" title="二、必备知识（打洞原理基础）"></a>二、必备知识（打洞原理基础）</h2><p>路由器通过两张核心表实现NAT管控，这是打洞能否成功的关键：</p><ol><li><strong>NAT映射表</strong>：记录内部主机（内网IP:端口）与公网（公网IP:端口）的映射关系，仅当内部主机向外发起请求时生成/更新；</li><li><strong>状态表</strong>：记录内部主机的对外请求状态（如请求目标IP:端口、连接状态），仅当外部流量与状态表匹配时，才允许穿透NAT进入内网。</li></ol><p>打洞的核心逻辑：通过中转服务器交换双方公网映射信息，再主动发起请求触发对方状态表更新，让后续流量能被放行。</p><h3 id="2-1-NAT映射行为（决定公网端口是否固定）"><a href="#2-1-NAT映射行为（决定公网端口是否固定）" class="headerlink" title="2.1 NAT映射行为（决定公网端口是否固定）"></a>2.1 NAT映射行为（决定公网端口是否固定）</h3><ul><li><strong>地址和端口相关映射</strong>（HardNAT特征）：仅当请求的「目标IP+目标端口」与历史请求完全一致时，才复用原公网映射端口；只要有一项不同，就生成新的公网端口（如内网主机先访问中转服务器A:8080，再访问主机B:80，会生成两个不同公网端口）。</li><li><strong>地址相关映射</strong>（HardNAT特征）：只要请求的「目标IP」与历史一致，即使目标端口不同，也复用原公网端口；若目标IP不同，则生成新端口（如内网主机访问A:8080和A:80，共用一个公网端口；访问A:8080和B:80，生成两个端口）。</li><li><strong>端点无关映射</strong>（EasyNAT特征）：无论请求的目标IP、目标端口如何变化，始终复用同一个公网映射端口（如内网主机访问A:8080、B:80、C:443，均使用同一个公网端口）。</li></ul><h3 id="2-2-NAT过滤行为（决定外部流量能否放行）"><a href="#2-2-NAT过滤行为（决定外部流量能否放行）" class="headerlink" title="2.2 NAT过滤行为（决定外部流量能否放行）"></a>2.2 NAT过滤行为（决定外部流量能否放行）</h3><ul><li><strong>地址和端口相关过滤</strong>：仅当外部响应的「源IP+源端口」与内部主机之前的请求目标完全一致时，才放行流量；否则直接过滤。</li><li><strong>地址相关过滤</strong>：只要外部响应的「源IP」与内部请求的目标IP一致，即使源端口不同，也放行流量；源IP不同则过滤。</li><li><strong>端点无关过滤</strong>：无论外部响应的源IP、源端口是什么，均直接放行（最宽松的过滤规则）。</li></ul><h3 id="2-3-NAT类型对照表（含打洞难度标注）"><a href="#2-3-NAT类型对照表（含打洞难度标注）" class="headerlink" title="2.3 NAT类型对照表（含打洞难度标注）"></a>2.3 NAT类型对照表（含打洞难度标注）</h3><div class="table-container"><table><thead><tr><th style="text-align:left">NAT类型</th><th style="text-align:left">RFC 5780映射方式</th><th style="text-align:left">RFC 5780过滤方式</th><th style="text-align:left">RFC 3489对应类型</th><th style="text-align:left">分类</th><th style="text-align:left">打洞难度</th></tr></thead><tbody><tr><td style="text-align:left">NAT Type 1</td><td style="text-align:left">端点无关映射</td><td style="text-align:left">端点无关过滤</td><td style="text-align:left">全锥型（Full Cone）</td><td style="text-align:left">EasyNAT</td><td style="text-align:left">⭐ 极低</td></tr><tr><td style="text-align:left">NAT Type 2</td><td style="text-align:left">端点无关映射</td><td style="text-align:left">地址相关过滤</td><td style="text-align:left">受限锥型（Restricted Cone）</td><td style="text-align:left">EasyNAT</td><td style="text-align:left">⭐ 低</td></tr><tr><td style="text-align:left">NAT Type 3</td><td style="text-align:left">端点无关映射</td><td style="text-align:left">地址和端口相关过滤</td><td style="text-align:left">端口受限锥型（Port Restricted Cone）</td><td style="text-align:left">EasyNAT</td><td style="text-align:left">⭐⭐ 中低</td></tr><tr><td style="text-align:left">NAT Type 4</td><td style="text-align:left">地址相关映射</td><td style="text-align:left">端点无关过滤</td><td style="text-align:left">-</td><td style="text-align:left">HardNAT</td><td style="text-align:left">⭐⭐⭐⭐ 高</td></tr><tr><td style="text-align:left">NAT Type 5</td><td style="text-align:left">地址相关映射</td><td style="text-align:left">地址相关过滤</td><td style="text-align:left">-</td><td style="text-align:left">HardNAT</td><td style="text-align:left">⭐⭐⭐⭐ 高</td></tr><tr><td style="text-align:left">NAT Type 6</td><td style="text-align:left">地址相关映射</td><td style="text-align:left">地址和端口相关过滤</td><td style="text-align:left">-</td><td style="text-align:left">HardNAT</td><td style="text-align:left">⭐⭐⭐⭐⭐ 极高</td></tr><tr><td style="text-align:left">NAT Type 7</td><td style="text-align:left">地址和端口相关映射</td><td style="text-align:left">端点无关过滤</td><td style="text-align:left">-</td><td style="text-align:left">HardNAT</td><td style="text-align:left">⭐⭐⭐⭐ 高</td></tr><tr><td style="text-align:left">NAT Type 8</td><td style="text-align:left">地址和端口相关映射</td><td style="text-align:left">地址相关过滤</td><td style="text-align:left">-</td><td style="text-align:left">HardNAT</td><td style="text-align:left">⭐⭐⭐⭐⭐ 极高</td></tr><tr><td style="text-align:left">NAT Type 9</td><td style="text-align:left">地址和端口相关映射</td><td style="text-align:left">地址和端口相关过滤</td><td style="text-align:left">对称型（Symmetric）</td><td style="text-align:left">HardNAT</td><td style="text-align:left">⭐⭐⭐⭐⭐ 极高</td></tr></tbody></table></div><h2 id="三、各NAT类型打洞可行性（详细拆解）"><a href="#三、各NAT类型打洞可行性（详细拆解）" class="headerlink" title="三、各NAT类型打洞可行性（详细拆解）"></a>三、各NAT类型打洞可行性（详细拆解）</h2><p>前置操作：双方均通过NAT Checker确认自身NAT类型，安装Tailscale（基础场景）或Easytier（复杂场景），确保双方网络能访问中转服务器（Tailscale/Easytier官方服务器）。</p><h3 id="3-1-NAT1（全锥型）-↔-NAT1（全锥型）"><a href="#3-1-NAT1（全锥型）-↔-NAT1（全锥型）" class="headerlink" title="3.1 NAT1（全锥型） ↔ NAT1（全锥型）"></a>3.1 NAT1（全锥型） ↔ NAT1（全锥型）</h3><h4 id="核心原理"><a href="#核心原理" class="headerlink" title="核心原理"></a>核心原理</h4><p>双方均为端点无关映射（公网端口固定）+ 端点无关过滤（任意外部流量放行），无需触发状态表更新，交换公网信息后可直接通信。</p><h4 id="实战步骤"><a href="#实战步骤" class="headerlink" title="实战步骤"></a>实战步骤</h4><ol><li>双方启动Tailscale，自动连接中转服务器，完成身份认证；</li><li>中转服务器同步双方的公网IP和映射端口（因端点无关映射，此端口固定不变）；</li><li>双方获取对方公网信息后，直接向对方的「公网IP:映射端口」发起TCP/UDP连接；</li><li>由于双方过滤规则宽松，流量直接放行，打洞成功，后续通信脱离中转服务器。</li></ol><h4 id="可行性结论"><a href="#可行性结论" class="headerlink" title="可行性结论"></a>可行性结论</h4><p>✅ 100%稳定可行，无特殊条件限制，是打洞最理想的场景。</p><h4 id="问题排查"><a href="#问题排查" class="headerlink" title="问题排查"></a>问题排查</h4><p>若失败，优先排查：① 防火墙是否拦截对应端口（如Tailscale默认使用41641端口，需放行）；② 公网IP是否为运营商内网IP（极少数情况，可通过IP138查询公网IP是否一致）。</p><h3 id="3-2-NAT1（全锥型）-↔-NAT3（端口受限锥型）"><a href="#3-2-NAT1（全锥型）-↔-NAT3（端口受限锥型）" class="headerlink" title="3.2 NAT1（全锥型） ↔ NAT3（端口受限锥型）"></a>3.2 NAT1（全锥型） ↔ NAT3（端口受限锥型）</h3><h4 id="核心原理-1"><a href="#核心原理-1" class="headerlink" title="核心原理"></a>核心原理</h4><p>NAT1公网端口固定+过滤宽松，NAT3公网端口固定但过滤严格（仅放行「之前请求过的IP+端口」的响应）。关键是让NAT3先向NAT1发起请求，更新NAT3的状态表，从而放行NAT1的后续请求。</p><h4 id="实战步骤-1"><a href="#实战步骤-1" class="headerlink" title="实战步骤"></a>实战步骤</h4><ol><li>双方启动Tailscale，连接中转服务器，同步对方公网IP:端口（记为NAT1：A:PortA；NAT3：B:PortB）；</li><li>关键操作：让NAT3主机主动向NAT1的A:PortA发起请求（Tailscale会自动触发此步骤，无需手动操作）；</li><li>NAT3的状态表更新：记录「请求目标A:PortA」，此时允许A:PortA的流量进入内网；</li><li>NAT1主机向NAT3的B:PortB发起请求，由于NAT3状态表已记录A:PortA，此请求被放行；</li><li>双方建立双向连接，打洞成功。</li></ol><h4 id="可行性结论-1"><a href="#可行性结论-1" class="headerlink" title="可行性结论"></a>可行性结论</h4><p>✅ 可行性95%以上，核心依赖「NAT3先发起请求」的顺序，Tailscale等工具会自动协调此顺序，无需手动干预。</p><h4 id="问题排查-1"><a href="#问题排查-1" class="headerlink" title="问题排查"></a>问题排查</h4><p>若失败，排查：① NAT3主机是否被路由器限制“主动对外发起连接”；② 双方是否同时启动工具（若NAT1先发起请求，会被NAT3过滤，可重启双方Tailscale重试）。</p><h3 id="3-3-NAT3（端口受限锥型）-↔-NAT3（端口受限锥型）"><a href="#3-3-NAT3（端口受限锥型）-↔-NAT3（端口受限锥型）" class="headerlink" title="3.3 NAT3（端口受限锥型） ↔ NAT3（端口受限锥型）"></a>3.3 NAT3（端口受限锥型） ↔ NAT3（端口受限锥型）</h3><h4 id="核心原理-2"><a href="#核心原理-2" class="headerlink" title="核心原理"></a>核心原理</h4><p>双方均为“端口固定+地址和端口相关过滤”，仅放行「自身之前请求过的IP:端口」的流量。需让双方在极短时间内同时向对方发起请求，互相更新对方的状态表，实现流量双向放行。</p><h4 id="实战步骤-2"><a href="#实战步骤-2" class="headerlink" title="实战步骤"></a>实战步骤</h4><ol><li>双方启动Tailscale，连接中转服务器，同步对方公网IP:端口（NAT3-1：C:PortC；NAT3-2：D:PortD）；</li><li>工具协调：Tailscale会通过中转服务器向双方发送“同步发起请求”的指令，确保双方在1秒内同时行动；</li><li>双方同时向对方公网地址发起请求：NAT3-1→D:PortD；NAT3-2→C:PortC；</li><li>双方状态表同时更新：NAT3-1记录D:PortD，允许其流量进入；NAT3-2记录C:PortC，允许其流量进入；</li><li>双方请求均被对方放行，建立双向连接，打洞成功。</li></ol><h4 id="可行性结论-2"><a href="#可行性结论-2" class="headerlink" title="可行性结论"></a>可行性结论</h4><p>✅ 可行性90%以上，核心依赖「双方同时发起请求」的时间同步。若网络延迟过高（如跨运营商、跨地域），同步难度增加，成功率会略降。</p><h4 id="问题排查-2"><a href="#问题排查-2" class="headerlink" title="问题排查"></a>问题排查</h4><p>若失败，排查：① 双方网络延迟是否过高（可通过ping中转服务器判断，延迟&gt;200ms建议换同运营商网络）；② 工具是否未正常协调同步请求（重启工具或切换网络重试）。</p><h3 id="3-4-NAT1（全锥型）-↔-NAT4（地址相关映射-端点无关过滤）"><a href="#3-4-NAT1（全锥型）-↔-NAT4（地址相关映射-端点无关过滤）" class="headerlink" title="3.4 NAT1（全锥型） ↔ NAT4（地址相关映射+端点无关过滤）"></a>3.4 NAT1（全锥型） ↔ NAT4（地址相关映射+端点无关过滤）</h3><h4 id="核心原理-3"><a href="#核心原理-3" class="headerlink" title="核心原理"></a>核心原理</h4><p>NAT1公网端口固定，NAT4为HardNAT（映射端口随目标IP变化）：NAT4向中转服务器发起请求时，生成映射端口Port4-1；当NAT4向NAT1发起请求时，因目标IP变化，会生成新的映射端口Port4-2（与Port4-1不同）。打洞关键是让NAT1识别到NAT4的新端口Port4-2，重新发起请求。</p><h4 id="实战步骤-3"><a href="#实战步骤-3" class="headerlink" title="实战步骤"></a>实战步骤</h4><ol><li>双方启动Tailscale，连接中转服务器，同步的NAT4公网信息为「E:Port4-1」（NAT4与中转服务器通信的端口）；</li><li>NAT1先向E:Port4-1发起请求，因Port4-1仅对应“NAT4→中转服务器”的状态，此请求被NAT4的映射表匹配失败，直接过滤；</li><li>中转服务器触发NAT4向NAT1的A:PortA发起请求，此时NAT4因目标IP变为A，生成新映射端口Port4-2；</li><li>NAT1接收到NAT4的请求（源地址E:Port4-2），自动更新NAT4的公网信息为「E:Port4-2」；</li><li>NAT1向E:Port4-2重新发起请求，此时NAT4的状态表已记录「请求目标A:PortA」，且Port4-2为当前映射端口，请求被放行；</li><li>双方建立连接，打洞成功。</li></ol><h4 id="可行性结论-3"><a href="#可行性结论-3" class="headerlink" title="可行性结论"></a>可行性结论</h4><p>⚠️ 可行性70%左右，需依赖工具支持“动态端口识别与二次请求”（Tailscale、Easytier均支持）。若NAT4的映射端口变化频繁（如部分运营商每30秒刷新一次），可能导致连接不稳定。</p><h4 id="问题排查-3"><a href="#问题排查-3" class="headerlink" title="问题排查"></a>问题排查</h4><p>若失败，排查：① 工具是否支持动态端口更新（老旧版本可能不支持，需升级）；② NAT4的映射端口刷新周期是否过短（可通过Wireshark抓包观察端口变化频率）。</p><h3 id="3-5-NAT3（端口受限锥型）-↔-NAT4（地址相关映射-端点无关过滤）"><a href="#3-5-NAT3（端口受限锥型）-↔-NAT4（地址相关映射-端点无关过滤）" class="headerlink" title="3.5 NAT3（端口受限锥型） ↔ NAT4（地址相关映射+端点无关过滤）"></a>3.5 NAT3（端口受限锥型） ↔ NAT4（地址相关映射+端点无关过滤）</h3><h4 id="核心原理-4"><a href="#核心原理-4" class="headerlink" title="核心原理"></a>核心原理</h4><p>双方均有严格限制：NAT3仅放行「自身请求过的IP:端口」，NAT4映射端口随目标IP变化。常规情况下，NAT3请求NAT4的“中转端口”会被过滤，NAT4请求NAT3时，因NAT3未记录其新端口，也会被过滤。需通过辅助方案突破限制。</p><h4 id="实战步骤（分两种辅助方案）"><a href="#实战步骤（分两种辅助方案）" class="headerlink" title="实战步骤（分两种辅助方案）"></a>实战步骤（分两种辅助方案）</h4><h5 id="方案1：开启UPnP-NAT-PNP（推荐，成功率高）"><a href="#方案1：开启UPnP-NAT-PNP（推荐，成功率高）" class="headerlink" title="方案1：开启UPnP/NAT-PNP（推荐，成功率高）"></a>方案1：开启UPnP/NAT-PNP（推荐，成功率高）</h5><ol><li>登录NAT4所在的路由器管理后台，找到「UPnP」或「NAT-PNP」功能（通常在“高级设置-端口映射”中），开启该功能；</li><li>双方启动Tailscale，工具会通过UPnP向路由器申请“固定映射端口”（强制NAT4使用固定端口，而非动态变化）；</li><li>后续步骤与「NAT3↔NAT3」一致：双方通过中转服务器同步固定端口，同时发起请求，互相更新状态表；</li><li>流量双向放行，打洞成功。</li></ol><h5 id="方案2：使用Easytier（无UPnP时备选，成功率低）"><a href="#方案2：使用Easytier（无UPnP时备选，成功率低）" class="headerlink" title="方案2：使用Easytier（无UPnP时备选，成功率低）"></a>方案2：使用Easytier（无UPnP时备选，成功率低）</h5><ol><li>双方启动Easytier，工具自动检测到NAT类型组合，触发“端口扫描+预测”模式；</li><li>NAT4主机向NAT3的C:PortC连续发送100-500条请求（目标端口固定，源端口动态生成，形成多个映射端口：Port4-3、Port4-4…）；</li><li>NAT3主机同时向NAT4的E:Port4-3~Port4-500依次发起扫描请求；</li><li>当NAT3扫描到NAT4正在向自己发起请求的端口（如Port4-10）时，双方状态表同时记录对方信息，请求被放行，建立连接。</li></ol><h4 id="可行性结论-4"><a href="#可行性结论-4" class="headerlink" title="可行性结论"></a>可行性结论</h4><p>❌ 常规不可行；✅ 开启UPnP后可行性85%；⚠️ 用Easytier可行性30%-50%（依赖端口扫描命中率，且易被运营商判定为网络攻击，不推荐公网使用）。</p><h4 id="问题排查-4"><a href="#问题排查-4" class="headerlink" title="问题排查"></a>问题排查</h4><p>若UPnP方案失败，排查：① 路由器是否支持UPnP（部分老旧路由器无此功能）；② 工具是否有权限调用UPnP（需以管理员身份运行）；若Easytier方案失败，排查：① 网络是否限制高频端口扫描（如企业内网可能拦截）；② 双方网络延迟是否过高（延迟越高，扫描命中率越低）。</p><h3 id="3-6-NAT4（地址相关映射）-↔-NAT4（地址相关映射）"><a href="#3-6-NAT4（地址相关映射）-↔-NAT4（地址相关映射）" class="headerlink" title="3.6 NAT4（地址相关映射） ↔ NAT4（地址相关映射）"></a>3.6 NAT4（地址相关映射） ↔ NAT4（地址相关映射）</h3><h4 id="核心原理-5"><a href="#核心原理-5" class="headerlink" title="核心原理"></a>核心原理</h4><p>双方均为HardNAT，映射端口随目标IP动态变化，且过滤规则严格。打洞需满足“双方同时扫描到对方当前的动态端口”，而每个动态端口的有效期通常仅几十秒，命中率极低；同时高频扫描易触发运营商的DDoS防护机制，导致IP被临时封禁。</p><h4 id="实战尝试（不推荐）"><a href="#实战尝试（不推荐）" class="headerlink" title="实战尝试（不推荐）"></a>实战尝试（不推荐）</h4><ol><li>双方启动Easytier，开启“深度端口扫描”模式（需手动设置扫描范围，如1000-65535）；</li><li>双方同时向对方公网IP的全端口范围发起扫描请求，试图匹配对方的动态映射端口；</li><li>若运气极佳，双方扫描到对方当前的动态端口，可建立连接；否则扫描持续数分钟后失败。</li></ol><h4 id="可行性结论-5"><a href="#可行性结论-5" class="headerlink" title="可行性结论"></a>可行性结论</h4><p>❌ 强烈不建议尝试，可行性低于10%，且存在IP被封禁的风险。推荐替代方案：① 让一方切换到EasyNAT网络（如连接手机热点）；② 使用中转服务器转发流量（Tailscale默认支持中转，稳定性高但延迟略高）；③ 升级IPv6网络。</p><h2 id="四、打洞可行性总结表（详细版）"><a href="#四、打洞可行性总结表（详细版）" class="headerlink" title="四、打洞可行性总结表（详细版）"></a>四、打洞可行性总结表（详细版）</h2><div class="table-container"><table><thead><tr><th style="text-align:left">通信组合</th><th style="text-align:left">打洞结果</th><th style="text-align:left">核心条件</th><th style="text-align:left">推荐工具</th><th style="text-align:left">风险/注意事项</th></tr></thead><tbody><tr><td style="text-align:left">NAT1↔NAT1</td><td style="text-align:left">✅ 100%稳定</td><td style="text-align:left">无需特殊条件，交换公网信息即可</td><td style="text-align:left">Tailscale、Easytier</td><td style="text-align:left">低风险，仅需放行工具端口</td></tr><tr><td style="text-align:left">NAT1↔NAT3</td><td style="text-align:left">✅ 95%可行</td><td style="text-align:left">NAT3先向NAT1发起请求</td><td style="text-align:left">Tailscale（自动协调顺序）</td><td style="text-align:left">低风险，跨地域延迟高时成功率略降</td></tr><tr><td style="text-align:left">NAT3↔NAT3</td><td style="text-align:left">✅ 90%可行</td><td style="text-align:left">双方同时发起请求，时间同步</td><td style="text-align:left">Tailscale（自动同步）</td><td style="text-align:left">中低风险，延迟&gt;200ms需重试</td></tr><tr><td style="text-align:left">NAT1↔NAT4</td><td style="text-align:left">⚠️ 70%可行</td><td style="text-align:left">工具支持动态端口识别与二次请求</td><td style="text-align:left">Tailscale、Easytier</td><td style="text-align:left">中风险，NAT4端口刷新频繁可能导致连接不稳定</td></tr><tr><td style="text-align:left">NAT3↔NAT4</td><td style="text-align:left">❌ 常规不可行；✅ UPnP后85%可行；⚠️ 扫描后30%-50%可行</td><td style="text-align:left">开启UPnP（推荐）或工具端口扫描（备选）</td><td style="text-align:left">Tailscale（UPnP）、Easytier（扫描）</td><td style="text-align:left">高风险，扫描模式易被判定为攻击</td></tr><tr><td style="text-align:left">NAT4↔NAT4</td><td style="text-align:left">❌ 可行性&lt;10%</td><td style="text-align:left">双方同时扫描到对方动态端口（极难）</td><td style="text-align:left">Easytier（不推荐）</td><td style="text-align:left">极高风险，易导致IP被封禁</td></tr></tbody></table></div><h2 id="五、补充：IPv6替代方案（最优解）"><a href="#五、补充：IPv6替代方案（最优解）" class="headerlink" title="五、补充：IPv6替代方案（最优解）"></a>五、补充：IPv6替代方案（最优解）</h2><p>若双方网络支持IPv6（可通过<a href="https://test-ipv6.com/">test-ipv6.com</a>检测），优先放弃NAT打洞，直接使用IPv6通信：</p><ol><li><strong>核心优势</strong>：IPv6为每个设备分配独立公网地址，无需NAT转换，直接通过IPv6地址建立连接，稳定性100%，延迟低于NAT打洞；</li><li><strong>实战步骤</strong>：        <ol><li>双方开启设备IPv6功能（电脑：设置-网络-属性，勾选IPv6；路由器：开启IPv6拨号，获取前缀）；</li><li>通过命令行获取自身IPv6地址（Windows：ipconfig；Linux：ifconfig）；</li><li>使用支持IPv6的工具（如Tailscale、SSH、FTP），直接输入对方IPv6地址建立连接，无需中转和打洞。</li></ol></li><li><strong>注意事项</strong>：部分运营商IPv6地址为动态分配（重启路由器会变化），可在路由器中设置“IPv6地址绑定”，固定设备的IPv6后缀。</li></ol><h2 id="六、打洞核心规律与建议"><a href="#六、打洞核心规律与建议" class="headerlink" title="六、打洞核心规律与建议"></a>六、打洞核心规律与建议</h2><ol><li><strong>EasyNAT之间（Type1-3）</strong>：打洞成功率高，优先使用Tailscale，操作简单且稳定，适合大多数个人/家庭场景；</li><li><strong>EasyNAT↔HardNAT</strong>：优先尝试开启UPnP，其次使用Easytier，避免手动操作，减少风险；</li><li><strong>HardNAT之间（Type4+）</strong>：放弃打洞，选择中转服务器或IPv6，既保证稳定性，又避免IP被封禁；</li><li><strong>实战优先级</strong>：检测NAT类型 → 尝试EasyNAT打洞 → 开启UPnP重试 → 切换IPv6 → 使用中转服务器。</li></ol>]]></content:encoded>
      
      
      <category domain="https://blog.itrf.cn/tags/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</category>
      
      <category domain="https://blog.itrf.cn/tags/NAT/">NAT</category>
      
    </item>
    
    <item>
      <title>EdgeOne加速Vercel配置及踩坑</title>
      <link>https://blog.itrf.cn/posts/7eac/</link>
      <guid>https://blog.itrf.cn/posts/7eac/</guid>
      <pubDate>2025-12-06T08:39:55.000Z</pubDate>
      
      <description>用过 Vercel 的开发者大概都对它的便捷性印象深刻 —— 将 Node 项目推送到 GitHub 后，几分钟内就能自动构建部署并提供访问服务，免费计划也足够日常折腾。但随着 Vercel 域名 DNS 污染越来越频繁，不挂梯子几乎无法访问，好好的免费资源就这么闲置了实在可惜。于是我尝试用腾讯云 EO（EdgeOne）对其进行加速，过程中踩了个典型的坑，最终摸索出解决方案，特此记录分享给有同样需求的朋友。</description>
      
      
      <enclosure url="https://t.alcy.cc/moez?_r_=439011fa-d2ee-d5e5-4c19-5f8914dd119e" type="image"/>
      
      
      <content:encoded><![CDATA[<p>用过 Vercel 的开发者大概都对它的便捷性印象深刻 —— 将 Node 项目推送到 GitHub 后，几分钟内就能自动构建部署并提供访问服务，免费计划也足够日常折腾。但随着 Vercel 域名 DNS 污染越来越频繁，不挂梯子几乎无法访问，好好的免费资源就这么闲置了实在可惜。于是我尝试用腾讯云 EO（EdgeOne）对其进行加速，过程中踩了个典型的坑，最终摸索出解决方案，特此记录分享给有同样需求的朋友。</p><h2 id="一、踩坑现场：自信配置却遭遇-522-死循环"><a href="#一、踩坑现场：自信配置却遭遇-522-死循环" class="headerlink" title="一、踩坑现场：自信配置却遭遇 522 死循环"></a>一、踩坑现场：自信配置却遭遇 522 死循环</h2><p>核心诉求很明确：利用腾讯云 EO 的免费计划，解决 Vercel 域名访问受阻的问题。我想当然地认为加速配置的关键就是 “回源 Host 填写 Vercel 提供的默认域名（如 xxx-project.vercel.app）”。</p><p>按照这个思路配置完成后，满心期待地等待部署结束，结果访问时直接弹出 522 错误 —— 服务器无响应。反复检查配置参数、重新部署项目，甚至更换网络环境测试，522 错误始终如影随形，完全摸不着头脑。</p><h2 id="二、问题本质：多层-CDN-嵌套导致回源失效"><a href="#二、问题本质：多层-CDN-嵌套导致回源失效" class="headerlink" title="二、问题本质：多层 CDN 嵌套导致回源失效"></a>二、问题本质：多层 CDN 嵌套导致回源失效</h2><p>我翻了一圈 CDN 加速的核心原理文档，再结合 Google 到的案例，终于理清了 522 错误的根源，本质是<strong>多层 CDN 嵌套引发的回源链路异常</strong>：</p><ol><li><strong>Vercel 默认域名的本质</strong>：xxx-project.vercel.app 并不是直接指向项目服务器的 “源站地址”，而是 Vercel 自家的 CDN 边缘节点域名。当我把这个域名填作 EO 的回源 Host 时，EO 实际是向 Vercel 的 CDN 节点发起回源请求，而非项目的真实源站。</li><li><strong>522 错误的触发逻辑</strong>：Vercel 的 CDN 节点会校验请求的 Host 头是否匹配其配置的域名，而 EO 向 Vercel CDN 发起的请求，其 Host 头与 Vercel 配置不兼容，再加上跨地域链路的延迟，最终导致回源超时，返回 522 错误。</li><li><strong>我的认知误区</strong>：之前误以为 EO 加速是简单的 “反代”，只要填个目标域名就行，忽略了源站是否本身已接入 CDN 这一关键前提。</li></ol><h2 id="三、正确配置：两步搞定-Vercel-加速"><a href="#三、正确配置：两步搞定-Vercel-加速" class="headerlink" title="三、正确配置：两步搞定 Vercel 加速"></a>三、正确配置：两步搞定 Vercel 加速</h2><p>找到问题根源后，解决方案就清晰了 —— 跳过 Vercel 的 CDN 层，让 EO 直接指向项目的真实源站。具体配置步骤如下，同样适用于加速其他自带 CDN 的海外平台（如 Netlify、GitHub Pages）：</p><h3 id="1-获取-Vercel-真实回源-CNAME"><a href="#1-获取-Vercel-真实回源-CNAME" class="headerlink" title="1. 获取 Vercel 真实回源 CNAME"></a>1. 获取 Vercel 真实回源 CNAME</h3><p>首先要拿到 Vercel 项目的 “原始源站 CNAME”（非 CDN 域名），方法很简单：</p><ul><li>登录 Vercel 控制台，添加你自己的域名，会给你一个Vercel的CNAME值；</li><li>这个CNAME值是 Vercel 源站得 CNAME，我们不要解析这个CNAME值;</li></ul><h3 id="2-腾讯云-EO-核心配置"><a href="#2-腾讯云-EO-核心配置" class="headerlink" title="2. 腾讯云 EO 核心配置"></a>2. 腾讯云 EO 核心配置</h3><p>进入腾讯云 EO 控制台，添加与在Vercel添加的相同的自己的域名，重点修改以下两项：</p><div class="table-container"><table><thead><tr><th>配置项</th><th>正确取值</th><th>配置目的</th></tr></thead><tbody><tr><td>回源域名</td><td>第一步获取的 Vercel 原始 CNAME</td><td>跳过 Vercel CDN 层，直接指向真实源站</td></tr><tr><td>Host 头</td><td>你在Vercel添加的自己的域名</td><td>让 Vercel 源站正确识别请求来源，通过校验</td></tr></tbody></table></div><h3 id="3-域名控制台添加-CNAME-解析"><a href="#3-域名控制台添加-CNAME-解析" class="headerlink" title="3. 域名控制台添加 CNAME 解析"></a>3. 域名控制台添加 CNAME 解析</h3><p>到域名控制台，添加 CNAME 解析，注意填的是EO平台给你的CNAME值，而不是Vercel给你的CNAME值。</p><h2 id="四、拓展：海外-CDN-加速通用逻辑"><a href="#四、拓展：海外-CDN-加速通用逻辑" class="headerlink" title="四、拓展：海外 CDN 加速通用逻辑"></a>四、拓展：海外 CDN 加速通用逻辑</h2><p>这次踩坑让我总结出一个通用规律：<strong>当需要用 CDN 加速海外平台时，若目标平台自带 CDN，绝对不能直接用其公开的 CDN 域名作为回源地址</strong>。正确的逻辑是：</p><ol><li>在平台添加自己的域名，获取平台的CNAME值，不要做解析；</li><li>在CDN控制台，回源地址填平台的CNAME值，Host 头填平台添加的域名；</li><li>在域名控制台，添加CNAME解析，值填CDN给你的CNAME值;</li></ol><p>希望这篇踩坑记能帮到同样被 Vercel 访问问题困扰的朋友，少走弯路，充分利用好免费的技术资源～</p>]]></content:encoded>
      
      
      <category domain="https://blog.itrf.cn/tags/%E9%9B%B6%E7%A2%8E%E7%9F%A5%E8%AF%86/">零碎知识</category>
      
      <category domain="https://blog.itrf.cn/tags/EdgeOne/">EdgeOne</category>
      
      <category domain="https://blog.itrf.cn/tags/Vercel/">Vercel</category>
      
    </item>
    
    <item>
      <title>数据库分页 vs 程序分页：核心决策指南与实践方案</title>
      <link>https://blog.itrf.cn/posts/516b/</link>
      <guid>https://blog.itrf.cn/posts/516b/</guid>
      <pubDate>2025-11-29T05:00:00.000Z</pubDate>
      
      <description>在数据查询场景中，“如何高效呈现大量数据” 是开发者绕不开的问题。分页作为解决 “数据量过大导致的内存溢出、响应缓慢” 的核心手段，主要分为数据库分页（数据库端过滤 + 限制返回条数）和程序分页（全量查询后内存中分片）两种实现方式。很多开发者在选型时会陷入纠结：到底该让数据库 “多干活”，还是让应用程序 “扛压力”？其实两者没有绝对优劣，核心取决于数据量、业务复杂度、实时性要求等关键因素。本文将从原理、场景、优缺点、实操建议四个维度，帮你彻底理清选型逻辑，避免踩坑。</description>
      
      
      <enclosure url="https://t.alcy.cc/moez?_r_=ef1e700a-3199-6a4f-511d-88d022191626" type="image"/>
      
      
      <content:encoded><![CDATA[<p>在数据查询场景中，“如何高效呈现大量数据” 是开发者绕不开的问题。分页作为解决 “数据量过大导致的内存溢出、响应缓慢” 的核心手段，主要分为<strong>数据库分页</strong>（数据库端过滤 + 限制返回条数）和<strong>程序分页</strong>（全量查询后内存中分片）两种实现方式。</p><p>很多开发者在选型时会陷入纠结：到底该让数据库 “多干活”，还是让应用程序 “扛压力”？其实两者没有绝对优劣，核心取决于数据量、业务复杂度、实时性要求等关键因素。本文将从原理、场景、优缺点、实操建议四个维度，帮你彻底理清选型逻辑，避免踩坑。</p><h2 id="一、先搞懂：两种分页的核心原理"><a href="#一、先搞懂：两种分页的核心原理" class="headerlink" title="一、先搞懂：两种分页的核心原理"></a>一、先搞懂：两种分页的核心原理</h2><p>在深入选型前，我们需要先明确两种分页的本质区别 ——<strong>数据过滤和分片的 “执行位置” 不同</strong>。</p><h3 id="1-数据库分页：数据库端-“按需取数”"><a href="#1-数据库分页：数据库端-“按需取数”" class="headerlink" title="1. 数据库分页：数据库端 “按需取数”"></a>1. 数据库分页：数据库端 “按需取数”</h3><p>数据库分页的核心是<strong>让数据库只返回当前页需要的数据</strong>，通过 SQL 语法（如 <code>LIMIT/OFFSET</code>、<code>ROW_NUMBER()</code>）或条件过滤，在数据查询阶段就完成 “筛选 + 分片”，最终只将单页数据（如 10 条、20 条）返回给应用程序。</p><p><strong>典型实现示例</strong>：</p><ul><li>MySQL：<code>SELECT * FROM orders WHERE status = 1 ORDER BY id DESC LIMIT 10 OFFSET 20;</code>（查询第 3 页，每页 10 条待发货订单）</li><li>PostgreSQL：<code>SELECT * FROM orders WHERE status = 1 ORDER BY id DESC LIMIT 10 OFFSET 20;</code></li><li>Oracle：<code>SELECT * FROM (SELECT t.*, ROW_NUMBER() OVER(ORDER BY id DESC) rn FROM orders t WHERE status = 1) WHERE rn BETWEEN 21 AND 30;</code></li></ul><p>其核心逻辑是：数据库利用索引快速过滤条件数据，再通过分页语法截取目标片段，减少数据传输和应用内存占用。</p><h3 id="2-程序分页：应用端-“全量处理”"><a href="#2-程序分页：应用端-“全量处理”" class="headerlink" title="2. 程序分页：应用端 “全量处理”"></a>2. 程序分页：应用端 “全量处理”</h3><p>程序分页的核心是<strong>先从数据库查询符合条件的全量数据</strong>，加载到应用程序内存中，再通过代码（如 Java 的 <code>List.subList()</code>、Python 的切片 <code>[start:end]</code>）实现分片，返回当前页数据。</p><p><strong>典型实现示例</strong>（Java）：</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 1. 查全量数据（数据库返回所有待发货订单）</span></span><br><span class="line">List&lt;Order&gt; allOrders = orderMapper.selectByStatus(<span class="number">1</span>);</span><br><span class="line"><span class="comment">// 2. 程序分页：第3页（页码从1开始），每页10条</span></span><br><span class="line"><span class="type">int</span> <span class="variable">pageNum</span> <span class="operator">=</span> <span class="number">3</span>;</span><br><span class="line"><span class="type">int</span> <span class="variable">pageSize</span> <span class="operator">=</span> <span class="number">10</span>;</span><br><span class="line"><span class="type">int</span> <span class="variable">start</span> <span class="operator">=</span> (pageNum - <span class="number">1</span>) * pageSize;</span><br><span class="line"><span class="type">int</span> <span class="variable">end</span> <span class="operator">=</span> Math.min(start + pageSize, allOrders.size());</span><br><span class="line">List&lt;Order&gt; pageOrders = allOrders.subList(start, end);</span><br></pre></td></tr></table></figure><p>其核心逻辑是：数据库仅负责 “全量查询”，分页的计算、分片均在应用内存中完成，依赖应用的计算能力而非数据库。</p><h2 id="二、核心选型：3-个维度定答案"><a href="#二、核心选型：3-个维度定答案" class="headerlink" title="二、核心选型：3 个维度定答案"></a>二、核心选型：3 个维度定答案</h2><p>选型的核心逻辑的是：<strong>让 “擅长的角色做擅长的事”</strong>—— 数据库擅长高效过滤、排序和海量数据存储，应用程序擅长复杂逻辑处理和灵活计算。具体可通过以下 3 个维度快速决策：</p><h3 id="维度-1：数据量大小（最关键因素）"><a href="#维度-1：数据量大小（最关键因素）" class="headerlink" title="维度 1：数据量大小（最关键因素）"></a>维度 1：数据量大小（最关键因素）</h3><p>数据量直接决定了两种方案的可行性和性能上限：</p><ul><li><p>小数据量（万级以内）：优先选程序分页。<br>万级数据占用内存通常在几十 MB 以内（如每条数据 1KB，1 万条仅 10MB），应用程序完全可以承载。此时全量查询的 IO 开销远低于 “多次数据库分页查询”，且程序分页无需频繁与数据库交互，响应更连贯。</p></li><li><p>大数据量（10 万级以上）：必须选数据库分页。<br>百万级 / 千万级数据全量查询会导致两个致命问题：① 数据库查询 + 数据传输耗时极长（可能秒级甚至分钟级）；② 应用内存溢出（OOM）—— 比如 100 万条数据约占 1GB 内存，远超普通应用的内存阈值。而数据库分页仅返回单页数据（几十条），内存占用可忽略，且依赖索引能快速响应。</p></li></ul><h3 id="维度-2：业务复杂度（分类-筛选逻辑）"><a href="#维度-2：业务复杂度（分类-筛选逻辑）" class="headerlink" title="维度 2：业务复杂度（分类 / 筛选逻辑）"></a>维度 2：业务复杂度（分类 / 筛选逻辑）</h3><p>业务逻辑的复杂程度决定了 “谁更适合处理分页前的过滤”：</p><ul><li><p>简单逻辑（单一条件 / 多条件且可 SQL 表达）：优先选数据库分页。<br>比如 “按订单状态筛选 + 按创建时间排序分页”“按地区 + 金额区间过滤分页”，这些逻辑可通过 SQL 的 <code>WHERE</code>子句 + 索引快速实现，数据库处理效率远高于应用程序全量加载后过滤。</p></li><li><p>复杂逻辑（多维度交叉 / 非 SQL 可表达）：优先选程序分页。<br>比如 “按「金额区间（高 / 中 / 低）+ 配送区域 + 下单设备」三维交叉分类，且每个分类需独立分页”“按模糊匹配（如备注含特定关键词）+ 自定义规则（如 VIP 用户优先）筛选”，这些逻辑用 SQL 实现会非常繁琐（多层 <code>CASE WHEN</code>+<code>GROUP BY</code>），甚至无法实现，而应用程序可通过哈希表分组、流处理等方式灵活实现。</p></li></ul><h3 id="维度-3：业务场景（实时性-复用性要求）"><a href="#维度-3：业务场景（实时性-复用性要求）" class="headerlink" title="维度 3：业务场景（实时性 / 复用性要求）"></a>维度 3：业务场景（实时性 / 复用性要求）</h3><ul><li><p>实时性要求高（如订单列表、实时数据监控）：选数据库分页。<br>数据库分页每次查询都是实时从数据库取数，能保证数据最新状态（如用户刚创建的订单立即显示）。而程序分页若不重新查询，会存在数据滞后问题。</p></li><li><p>需频繁复用数据（如多维度筛选、前端切换分类）：选程序分页。<br>比如前端页面需要同时展示 “待付款”“待发货”“已完成” 三个分类的分页列表，程序分页可全量查询后在内存中缓存所有分类数据，用户切换分类时直接从内存取数，无需再次查询数据库；而数据库分页需要执行 3 次独立查询，增加数据库压力。</p></li></ul><h2 id="三、两种方案的优缺点深度对比"><a href="#三、两种方案的优缺点深度对比" class="headerlink" title="三、两种方案的优缺点深度对比"></a>三、两种方案的优缺点深度对比</h2><div class="table-container"><table><thead><tr><th>对比维度</th><th>数据库分页</th><th>程序分页</th></tr></thead><tbody><tr><td>内存占用</td><td>极低（仅单页数据）</td><td>较高（全量数据加载到内存）</td></tr><tr><td>响应速度（首次）</td><td>快（数据量小，传输 + 处理耗时短）</td><td>慢（全量数据查询 + 传输耗时）</td></tr><tr><td>响应速度（后续）</td><td>每次分页需查询数据库，存在 IO 延迟</td><td>后续分页 / 切换分类无 IO 延迟，响应连贯</td></tr><tr><td>数据库压力</td><td>多次查询（如多分类分页），压力中等</td><td>一次查询，压力小</td></tr><tr><td>灵活性</td><td>低（依赖 SQL 逻辑，复杂场景难以扩展）</td><td>高（支持任意自定义逻辑，扩展方便）</td></tr><tr><td>实时性</td><td>高（每次查询实时取数）</td><td>低（全量查询后数据静态，需重新查询更新）</td></tr><tr><td>适用数据量</td><td>10 万级以上（海量数据）</td><td>万级以内（小数据量）</td></tr><tr><td>开发成本</td><td>中等（需编写分页 SQL，处理深分页优化）</td><td>低（全量查询后简单分片，逻辑直观）</td></tr></tbody></table></div><h2 id="四、实操建议：避坑指南与优化技巧"><a href="#四、实操建议：避坑指南与优化技巧" class="headerlink" title="四、实操建议：避坑指南与优化技巧"></a>四、实操建议：避坑指南与优化技巧</h2><p>选型后如何落地才能最大化性能？以下是两种方案的实操优化建议，避免踩常见坑：</p><h3 id="数据库分页：3-个优化技巧，避免性能瓶颈"><a href="#数据库分页：3-个优化技巧，避免性能瓶颈" class="headerlink" title="数据库分页：3 个优化技巧，避免性能瓶颈"></a>数据库分页：3 个优化技巧，避免性能瓶颈</h3><ol><li><p><strong>优先用 “游标分页” 替代<code>OFFSET</code>深分页</strong>传统的 <code>LIMIT OFFSET</code> 分页在深分页场景（如 <code>LIMIT 100000, 10</code>）会出现性能问题 —— 数据库需要扫描前 100010 条数据才能返回目标 10 条。优化方案是 “游标分页”：按唯一有序字段（如 ID、创建时间戳）排序，通过 <code>WHERE</code> 条件替代 <code>OFFSET</code>：</p><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="comment">-- 上一页最大ID为10000，查询下一页（每页10条）</span></span><br><span class="line"><span class="keyword">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span> orders <span class="keyword">WHERE</span> status <span class="operator">=</span> <span class="number">1</span> <span class="keyword">AND</span> id <span class="operator">&gt;</span> <span class="number">10000</span> <span class="keyword">ORDER</span> <span class="keyword">BY</span> id <span class="keyword">ASC</span> LIMIT <span class="number">10</span>;</span><br></pre></td></tr></table></figure><p>优点：利用索引直接定位起始位置，无需扫描前置数据，深分页性能无衰减。</p></li><li><p><strong>给过滤 / 排序字段加索引</strong>分页查询的 <code>WHERE</code> 条件字段（如 <code>status</code>、<code>region</code>）和 <code>ORDER BY</code> 字段（如 <code>create_time</code>、<code>id</code>）必须加索引，否则数据库会全表扫描，分页性能骤降。示例索引设计：</p><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="comment">-- 复合索引：适配“按状态筛选+按创建时间排序”的分页场景</span></span><br><span class="line"><span class="keyword">CREATE</span> INDEX idx_orders_status_create_time <span class="keyword">ON</span> orders(status, create_time);</span><br></pre></td></tr></table></figure></li><li><p><strong>合并统计查询，减少数据库交互</strong>若需要同时展示 “总条数”“总页数” 和 “当前页数据”，避免执行两次查询（一次查总数 + 一次查分页数据），可通过 <code>SQL_CALC_FOUND_ROWS</code>（MySQL）或子查询合并：</p><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="comment">-- MySQL：查询分页数据的同时获取总条数</span></span><br><span class="line"><span class="keyword">SELECT</span> SQL_CALC_FOUND_ROWS <span class="operator">*</span> <span class="keyword">FROM</span> orders <span class="keyword">WHERE</span> status <span class="operator">=</span> <span class="number">1</span> <span class="keyword">ORDER</span> <span class="keyword">BY</span> id <span class="keyword">DESC</span> LIMIT <span class="number">10</span> <span class="keyword">OFFSET</span> <span class="number">20</span>;</span><br><span class="line"><span class="keyword">SELECT</span> FOUND_ROWS(); <span class="comment">-- 获取总条数（无需再次扫描表）</span></span><br></pre></td></tr></table></figure></li></ol><h3 id="程序分页：3-个避坑要点，保证稳定性"><a href="#程序分页：3-个避坑要点，保证稳定性" class="headerlink" title="程序分页：3 个避坑要点，保证稳定性"></a>程序分页：3 个避坑要点，保证稳定性</h3><ol><li><p><strong>加数据量限制，防止意外 OOM</strong>即使是小数据量场景，也需限制全量查询的最大条数，避免因业务异常导致数据量暴增（如用户查询 “所有历史订单” 时，意外返回 10 万条）。示例代码（Java）：</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 限制最大查询条数为2万条</span></span><br><span class="line">List&lt;Order&gt; allOrders = orderMapper.selectByCondition(condition, <span class="number">20000</span>);</span><br></pre></td></tr></table></figure></li><li><p><strong>用高效数据结构，优化分页性能</strong>全量数据加载后，优先用 <code>ArrayList</code>（随机访问效率高）存储，分页时通过 <code>subList()</code> 分片（时间复杂度 O (1)），避免使用链表（随机访问效率低）。若需多分类分页，可提前用 <code>HashMap</code> 按分类键分组，后续分页直接从分组结果中取数：</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 多分类分组+分页优化</span></span><br><span class="line">Map&lt;String, List&lt;Order&gt;&gt; categoryMap = allOrders.stream()</span><br><span class="line">        .collect(Collectors.groupingBy(order -&gt; getCategoryKey(order))); <span class="comment">// 自定义分类键</span></span><br><span class="line"><span class="comment">// 某分类分页</span></span><br><span class="line">List&lt;Order&gt; categoryOrders = categoryMap.get(<span class="string">&quot;高金额-华东地区&quot;</span>);</span><br><span class="line">List&lt;Order&gt; pageOrders = categoryOrders.subList(start, end);</span><br></pre></td></tr></table></figure></li><li><p><strong>缓存结果，减少重复查询</strong>若数据不频繁变化（如统计数据、历史订单），可将全量查询 + 分组后的结果缓存到 Redis（设置合理过期时间），下次分页直接从缓存取数，避免重复查询数据库。</p></li></ol><h2 id="五、进阶场景：混合分页方案（取长补短）"><a href="#五、进阶场景：混合分页方案（取长补短）" class="headerlink" title="五、进阶场景：混合分页方案（取长补短）"></a>五、进阶场景：混合分页方案（取长补短）</h2><p>当遇到 “大数据量 + 复杂逻辑” 的场景（如 “千万级订单，需按三维交叉分类分页，且每个分类需实时展示最新数据”），单一方案无法满足需求，此时可采用<strong>混合分页方案</strong>：</p><h3 id="核心思路：统计数预计算，详情页实时分页"><a href="#核心思路：统计数预计算，详情页实时分页" class="headerlink" title="核心思路：统计数预计算，详情页实时分页"></a>核心思路：统计数预计算，详情页实时分页</h3><ol><li><strong>预计算分类统计</strong>：用定时任务（如 Quartz）或流处理（如 Flink）将复杂分类的统计结果（如 “华东地区 - 高金额订单数”“华北地区 - APP 下单订单数”）存储到 Redis 或宽表，避免实时全量计算。</li><li><strong>实时分页查详情</strong>：用户点击某个分类时，通过 “分类条件 + 游标分页” 查询该分类下的实时数据 —— 比如 “华东地区 - 高金额订单” 的分页，可通过 SQL <code>WHERE 金额 &gt; 1000 AND 地区 = &#39;华东&#39; AND id &gt; 上一页最大ID LIMIT 10</code> 实现，兼顾实时性和性能。</li></ol><h3 id="适用场景："><a href="#适用场景：" class="headerlink" title="适用场景："></a>适用场景：</h3><ul><li>海量数据 + 复杂分类分页（如电商平台的订单多维度筛选分页）；</li><li>需兼顾实时性（详情数据）和性能（统计数据）的场景。</li></ul><h2 id="六、总结：选型决策表（直接套用）"><a href="#六、总结：选型决策表（直接套用）" class="headerlink" title="六、总结：选型决策表（直接套用）"></a>六、总结：选型决策表（直接套用）</h2><div class="table-container"><table><thead><tr><th>场景描述</th><th>推荐方案</th><th>核心理由</th></tr></thead><tbody><tr><td>小数据量（万级内）+ 复杂分类 / 筛选逻辑</td><td>程序分页</td><td>内存承载无压力，逻辑灵活，响应连贯</td></tr><tr><td>小数据量（万级内）+ 简单筛选逻辑</td><td>程序分页 / 数据库分页均可</td><td>程序分页开发效率高，数据库分页更省内存</td></tr><tr><td>大数据量（10 万级以上）+ 任意筛选逻辑</td><td>数据库分页</td><td>避免 OOM，依赖索引高效处理海量数据</td></tr><tr><td>实时性要求高（如订单列表）+ 简单筛选</td><td>数据库分页</td><td>实时取数，响应快</td></tr><tr><td>需频繁切换分类 / 复用数据（如多维度筛选页）</td><td>程序分页 + 缓存</td><td>减少数据库交互，切换分类无延迟</td></tr><tr><td>大数据量 + 复杂分类分页（进阶场景）</td><td>混合分页方案</td><td>预计算统计数 + 实时分页详情，兼顾性能和灵活</td></tr></tbody></table></div><h2 id="最终结论"><a href="#最终结论" class="headerlink" title="最终结论"></a>最终结论</h2><p>数据库分页和程序分页不是 “非此即彼” 的选择，而是 “按需适配” 的方案：</p><ul><li>小数据量、复杂逻辑场景，让应用程序 “扛下” 分页，享受灵活和高效；</li><li>大数据量、简单逻辑场景，让数据库 “搞定” 分页，保证稳定和性能；</li><li>复杂场景下，用混合方案取长补短，兼顾业务需求和技术上限。</li></ul><p>核心原则是：<strong>避免 “用数据库分页解决复杂逻辑”（逻辑繁琐、性能差），或 “用程序分页处理海量数据”（内存崩溃、响应慢）</strong>，让技术方案适配业务场景，而非反过来让业务妥协于技术。</p>]]></content:encoded>
      
      
      <category domain="https://blog.itrf.cn/tags/%E9%9B%B6%E7%A2%8E%E7%9F%A5%E8%AF%86/">零碎知识</category>
      
    </item>
    
    <item>
      <title>Flask项目无法获取favicon.ico原因以及解决方案</title>
      <link>https://blog.itrf.cn/posts/c93f/</link>
      <guid>https://blog.itrf.cn/posts/c93f/</guid>
      <pubDate>2025-11-29T04:59:16.000Z</pubDate>
      
      <description>Flask项目无法获取favicon.ico原因以及解决方案</description>
      
      
      <enclosure url="https://t.alcy.cc/moez?_r_=3996f244-9269-c9ff-4a6e-b6797e051080" type="image"/>
      
      
      <content:encoded><![CDATA[<p>部署后浏览器从「根目录（<code>/favicon.ico</code>）」获取图标，而非你设置的 <code>static/favicon.ico</code>，核心原因是 <strong>浏览器的默认行为 + 配置 / 引用缺失</strong>，具体拆解和解决步骤如下：</p><h3 id="一、核心原因：浏览器的「默认-favicon-ico-请求」"><a href="#一、核心原因：浏览器的「默认-favicon-ico-请求」" class="headerlink" title="一、核心原因：浏览器的「默认 /favicon.ico 请求」"></a>一、核心原因：浏览器的「默认 /favicon.ico 请求」</h3><p>这是最根本的原因 ——<strong>浏览器会自动向网站根目录发送 <code>/favicon.ico</code> 请求</strong>，无论你是否在页面中引用：</p><ul><li>即使你在 <code>&lt;head&gt;</code> 中写了 <code>&lt;link rel=&quot;icon&quot; href=&quot;/static/favicon.ico&quot;&gt;</code>，部分浏览器（如 Chrome、Edge）仍会先尝试请求 <code>/favicon.ico</code>（根目录），失败后才会使用你指定的路径；</li><li>部署环境中，若根目录没有 <code>favicon.ico</code>，且你的页面引用有问题（如路径错误、缓存），就会出现「图标不显示，Network 面板看到 <code>/favicon.ico 404</code>」的现象。</li></ul><h3 id="二、其他辅助原因（部署环境常见）"><a href="#二、其他辅助原因（部署环境常见）" class="headerlink" title="二、其他辅助原因（部署环境常见）"></a>二、其他辅助原因（部署环境常见）</h3><ol><li><p><strong>页面引用路径错误</strong>：</p><ul><li>本地开发时 <code>url_for(&#39;static&#39;, filename=&#39;favicon.ico&#39;)</code> 生成 <code>/static/favicon.ico</code>，但部署后若应用有「子路径前缀」（如 <code>https://your-domain.com/app/</code>），会生成 <code>/app/static/favicon.ico</code>，而浏览器仍请求 <code>/favicon.ico</code>；</li><li>引用时用了相对路径（如 <code>../static/favicon.ico</code>），部署后路径层级变化导致失效。</li></ul></li><li><p><strong>Nginx/Apache 反向代理配置覆盖</strong>：生产环境用反向代理时，可能配置了「根目录 <code>favicon.ico</code> 优先返回」，忽略 Flask 的静态文件路由：</p><figure class="highlight nginx"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 错误示例：Nginx 配置了根目录/favicon.ico，但实际文件不存在</span></span><br><span class="line"><span class="section">location</span> /favicon.ico &#123;</span><br><span class="line">    <span class="attribute">root</span> /var/www/html/;  <span class="comment"># 若该目录没有favicon.ico，直接404</span></span><br><span class="line">    <span class="attribute">expires</span> <span class="number">7d</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p><strong>浏览器缓存</strong>：之前访问过你的网站（或测试环境），浏览器缓存了「根目录 <code>/favicon.ico</code> 不存在」的结果，部署后即使修复了引用，仍会优先使用缓存。</p></li></ol><h3 id="三、3-种解决方法（从简单到彻底）"><a href="#三、3-种解决方法（从简单到彻底）" class="headerlink" title="三、3 种解决方法（从简单到彻底）"></a>三、3 种解决方法（从简单到彻底）</h3><h4 id="方法-1：确保页面中正确引用（最基础）"><a href="#方法-1：确保页面中正确引用（最基础）" class="headerlink" title="方法 1：确保页面中正确引用（最基础）"></a>方法 1：确保页面中正确引用（最基础）</h4><p>首先保证 <code>&lt;head&gt;</code> 中明确指定图标路径，覆盖浏览器默认行为：</p><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="comment">&lt;!-- templates/index.html（SPA入口页，全局生效） --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">  <span class="comment">&lt;!-- 核心：同时指定 rel=&quot;icon&quot; 和 rel=&quot;shortcut icon&quot;，兼容所有浏览器 --&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;icon&quot;</span> <span class="attr">href</span>=<span class="string">&quot;&#123;&#123; url_for(&#x27;static&#x27;, filename=&#x27;favicon.ico&#x27;) &#125;&#125;&quot;</span> <span class="attr">type</span>=<span class="string">&quot;image/x-icon&quot;</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;shortcut icon&quot;</span> <span class="attr">href</span>=<span class="string">&quot;&#123;&#123; url_for(&#x27;static&#x27;, filename=&#x27;favicon.ico&#x27;) &#125;&#125;&quot;</span> <span class="attr">type</span>=<span class="string">&quot;image/x-icon&quot;</span>&gt;</span></span><br><span class="line">  <span class="comment">&lt;!-- 可选：添加不同尺寸/格式，适配更多场景（如PWA） --&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;apple-touch-icon&quot;</span> <span class="attr">sizes</span>=<span class="string">&quot;180x180&quot;</span> <span class="attr">href</span>=<span class="string">&quot;&#123;&#123; url_for(&#x27;static&#x27;, filename=&#x27;apple-touch-icon.png&#x27;) &#125;&#125;&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br></pre></td></tr></table></figure><ul><li>关键：用 <code>url_for(&#39;static&#39;, filename=&#39;favicon.ico&#39;)</code> 动态生成路径，避免硬编码（适配部署后的子路径前缀）；</li><li>验证：部署后查看页面源码，确认 <code>href</code> 是正确的 <code>https://your-domain.com/static/favicon.ico</code>（而非 <code>/favicon.ico</code>）。</li></ul><h4 id="方法-2：路由重定向（让根目录请求指向-static）"><a href="#方法-2：路由重定向（让根目录请求指向-static）" class="headerlink" title="方法 2：路由重定向（让根目录请求指向 static）"></a>方法 2：路由重定向（让根目录请求指向 static）</h4><p>即使浏览器请求 <code>/favicon.ico</code>，也通过 Flask 路由重定向到 <code>static</code> 目录的文件，彻底解决默认请求问题：</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment"># app.py（在create_app()中添加路由）</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">create_app</span>(<span class="params">config_name=<span class="literal">None</span></span>):</span><br><span class="line">    app = Flask(__name__)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 新增：根目录favicon.ico请求 → 重定向到static目录</span></span><br><span class="line"><span class="meta">    @app.route(<span class="params"><span class="string">&#x27;/favicon.ico&#x27;</span></span>)</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">favicon_redirect</span>():</span><br><span class="line">        <span class="keyword">from</span> flask <span class="keyword">import</span> redirect, url_for</span><br><span class="line">        <span class="keyword">return</span> redirect(url_for(<span class="string">&#x27;static&#x27;</span>, filename=<span class="string">&#x27;favicon.ico&#x27;</span>))</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 其他配置（蓝图、错误处理等）...</span></span><br><span class="line">    <span class="keyword">return</span> app</span><br></pre></td></tr></table></figure><ul><li>效果：访问 <code>/favicon.ico</code> 会自动跳转到 <code>/static/favicon.ico</code>，浏览器能正确获取图标；</li><li>优势：无需修改 Nginx 配置，纯 Flask 代码解决，适配所有部署环境。</li></ul><h4 id="方法-3：Nginx-配置（生产环境最优，性能最高）"><a href="#方法-3：Nginx-配置（生产环境最优，性能最高）" class="headerlink" title="方法 3：Nginx 配置（生产环境最优，性能最高）"></a>方法 3：Nginx 配置（生产环境最优，性能最高）</h4><p>生产环境用 Nginx 反向代理时，直接让 Nginx 处理 <code>favicon.ico</code> 请求（不经过 Flask），提升性能：</p><figure class="highlight nginx"><table><tr><td class="code"><pre><span class="line"><span class="section">server</span> &#123;</span><br><span class="line">    <span class="attribute">listen</span> <span class="number">80</span>;</span><br><span class="line"><span class="attribute">server_name</span> your-domain.com;</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 核心配置：/favicon.ico 直接指向 static 目录的文件</span></span><br><span class="line">    <span class="section">location</span> /favicon.ico &#123;</span><br><span class="line">        <span class="comment"># 注意：root 是项目根目录（static的上级目录），不是static目录本身</span></span><br><span class="line">        <span class="attribute">root</span> /path/to/your_project/;  <span class="comment"># 示例：/var/www/music_project/</span></span><br><span class="line">        <span class="comment"># 实际文件路径 = root + static/favicon.ico → /var/www/music_project/static/favicon.ico</span></span><br><span class="line">        <span class="attribute">try_files</span> /static/favicon.ico =<span class="number">404</span>;  <span class="comment"># 尝试读取文件，不存在返回404</span></span><br><span class="line">        <span class="attribute">expires</span> <span class="number">7d</span>;  <span class="comment"># 缓存7天，减少重复请求</span></span><br><span class="line">        <span class="attribute">add_header</span> Cache-Control <span class="string">&quot;public&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 静态文件配置（已有的）</span></span><br><span class="line">    <span class="section">location</span> /static/ &#123;</span><br><span class="line">        <span class="attribute">root</span> /path/to/your_project/;</span><br><span class="line">        <span class="attribute">expires</span> <span class="number">7d</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 动态请求转发给Flask</span></span><br><span class="line">    <span class="section">location</span> / &#123;</span><br><span class="line">        <span class="attribute">proxy_pass</span> http://127.0.0.1:5000;</span><br><span class="line">        <span class="attribute">proxy_set_header</span> Host <span class="variable">$host</span>;</span><br><span class="line">        <span class="attribute">proxy_set_header</span> X-Real-IP <span class="variable">$remote_addr</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>关键：<code>root</code> 指向项目根目录（如 <code>/var/www/music_project/</code>），<code>try_files /static/favicon.ico</code> 表示「在 root 目录下查找 <code>static/favicon.ico</code> 文件」；</li><li>优势：Nginx 直接返回静态文件，比 Flask 路由重定向性能更高，适合高并发场景。</li></ul><h3 id="四、部署后验证步骤（确保生效）"><a href="#四、部署后验证步骤（确保生效）" class="headerlink" title="四、部署后验证步骤（确保生效）"></a>四、部署后验证步骤（确保生效）</h3><ol><li><p><strong>清除浏览器缓存</strong>：按 <code>Ctrl+Shift+R</code>（Chrome）强制刷新，或打开「开发者工具 → Network → 勾选 Disable cache」后刷新；</p></li><li><p>查看 Network 面板：</p><ul><li>搜索 <code>favicon</code>，查看请求路径：应为 <code>/static/favicon.ico</code> 或 <code>/favicon.ico</code>（状态码 200）；</li><li>若仍为 404，检查路径是否正确（如 <code>static</code> 目录是否上传到服务器、文件权限是否为可读）；</li></ul></li><li><p>直接访问验证：</p><ul><li>访问 <code>https://your-domain.com/static/favicon.ico</code>，能看到图标说明文件存在且路径正确；</li><li>访问 <code>https://your-domain.com/favicon.ico</code>，能看到图标说明重定向 / Nginx 配置生效。</li></ul></li></ol><h3 id="五、常见坑点排查"><a href="#五、常见坑点排查" class="headerlink" title="五、常见坑点排查"></a>五、常见坑点排查</h3><ol><li><p><strong>文件路径错误</strong>：</p><ul><li>服务器上的 <code>static</code> 目录是否和本地一致？（如本地 <code>static/favicon.ico</code>，服务器是否误放在 <code>static/icons/favicon.ico</code>）；</li><li>文件名是否大小写敏感？（Linux 服务器区分 <code>favicon.ico</code> 和 <code>Favicon.ico</code>，Windows 不区分）。</li></ul></li><li><p><strong>文件权限问题</strong>：</p><ul><li>服务器上的 <code>favicon.ico</code> 文件是否有「读权限」？（执行 <code>chmod 644 /path/to/your_project/static/favicon.ico</code> 赋予权限）；</li><li><code>static</code> 目录是否有「执行权限」？（执行 <code>chmod 755 /path/to/your_project/static</code>，确保浏览器能进入目录）。</li></ul></li><li><p><strong>子路径部署场景</strong>：若应用部署在子路径（如 <code>https://your-domain.com/music/</code>），需注意：</p><ul><li><p>页面引用用 <code>url_for(&#39;static&#39;, filename=&#39;favicon.ico&#39;)</code> 会自动生成 <code>/music/static/favicon.ico</code>；</p></li><li><p>Nginx 配置需调整 <code>root</code> 或 <code>alias</code><br>例如：</p><figure class="highlight nginx"><table><tr><td class="code"><pre><span class="line"><span class="section">location</span> /music/favicon.ico &#123;</span><br><span class="line">    <span class="attribute">alias</span> /path/to/your_project/static/favicon.ico;  <span class="comment"># 直接指向文件</span></span><br><span class="line">    <span class="attribute">expires</span> <span class="number">7d</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul></li></ol><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>部署后浏览器从根目录获取 <code>favicon.ico</code> 是「浏览器默认行为」导致的，解决核心是：</p><ol><li>页面中明确引用 <code>static/favicon.ico</code>（用 <code>url_for</code> 动态生成路径）；</li><li>用「Flask 路由重定向」或「Nginx 配置」让 <code>/favicon.ico</code> 指向正确文件；</li><li>清除缓存并验证文件路径和权限。</li></ol><p>按方法 1 + 方法 2（或方法 1 + 方法 3）配置后，所有浏览器都能正确获取图标，不会再出现「根目录请求 404」的问题。</p>]]></content:encoded>
      
      
      <category domain="https://blog.itrf.cn/tags/%E9%9B%B6%E7%A2%8E%E7%9F%A5%E8%AF%86/">零碎知识</category>
      
      <category domain="https://blog.itrf.cn/tags/Flask/">Flask</category>
      
    </item>
    
    <item>
      <title>EdgeOne 获取用户真实 IP 完整解决方案（Nginx 适配版）</title>
      <link>https://blog.itrf.cn/posts/8ab6/</link>
      <guid>https://blog.itrf.cn/posts/8ab6/</guid>
      <pubDate>2025-11-29T04:58:50.000Z</pubDate>
      
      <description>在使用腾讯云 EdgeOne 海外节点加速海外业务时，很多开发者会遇到一个核心问题：Nginx 日志中记录的是 EdgeOne 节点 IP 而非用户真实 IP，导致无法进行用户行为分析、地域统计和异常访问拦截。本文将从「问题原理」「手动配置」「自动化更新」「故障排查」四个维度，提供一套适配 Nginx + 宝塔面板的完整解决方案，新手也能快速上手。</description>
      
      
      <enclosure url="https://t.alcy.cc/ycy?_r_=d9c46573-ff5b-f2e3-9463-030c9adcf1e7" type="image"/>
      
      
      <content:encoded><![CDATA[<p>在使用腾讯云 EdgeOne 海外节点加速海外业务时，很多开发者会遇到一个核心问题：Nginx 日志中记录的是 EdgeOne 节点 IP 而非用户真实 IP，导致无法进行用户行为分析、地域统计和异常访问拦截。本文将从「问题原理」「手动配置」「自动化更新」「故障排查」四个维度，提供一套适配 Nginx + 宝塔面板的完整解决方案，新手也能快速上手。</p><h2 id="一、核心原理：为什么需要特殊配置？"><a href="#一、核心原理：为什么需要特殊配置？" class="headerlink" title="一、核心原理：为什么需要特殊配置？"></a>一、核心原理：为什么需要特殊配置？</h2><p>当用户访问经过 EdgeOne 加速的网站时，请求会先经过 EdgeOne 海外节点（反向代理），再由节点转发到源站 Nginx。此时 Nginx 会默认将「直接连接的客户端 IP」（即 EdgeOne 节点 IP）识别为用户 IP，而非真实的用户公网 IP。</p><p>解决思路的核心是「信任代理」：</p><ol><li>EdgeOne 节点会在转发请求时，通过 <code>X-Forwarded-For</code> 头携带用户真实 IP（格式：<code>用户真实IP, 节点IP1, 节点IP2</code>）；</li><li>配置 Nginx 信任所有 EdgeOne 节点 IP，让 Nginx 从 <code>X-Forwarded-For</code> 头中自动跳过节点 IP，提取最原始的用户真实 IP。</li></ol><blockquote><p>关键提示：本文以「海外业务场景」为例，适配 CentOS 系统 + Nginx + 宝塔面板，其他系统（如 Ubuntu）或 Web 服务器（如 Apache）可参考原理调整。</p></blockquote><h2 id="二、前置准备：确认-EdgeOne-基础配置"><a href="#二、前置准备：确认-EdgeOne-基础配置" class="headerlink" title="二、前置准备：确认 EdgeOne 基础配置"></a>二、前置准备：确认 EdgeOne 基础配置</h2><p>在配置 Nginx 前，需先确保 EdgeOne 已正确配置「回源透传」，否则节点不会携带用户真实 IP 信息：</p><ol><li>登录 <a href="https://console.cloud.tencent.com/edgeone">EdgeOne 控制台</a>，进入目标站点的「回源配置」；</li><li>开启「HTTP 头配置」，确认已添加「透传 X-Forwarded-For 头」（默认开启，若未开启需手动添加）；</li><li>关闭「源站保护」功能（重要！开启后无法获取完整节点 IP 段，配置完成后可重新开启）。</li></ol><h2 id="三、手动配置：快速实现真实-IP-解析"><a href="#三、手动配置：快速实现真实-IP-解析" class="headerlink" title="三、手动配置：快速实现真实 IP 解析"></a>三、手动配置：快速实现真实 IP 解析</h2><p>若业务规模较小，可先通过手动配置验证效果，步骤如下：</p><h3 id="3-1-获取-EdgeOne-海外节点-IP-段"><a href="#3-1-获取-EdgeOne-海外节点-IP-段" class="headerlink" title="3.1 获取 EdgeOne 海外节点 IP 段"></a>3.1 获取 EdgeOne 海外节点 IP 段</h3><p>EdgeOne 提供官方接口用于获取最新节点 IP 段，海外 IPv4 段专属获取方式：</p><ol><li>本地浏览器访问接口：<code>https://api.edgeone.ai/ips?version=v4&amp;area=overseas</code>（仅返回海外节点 IP 段，精准适配海外业务）；</li><li>复制页面中所有 IP 段（纯文本格式，如 <code>43.174.0.0/15</code>），保存到本地文档。</li></ol><blockquote><p>注意事项：接口返回的 IP 段会不定期更新，建议每 1-2 个月重新获取一次，避免因 IP 段过期导致解析失效。</p></blockquote><h3 id="3-2-配置-Nginx-信任节点-IP-段"><a href="#3-2-配置-Nginx-信任节点-IP-段" class="headerlink" title="3.2 配置 Nginx 信任节点 IP 段"></a>3.2 配置 Nginx 信任节点 IP 段</h3><p>通过宝塔面板修改 Nginx 配置（手动操作更直观，适合新手）：</p><ol><li>登录宝塔面板，进入「网站」→ 目标站点（如 <code>music.686909.xyz</code>）→ 「配置文件」；</li><li>在 <code>server</code> 块顶部添加「节点 IP 段信任配置」，完整配置示例如下（重点关注标记内内容）：</li></ol><figure class="highlight nginx"><table><tr><td class="code"><pre><span class="line"><span class="section">server</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="attribute">listen</span> <span class="number">80</span>;</span><br><span class="line">    <span class="attribute">listen</span> <span class="number">443</span> ssl;</span><br><span class="line">    <span class="attribute">listen</span> <span class="number">443</span> quic;</span><br><span class="line">    <span class="attribute">http2</span> <span class="literal">on</span>;</span><br><span class="line">    <span class="attribute">server_name</span> music.<span class="number">686909</span>.xyz;</span><br><span class="line"></span><br><span class="line">    <span class="comment"># ====== EDGEONE_IP_START 标记（后续自动化需用到）======</span></span><br><span class="line">    <span class="comment"># EdgeOne 海外节点 IP 段（手动复制接口返回内容）</span></span><br><span class="line">    <span class="attribute">set_real_ip_from</span> <span class="number">43.174.0.0</span>/<span class="number">15</span>;</span><br><span class="line">    <span class="attribute">set_real_ip_from</span> <span class="number">45.12.0.0</span>/<span class="number">16</span>;</span><br><span class="line">    <span class="attribute">set_real_ip_from</span> <span class="number">45.65.0.0</span>/<span class="number">16</span>;</span><br><span class="line">    <span class="attribute">set_real_ip_from</span> <span class="number">45.113.0.0</span>/<span class="number">16</span>;</span><br><span class="line">    <span class="attribute">set_real_ip_from</span> <span class="number">52.58.0.0</span>/<span class="number">15</span>;</span><br><span class="line">    <span class="comment"># ... 此处省略其他从接口获取的 IP 段，需完整粘贴 ...</span></span><br><span class="line">    <span class="attribute">set_real_ip_from</span> <span class="number">217.160.0.0</span>/<span class="number">16</span>;</span><br><span class="line">    <span class="comment"># ====== EDGEONE_IP_END 标记（后续自动化需用到）======</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 真实 IP 解析核心规则</span></span><br><span class="line">    <span class="attribute">real_ip_header</span> X-Forwarded-For;        <span class="comment"># 从该头提取真实 IP</span></span><br><span class="line">    <span class="attribute">real_ip_recursive</span> <span class="literal">on</span>;                  <span class="comment"># 递归跳过信任的节点 IP</span></span><br><span class="line">    <span class="attribute">set_real_ip_from</span> <span class="number">127.0.0.1</span>;            <span class="comment"># 额外信任本地代理（避免干扰）</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 以下为原有配置（SSL、反向代理等，无需修改）</span></span><br><span class="line">    <span class="attribute">index</span> index.html index.htm default.htm default.html;</span><br><span class="line">    <span class="attribute">root</span> /www/wwwroot/music;</span><br><span class="line">    <span class="attribute">ssl_certificate</span>    /www/server/panel/vhost/cert/music/fullchain.pem;</span><br><span class="line">    <span class="attribute">ssl_certificate_key</span>    /www/server/panel/vhost/cert/music/privkey.pem;</span><br><span class="line">    <span class="comment"># ... 其他配置省略 ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol><li>点击「保存」，然后在宝塔面板「服务」中重启 Nginx，配置生效。</li></ol><h3 id="3-3-验证配置效果"><a href="#3-3-验证配置效果" class="headerlink" title="3.3 验证配置效果"></a>3.3 验证配置效果</h3><p>配置完成后，通过以下步骤确认真实 IP 是否解析成功：</p><ol><li>用海外网络访问目标网站（如通过海外 VPN 或在线代理工具）；</li><li>在宝塔面板进入「网站」→ 目标站点 → 「日志」，查看访问日志（如 <code>music.log</code>）；</li><li>日志中「IP」字段若显示为非 EdgeOne 网段的公网 IP（可通过 <a href="https://www.ip138.com/">IP138</a> 验证归属地），说明解析成功。</li></ol><h2 id="四、自动化方案：IP-段自动更新（运维必备）"><a href="#四、自动化方案：IP-段自动更新（运维必备）" class="headerlink" title="四、自动化方案：IP 段自动更新（运维必备）"></a>四、自动化方案：IP 段自动更新（运维必备）</h2><p>手动更新 IP 段效率低且易遗漏，通过「Shell 脚本 + 定时任务」可实现全自动更新，核心逻辑：定期从官方接口获取最新 IP 段 → 自动替换 Nginx 配置 → 验证并重启 Nginx。</p><h3 id="4-1-编写自动更新脚本"><a href="#4-1-编写自动更新脚本" class="headerlink" title="4.1 编写自动更新脚本"></a>4.1 编写自动更新脚本</h3><p>创建脚本 <code>update_edgeone_ips.sh</code>，上传到服务器 <code>/root/</code> 目录，脚本已适配海外节点和宝塔面板，关键部分已加注释（需替换脚本中 <code>NGINX_CONF</code> 为你的站点配置路径）：</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash</span></span><br><span class="line"><span class="comment"># ==============================================</span></span><br><span class="line"><span class="comment"># EdgeOne 海外节点 IP 段自动更新脚本（Nginx 适配版）</span></span><br><span class="line"><span class="comment"># 功能：接口获取IP段 → 替换配置 → 验证重启 → 备份回滚</span></span><br><span class="line"><span class="comment"># 适配：CentOS + 宝塔面板 + 海外业务</span></span><br><span class="line"><span class="comment"># ==============================================</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ---------------------- 核心参数（需根据实际调整）----------------------</span></span><br><span class="line">NGINX_CONF=<span class="string">&quot;/www/server/panel/vhost/nginx/python_music.conf&quot;</span>  <span class="comment"># 你的站点Nginx配置路径</span></span><br><span class="line">IP_API=<span class="string">&quot;https://api.edgeone.ai/ips?version=v4&amp;area=overseas&quot;</span>  <span class="comment"># 海外IPv4专属接口</span></span><br><span class="line">TIMEOUT=30  <span class="comment"># 接口超时时间（秒）</span></span><br><span class="line">RETRY=3     <span class="comment"># 接口失败重试次数</span></span><br><span class="line"><span class="comment"># ---------------------------------------------------------------------------</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 临时文件（规避Sed长字符串语法问题）</span></span><br><span class="line">TMP_IP_FILE=$(<span class="built_in">mktemp</span>)</span><br><span class="line">TMP_NGINX_CONF=$(<span class="built_in">mktemp</span>)</span><br><span class="line">BACKUP_CONF=<span class="string">&quot;<span class="variable">$&#123;NGINX_CONF&#125;</span>.bak.<span class="subst">$(date +%Y%m%d)</span>&quot;</span>  <span class="comment"># 带日期的配置备份文件</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 1. 接口获取IP段（适配海外网络，带重试+证书兼容）</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;[<span class="subst">$(date +&#x27;%Y-%m-%d %H:%M:%S&#x27;)</span>] 从官方接口获取EdgeOne海外IP段（最多重试<span class="variable">$RETRY</span>次）...&quot;</span></span><br><span class="line"><span class="keyword">for</span> ((i=<span class="number">1</span>; i&lt;=<span class="variable">$RETRY</span>; i++)); <span class="keyword">do</span></span><br><span class="line">    <span class="comment"># curl参数说明：跳过证书校验、设置超时、失败重试</span></span><br><span class="line">    curl -s --connect-timeout 10 --max-time <span class="variable">$TIMEOUT</span> \</span><br><span class="line">         --retry <span class="variable">$RETRY</span> --retry-delay 3 \</span><br><span class="line">         --insecure \</span><br><span class="line">         <span class="string">&quot;<span class="variable">$IP_API</span>&quot;</span> &gt; <span class="string">&quot;<span class="variable">$TMP_IP_FILE</span>&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 验证获取结果：非空且包含标准IPv4网段格式</span></span><br><span class="line">    <span class="keyword">if</span> [ -s <span class="string">&quot;<span class="variable">$TMP_IP_FILE</span>&quot;</span> ] &amp;&amp; grep -qE <span class="string">&#x27;^([0-9]&#123;1,3&#125;\.)&#123;3&#125;[0-9]&#123;1,3&#125;/[0-9]&#123;1,2&#125;$&#x27;</span> <span class="string">&quot;<span class="variable">$TMP_IP_FILE</span>&quot;</span>; <span class="keyword">then</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">&quot;[<span class="subst">$(date +&#x27;%Y-%m-%d %H:%M:%S&#x27;)</span>] ✅ 第<span class="variable">$i</span>次尝试成功，获取到有效IP段&quot;</span></span><br><span class="line">        <span class="built_in">break</span></span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="built_in">echo</span> <span class="string">&quot;[<span class="subst">$(date +&#x27;%Y-%m-%d %H:%M:%S&#x27;)</span>] ❌ 第<span class="variable">$i</span>次尝试失败，正在重试...&quot;</span></span><br><span class="line">        <span class="keyword">if</span> [ <span class="variable">$i</span> -eq <span class="variable">$RETRY</span> ]; <span class="keyword">then</span></span><br><span class="line">            <span class="built_in">echo</span> <span class="string">&quot;[<span class="subst">$(date +&#x27;%Y-%m-%d %H:%M:%S&#x27;)</span>] ❌ 所有重试失败，无法获取IP段，退出脚本&quot;</span></span><br><span class="line">            <span class="built_in">rm</span> -f <span class="string">&quot;<span class="variable">$TMP_IP_FILE</span>&quot;</span> <span class="string">&quot;<span class="variable">$TMP_NGINX_CONF</span>&quot;</span></span><br><span class="line">            <span class="built_in">exit</span> 1</span><br><span class="line">        <span class="keyword">fi</span></span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line"><span class="keyword">done</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 过滤无效IP段并生成Nginx格式配置</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;[<span class="subst">$(date +&#x27;%Y-%m-%d %H:%M:%S&#x27;)</span>] 过滤无效IP段并生成配置...&quot;</span></span><br><span class="line"><span class="comment"># 仅保留标准IPv4网段，剔除异常内容</span></span><br><span class="line">grep -E <span class="string">&#x27;^([0-9]&#123;1,3&#125;\.)&#123;3&#125;[0-9]&#123;1,3&#125;/[0-9]&#123;1,2&#125;$&#x27;</span> <span class="string">&quot;<span class="variable">$TMP_IP_FILE</span>&quot;</span> &gt; <span class="string">&quot;<span class="variable">$TMP_IP_FILE</span>.tmp&quot;</span></span><br><span class="line"><span class="built_in">mv</span> <span class="string">&quot;<span class="variable">$TMP_IP_FILE</span>.tmp&quot;</span> <span class="string">&quot;<span class="variable">$TMP_IP_FILE</span>&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 检查过滤后是否有有效IP段</span></span><br><span class="line"><span class="keyword">if</span> [ ! -s <span class="string">&quot;<span class="variable">$TMP_IP_FILE</span>&quot;</span> ]; <span class="keyword">then</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;[<span class="subst">$(date +&#x27;%Y-%m-%d %H:%M:%S&#x27;)</span>] ❌ 过滤后无有效IP段，退出脚本&quot;</span></span><br><span class="line">    <span class="built_in">rm</span> -f <span class="string">&quot;<span class="variable">$TMP_IP_FILE</span>&quot;</span> <span class="string">&quot;<span class="variable">$TMP_NGINX_CONF</span>&quot;</span></span><br><span class="line">    <span class="built_in">exit</span> 1</span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 生成带标记的完整IP配置（写入临时文件，避免Sed拼接过长）</span></span><br><span class="line">IP_CONFIG_FILE=$(<span class="built_in">mktemp</span>)</span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;# ====== EDGEONE_IP_START 自动更新标记（请勿删除）======&quot;</span> &gt; <span class="string">&quot;<span class="variable">$IP_CONFIG_FILE</span>&quot;</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;# 自动更新时间：<span class="subst">$(date +&#x27;%Y-%m-%d %H:%M:%S&#x27;)</span>&quot;</span> &gt;&gt; <span class="string">&quot;<span class="variable">$IP_CONFIG_FILE</span>&quot;</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;# EdgeOne 海外节点IP段（从官方接口获取）&quot;</span> &gt;&gt; <span class="string">&quot;<span class="variable">$IP_CONFIG_FILE</span>&quot;</span></span><br><span class="line"><span class="comment"># 将IP段转换为Nginx的set_real_ip_from格式</span></span><br><span class="line">awk <span class="string">&#x27;&#123;print &quot;    set_real_ip_from &quot; $0 &quot;;&quot;&#125;&#x27;</span> <span class="string">&quot;<span class="variable">$TMP_IP_FILE</span>&quot;</span> &gt;&gt; <span class="string">&quot;<span class="variable">$IP_CONFIG_FILE</span>&quot;</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;# ====== EDGEONE_IP_END 自动更新标记（请勿删除）======&quot;</span> &gt;&gt; <span class="string">&quot;<span class="variable">$IP_CONFIG_FILE</span>&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 备份原Nginx配置（避免替换失败导致服务异常）</span></span><br><span class="line"><span class="built_in">cp</span> <span class="string">&quot;<span class="variable">$NGINX_CONF</span>&quot;</span> <span class="string">&quot;<span class="variable">$BACKUP_CONF</span>&quot;</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;[<span class="subst">$(date +&#x27;%Y-%m-%d %H:%M:%S&#x27;)</span>] ✅ 原配置已备份至：<span class="variable">$BACKUP_CONF</span>&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 4. 替换配置文件中的IP段（文件分段拼接，兼容长内容）</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;[<span class="subst">$(date +&#x27;%Y-%m-%d %H:%M:%S&#x27;)</span>] 开始替换Nginx配置中的IP段...&quot;</span></span><br><span class="line"><span class="comment"># 步骤1：提取标记前的内容</span></span><br><span class="line">sed -n <span class="string">&#x27;/# ====== EDGEONE_IP_START 自动更新标记（请勿删除）======/q;p&#x27;</span> <span class="string">&quot;<span class="variable">$NGINX_CONF</span>&quot;</span> &gt; <span class="string">&quot;<span class="variable">$TMP_NGINX_CONF</span>&quot;</span></span><br><span class="line"><span class="comment"># 步骤2：插入新生成的IP配置</span></span><br><span class="line"><span class="built_in">cat</span> <span class="string">&quot;<span class="variable">$IP_CONFIG_FILE</span>&quot;</span> &gt;&gt; <span class="string">&quot;<span class="variable">$TMP_NGINX_CONF</span>&quot;</span></span><br><span class="line"><span class="comment"># 步骤3：提取标记后的内容（排除结束标记本身，避免重复）</span></span><br><span class="line">sed -n <span class="string">&#x27;/# ====== EDGEONE_IP_END 自动更新标记（请勿删除）======/,$!d;/# ====== EDGEONE_IP_END 自动更新标记（请勿删除）======/!p&#x27;</span> <span class="string">&quot;<span class="variable">$NGINX_CONF</span>&quot;</span> &gt;&gt; <span class="string">&quot;<span class="variable">$TMP_NGINX_CONF</span>&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 覆盖原配置并保持文件权限（宝塔默认Nginx配置权限为644）</span></span><br><span class="line"><span class="built_in">cp</span> <span class="string">&quot;<span class="variable">$TMP_NGINX_CONF</span>&quot;</span> <span class="string">&quot;<span class="variable">$NGINX_CONF</span>&quot;</span></span><br><span class="line"><span class="built_in">chmod</span> 644 <span class="string">&quot;<span class="variable">$NGINX_CONF</span>&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 5. 验证配置替换结果</span></span><br><span class="line"><span class="keyword">if</span> grep -q <span class="string">&quot;set_real_ip_from 43.174.0.0/15;&quot;</span> <span class="string">&quot;<span class="variable">$NGINX_CONF</span>&quot;</span>; <span class="keyword">then</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;[<span class="subst">$(date +&#x27;%Y-%m-%d %H:%M:%S&#x27;)</span>] ✅ IP段成功写入Nginx配置&quot;</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;[<span class="subst">$(date +&#x27;%Y-%m-%d %H:%M:%S&#x27;)</span>] ❌ IP段写入失败，正在恢复原配置...&quot;</span></span><br><span class="line">    <span class="built_in">cp</span> <span class="string">&quot;<span class="variable">$BACKUP_CONF</span>&quot;</span> <span class="string">&quot;<span class="variable">$NGINX_CONF</span>&quot;</span></span><br><span class="line">    <span class="built_in">rm</span> -f <span class="string">&quot;<span class="variable">$TMP_IP_FILE</span>&quot;</span> <span class="string">&quot;<span class="variable">$TMP_NGINX_CONF</span>&quot;</span> <span class="string">&quot;<span class="variable">$IP_CONFIG_FILE</span>&quot;</span></span><br><span class="line">    <span class="built_in">exit</span> 1</span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 6. 测试Nginx配置并重启服务</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;[<span class="subst">$(date +&#x27;%Y-%m-%d %H:%M:%S&#x27;)</span>] 测试Nginx配置语法有效性...&quot;</span></span><br><span class="line"><span class="keyword">if</span> /www/server/nginx/sbin/nginx -t; <span class="keyword">then</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;[<span class="subst">$(date +&#x27;%Y-%m-%d %H:%M:%S&#x27;)</span>] ✅ 配置语法正确，重启Nginx服务...&quot;</span></span><br><span class="line">    /www/server/nginx/sbin/nginx -s reload</span><br><span class="line">    IP_COUNT=$(<span class="built_in">wc</span> -l &lt; <span class="string">&quot;<span class="variable">$TMP_IP_FILE</span>&quot;</span>)</span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;[<span class="subst">$(date +&#x27;%Y-%m-%d %H:%M:%S&#x27;)</span>] 🎉 全部操作完成！本次共更新<span class="variable">$IP_COUNT</span>个海外IP段&quot;</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;[<span class="subst">$(date +&#x27;%Y-%m-%d %H:%M:%S&#x27;)</span>] ❌ 配置语法错误，正在恢复原配置...&quot;</span></span><br><span class="line">    <span class="built_in">cp</span> <span class="string">&quot;<span class="variable">$BACKUP_CONF</span>&quot;</span> <span class="string">&quot;<span class="variable">$NGINX_CONF</span>&quot;</span></span><br><span class="line">    /www/server/nginx/sbin/nginx -s reload</span><br><span class="line">    <span class="built_in">rm</span> -f <span class="string">&quot;<span class="variable">$TMP_IP_FILE</span>&quot;</span> <span class="string">&quot;<span class="variable">$TMP_NGINX_CONF</span>&quot;</span> <span class="string">&quot;<span class="variable">$IP_CONFIG_FILE</span>&quot;</span></span><br><span class="line">    <span class="built_in">exit</span> 1</span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 清理临时文件</span></span><br><span class="line"><span class="built_in">rm</span> -f <span class="string">&quot;<span class="variable">$TMP_IP_FILE</span>&quot;</span> <span class="string">&quot;<span class="variable">$TMP_NGINX_CONF</span>&quot;</span> <span class="string">&quot;<span class="variable">$IP_CONFIG_FILE</span>&quot;</span></span><br></pre></td></tr></table></figure><h3 id="4-2-脚本授权与测试运行"><a href="#4-2-脚本授权与测试运行" class="headerlink" title="4.2 脚本授权与测试运行"></a>4.2 脚本授权与测试运行</h3><p>通过 SSH 工具登录服务器（如 Xshell、FinalShell），执行以下命令完成脚本授权和手动测试，确保脚本可正常运行：</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 1. 给脚本添加可执行权限（仅需执行一次）</span></span><br><span class="line"><span class="built_in">chmod</span> +x /root/update_edgeone_ips.sh</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 手动执行脚本，验证流程是否正常</span></span><br><span class="line">/root/update_edgeone_ips.sh</span><br></pre></td></tr></table></figure><h4 id="成功运行的输出示例："><a href="#成功运行的输出示例：" class="headerlink" title="成功运行的输出示例："></a>成功运行的输出示例：</h4><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">[2025-12-01 09:30:00] 从官方接口获取EdgeOne海外IP段（最多重试3次）...</span><br><span class="line">[2025-12-01 09:30:02] ✅ 第1次尝试成功，获取到有效IP段</span><br><span class="line">[2025-12-01 09:30:02] 过滤无效IP段并生成配置...</span><br><span class="line">[2025-12-01 09:30:02] ✅ 原配置已备份至：/www/server/panel/vhost/nginx/python_music.conf.bak.20251201</span><br><span class="line">[2025-12-01 09:30:03] 开始替换Nginx配置中的IP段...</span><br><span class="line">[2025-12-01 09:30:03] ✅ IP段成功写入Nginx配置</span><br><span class="line">[2025-12-01 09:30:04] 测试Nginx配置语法有效性...</span><br><span class="line">nginx: the configuration file /www/server/nginx/conf/nginx.conf syntax is ok</span><br><span class="line">nginx: configuration file /www/server/nginx/conf/nginx.conf test is successful</span><br><span class="line">[2025-12-01 09:30:04] ✅ 配置语法正确，重启Nginx服务...</span><br><span class="line">[2025-12-01 09:30:05] 🎉 全部操作完成！本次共更新36个海外IP段</span><br></pre></td></tr></table></figure><h4 id="常见测试失败处理："><a href="#常见测试失败处理：" class="headerlink" title="常见测试失败处理："></a>常见测试失败处理：</h4><p>若提示 “接口访问失败”，可先执行 <code>curl -v --insecure https://api.edgeone.ai/ips?version=v4&amp;area=overseas</code> 查看具体错误（如防火墙拦截、DNS 解析异常），针对性解决后再重新测试。</p><h3 id="4-3-配置定时任务（实现每月自动更新）"><a href="#4-3-配置定时任务（实现每月自动更新）" class="headerlink" title="4.3 配置定时任务（实现每月自动更新）"></a>4.3 配置定时任务（实现每月自动更新）</h3><p>手动执行脚本可验证效果，但为避免 IP 段过期，需配置定时任务每月自动更新，通过宝塔面板操作更直观：</p><ol><li>登录宝塔面板，进入「计划任务」模块，点击「添加任务」；</li><li>任务类型：选择「Shell 脚本」（核心类型，执行服务器命令）；</li><li>执行周期：选择「每月」，日期设为「1」，时间选凌晨空闲时段（如 03:00，避免影响业务）；</li><li>脚本路径：填写 <code>/root/update_edgeone_ips.sh</code>（脚本实际存放路径）；</li><li>日志设置：勾选「保存日志」，日志路径默认即可（便于后续排查执行情况）；</li><li>点击「添加」，系统会自动生成定时任务，每月 1 号凌晨自动执行 IP 段更新。</li></ol><blockquote><p>⚠️ 注意事项：添加定时任务后，建议手动执行一次「测试」按钮，确认任务可正常触发脚本，避免因路径错误、权限问题导致定时执行失败。</p></blockquote><h2 id="五、常见问题与故障排查"><a href="#五、常见问题与故障排查" class="headerlink" title="五、常见问题与故障排查"></a>五、常见问题与故障排查</h2><p>配置过程中可能遇到接口访问失败、真实 IP 解析无效等问题，以下是高频问题的解决方案，按排查优先级排序：</p><h3 id="5-1-接口无法获取-IP-段（脚本第一步失败）"><a href="#5-1-接口无法获取-IP-段（脚本第一步失败）" class="headerlink" title="5.1 接口无法获取 IP 段（脚本第一步失败）"></a>5.1 接口无法获取 IP 段（脚本第一步失败）</h3><div class="table-container"><table><thead><tr><th>错误现象</th><th>核心原因</th><th>解决方案</th></tr></thead><tbody><tr><td>curl: (7) Connection timed out</td><td>服务器防火墙 / 安全组禁止 443 出站</td><td>1. 宝塔面板「安全」→「防火墙」→ 放行「443 出站」；2. 云厂商控制台（如腾讯云）安全组添加规则：允许 TCP:443 出站</td></tr><tr><td>curl: (6) Could not resolve host</td><td>DNS 解析异常，无法识别 api.edgeone.ai</td><td>修改服务器 DNS 配置：编辑 <code>/etc/resolv.conf</code>，添加 <code>nameserver 8.8.8.8</code>（谷歌 DNS）或 <code>nameserver 1.1.1.1</code>（Cloudflare DNS），保存后执行 <code>systemctl restart network</code></td></tr><tr><td>返回内容为空或非 IP 段</td><td>接口参数错误，未指定海外区域</td><td>确认脚本中 IP_API 为 <code>https://api.edgeone.ai/ips?version=v4&amp;area=overseas</code>，而非默认的全球接口</td></tr></tbody></table></div><h3 id="5-2-真实-IP-解析不生效（日志仍为节点-IP）"><a href="#5-2-真实-IP-解析不生效（日志仍为节点-IP）" class="headerlink" title="5.2 真实 IP 解析不生效（日志仍为节点 IP）"></a>5.2 真实 IP 解析不生效（日志仍为节点 IP）</h3><p>这是最常见的问题，按以下步骤逐步排查，可覆盖 90% 以上场景：</p><ol><li><strong>检查 EdgeOne 透传配置</strong>：登录 EdgeOne 控制台，确认「回源配置」→「HTTP 头配置」中已开启「透传 X-Forwarded-For 头」，且未开启「源站保护」（开启后会隐藏节点 IP）；</li><li><strong>验证 Nginx 核心规则</strong>：确保 <code>real_ip_header X-Forwarded-For;</code> 和 <code>real_ip_recursive on;</code> 已配置，且位于所有 <code>set_real_ip_from</code> 规则之后（顺序错误会导致解析失效）；</li><li><strong>确认 IP 段完整性</strong>：在访问日志中复制记录的 “节点 IP”，通过 EdgeOne 控制台「工具」→「IP 归属查询」确认是否为 EdgeOne 节点 IP，若不在配置的 IP 段中，说明 IP 段未更新，需重新执行脚本；</li><li><strong>重启 Nginx 服务</strong>：配置修改后未重启服务，执行 <code>www/server/nginx/sbin/nginx -s reload</code> 确保配置生效。</li></ol><h3 id="5-3-脚本替换配置后-Nginx-启动失败"><a href="#5-3-脚本替换配置后-Nginx-启动失败" class="headerlink" title="5.3 脚本替换配置后 Nginx 启动失败"></a>5.3 脚本替换配置后 Nginx 启动失败</h3><p>主要原因是配置语法错误，脚本已内置回滚机制，会自动恢复原配置，可通过以下方式定位问题：</p><ol><li>查看 Nginx 错误日志：执行 <code>cat /www/server/nginx/logs/error.log</code>，重点关注 “syntax error” 相关内容；</li><li>检查 IP 段格式：手动打开 Nginx 配置文件，确认标记内的 <code>set_real_ip_from</code> 规则是否均为 <code>set_real_ip_from x.x.x.x/x;</code> 格式，无多余空格、逗号；</li><li>恢复原配置：若脚本未自动回滚，执行 <code>cp 备份文件路径 原配置路径</code>（如 <code>cp /www/server/panel/vhost/nginx/python_music.conf.bak.20251201 /www/server/panel/vhost/nginx/python_music.conf</code>），重启 Nginx 即可恢复服务。</li></ol><h2 id="六、总结与扩展"><a href="#六、总结与扩展" class="headerlink" title="六、总结与扩展"></a>六、总结与扩展</h2><p>本文提供的 EdgeOne 海外节点真实 IP 解析方案，核心是「信任代理节点 IP + 提取 X-Forwarded-For 头真实 IP」，通过 “手动配置验证 + 自动化脚本更新” 的组合，既保证了新手可快速上手，又解决了运维过程中 IP 段过期的痛点。</p><h3 id="方案扩展场景"><a href="#方案扩展场景" class="headerlink" title="方案扩展场景"></a>方案扩展场景</h3><ol><li><strong>多站点适配</strong>：若服务器部署多个站点，可复制脚本并修改 <code>NGINX_CONF</code> 路径，或在脚本中添加多路径数组循环处理；</li><li><strong>IPv6 场景</strong>：将脚本中 <code>IP_API</code> 改为 <code>https://api.edgeone.ai/ips?version=v6&amp;area=overseas</code>，同时在 Nginx 配置中添加 <code>real_ip_header X-Forwarded-For;</code>（IPv6 解析规则与 IPv4 一致）；</li><li><strong>非宝塔环境</strong>：修改脚本中 Nginx 启动命令（如 <code>systemctl restart nginx</code>）和配置路径（如 <code>/etc/nginx/conf.d/your_site.conf</code>）即可适配。</li></ol><p>通过本文方案配置后，Nginx 日志将准确记录用户真实 IP，可正常开展地域访问统计、异常 IP 拦截等运维工作，且每月自动更新 IP 段，实现 “一次配置，长期稳定” 的效果。</p>]]></content:encoded>
      
      
      <category domain="https://blog.itrf.cn/tags/%E9%9B%B6%E7%A2%8E%E7%9F%A5%E8%AF%86/">零碎知识</category>
      
      <category domain="https://blog.itrf.cn/tags/EdgeOne/">EdgeOne</category>
      
    </item>
    
    <item>
      <title>Pymusic一款Flask框架开发的音乐网站</title>
      <link>https://blog.itrf.cn/posts/3a52/</link>
      <guid>https://blog.itrf.cn/posts/3a52/</guid>
      <pubDate>2025-11-14T04:49:40.000Z</pubDate>
      
      <description>这是一个基于Flask框架开发的个人音乐播放器项目，旨在模拟网易云音乐的核心功能并提供简洁的用户体验。该项目集成了网易云音乐API，支持音乐搜索、歌单管理、用户认证等功能，为音乐爱好者提供一个轻量级的音乐播放和管理平台。</description>
      
      
      <enclosure url="https://t.alcy.cc/moez?_r_=039e427c-e84c-9063-fd22-52fa686378a8" type="image"/>
      
      
      <content:encoded><![CDATA[<p>体验网址：<a href="https://music.686909.xyz">音悦台</a><br>开源仓库：<a href="https://github.com/itrfcn/Pymusic">Pymusic</a></p><p>🌐 项目简介</p><p>这是一个基于Flask框架开发的个人音乐播放器项目，旨在模拟网易云音乐的核心功能并提供简洁的用户体验。该项目集成了网易云音乐API，支持音乐搜索、歌单管理、用户认证等功能，为音乐爱好者提供一个轻量级的音乐播放和管理平台。</p><p>🌟 核心亮点</p><ul><li>完全免费：基于网易云音乐资源爬虫，无付费门槛</li><li>安全稳定：依托网易云底层资源，播放与数据传输更可靠</li><li>全端适配：自适应各类分辨率设备，手机、电脑端体验一致</li><li>开放接口：提供公开 API 接口，支持二次开发（详见「关于我们」页面）</li><li>轻量高效：无冗余功能，聚焦核心需求，加载速度快</li></ul><p>🚀 功能特性</p><ul><li>精准音乐搜索：支持按歌曲名、歌手检索网易云资源，分页展示结果，快速定位目标音乐</li><li>灵活歌单管理：创建、编辑、删除个人歌单，自由添加 / 移除歌曲，支持自定义歌单封面</li><li>安全用户系统：完整的注册、登录与身份验证机制，保障个人歌单与播放数据安全</li><li>历史记录管理：自动记录播放轨迹，支持查看最近播放，内置数据清理工具优化性能</li><li>热门音乐推荐：同步网易云热门歌单，一键发现优质音乐，拓宽听歌边界</li><li>基础播放功能：支持歌曲播放 / 暂停、实时歌词显示、封面查看，满足日常听歌需求</li><li><p>🛠 技术栈选型</p></li><li><p>后端框架：Flask 2.x（Python）</p></li><li>数据库：MySQL 5.7+（自定义工具类操作）</li><li>前端技术：HTML5 + CSS3 + JavaScript</li><li>API 集成：网易云音乐非官方 API（爬虫获取）</li><li>认证机制：Session-based 身份认证</li><li>配置管理：环境变量 + 配置文件双模式</li><li>工具依赖：自定义数据库、文件及 JS 操作工具类</li></ul><p>💡 项目心得</p><p>Flask 框架在 API 开发和小型网站场景中优势明显，开发效率高、配置灵活。但当项目业务复杂度提升或页面交互增多时，其灵活性会带来代码耦合度高的问题。<br>此外，不建议用原生 CSS/JS 开发 SPA 类型网站，后续维护成本高、功能扩展难度大，推荐引入前端框架拆分组件。<br>这是我的第一个 Flask 项目，可能存在潜在 bug，欢迎大家提交 Issues 反馈问题，或提出优化建议，共同完善项目～</p>]]></content:encoded>
      
      
      <category domain="https://blog.itrf.cn/tags/Flask/">Flask</category>
      
      <category domain="https://blog.itrf.cn/tags/Python/">Python</category>
      
      <category domain="https://blog.itrf.cn/tags/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/">原创项目</category>
      
    </item>
    
    <item>
      <title>href属性中/的不同作用</title>
      <link>https://blog.itrf.cn/posts/b272/</link>
      <guid>https://blog.itrf.cn/posts/b272/</guid>
      <pubDate>2025-11-09T04:58:36.000Z</pubDate>
      
      <description>href属性中/的不同作用</description>
      
      
      <enclosure url="https://t.alcy.cc/moez?_r_=47fd5e0e-1939-03e9-8bb6-1435765f72ca" type="image"/>
      
      
      <content:encoded><![CDATA[<h1 id="首部-的作用"><a href="#首部-的作用" class="headerlink" title="首部/的作用"></a>首部/的作用</h1><p>加/ 是根路径相对定位，不加/ 是当前路径相对定位，最终指向的 URL 会完全不同</p><h2 id="1-加-：从网站根目录开始找"><a href="#1-加-：从网站根目录开始找" class="headerlink" title="1. 加 /：从网站根目录开始找"></a>1. 加 /：从网站根目录开始找</h2><p>href=”/xxx” 中的 / 代表网站的根目录（比如 <a href="https://www.example.com/）">https://www.example.com/）</a> ，不管当前页面在哪个层级，都会从根目录出发拼接路径。示例：</p><ul><li>网站根目录：<a href="https://www.example.com/">https://www.example.com/</a></li><li>当前页面 URL：<a href="https://www.example.com/a/b/c.html">https://www.example.com/a/b/c.html</a></li><li>href=”/about” → 最终指向：<a href="https://www.example.com/about">https://www.example.com/about</a></li><li>href=”/static/css/style.css” → 最终指向：<a href="https://www.example.com/static/css/style.css">https://www.example.com/static/css/style.css</a></li></ul><h2 id="2-不加-：从当前页面所在目录开始找"><a href="#2-不加-：从当前页面所在目录开始找" class="headerlink" title="2. 不加 /：从当前页面所在目录开始找"></a>2. 不加 /：从当前页面所在目录开始找</h2><p>href=”xxx” 是相对于当前页面的 URL 路径拼接，相当于 “在当前文件夹里找目标资源”。示例（和上面同个网站、同个当前页面）：</p><ul><li>当前页面 URL：<a href="https://www.example.com/a/b/c.html">https://www.example.com/a/b/c.html</a> （当前目录是 /a/b/）</li><li>href=”about” → 最终指向：<a href="https://www.example.com/a/b/about">https://www.example.com/a/b/about</a></li><li>href=”static/css/style.css” → 最终指向：<a href="https://www.example.com/a/b/static/css/style.css">https://www.example.com/a/b/static/css/style.css</a></li></ul><h1 id="尾部-的作用"><a href="#尾部-的作用" class="headerlink" title="尾部/的作用"></a>尾部/的作用</h1><p>核心区别是是否默认指向 “目录” ，会影响服务器解析和最终跳转的 URL</p><h2 id="1-末尾加-：表示-“这是一个目录”"><a href="#1-末尾加-：表示-“这是一个目录”" class="headerlink" title="1. 末尾加 /：表示 “这是一个目录”"></a>1. 末尾加 /：表示 “这是一个目录”</h2><p>当 URL 末尾带 / 时，浏览器和服务器会默认把它当作目录路径，优先查找目录下的默认索引文件（如 index.html、index.php 等）。示例：</p><ul><li>href=”<a href="https://www.example.com/about/">https://www.example.com/about/</a>“ → 服务器会去 about 目录下找默认索引文件（如 about/index.html）</li><li>若当前页面是 <a href="https://www.example.com/a/">https://www.example.com/a/</a> ， href=”b/“ → 指向 <a href="https://www.example.com/a/b/">https://www.example.com/a/b/</a> （目录）</li></ul><h2 id="2-末尾不加-：表示-“可能是文件，也可能是目录”"><a href="#2-末尾不加-：表示-“可能是文件，也可能是目录”" class="headerlink" title="2. 末尾不加 /：表示 “可能是文件，也可能是目录”"></a>2. 末尾不加 /：表示 “可能是文件，也可能是目录”</h2><p>末尾不带 / 时，服务器会先尝试当作文件路径查找；若找不到对应文件，会自动做 “重定向”—— 在末尾加上 /，再按目录查找默认索引文件。示例：</p><ul><li>href=”<a href="https://www.example.com/about">https://www.example.com/about</a>“ → 服务器先找 about 文件，找不到就重定向到 <a href="https://www.example.com/about/">https://www.example.com/about/</a></li><li>若当前页面是 <a href="https://www.example.com/a/">https://www.example.com/a/</a> ， href=”b” → 先找 a/b 文件，找不到就重定向到 <a href="https://www.example.com/a/b/">https://www.example.com/a/b/</a></li></ul>]]></content:encoded>
      
      
      <category domain="https://blog.itrf.cn/tags/%E9%9B%B6%E7%A2%8E%E7%9F%A5%E8%AF%86/">零碎知识</category>
      
    </item>
    
    <item>
      <title>Linux 常用命令大全</title>
      <link>https://blog.itrf.cn/posts/bf2c/</link>
      <guid>https://blog.itrf.cn/posts/bf2c/</guid>
      <pubDate>2025-10-29T05:06:38.000Z</pubDate>
      
      <description>Linux 常用命令大全</description>
      
      
      <enclosure url="https://t.alcy.cc/moez?_r_=dec991be-30b6-9dd6-7f75-cb94a62f11df" type="image"/>
      
      
      <content:encoded><![CDATA[<p>本文整理了 Linux 系统中<strong>文件操作、权限管理、系统监控、网络操作</strong>等高频命令，覆盖新手入门到日常运维场景，格式清晰可直接查阅。</p><h2 id="一、文件与目录操作命令（最基础高频）"><a href="#一、文件与目录操作命令（最基础高频）" class="headerlink" title="一、文件与目录操作命令（最基础高频）"></a>一、文件与目录操作命令（最基础高频）</h2><h3 id="1-ls-列出目录内容"><a href="#1-ls-列出目录内容" class="headerlink" title="1. ls - 列出目录内容"></a>1. ls - 列出目录内容</h3><p><strong>功能</strong>：列出指定目录下的文件 / 目录，默认列出当前目录。<strong>核心参数</strong>：</p><ul><li><code>-l</code>（长格式）：显示文件权限、所有者、大小、修改时间等详细信息。</li><li><code>-a</code>（all）：显示所有文件（含隐藏文件，以<code>.</code>开头）。</li><li><code>-h</code>（human-readable）：以人类可读格式显示文件大小（如 KB、MB）。</li><li><code>-r</code>（reverse）：反向排序（默认按字母升序）。</li><li><code>-t</code>（time）：按修改时间排序（最新在前）。</li></ul><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">ls</span> -lha  <span class="comment"># 长格式显示当前目录所有文件，含隐藏文件，大小人性化显示</span></span><br><span class="line"><span class="built_in">ls</span> /home/user  <span class="comment"># 列出指定目录/home/user下的内容</span></span><br></pre></td></tr></table></figure></p><h3 id="2-cd-切换工作目录"><a href="#2-cd-切换工作目录" class="headerlink" title="2. cd - 切换工作目录"></a>2. cd - 切换工作目录</h3><p><strong>功能</strong>：切换当前终端的工作目录。<strong>核心用法</strong>：</p><ul><li><code>cd 目录路径</code>：切换到指定目录（绝对路径 / 相对路径）。</li><li><code>cd ~</code> 或 <code>cd</code>：切换到当前用户的家目录（如<code>/home/ubuntu</code>）。</li><li><code>cd ..</code>：切换到上级目录。</li><li><code>cd -</code>：切换到上一次所在的目录。</li></ul><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">cd</span> /etc/nginx  <span class="comment"># 绝对路径切换到nginx配置目录</span></span><br><span class="line"><span class="built_in">cd</span> ../logs  <span class="comment"># 相对路径切换到上级目录的logs子目录</span></span><br><span class="line"><span class="built_in">cd</span> ~/Documents  <span class="comment"># 切换到家目录的Documents文件夹</span></span><br></pre></td></tr></table></figure></p><h3 id="3-pwd-显示当前工作目录"><a href="#3-pwd-显示当前工作目录" class="headerlink" title="3. pwd - 显示当前工作目录"></a>3. pwd - 显示当前工作目录</h3><p><strong>功能</strong>：打印当前终端所在的绝对路径。</p><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">pwd</span>  <span class="comment"># 输出如：/home/user/Documents</span></span><br></pre></td></tr></table></figure></p><h3 id="4-mkdir-创建目录"><a href="#4-mkdir-创建目录" class="headerlink" title="4. mkdir - 创建目录"></a>4. mkdir - 创建目录</h3><p><strong>功能</strong>：创建新目录。<strong>核心参数</strong>：</p><ul><li><code>-p</code>（parents）：递归创建多级目录（父目录不存在时自动创建）。</li><li><code>-m</code>（mode）：创建时指定目录权限（如 755）。</li></ul><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">mkdir</span> <span class="built_in">test</span>  <span class="comment"># 创建单个目录test</span></span><br><span class="line"><span class="built_in">mkdir</span> -p data/logs  <span class="comment"># 递归创建data目录及子目录logs</span></span><br><span class="line"><span class="built_in">mkdir</span> -m 700 private  <span class="comment"># 创建权限为700（仅所有者可读写执行）的目录</span></span><br></pre></td></tr></table></figure></p><h3 id="5-rm-删除文件-目录"><a href="#5-rm-删除文件-目录" class="headerlink" title="5. rm - 删除文件 / 目录"></a>5. rm - 删除文件 / 目录</h3><p><strong>功能</strong>：删除文件或目录，慎用（无回收站，删除后难恢复）。<strong>核心参数</strong>：</p><ul><li><code>-f</code>（force）：强制删除（忽略不存在的文件，不提示）。</li><li><code>-r</code>（recursive）：递归删除目录及目录内所有内容（删除目录必须用）。</li><li><code>-i</code>（interactive）：删除前提示确认（默认部分系统启用）。</li></ul><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">rm</span> file.txt  <span class="comment"># 删除单个文件file.txt</span></span><br><span class="line"><span class="built_in">rm</span> -rf <span class="built_in">dir</span>/  <span class="comment"># 强制递归删除dir目录及所有内容（高危命令，谨慎使用）</span></span><br><span class="line"><span class="built_in">rm</span> -i *.<span class="built_in">log</span>  <span class="comment"># 删除所有.log文件，删除前逐一提示</span></span><br></pre></td></tr></table></figure></p><h3 id="6-cp-复制文件-目录"><a href="#6-cp-复制文件-目录" class="headerlink" title="6. cp - 复制文件 / 目录"></a>6. cp - 复制文件 / 目录</h3><p><strong>功能</strong>：复制文件或目录到指定位置。<strong>核心参数</strong>：</p><ul><li><code>-r</code>（recursive）：递归复制目录（复制目录必须用）。</li><li><code>-p</code>（preserve）：保留文件的权限、所有者、修改时间等属性。</li><li><code>-f</code>（force）：强制覆盖目标文件（无需提示）。</li><li><code>-a</code>（archive）：等价于<code>-dR --preserve=all</code>，适用于备份（保留所有属性 + 递归复制）。</li></ul><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">cp</span> file.txt /home/user/  <span class="comment"># 复制file.txt到/home/user目录下</span></span><br><span class="line"><span class="built_in">cp</span> -r <span class="built_in">dir</span>/ /backup/  <span class="comment"># 递归复制dir目录到/backup目录下</span></span><br><span class="line"><span class="built_in">cp</span> -a docs/ docs_backup/  <span class="comment"># 备份docs目录，保留所有属性</span></span><br></pre></td></tr></table></figure></p><h3 id="7-mv-移动-重命名文件-目录"><a href="#7-mv-移动-重命名文件-目录" class="headerlink" title="7. mv - 移动 / 重命名文件 / 目录"></a>7. mv - 移动 / 重命名文件 / 目录</h3><p><strong>功能</strong>：移动文件 / 目录到指定位置，或重命名文件 / 目录。<strong>核心参数</strong>：</p><ul><li><code>-f</code>（force）：强制覆盖目标文件（无提示）。</li><li><code>-i</code>（interactive）：覆盖前提示确认。</li></ul><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">mv</span> file.txt newfile.txt  <span class="comment"># 重命名file.txt为newfile.txt</span></span><br><span class="line"><span class="built_in">mv</span> data/ /home/user/  <span class="comment"># 移动data目录到/home/user目录下</span></span><br><span class="line"><span class="built_in">mv</span> -f old.log /tmp/  <span class="comment"># 强制移动old.log到/tmp目录，覆盖已存在文件</span></span><br></pre></td></tr></table></figure></p><h3 id="8-touch-创建空文件-修改文件时间戳"><a href="#8-touch-创建空文件-修改文件时间戳" class="headerlink" title="8. touch - 创建空文件 / 修改文件时间戳"></a>8. touch - 创建空文件 / 修改文件时间戳</h3><p><strong>功能</strong>：创建空白文件，或更新文件的访问时间、修改时间（默认两者都更新）。<strong>核心参数</strong>：</p><ul><li><code>-d</code>（date）：指定时间戳（如<code>2024-01-01 10:00</code>）。</li><li><code>-m</code>（modify）：仅更新修改时间。</li><li><code>-a</code>（access）：仅更新访问时间。</li></ul><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">touch</span> test.txt  <span class="comment"># 创建空文件test.txt</span></span><br><span class="line"><span class="built_in">touch</span> -d <span class="string">&quot;2024-05-20 14:30&quot;</span> old.txt  <span class="comment"># 修改old.txt的时间戳为指定时间</span></span><br><span class="line"><span class="built_in">touch</span> -a readme.md  <span class="comment"># 仅更新readme.md的访问时间</span></span><br></pre></td></tr></table></figure></p><h3 id="9-cat-查看文件内容"><a href="#9-cat-查看文件内容" class="headerlink" title="9. cat - 查看文件内容"></a>9. cat - 查看文件内容</h3><p><strong>功能</strong>：连接并打印文件内容（适合查看小文件）。<strong>核心参数</strong>：</p><ul><li><code>-n</code>（number）：显示行号。</li><li><code>-b</code>（number-nonblank）：仅对非空行显示行号。</li><li><code>-T</code>（show-tabs）：显示制表符（Tab）为<code>^I</code>。</li></ul><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">cat</span> /etc/hosts  <span class="comment"># 查看hosts文件内容</span></span><br><span class="line"><span class="built_in">cat</span> -n test.txt  <span class="comment"># 显示test.txt内容并标注行号</span></span><br><span class="line"><span class="built_in">cat</span> file1.txt file2.txt &gt; file3.txt  <span class="comment"># 合并file1和file2的内容到file3（覆盖）</span></span><br></pre></td></tr></table></figure></p><h3 id="10-more-less-分页查看大文件"><a href="#10-more-less-分页查看大文件" class="headerlink" title="10. more/less - 分页查看大文件"></a>10. more/less - 分页查看大文件</h3><p><strong>功能</strong>：分页显示文件内容（适合大文件，避免内容刷屏）。<strong>核心区别</strong>：</p><ul><li><code>more</code>：仅支持向下滚动（空格翻页，Enter 换行，q 退出）。</li><li><code>less</code>：支持上下滚动、搜索（更灵活，<code>/关键词</code>搜索，<code>n</code>下一个，<code>q</code>退出）。</li></ul><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">more /var/log/syslog  <span class="comment"># 分页查看系统日志（仅向下翻）</span></span><br><span class="line">less /etc/passwd  <span class="comment"># 分页查看用户配置文件（支持上下滚动和搜索）</span></span><br></pre></td></tr></table></figure></p><h3 id="11-head-tail-查看文件首尾内容"><a href="#11-head-tail-查看文件首尾内容" class="headerlink" title="11. head/tail - 查看文件首尾内容"></a>11. head/tail - 查看文件首尾内容</h3><p><strong>功能</strong>：<code>head</code>查看文件开头部分，<code>tail</code>查看文件结尾部分。<strong>核心参数</strong>：</p><ul><li><code>-n N</code>：显示前 N 行（head）或后 N 行（tail），默认 10 行。</li><li><code>-f</code>（follow）：实时跟踪文件更新（tail 专属，适合查看日志）。</li></ul><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">head</span> -5 /etc/passwd  <span class="comment"># 查看passwd文件前5行</span></span><br><span class="line"><span class="built_in">tail</span> -10 /var/log/nginx/access.log  <span class="comment"># 查看nginx访问日志最后10行</span></span><br><span class="line"><span class="built_in">tail</span> -f /var/log/syslog  <span class="comment"># 实时跟踪系统日志更新（按Ctrl+C退出）</span></span><br></pre></td></tr></table></figure></p><h3 id="12-find-查找文件-目录"><a href="#12-find-查找文件-目录" class="headerlink" title="12. find - 查找文件 / 目录"></a>12. find - 查找文件 / 目录</h3><p><strong>功能</strong>：按路径、名称、大小、时间等条件查找文件 / 目录。<strong>核心参数</strong>：</p><ul><li><code>-name &quot;文件名&quot;</code>：按名称查找（支持通配符<code>*</code>、<code>?</code>）。</li><li><code>-path &quot;路径&quot;</code>：按路径查找。</li><li><code>-size [+/-]大小</code>：按大小查找（如<code>+100M</code>大于 100MB，<code>-10K</code>小于 10KB）。</li><li><code>-mtime [+/-]天数</code>：按修改时间查找（如<code>+7</code>修改时间超过 7 天，<code>-1</code>24 小时内修改）。</li><li><code>-type f/d</code>：指定查找类型（f 文件，d 目录）。</li><li><code>-exec 命令 {} \;</code>：对查找结果执行指定命令（<code>{}</code>代指查找结果）。</li></ul><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">find /home -name <span class="string">&quot;*.txt&quot;</span>  <span class="comment"># 在/home目录下查找所有.txt文件</span></span><br><span class="line">find /etc -<span class="built_in">type</span> d -name <span class="string">&quot;nginx&quot;</span>  <span class="comment"># 在/etc目录下查找名为nginx的目录</span></span><br><span class="line">find /var/log -size +50M  <span class="comment"># 在/var/log目录下查找大于50MB的文件</span></span><br><span class="line">find /tmp -mtime +3 -<span class="built_in">exec</span> <span class="built_in">rm</span> -rf &#123;&#125; \;  <span class="comment"># 删除/tmp目录下3天前修改的文件/目录</span></span><br></pre></td></tr></table></figure></p><h3 id="13-grep-文本搜索工具"><a href="#13-grep-文本搜索工具" class="headerlink" title="13. grep - 文本搜索工具"></a>13. grep - 文本搜索工具</h3><p><strong>功能</strong>：在文件或命令输出中搜索匹配的字符串 / 正则表达式。<strong>核心参数</strong>：</p><ul><li><code>-i</code>（ignore-case）：忽略大小写。</li><li><code>-n</code>（line-number）：显示匹配行的行号。</li><li><code>-v</code>（invert-match）：反向匹配（显示不包含关键词的行）。</li><li><code>-r</code>（recursive）：递归搜索目录下所有文件。</li><li><code>-E</code>（extended-regexp）：支持扩展正则表达式（无需转义<code>+</code>、<code>|</code>等）。</li></ul><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">grep <span class="string">&quot;error&quot;</span> /var/log/syslog  <span class="comment"># 在系统日志中搜索&quot;error&quot;字符串</span></span><br><span class="line">grep -<span class="keyword">in</span> <span class="string">&quot;warning&quot;</span> /etc/nginx/nginx.conf  <span class="comment"># 忽略大小写搜索nginx配置文件中的&quot;warning&quot;，显示行号</span></span><br><span class="line">grep -r <span class="string">&quot;test&quot;</span> /home/user  <span class="comment"># 递归搜索/home/user目录下所有文件中的&quot;test&quot;</span></span><br><span class="line">ps aux | grep <span class="string">&quot;nginx&quot;</span>  <span class="comment"># 结合管道，搜索nginx进程</span></span><br></pre></td></tr></table></figure></p><h2 id="二、文件权限与用户管理命令"><a href="#二、文件权限与用户管理命令" class="headerlink" title="二、文件权限与用户管理命令"></a>二、文件权限与用户管理命令</h2><h3 id="1-chmod-修改文件-目录权限"><a href="#1-chmod-修改文件-目录权限" class="headerlink" title="1. chmod - 修改文件 / 目录权限"></a>1. chmod - 修改文件 / 目录权限</h3><p><strong>功能</strong>：修改文件或目录的访问权限（所有者、组用户、其他用户的读 / 写 / 执行权限）。<strong>权限表示</strong>：</p><ul><li><strong>数字法</strong>（常用）：<code>r=4</code>、<code>w=2</code>、<code>x=1</code>，权限组合为三者之和（如<code>755</code>= 所有者 rwx，组用户 rx，其他用户 rx）。</li><li><strong>符号法</strong>：<code>u</code>（所有者）、<code>g</code>（组用户）、<code>o</code>（其他用户）、<code>a</code>（所有用户）；<code>+</code>（添加权限）、<code>-</code>（移除权限）、<code>=</code>（设置权限）。</li></ul><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">chmod</span> 755 script.sh  <span class="comment"># 设为所有者可读写执行，其他用户只读执行</span></span><br><span class="line"><span class="built_in">chmod</span> 644 file.txt  <span class="comment"># 设为所有者读写，其他用户只读（文件常用权限）</span></span><br><span class="line"><span class="built_in">chmod</span> u+x test.sh  <span class="comment"># 给所有者添加执行权限</span></span><br><span class="line"><span class="built_in">chmod</span> g-w <span class="built_in">dir</span>/  <span class="comment"># 给组用户移除写权限</span></span><br></pre></td></tr></table></figure></p><h3 id="2-chown-修改文件-目录所有者"><a href="#2-chown-修改文件-目录所有者" class="headerlink" title="2. chown - 修改文件 / 目录所有者"></a>2. chown - 修改文件 / 目录所有者</h3><p><strong>功能</strong>：修改文件或目录的所有者（需 root 权限或文件所有者）。<strong>核心用法</strong>：<code>chown [所有者][:组] 文件/目录</code></p><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">sudo</span> <span class="built_in">chown</span> user:user file.txt  <span class="comment"># 将file.txt的所有者和组改为user</span></span><br><span class="line"><span class="built_in">sudo</span> <span class="built_in">chown</span> root /home/user/test.sh  <span class="comment"># 将test.sh的所有者改为root</span></span><br><span class="line"><span class="built_in">sudo</span> <span class="built_in">chown</span> -R www-data:www-data /var/www  <span class="comment"># 递归修改/var/www目录的所有者为www-data（web服务常用）</span></span><br></pre></td></tr></table></figure></p><h3 id="3-chgrp-修改文件-目录所属组"><a href="#3-chgrp-修改文件-目录所属组" class="headerlink" title="3. chgrp - 修改文件 / 目录所属组"></a>3. chgrp - 修改文件 / 目录所属组</h3><p><strong>功能</strong>：单独修改文件或目录的所属组（<code>chown</code>可替代，但该命令更专注）。</p><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">chgrp</span> <span class="built_in">users</span> file.txt  <span class="comment"># 将file.txt的所属组改为users</span></span><br><span class="line"><span class="built_in">chgrp</span> -R staff /home/shared  <span class="comment"># 递归修改/shared目录的所属组为staff</span></span><br></pre></td></tr></table></figure></p><h3 id="4-useradd-创建新用户"><a href="#4-useradd-创建新用户" class="headerlink" title="4. useradd - 创建新用户"></a>4. useradd - 创建新用户</h3><p><strong>功能</strong>：创建 Linux 系统用户（需 root 权限）。<strong>核心参数</strong>：</p><ul><li><code>-m</code>（create-home）：自动创建用户家目录（如<code>/home/用户名</code>）。</li><li><code>-s shell路径</code>：指定用户登录 shell（如<code>/bin/bash</code>，默认<code>/bin/sh</code>）。</li><li><code>-g 组名</code>：指定用户初始组。</li><li><code>-G 组名1,组名2</code>：指定用户附加组。</li></ul><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">sudo</span> useradd -m -s /bin/bash newuser  <span class="comment"># 创建新用户newuser，自动创建家目录，登录shell为bash</span></span><br><span class="line"><span class="built_in">sudo</span> useradd -g staff -G <span class="built_in">sudo</span> newuser  <span class="comment"># 创建用户newuser，初始组为staff，附加组为sudo（获取管理员权限）</span></span><br></pre></td></tr></table></figure></p><h3 id="5-passwd-修改用户密码"><a href="#5-passwd-修改用户密码" class="headerlink" title="5. passwd - 修改用户密码"></a>5. passwd - 修改用户密码</h3><p><strong>功能</strong>：修改当前用户或其他用户的密码（修改其他用户需 root 权限）。<strong>核心参数</strong>：</p><ul><li><code>-l</code>（lock）：锁定用户（禁止登录）。</li><li><code>-u</code>（unlock）：解锁用户。</li><li><code>-d</code>（delete）：删除用户密码（登录无需密码）。</li></ul><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">passwd  <span class="comment"># 修改当前用户密码（输入时密码不显示）</span></span><br><span class="line"><span class="built_in">sudo</span> passwd newuser  <span class="comment"># 管理员修改newuser的密码</span></span><br><span class="line"><span class="built_in">sudo</span> passwd -l newuser  <span class="comment"># 锁定newuser用户</span></span><br></pre></td></tr></table></figure></p><h3 id="6-userdel-删除用户"><a href="#6-userdel-删除用户" class="headerlink" title="6. userdel - 删除用户"></a>6. userdel - 删除用户</h3><p><strong>功能</strong>：删除系统用户（需 root 权限）。<strong>核心参数</strong>：</p><ul><li><code>-r</code>（remove）：递归删除用户家目录和邮件目录（默认仅删除用户账号）。</li></ul><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">sudo</span> userdel -r newuser  <span class="comment"># 彻底删除newuser用户及家目录</span></span><br></pre></td></tr></table></figure></p><h3 id="7-groupadd-groupdel-组管理"><a href="#7-groupadd-groupdel-组管理" class="headerlink" title="7. groupadd/groupdel - 组管理"></a>7. groupadd/groupdel - 组管理</h3><p><strong>功能</strong>：<code>groupadd</code>创建用户组，<code>groupdel</code>删除用户组（需 root 权限）。</p><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">sudo</span> groupadd dev  <span class="comment"># 创建名为dev的用户组</span></span><br><span class="line"><span class="built_in">sudo</span> groupdel dev  <span class="comment"># 删除dev用户组</span></span><br></pre></td></tr></table></figure></p><h3 id="8-id-whoami-w-查看用户信息"><a href="#8-id-whoami-w-查看用户信息" class="headerlink" title="8. id/whoami/w - 查看用户信息"></a>8. id/whoami/w - 查看用户信息</h3><ul><li><code>id</code>：显示当前用户的 UID、GID 及所属组。</li><li><code>whoami</code>：显示当前登录用户名。</li><li><code>w</code>：显示当前系统所有登录用户及他们的操作。</li></ul><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">id</span>  <span class="comment"># 输出如：uid=1000(user) gid=1000(user) groups=1000(user),4(adm),24(cdrom)...</span></span><br><span class="line"><span class="built_in">whoami</span>  <span class="comment"># 输出当前用户名，如user</span></span><br><span class="line">w  <span class="comment"># 显示所有登录用户及进程信息</span></span><br></pre></td></tr></table></figure></p><h2 id="三、系统管理与进程命令"><a href="#三、系统管理与进程命令" class="headerlink" title="三、系统管理与进程命令"></a>三、系统管理与进程命令</h2><h3 id="1-ps-查看进程状态"><a href="#1-ps-查看进程状态" class="headerlink" title="1. ps - 查看进程状态"></a>1. ps - 查看进程状态</h3><p><strong>功能</strong>：列出当前系统的进程信息。<strong>核心参数</strong>：</p><ul><li><code>aux</code>：显示所有用户的所有进程（最常用组合）。</li><li><code>-ef</code>：显示进程的父进程 ID（PPID）、进程 ID（PID）等详细信息。</li><li><code>-u 用户名</code>：仅显示指定用户的进程。</li></ul><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">ps aux  <span class="comment"># 查看所有进程（USER用户名、PID进程ID、%CPU CPU使用率、%MEM内存使用率、COMMAND命令）</span></span><br><span class="line">ps aux | grep <span class="string">&quot;nginx&quot;</span>  <span class="comment"># 查看nginx相关进程</span></span><br><span class="line">ps -ef | grep <span class="string">&quot;python&quot;</span>  <span class="comment"># 查看python进程及父进程ID</span></span><br></pre></td></tr></table></figure></p><h3 id="2-top-实时监控系统资源与进程"><a href="#2-top-实时监控系统资源与进程" class="headerlink" title="2. top - 实时监控系统资源与进程"></a>2. top - 实时监控系统资源与进程</h3><p><strong>功能</strong>：实时动态显示系统 CPU、内存、进程等资源使用情况（默认每 3 秒刷新）。<strong>交互快捷键</strong>：</p><ul><li><code>P</code>：按 CPU 使用率排序（默认）。</li><li><code>M</code>：按内存使用率排序。</li><li><code>N</code>：按进程 ID（PID）排序。</li><li><code>k</code>：终止指定 PID 的进程。</li><li><code>q</code>：退出 top。</li></ul><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">top  <span class="comment"># 启动实时监控</span></span><br><span class="line">top -u user  <span class="comment"># 仅监控用户user的进程</span></span><br></pre></td></tr></table></figure></p><h3 id="3-kill-终止进程"><a href="#3-kill-终止进程" class="headerlink" title="3. kill - 终止进程"></a>3. kill - 终止进程</h3><p><strong>功能</strong>：向进程发送信号，实现终止、重启等操作（需知道进程 PID，root 可终止任意进程）。<strong>核心信号</strong>：</p><ul><li><code>-15</code>（SIGTERM，默认）：优雅终止进程（允许进程清理资源）。</li><li><code>-9</code>（SIGKILL）：强制终止进程（立即杀死，无清理，慎用）。</li><li><code>-1</code>（SIGHUP）：重启进程（部分服务如 nginx、apache 支持）。</li></ul><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">kill</span> 1234  <span class="comment"># 向PID为1234的进程发送SIGTERM信号（优雅终止）</span></span><br><span class="line"><span class="built_in">kill</span> -9 1234  <span class="comment"># 强制终止PID为1234的进程</span></span><br><span class="line"><span class="built_in">kill</span> -1 8080  <span class="comment"># 重启PID为8080的服务进程</span></span><br><span class="line">killall nginx  <span class="comment"># 终止所有nginx进程（按进程名终止，需安装psmisc包）</span></span><br></pre></td></tr></table></figure></p><h3 id="4-systemctl-系统服务管理（Systemd-系统）"><a href="#4-systemctl-系统服务管理（Systemd-系统）" class="headerlink" title="4. systemctl - 系统服务管理（Systemd 系统）"></a>4. systemctl - 系统服务管理（Systemd 系统）</h3><p><strong>功能</strong>：管理系统服务（启动、停止、重启、设置开机自启，适用于 Ubuntu 16.04+、CentOS 7+）。<strong>核心用法</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">systemctl start 服务名    <span class="comment"># 启动服务</span></span><br><span class="line">systemctl stop 服务名     <span class="comment"># 停止服务</span></span><br><span class="line">systemctl restart 服务名  <span class="comment"># 重启服务</span></span><br><span class="line">systemctl status 服务名   <span class="comment"># 查看服务状态</span></span><br><span class="line">systemctl <span class="built_in">enable</span> 服务名   <span class="comment"># 设置开机自启</span></span><br><span class="line">systemctl <span class="built_in">disable</span> 服务名  <span class="comment"># 取消开机自启</span></span><br><span class="line">systemctl list-unit-files --<span class="built_in">type</span>=service  <span class="comment"># 列出所有服务及开机自启状态</span></span><br></pre></td></tr></table></figure></p><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">sudo</span> systemctl start nginx  <span class="comment"># 启动nginx服务</span></span><br><span class="line"><span class="built_in">sudo</span> systemctl stop mysql  <span class="comment"># 停止mysql服务</span></span><br><span class="line"><span class="built_in">sudo</span> systemctl restart sshd  <span class="comment"># 重启ssh服务</span></span><br><span class="line"><span class="built_in">sudo</span> systemctl status docker  <span class="comment"># 查看docker服务状态</span></span><br><span class="line"><span class="built_in">sudo</span> systemctl <span class="built_in">enable</span> nginx  <span class="comment"># 设置nginx开机自启</span></span><br></pre></td></tr></table></figure></p><h3 id="5-df-查看磁盘空间使用情况"><a href="#5-df-查看磁盘空间使用情况" class="headerlink" title="5. df - 查看磁盘空间使用情况"></a>5. df - 查看磁盘空间使用情况</h3><p><strong>功能</strong>：显示文件系统的磁盘空间使用情况（总容量、已用、可用、挂载点）。<strong>核心参数</strong>：</p><ul><li><code>-h</code>（human-readable）：人性化显示大小（KB/MB/GB）。</li><li><code>-T</code>（type）：显示文件系统类型（如 ext4、xfs）。</li></ul><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">df</span> -h  <span class="comment"># 查看所有挂载的文件系统磁盘使用情况</span></span><br><span class="line"><span class="built_in">df</span> -h /home  <span class="comment"># 查看/home目录所在文件系统的磁盘使用情况</span></span><br><span class="line"><span class="built_in">df</span> -T  <span class="comment"># 显示文件系统类型</span></span><br></pre></td></tr></table></figure></p><h3 id="6-du-查看目录-文件磁盘占用"><a href="#6-du-查看目录-文件磁盘占用" class="headerlink" title="6. du - 查看目录 / 文件磁盘占用"></a>6. du - 查看目录 / 文件磁盘占用</h3><p><strong>功能</strong>：计算文件或目录占用的磁盘空间（与<code>df</code>区别：<code>df</code>看文件系统整体，<code>du</code>看具体文件 / 目录）。<strong>核心参数</strong>：</p><ul><li><code>-h</code>：人性化显示大小。</li><li><code>-s</code>（summarize）：仅显示总大小（不显示子目录详情）。</li><li><code>-c</code>（total）：显示总计大小。</li></ul><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">du</span> -sh /home/user  <span class="comment"># 查看/home/user目录总占用空间</span></span><br><span class="line"><span class="built_in">du</span> -h /var/log/  <span class="comment"># 查看/var/log目录及子目录的占用详情</span></span><br><span class="line"><span class="built_in">du</span> -hc *.txt  <span class="comment"># 查看所有.txt文件的占用大小，并显示总计</span></span><br></pre></td></tr></table></figure></p><h3 id="7-free-查看内存使用情况"><a href="#7-free-查看内存使用情况" class="headerlink" title="7. free - 查看内存使用情况"></a>7. free - 查看内存使用情况</h3><p><strong>功能</strong>：显示系统内存（物理内存、交换分区）的使用情况。<strong>核心参数</strong>：</p><ul><li><code>-h</code>：人性化显示大小。</li><li><code>-m</code>：以 MB 为单位显示（默认 KB）。</li><li><code>-g</code>：以 GB 为单位显示。</li></ul><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">free -h  <span class="comment"># 人性化显示内存使用情况</span></span><br><span class="line">free -m  <span class="comment"># 以MB为单位显示</span></span><br></pre></td></tr></table></figure></p><h3 id="8-uname-查看系统信息"><a href="#8-uname-查看系统信息" class="headerlink" title="8. uname - 查看系统信息"></a>8. uname - 查看系统信息</h3><p><strong>功能</strong>：显示系统内核版本、主机名、硬件架构等信息。<strong>核心参数</strong>：</p><ul><li><code>-a</code>（all）：显示所有系统信息（最常用）。</li><li><code>-r</code>：仅显示内核版本。</li><li><code>-n</code>：仅显示主机名。</li></ul><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">uname</span> -a  <span class="comment"># 输出如：Linux ubuntu 5.4.0-150-generic #167-Ubuntu SMP Wed May 24 15:51:08 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux</span></span><br><span class="line"><span class="built_in">uname</span> -r  <span class="comment"># 输出内核版本：5.4.0-150-generic</span></span><br></pre></td></tr></table></figure></p><h3 id="9-hostname-查看-修改主机名"><a href="#9-hostname-查看-修改主机名" class="headerlink" title="9. hostname - 查看 / 修改主机名"></a>9. hostname - 查看 / 修改主机名</h3><p><strong>功能</strong>：显示或临时修改系统主机名（永久修改需编辑配置文件）。</p><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">hostname  <span class="comment"># 显示当前主机名</span></span><br><span class="line"><span class="built_in">sudo</span> hostname new-hostname  <span class="comment"># 临时修改主机名为new-hostname（重启失效）</span></span><br><span class="line"><span class="built_in">sudo</span> hostnamectl set-hostname new-hostname  <span class="comment"># 永久修改主机名（Systemd系统，无需重启）</span></span><br></pre></td></tr></table></figure></p><h3 id="10-date-查看-修改系统时间"><a href="#10-date-查看-修改系统时间" class="headerlink" title="10. date - 查看 / 修改系统时间"></a>10. date - 查看 / 修改系统时间</h3><p><strong>功能</strong>：显示或修改系统日期和时间。</p><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">date</span>  <span class="comment"># 显示当前系统时间（如：Wed Oct 18 15:30:22 CST 2023）</span></span><br><span class="line"><span class="built_in">date</span> <span class="string">&quot;+%Y-%m-%d %H:%M:%S&quot;</span>  <span class="comment"># 自定义格式显示（年-月-日 时:分:秒）</span></span><br><span class="line"><span class="built_in">sudo</span> <span class="built_in">date</span> -s <span class="string">&quot;2024-01-01 10:00:00&quot;</span>  <span class="comment"># 修改系统时间为指定时间</span></span><br></pre></td></tr></table></figure></p><h2 id="四、网络操作命令"><a href="#四、网络操作命令" class="headerlink" title="四、网络操作命令"></a>四、网络操作命令</h2><h3 id="1-ifconfig-ip-网络接口配置"><a href="#1-ifconfig-ip-网络接口配置" class="headerlink" title="1. ifconfig/ip - 网络接口配置"></a>1. ifconfig/ip - 网络接口配置</h3><p><strong>功能</strong>：查看或配置网络接口（IP 地址、子网掩码、MAC 地址等）。</p><ul><li><code>ifconfig</code>：传统命令（部分系统需安装 net-tools 包）。</li><li><code>ip</code>：新一代命令（功能更全，推荐使用）。</li></ul><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">ifconfig  <span class="comment"># 查看所有网络接口信息</span></span><br><span class="line">ip addr  <span class="comment"># 查看IP地址（等价于ifconfig）</span></span><br><span class="line">ip <span class="built_in">link</span> <span class="built_in">set</span> eth0 up/down  <span class="comment"># 启用/禁用eth0网卡</span></span><br><span class="line"><span class="built_in">sudo</span> ifconfig eth0 192.168.1.100 netmask 255.255.255.0  <span class="comment"># 临时设置eth0的IP地址</span></span><br></pre></td></tr></table></figure></p><h3 id="2-ping-测试网络连通性"><a href="#2-ping-测试网络连通性" class="headerlink" title="2. ping - 测试网络连通性"></a>2. ping - 测试网络连通性</h3><p><strong>功能</strong>：向目标主机发送 ICMP 请求包，测试网络是否可达。<strong>核心参数</strong>：</p><ul><li><code>-c 次数</code>：指定发送包的次数（默认无限发送，Ctrl+C 终止）。</li><li><code>-i 间隔</code>：指定发送间隔（默认 1 秒）。</li><li><code>-s 大小</code>：指定数据包大小。</li></ul><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">ping www.baidu.com  <span class="comment"># 测试与百度的连通性（无限发送）</span></span><br><span class="line">ping -c 4 192.168.1.1  <span class="comment"># 向网关发送4个数据包测试连通性</span></span><br></pre></td></tr></table></figure></p><h3 id="3-netstat-ss-查看网络连接状态"><a href="#3-netstat-ss-查看网络连接状态" class="headerlink" title="3. netstat/ss - 查看网络连接状态"></a>3. netstat/ss - 查看网络连接状态</h3><p><strong>功能</strong>：显示系统的网络连接、端口监听、进程关联等信息。</p><ul><li><p><code>netstat</code>：传统命令（需安装 net-tools 包）。</p></li><li><p><code>ss</code>：新一代命令（更快、功能更全）。<br>核心参数：</p></li><li><p><code>-t</code>（tcp）：仅显示 TCP 连接。</p></li><li><p><code>-u</code>（udp）：仅显示 UDP 连接。</p></li><li><p><code>-l</code>（listen）：仅显示监听状态的端口。</p></li><li><p><code>-n</code>（numeric）：显示 IP 和端口号（不解析域名）。</p></li><li><p><code>-p</code>（program）：显示关联的进程 PID 和名称（需 root 权限）。</p></li></ul><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">netstat -tlnp  <span class="comment"># 查看所有监听的TCP端口及关联进程</span></span><br><span class="line">ss -ulnp  <span class="comment"># 查看所有监听的UDP端口及关联进程</span></span><br><span class="line">netstat -an | grep 80  <span class="comment"># 查看80端口的连接状态</span></span><br></pre></td></tr></table></figure></p><h3 id="4-curl-wget-网络下载工具"><a href="#4-curl-wget-网络下载工具" class="headerlink" title="4. curl/wget - 网络下载工具"></a>4. curl/wget - 网络下载工具</h3><ul><li><strong>curl</strong>：多功能网络工具，支持 HTTP/HTTPS/FTP 等协议，可下载文件、发送请求。</li><li><strong>wget</strong>：专注于文件下载（支持断点续传、后台下载）。</li></ul><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># curl用法</span></span><br><span class="line">curl https://www.baidu.com  <span class="comment"># 访问百度并输出HTML内容</span></span><br><span class="line">curl -O https://example.com/file.zip  <span class="comment"># 下载文件并保存为原文件名</span></span><br><span class="line">curl -L https://example.com/redirect  <span class="comment"># 跟随重定向下载</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># wget用法</span></span><br><span class="line">wget https://example.com/file.zip  <span class="comment"># 下载文件</span></span><br><span class="line">wget -c https://example.com/large.zip  <span class="comment"># 断点续传下载大文件</span></span><br><span class="line">wget -b https://example.com/bigfile.iso  <span class="comment"># 后台下载（日志保存到wget-log）</span></span><br></pre></td></tr></table></figure></p><h3 id="5-scp-远程文件复制"><a href="#5-scp-远程文件复制" class="headerlink" title="5. scp - 远程文件复制"></a>5. scp - 远程文件复制</h3><p><strong>功能</strong>：通过 SSH 协议在本地和远程主机之间复制文件 / 目录（加密传输）。<strong>核心参数</strong>：</p><ul><li><code>-r</code>：递归复制目录。</li><li><code>-P 端口</code>：指定远程主机 SSH 端口（默认 22）。</li></ul><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 本地文件复制到远程主机</span></span><br><span class="line">scp localfile.txt user@remote-ip:/home/user/</span><br><span class="line"></span><br><span class="line"><span class="comment"># 远程文件复制到本地</span></span><br><span class="line">scp user@remote-ip:/home/user/remotefile.txt ./</span><br><span class="line"></span><br><span class="line"><span class="comment"># 复制目录（加-r）</span></span><br><span class="line">scp -r localdir/ user@remote-ip:/home/user/</span><br></pre></td></tr></table></figure></p><h3 id="6-ssh-远程登录"><a href="#6-ssh-远程登录" class="headerlink" title="6. ssh - 远程登录"></a>6. ssh - 远程登录</h3><p><strong>功能</strong>：通过 SSH 协议远程登录 Linux 主机（加密传输，安全）。<strong>核心参数</strong>：</p><ul><li><code>-p 端口</code>：指定远程主机 SSH 端口（默认 22）。</li><li><code>-X</code>：启用 X11 转发（远程图形界面）。</li></ul><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">ssh user@remote-ip  <span class="comment"># 登录远程主机（默认端口22）</span></span><br><span class="line">ssh -p 2222 user@remote-ip  <span class="comment"># 登录指定端口2222的远程主机</span></span><br><span class="line">ssh -X user@remote-ip  <span class="comment"># 登录并启用图形界面转发</span></span><br></pre></td></tr></table></figure></p><h2 id="五、压缩与解压命令"><a href="#五、压缩与解压命令" class="headerlink" title="五、压缩与解压命令"></a>五、压缩与解压命令</h2><h3 id="1-tar-归档-压缩工具（最常用）"><a href="#1-tar-归档-压缩工具（最常用）" class="headerlink" title="1. tar - 归档 / 压缩工具（最常用）"></a>1. tar - 归档 / 压缩工具（最常用）</h3><p><strong>功能</strong>：将多个文件 / 目录归档为一个 tar 包，可结合 gzip/bzip2/xz 压缩。<strong>核心参数</strong>（记忆口诀：打包压缩<code>czf</code>，解压<code>xzf</code>）：</p><ul><li><code>-c</code>（create）：创建归档包。</li><li><code>-x</code>（extract）：解压归档包。</li><li><code>-z</code>（gzip）：用 gzip 压缩 / 解压（后缀<code>.tar.gz</code>或<code>.tgz</code>）。</li><li><code>-j</code>（bzip2）：用 bzip2 压缩 / 解压（后缀<code>.tar.bz2</code>）。</li><li><code>-J</code>（xz）：用 xz 压缩 / 解压（后缀<code>.tar.xz</code>）。</li><li><code>-f</code>（file）：指定归档文件名（必须放在参数最后）。</li><li><code>-v</code>（verbose）：显示压缩 / 解压过程。</li><li><code>-C</code>（directory）：指定解压目录（默认当前目录）。</li></ul><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 压缩（tar.gz格式）</span></span><br><span class="line">tar -czvf file.tar.gz dir1/ file1.txt  <span class="comment"># 将dir1和file1.txt压缩为file.tar.gz</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 解压（tar.gz格式）</span></span><br><span class="line">tar -xzvf file.tar.gz  <span class="comment"># 解压到当前目录</span></span><br><span class="line">tar -xzvf file.tar.gz -C /home/user/  <span class="comment"># 解压到/home/user目录</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 压缩（tar.bz2格式）</span></span><br><span class="line">tar -cjvf file.tar.bz2 dir1/</span><br><span class="line"></span><br><span class="line"><span class="comment"># 解压（tar.xz格式）</span></span><br><span class="line">tar -xJvf file.tar.xz</span><br></pre></td></tr></table></figure></p><h3 id="2-zip-unzip-ZIP-格式压缩-解压"><a href="#2-zip-unzip-ZIP-格式压缩-解压" class="headerlink" title="2. zip/unzip - ZIP 格式压缩 / 解压"></a>2. zip/unzip - ZIP 格式压缩 / 解压</h3><p><strong>功能</strong>：处理 ZIP 格式的压缩文件（跨平台兼容，Windows/Linux 通用）。</p><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 压缩（需安装zip包）</span></span><br><span class="line">zip -r file.zip dir1/ file1.txt  <span class="comment"># 递归压缩dir1和file1.txt为file.zip</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 解压（需安装unzip包）</span></span><br><span class="line">unzip file.zip  <span class="comment"># 解压到当前目录</span></span><br><span class="line">unzip file.zip -d /home/user/  <span class="comment"># 解压到指定目录</span></span><br><span class="line">unzip -l file.zip  <span class="comment"># 查看压缩包内的文件列表</span></span><br></pre></td></tr></table></figure></p><h3 id="3-gzip-gunzip-GZ-格式压缩-解压"><a href="#3-gzip-gunzip-GZ-格式压缩-解压" class="headerlink" title="3. gzip/gunzip - GZ 格式压缩 / 解压"></a>3. gzip/gunzip - GZ 格式压缩 / 解压</h3><p><strong>功能</strong>：压缩单个文件（不能压缩目录），压缩后原文件删除（后缀<code>.gz</code>）。</p><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">gzip file.txt  <span class="comment"># 压缩file.txt为file.txt.gz（原文件删除）</span></span><br><span class="line">gzip -k file.txt  <span class="comment"># 压缩并保留原文件</span></span><br><span class="line">gunzip file.txt.gz  <span class="comment"># 解压为file.txt（原.gz文件删除）</span></span><br></pre></td></tr></table></figure></p><h2 id="六、其他实用命令"><a href="#六、其他实用命令" class="headerlink" title="六、其他实用命令"></a>六、其他实用命令</h2><h3 id="1-history-查看命令历史"><a href="#1-history-查看命令历史" class="headerlink" title="1. history - 查看命令历史"></a>1. history - 查看命令历史</h3><p><strong>功能</strong>：显示当前用户执行过的命令历史（默认保存 1000 条，可通过<code>HISTSIZE</code>调整）。</p><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">history</span>  <span class="comment"># 查看所有命令历史</span></span><br><span class="line"><span class="built_in">history</span> 10  <span class="comment"># 查看最近10条命令</span></span><br><span class="line">!123  <span class="comment"># 执行历史中第123条命令</span></span><br><span class="line">!<span class="built_in">ls</span>  <span class="comment"># 执行上一条以ls开头的命令</span></span><br><span class="line">Ctrl+R  <span class="comment"># 搜索命令历史（输入关键词匹配）</span></span><br></pre></td></tr></table></figure></p><h3 id="2-alias-设置命令别名"><a href="#2-alias-设置命令别名" class="headerlink" title="2. alias - 设置命令别名"></a>2. alias - 设置命令别名</h3><p><strong>功能</strong>：为常用命令设置简化别名（临时生效，永久生效需写入配置文件）。</p><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">alias</span> ll=<span class="string">&#x27;ls -lha&#x27;</span>  <span class="comment"># 临时设置ll为ls -lha的别名</span></span><br><span class="line"><span class="built_in">alias</span> <span class="built_in">rm</span>=<span class="string">&#x27;rm -i&#x27;</span>  <span class="comment"># 临时设置rm为带提示的删除（安全）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 永久生效（写入用户配置文件）</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;alias ll=&#x27;ls -lha&#x27;&quot;</span> &gt;&gt; ~/.bashrc  <span class="comment"># 对当前用户生效</span></span><br><span class="line"><span class="built_in">source</span> ~/.bashrc  <span class="comment"># 立即生效（无需重启终端）</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">unalias</span> ll  <span class="comment"># 取消别名</span></span><br></pre></td></tr></table></figure></p><h3 id="3-sudo-临时获取-root-权限"><a href="#3-sudo-临时获取-root-权限" class="headerlink" title="3. sudo - 临时获取 root 权限"></a>3. sudo - 临时获取 root 权限</h3><p><strong>功能</strong>：允许普通用户执行需 root 权限的命令（需在<code>/etc/sudoers</code>中配置权限）。</p><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt update  <span class="comment"># 以root权限更新软件源（Debian/Ubuntu）</span></span><br><span class="line"><span class="built_in">sudo</span> yum install nginx  <span class="comment"># 以root权限安装nginx（CentOS/RHEL）</span></span><br><span class="line"><span class="built_in">sudo</span> -i  <span class="comment"># 切换到root用户（需输入当前用户密码）</span></span><br></pre></td></tr></table></figure></p><h3 id="4-apt-yum-dnf-包管理命令"><a href="#4-apt-yum-dnf-包管理命令" class="headerlink" title="4. apt/yum/dnf - 包管理命令"></a>4. apt/yum/dnf - 包管理命令</h3><p><strong>功能</strong>：Linux 系统软件包管理工具（自动解决依赖关系）。</p><ul><li><code>apt</code>：Debian/Ubuntu 系列系统（<code>apt-get</code>的简化版）。</li><li><code>yum</code>：CentOS 6/RHEL 6 系列（已逐步被 dnf 替代）。</li><li><code>dnf</code>：CentOS 7+/RHEL 7 + 系列（yum 的升级版）。</li></ul><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># Debian/Ubuntu（apt）</span></span><br><span class="line"><span class="built_in">sudo</span> apt update  <span class="comment"># 更新软件源索引</span></span><br><span class="line"><span class="built_in">sudo</span> apt install nginx  <span class="comment"># 安装nginx</span></span><br><span class="line"><span class="built_in">sudo</span> apt remove nginx  <span class="comment"># 卸载nginx（保留配置文件）</span></span><br><span class="line"><span class="built_in">sudo</span> apt purge nginx  <span class="comment"># 彻底卸载nginx（删除配置文件）</span></span><br><span class="line"><span class="built_in">sudo</span> apt upgrade  <span class="comment"># 升级所有已安装软件</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># CentOS/RHEL（yum）</span></span><br><span class="line"><span class="built_in">sudo</span> yum update  <span class="comment"># 更新软件源</span></span><br><span class="line"><span class="built_in">sudo</span> yum install nginx  <span class="comment"># 安装nginx</span></span><br><span class="line"><span class="built_in">sudo</span> yum remove nginx  <span class="comment"># 卸载nginx</span></span><br><span class="line"><span class="built_in">sudo</span> yum list installed  <span class="comment"># 列出已安装软件</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># CentOS 8+/RHEL 8+（dnf）</span></span><br><span class="line"><span class="built_in">sudo</span> dnf install nginx</span><br><span class="line"><span class="built_in">sudo</span> dnf remove nginx</span><br></pre></td></tr></table></figure></p><h3 id="5-echo-输出字符串-变量"><a href="#5-echo-输出字符串-变量" class="headerlink" title="5. echo - 输出字符串 / 变量"></a>5. echo - 输出字符串 / 变量</h3><p><strong>功能</strong>：在终端输出指定字符串或环境变量的值。</p><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">echo</span> <span class="string">&quot;Hello Linux&quot;</span>  <span class="comment"># 输出字符串</span></span><br><span class="line"><span class="built_in">echo</span> <span class="variable">$PATH</span>  <span class="comment"># 输出环境变量PATH的值（系统命令搜索路径）</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;当前用户：<span class="variable">$USER</span>&quot;</span>  <span class="comment"># 输出当前用户名（变量需加$）</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;abc&quot;</span> &gt; file.txt  <span class="comment"># 将字符串写入文件（覆盖原有内容）</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;def&quot;</span> &gt;&gt; file.txt  <span class="comment"># 将字符串追加到文件末尾</span></span><br></pre></td></tr></table></figure></p><h3 id="6-wc-统计文本行数-字数-字节数"><a href="#6-wc-统计文本行数-字数-字节数" class="headerlink" title="6. wc - 统计文本行数 / 字数 / 字节数"></a>6. wc - 统计文本行数 / 字数 / 字节数</h3><p><strong>功能</strong>：统计文件或命令输出的行数（<code>-l</code>）、单词数（<code>-w</code>）、字节数（<code>-c</code>）。</p><p><strong>示例</strong>：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">wc</span> -l file.txt  <span class="comment"># 统计file.txt的行数</span></span><br><span class="line"><span class="built_in">wc</span> -w file.txt  <span class="comment"># 统计单词数</span></span><br><span class="line"><span class="built_in">wc</span> -c file.txt  <span class="comment"># 统计字节数</span></span><br><span class="line"><span class="built_in">cat</span> file.txt | <span class="built_in">wc</span> -l  <span class="comment"># 统计命令输出的行数</span></span><br></pre></td></tr></table></figure></p><h2 id="七、命令行实用技巧"><a href="#七、命令行实用技巧" class="headerlink" title="七、命令行实用技巧"></a>七、命令行实用技巧</h2><ol><li>管道符 <code>|</code>：将前一个命令的输出作为后一个命令的输入<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">ps aux | grep nginx  <span class="comment"># 筛选nginx进程</span></span><br></pre></td></tr></table></figure></li><li><p>重定向</p><ul><li><p>覆盖写入文件</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">ls</span> &gt; list.txt</span><br></pre></td></tr></table></figure></li><li><p>追加写入文件</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">echo</span> <span class="string">&quot;test&quot;</span> &gt;&gt; list.txt</span><br></pre></td></tr></table></figure></li><li><p>从文件读取输入</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">wc</span> -l &lt; file.txt</span><br></pre></td></tr></table></figure></li></ul></li></ol><ol><li><p>通配符</p><ul><li><code>*</code>：匹配任意多个字符（如<code>*.txt</code>所有.txt 文件）</li><li><code>?</code>：匹配单个字符（如<code>file?.txt</code>匹配 file1.txt、file2.txt）</li><li><code>[]</code>：匹配括号内的任意一个字符（如<code>file[1-3].txt</code>匹配 file1-3.txt）</li></ul></li><li><p>快捷键</p><ul><li><code>Tab</code>：自动补全命令 / 文件名 / 目录名（高效必备）</li><li><code>Ctrl+C</code>：终止当前运行的命令</li><li><code>Ctrl+D</code>：退出当前终端 / 登录会话（等价于 exit）</li><li><code>Ctrl+Z</code>：暂停当前命令（用<code>fg</code>恢复到前台，<code>bg</code>放到后台）</li></ul></li></ol>]]></content:encoded>
      
      
      <category domain="https://blog.itrf.cn/tags/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</category>
      
      <category domain="https://blog.itrf.cn/tags/Linux/">Linux</category>
      
    </item>
    
    <item>
      <title>SQL Server学习笔记</title>
      <link>https://blog.itrf.cn/posts/d3d/</link>
      <guid>https://blog.itrf.cn/posts/d3d/</guid>
      <pubDate>2025-07-24T04:46:41.000Z</pubDate>
      
      <description>SQL Server学习笔记</description>
      
      
      <enclosure url="https://t.alcy.cc/ycy?_r_=049646fc-be13-3503-eb5a-eb2db1a76955" type="image"/>
      
      
      <content:encoded><![CDATA[<h2 id="数据库管理技术的基本概念"><a href="#数据库管理技术的基本概念" class="headerlink" title="数据库管理技术的基本概念"></a>数据库管理技术的基本概念</h2><ol><li>数据</li><li>信息</li><li>数据处理</li></ol><h2 id="数据库管理技术的发展阶段"><a href="#数据库管理技术的发展阶段" class="headerlink" title="数据库管理技术的发展阶段"></a>数据库管理技术的发展阶段</h2><ol><li>人工管理</li><li>文件系统</li><li>数据库系统</li><li>大数据</li></ol><h2 id="数据库系统的组成"><a href="#数据库系统的组成" class="headerlink" title="数据库系统的组成"></a>数据库系统的组成</h2><ol><li>硬件系统</li><li>操作系统</li><li>.数据库</li><li>数据库管理系统</li><li>数据库应用系统的开发工具及相关接口软件</li><li>数据库应用系统</li><li>用户<ol><li>数据库管理员</li><li>数据库分析师</li><li>应用程序员</li><li>终端用户</li></ol></li></ol><h2 id="数据库体系结构"><a href="#数据库体系结构" class="headerlink" title="数据库体系结构"></a>数据库体系结构</h2><ol><li>集中式系统</li><li>分布式系统</li><li>个人计算机系统</li><li>客户/服务器系统</li><li>浏览器/服务器系统    </li></ol><h2 id="数据库的三级模式"><a href="#数据库的三级模式" class="headerlink" title="数据库的三级模式"></a>数据库的三级模式</h2><ol><li>内模式</li><li>模式</li><li>外模式    </li></ol><h2 id="数据库的二级映像"><a href="#数据库的二级映像" class="headerlink" title="数据库的二级映像"></a>数据库的二级映像</h2><ol><li>外模式/模式</li><li>模式/内模式</li></ol><h2 id="数据库管理系统的功能"><a href="#数据库管理系统的功能" class="headerlink" title="数据库管理系统的功能"></a>数据库管理系统的功能</h2><ol><li>数据定义</li><li>数据操控</li><li>数据库运行控制</li><li>数据字典</li></ol><h2 id="登录方式"><a href="#登录方式" class="headerlink" title="登录方式"></a>登录方式</h2><ol><li>windows登录，本地登录</li><li>sqlserver登录，远程登录</li></ol><h2 id="数据库备份"><a href="#数据库备份" class="headerlink" title="数据库备份"></a>数据库备份</h2><p>mdf数据文件，ldf日志文件</p><ol><li>分离-&gt;附加  分离后数据库会不可用</li><li>备份-&gt;还原  保存为.bak文件   任务——&gt;备份</li><li>数据库脚本</li></ol><h2 id="注释"><a href="#注释" class="headerlink" title="注释"></a>注释</h2><p>单行注释 —<br>多行注释 /<em>   </em>/</p><h2 id="数据库相关"><a href="#数据库相关" class="headerlink" title="数据库相关"></a>数据库相关</h2><h3 id="创建数据库"><a href="#创建数据库" class="headerlink" title="创建数据库"></a>创建数据库</h3><p>完整语法</p><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">create</span> database DBTEST</span><br><span class="line"><span class="keyword">on</span> <span class="comment">--数据文件</span></span><br><span class="line">(</span><br><span class="line">  name <span class="operator">=</span> <span class="string">&#x27;DBTEST&#x27;</span>, <span class="comment">--逻辑名称</span></span><br><span class="line">  filename <span class="operator">=</span> <span class="string">&#x27;&#x27;</span>,  <span class="comment">--物理路径和名称 以.mdf结尾</span></span><br><span class="line">  size <span class="operator">=</span> <span class="number">5</span>MB,<span class="comment">--文件的初始大小</span></span><br><span class="line">  maxsize <span class="operator">=</span> <span class="number">40</span>MB，  <span class="comment">--文件的最大大小</span></span><br><span class="line">  filegrowth<span class="operator">=</span><span class="number">2</span>MB,  <span class="comment">--文件增长方式 可以是大小，也可以是百分比</span></span><br><span class="line">)</span><br><span class="line">log <span class="keyword">on</span> <span class="comment">--日志文件</span></span><br><span class="line">(</span><br><span class="line">  name <span class="operator">=</span> <span class="string">&#x27;DBTEST_log&#x27;</span>, <span class="comment">--逻辑名称</span></span><br><span class="line">  filename <span class="operator">=</span> <span class="string">&#x27;&#x27;</span>,  <span class="comment">--物理路径和名称 以.ldf结尾</span></span><br><span class="line">  size <span class="operator">=</span> <span class="number">5</span>MB,<span class="comment">--文件的初始大小</span></span><br><span class="line">  filegrowth<span class="operator">=</span><span class="number">2</span>MB,  <span class="comment">--文件增长方式 可以是大小，也可以是百分比</span></span><br><span class="line">)</span><br></pre></td></tr></table></figure><p>简单语法</p><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">create</span> database DBTEST  <span class="comment">--采用默认值创建 </span></span><br></pre></td></tr></table></figure><h3 id="修改数据库"><a href="#修改数据库" class="headerlink" title="修改数据库"></a>修改数据库</h3><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">alter</span> database DNTEST</span><br></pre></td></tr></table></figure><h3 id="删除数据库"><a href="#删除数据库" class="headerlink" title="删除数据库"></a>删除数据库</h3><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">drop</span> database 数据库名称</span><br></pre></td></tr></table></figure><h3 id="切换数据库"><a href="#切换数据库" class="headerlink" title="切换数据库"></a>切换数据库</h3><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line">use DBTEST <span class="comment">--use 数据库名称</span></span><br></pre></td></tr></table></figure><h2 id="表相关"><a href="#表相关" class="headerlink" title="表相关"></a>表相关</h2><h3 id="创建表基础语法"><a href="#创建表基础语法" class="headerlink" title="创建表基础语法"></a>创建表基础语法</h3><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">create table</span> 表名</span><br><span class="line">(</span><br><span class="line"> 字段<span class="number">1</span> 数据类型，</span><br><span class="line"> 字段<span class="number">2</span> 数据类型，</span><br><span class="line"> 字段<span class="number">3</span> 数据类型，</span><br><span class="line">) </span><br></pre></td></tr></table></figure><h4 id="约束"><a href="#约束" class="headerlink" title="约束"></a>约束</h4><ul><li>primary key:主建 </li><li>references 表名(字段名)：外键</li><li>identify(1,1):自动增长，初始值1，增长步长1</li><li>not null：不为空</li><li>check():检查约束</li><li>default()：默认值</li><li>unique：唯一约束</li></ul><h4 id="数据类型"><a href="#数据类型" class="headerlink" title="数据类型"></a>数据类型</h4><ul><li>int:整型</li><li>float:浮点型</li><li>decimal(n,m):n表示总长度，m表示小数位数</li><li>date:年月日  —getdate() 获取当前时间</li><li>datetime：年月日时分秒</li><li>smalldatetime：范围更小</li><li>char():定长字符串</li><li>varchar():变长字符串</li><li>text:长文本</li><li>nchar,nvarchar,ntext:unicode字符,对中文友好</li></ul><h3 id="删除表"><a href="#删除表" class="headerlink" title="删除表"></a>删除表</h3><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">drop</span> <span class="keyword">table</span> 表名</span><br></pre></td></tr></table></figure><h3 id="修改表结构"><a href="#修改表结构" class="headerlink" title="修改表结构"></a>修改表结构</h3><h4 id="1-添加列"><a href="#1-添加列" class="headerlink" title="1.添加列"></a>1.添加列</h4><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">alter table</span> 表名 <span class="keyword">add</span> 新列名 数据类型</span><br></pre></td></tr></table></figure><h4 id="2-删除列"><a href="#2-删除列" class="headerlink" title="2.删除列"></a>2.删除列</h4><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">alter table</span> 表名 <span class="keyword">drop</span> <span class="keyword">column</span> 列名</span><br></pre></td></tr></table></figure><h4 id="3-修改列-—如果已经存在数据了则可能报错，建议建表的时候就有冗余"><a href="#3-修改列-—如果已经存在数据了则可能报错，建议建表的时候就有冗余" class="headerlink" title="3.修改列  —如果已经存在数据了则可能报错，建议建表的时候就有冗余"></a>3.修改列  —如果已经存在数据了则可能报错，建议建表的时候就有冗余</h4><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">alter table</span> 表名 <span class="keyword">alter</span> <span class="keyword">column</span> 列名 数据类型</span><br></pre></td></tr></table></figure><h3 id="维护约束"><a href="#维护约束" class="headerlink" title="维护约束"></a>维护约束</h3><h4 id="1-删除约束"><a href="#1-删除约束" class="headerlink" title="1.删除约束"></a>1.删除约束</h4><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">alter table</span> 表名 <span class="keyword">drop</span> <span class="keyword">constraint</span> 约束名</span><br></pre></td></tr></table></figure><h4 id="2-添加约束"><a href="#2-添加约束" class="headerlink" title="2.添加约束"></a>2.添加约束</h4><h5 id="check约束"><a href="#check约束" class="headerlink" title="check约束"></a>check约束</h5><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">alter table</span> 表名 <span class="keyword">add constraint</span> 约束名 <span class="keyword">check</span>(表达式)</span><br></pre></td></tr></table></figure><h5 id="主键约束"><a href="#主键约束" class="headerlink" title="主键约束"></a>主键约束</h5><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">alter table</span> 表名 <span class="keyword">add constraint</span> 约束名 <span class="keyword">primary key</span>(列名)</span><br></pre></td></tr></table></figure><h5 id="唯一约束"><a href="#唯一约束" class="headerlink" title="唯一约束"></a>唯一约束</h5><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">alter table</span> 表名 <span class="keyword">add constraint</span> 约束名 <span class="keyword">unique</span>(列名)</span><br></pre></td></tr></table></figure><h5 id="默认值约束"><a href="#默认值约束" class="headerlink" title="默认值约束"></a>默认值约束</h5><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">alter table</span> 表名 <span class="keyword">add constraint</span> 约束名 <span class="keyword">default</span> 默认值 <span class="keyword">for</span> 列名</span><br></pre></td></tr></table></figure><h5 id="外键约束"><a href="#外键约束" class="headerlink" title="外键约束"></a>外键约束</h5><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">alter table</span> 表名 <span class="keyword">add constraint</span> 约束名 <span class="keyword">foreign key</span>(列名)<span class="keyword">references</span> 关联表名(列名)</span><br></pre></td></tr></table></figure><h2 id="数据相关"><a href="#数据相关" class="headerlink" title="数据相关"></a>数据相关</h2><h3 id="插入数据"><a href="#插入数据" class="headerlink" title="插入数据"></a>插入数据</h3><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">insert into</span> 表名(字段<span class="number">1</span>,字段<span class="number">2</span>,字段<span class="number">3</span>)<span class="keyword">values</span>(内容<span class="number">1</span>,内容<span class="number">2</span>,内容<span class="number">3</span>)</span><br></pre></td></tr></table></figure><p>简写 —不建议</p><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">insert into</span> 表名 <span class="keyword">values</span>(内容<span class="number">1</span>,内容<span class="number">2</span>,内容<span class="number">3</span>)</span><br></pre></td></tr></table></figure><p>一次性插入多行数据</p><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">insert into</span> 表名(字段<span class="number">1</span>,字段<span class="number">2</span>,字段<span class="number">3</span>)</span><br><span class="line"><span class="keyword">select</span> 内容<span class="number">1</span>,内容<span class="number">2</span>,内容<span class="number">3</span> <span class="keyword">union</span></span><br><span class="line"><span class="keyword">select</span> 内容<span class="number">1</span>,内容<span class="number">2</span>,内容<span class="number">3</span> <span class="keyword">union</span></span><br><span class="line"><span class="keyword">select</span> 内容<span class="number">1</span>,内容<span class="number">2</span>,内容<span class="number">3</span></span><br></pre></td></tr></table></figure><h3 id="修改删除数据"><a href="#修改删除数据" class="headerlink" title="修改删除数据"></a>修改删除数据</h3><h4 id="1-修改"><a href="#1-修改" class="headerlink" title="1.修改"></a>1.修改</h4><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">update</span> 表名 <span class="keyword">set</span> 字段<span class="number">1</span><span class="operator">=</span>值<span class="number">1</span>,字段<span class="number">2</span><span class="operator">=</span>值<span class="number">2</span> <span class="keyword">where</span> 条件</span><br></pre></td></tr></table></figure><h4 id="2-删除数据"><a href="#2-删除数据" class="headerlink" title="2.删除数据"></a>2.删除数据</h4><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">delete</span> <span class="keyword">from</span> 表名 <span class="keyword">where</span> 条件</span><br></pre></td></tr></table></figure><h4 id="关于删除"><a href="#关于删除" class="headerlink" title="关于删除"></a>关于删除</h4><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">drop</span> <span class="keyword">table</span> 表名  <span class="comment">--删除表对象</span></span><br></pre></td></tr></table></figure><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">truncate</span> <span class="keyword">table</span> 表名  <span class="comment">--删除数据(清空数据)，表对象即表结构依然存在，不能有条件，自动编号存在</span></span><br></pre></td></tr></table></figure><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">delete</span> <span class="keyword">from</span> 表名  <span class="comment">--删除数据(清空数据)，表对象即表结构依然存在，可以有条件，自动编号清除</span></span><br></pre></td></tr></table></figure><h2 id="查询相关"><a href="#查询相关" class="headerlink" title="查询相关"></a>查询相关</h2><h3 id="基本查询"><a href="#基本查询" class="headerlink" title="基本查询"></a>基本查询</h3><h4 id="1-查询所有列所有行"><a href="#1-查询所有列所有行" class="headerlink" title="1.查询所有列所有行"></a>1.查询所有列所有行</h4><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> 表名</span><br></pre></td></tr></table></figure><h4 id="2-查询指定列"><a href="#2-查询指定列" class="headerlink" title="2.查询指定列"></a>2.查询指定列</h4><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">select</span> 列名<span class="number">1</span>,列名<span class="number">2</span>,列名<span class="number">3</span> <span class="keyword">from</span> 表名</span><br></pre></td></tr></table></figure><h4 id="3-查询指定列-别名显示"><a href="#3-查询指定列-别名显示" class="headerlink" title="3.查询指定列,别名显示"></a>3.查询指定列,别名显示</h4><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">select</span> 列名<span class="number">1</span> 别名<span class="number">1</span>,列名<span class="number">2</span> 别名<span class="number">2</span>,列名<span class="number">3</span> 别名<span class="number">3</span> <span class="keyword">from</span> 表名</span><br></pre></td></tr></table></figure><h4 id="4-查询指定列-去重"><a href="#4-查询指定列-去重" class="headerlink" title="4.查询指定列,去重"></a>4.查询指定列,去重</h4><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">select</span> <span class="keyword">distinct</span>(列名) <span class="keyword">from</span> 表名</span><br></pre></td></tr></table></figure><h3 id="条件查询"><a href="#条件查询" class="headerlink" title="条件查询"></a>条件查询</h3><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> 表名 <span class="keyword">where</span> 条件</span><br><span class="line"><span class="keyword">select</span> top <span class="number">10</span> <span class="operator">*</span> <span class="keyword">from</span> 表名 <span class="keyword">where</span> 条件  <span class="comment">--前十位</span></span><br><span class="line"><span class="keyword">select</span> top <span class="number">10</span> <span class="keyword">percent</span> <span class="operator">*</span> <span class="keyword">from</span> 表名 <span class="keyword">where</span> 条件  <span class="comment">--前百分之十</span></span><br><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> 表名 <span class="keyword">where</span> <span class="keyword">order</span> <span class="keyword">by</span> 字段 <span class="keyword">desc</span><span class="operator">/</span><span class="keyword">asc</span></span><br></pre></td></tr></table></figure><h3 id="模糊查询"><a href="#模糊查询" class="headerlink" title="模糊查询"></a>模糊查询</h3><ul><li>like</li><li>%：代表匹配0个字符，一个字符或多个字符</li><li>_:代表匹配有且只有一个字符</li><li>[]:代表匹配范围内</li><li>！[]:代表匹配不在范围内</li><li>substring(n,m,t) —n为字符串，m是起始位，t是跨度</li></ul><h4 id="聚合函数"><a href="#聚合函数" class="headerlink" title="聚合函数"></a>聚合函数</h4><ul><li>count:求数量</li><li>max:求最大值</li><li>min:求最小值</li><li>sum: 求和</li><li>avg:求平均值</li><li>round:保留小数位 round(2.556,2) ——&gt;2.55</li><li>datediff:求时间差 datediff(year,时间1,时间2)</li></ul><h3 id="分组查询"><a href="#分组查询" class="headerlink" title="分组查询"></a>分组查询</h3><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> 表名 <span class="keyword">where</span> 条件 <span class="keyword">group</span> <span class="keyword">by</span> 列名 <span class="keyword">having</span> 条件</span><br></pre></td></tr></table></figure><p> —普通条件在where后，聚合条件在 having后<br>注：分组查询只允许查询分组条件和聚合函数</p><h3 id="多表查询"><a href="#多表查询" class="headerlink" title="多表查询"></a>多表查询</h3><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> 表名<span class="number">1</span>,表名<span class="number">2</span> <span class="keyword">where</span> 条件</span><br></pre></td></tr></table></figure><h4 id="1-内连接"><a href="#1-内连接" class="headerlink" title="1.内连接"></a>1.内连接</h4><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> 表名<span class="number">1</span> <span class="keyword">inner</span> <span class="keyword">join</span> 表名<span class="number">2</span> <span class="keyword">on</span> 条件</span><br></pre></td></tr></table></figure><h4 id="2-外连接"><a href="#2-外连接" class="headerlink" title="2.外连接"></a>2.外连接</h4><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> 表名<span class="number">1</span> <span class="keyword">left</span> <span class="keyword">join</span> 表名<span class="number">2</span> <span class="keyword">on</span> 条件  <span class="comment">--左外</span></span><br><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> 表名<span class="number">1</span> <span class="keyword">right</span> <span class="keyword">join</span> 表名<span class="number">2</span> <span class="keyword">on</span> 条件  <span class="comment">--右外</span></span><br><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> 表名<span class="number">1</span> <span class="keyword">full</span> <span class="keyword">join</span> 表名<span class="number">2</span> <span class="keyword">on</span> 条件  <span class="comment">--全外</span></span><br></pre></td></tr></table></figure><h4 id="3-自连接"><a href="#3-自连接" class="headerlink" title="3.自连接"></a>3.自连接</h4><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">select</span> 别名<span class="number">1.</span>字段,别名<span class="number">2.</span>字段 <span class="keyword">from</span> 表名 别名<span class="number">1</span> <span class="keyword">inner</span> <span class="keyword">join</span> 表名 别名<span class="number">2</span> <span class="keyword">on</span> 条件</span><br></pre></td></tr></table></figure><h2 id="数据库设计"><a href="#数据库设计" class="headerlink" title="数据库设计"></a>数据库设计</h2><h3 id="1-三范式"><a href="#1-三范式" class="headerlink" title="1.三范式"></a>1.三范式</h3><ul><li>第一范式：原子性  不可再分</li><li>第二范式：实体唯一性</li><li>第三范式：不能有冗余</li></ul><h3 id="2-表关系"><a href="#2-表关系" class="headerlink" title="2.表关系"></a>2.表关系</h3><ul><li>一对一</li><li>一对多</li><li>多对多</li></ul><h2 id="变量"><a href="#变量" class="headerlink" title="变量"></a>变量</h2><h3 id="—信息打印："><a href="#—信息打印：" class="headerlink" title="—信息打印："></a>—信息打印：</h3><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line">print<span class="string">&#x27;hello,sql&#x27;</span>      <span class="comment">--消息窗口显示</span></span><br></pre></td></tr></table></figure><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">select</span><span class="string">&#x27;hello,sql&#x27;</span>     <span class="comment">--信息窗口显示</span></span><br></pre></td></tr></table></figure><h3 id="—变量："><a href="#—变量：" class="headerlink" title="—变量："></a>—变量：</h3><ol><li><p>局部变量：以@开头，先声明，在赋值</p> <figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">declare</span> <span class="variable">@str</span> <span class="type">varchar</span>(<span class="number">20</span>)</span><br><span class="line"><span class="keyword">set</span> <span class="variable">@str</span><span class="operator">=</span><span class="string">&#x27;i like sql&#x27;</span>        <span class="comment">--赋值变量指定的值</span></span><br><span class="line"><span class="keyword">select</span> <span class="variable">@str</span> <span class="operator">=</span><span class="string">&#x27;i like sql&#x27;</span> <span class="comment">--一般用于表中的查询出的数据赋值给变量，如果查询结果有多条，取最后一条赋值</span></span><br></pre></td></tr></table></figure></li><li><p>全局变量：以@@开头，由系统就行定义和维护</p></li></ol><h3 id="—go语句"><a href="#—go语句" class="headerlink" title="—go语句:"></a>—go语句:</h3><ol><li>等待go语句之前的代码执行完之后才能执行后面的代码</li><li>批处理结束的一个标志</li></ol><h2 id="运算符"><a href="#运算符" class="headerlink" title="运算符"></a>运算符</h2><h3 id="强制类型转化"><a href="#强制类型转化" class="headerlink" title="强制类型转化"></a>强制类型转化</h3><ol><li>Convert(数据类型，变量名)  例：Convert(varchar(10)，@zc)</li><li>cast(变量名 as 数据类型)   例：cast(@zc as varchar(10))</li></ol><h3 id="流程控制语句"><a href="#流程控制语句" class="headerlink" title="流程控制语句"></a>流程控制语句</h3><h4 id="1-选择分支"><a href="#1-选择分支" class="headerlink" title="(1)选择分支"></a>(1)选择分支</h4><p>case选择：</p><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">case</span> </span><br><span class="line"><span class="keyword">when</span> 条件 <span class="keyword">then</span> 执行<span class="number">1</span></span><br><span class="line"><span class="keyword">when</span> 条件 <span class="keyword">then</span> 执行<span class="number">2</span></span><br><span class="line"><span class="keyword">else</span> 最终执行</span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure><p>if判断语句：</p><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line">if 条件判断</span><br><span class="line">   <span class="keyword">begin</span></span><br><span class="line"> 语句</span><br><span class="line">   <span class="keyword">end</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">   <span class="keyword">begin</span></span><br><span class="line">     语句</span><br><span class="line">   <span class="keyword">end</span></span><br></pre></td></tr></table></figure><h4 id="2-循环"><a href="#2-循环" class="headerlink" title="(2)循环"></a>(2)循环</h4><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line">while 条件</span><br><span class="line"><span class="keyword">begin</span></span><br><span class="line">  语句</span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure><h2 id="子查询"><a href="#子查询" class="headerlink" title="子查询"></a>子查询</h2><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line">   <span class="keyword">where</span>条件</span><br><span class="line">临时表</span><br><span class="line">exist</span><br></pre></td></tr></table></figure><h2 id="分页"><a href="#分页" class="headerlink" title="分页"></a>分页</h2><h3 id="1-top方案"><a href="#1-top方案" class="headerlink" title="(1)top方案"></a>(1)top方案</h3><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">declare</span> <span class="variable">@PageSize</span> <span class="type">int</span> <span class="operator">=</span> <span class="number">5</span></span><br><span class="line"><span class="keyword">declare</span> <span class="variable">@PageIndex</span> <span class="type">int</span> <span class="operator">=</span> <span class="number">3</span></span><br><span class="line"><span class="keyword">select</span> top(<span class="variable">@PageSize</span>)<span class="operator">*</span><span class="keyword">from</span> Student</span><br><span class="line"><span class="keyword">where</span> StuId <span class="keyword">not</span> <span class="keyword">in</span> (<span class="keyword">select</span> top(<span class="variable">@PageSize</span><span class="operator">*</span>(PageIndex<span class="number">-1</span>))StuId <span class="keyword">from</span> Student)</span><br></pre></td></tr></table></figure><h3 id="2-使用row-number分页"><a href="#2-使用row-number分页" class="headerlink" title="(2)使用row_number分页"></a>(2)使用row_number分页</h3><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">declare</span> <span class="variable">@PageSize</span> <span class="type">int</span> <span class="operator">=</span> <span class="number">5</span></span><br><span class="line"><span class="keyword">declare</span> <span class="variable">@PageIndex</span> <span class="type">int</span> <span class="operator">=</span> <span class="number">3</span></span><br><span class="line"><span class="keyword">select</span> <span class="operator">*</span><span class="keyword">from</span></span><br><span class="line">(<span class="keyword">select</span> <span class="built_in">ROW_NUMBER</span>() <span class="keyword">over</span>(<span class="keyword">order</span> <span class="keyword">by</span> StuId) RowId,<span class="operator">*</span><span class="keyword">from</span> Student) Temp</span><br><span class="line"><span class="keyword">where</span> RowId <span class="keyword">between</span> (<span class="variable">@PageIndex</span><span class="number">-1</span>)<span class="operator">*</span><span class="variable">@PageSize</span><span class="operator">+</span><span class="number">1</span> <span class="keyword">and</span> <span class="variable">@PageIndex</span><span class="operator">*</span><span class="variable">@PageSize</span></span><br></pre></td></tr></table></figure><h2 id="事务"><a href="#事务" class="headerlink" title="事务"></a>事务</h2><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">begin</span> transaction   <span class="comment">--开始语句</span></span><br><span class="line">语句</span><br><span class="line">@<span class="variable">@ERROR</span>             <span class="comment">--统计错误 如果出现错误 @@ERROR的值不为零</span></span><br></pre></td></tr></table></figure><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">commit</span> transaction     <span class="comment">--提交事务</span></span><br><span class="line"><span class="keyword">rollback</span> transaction   <span class="comment">--回滚事务</span></span><br></pre></td></tr></table></figure><h2 id="索引"><a href="#索引" class="headerlink" title="索引"></a>索引</h2><ol><li>聚集索引     —类似于按照拼音查找     只能有一个</li><li>非聚集索引    —类似于按照部首查询     可以有多个</li></ol><h3 id="添加索引"><a href="#添加索引" class="headerlink" title="添加索引"></a>添加索引</h3><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">create</span> [<span class="keyword">unique</span>][clustered<span class="operator">|</span>nonclustered] index <span class="operator">&lt;</span>index name<span class="operator">&gt;</span> <span class="keyword">on</span> <span class="operator">&lt;</span><span class="keyword">table</span> <span class="keyword">or</span> <span class="keyword">view</span> name<span class="operator">&gt;</span>(<span class="operator">&lt;</span>colum name<span class="operator">&gt;</span>[<span class="keyword">ASC</span><span class="operator">|</span><span class="keyword">DESC</span>][,...n])</span><br></pre></td></tr></table></figure><h3 id="查看索引-（sys-indexes）"><a href="#查看索引-（sys-indexes）" class="headerlink" title="查看索引 （sys.indexes）"></a>查看索引 （sys.indexes）</h3><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> sys.indexes <span class="keyword">where</span> name <span class="operator">=</span> <span class="string">&#x27;索引名&#x27;</span></span><br></pre></td></tr></table></figure><h3 id="删除索引"><a href="#删除索引" class="headerlink" title="删除索引"></a>删除索引</h3><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">drop</span> index 索引名 <span class="keyword">on</span> 表名</span><br></pre></td></tr></table></figure><h3 id="使用索引"><a href="#使用索引" class="headerlink" title="使用索引"></a>使用索引</h3><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> 表名 <span class="keyword">with</span> (index<span class="operator">=</span>索引名) <span class="keyword">where</span> 条件</span><br></pre></td></tr></table></figure><h2 id="视图"><a href="#视图" class="headerlink" title="视图"></a>视图</h2><h3 id="创建视图："><a href="#创建视图：" class="headerlink" title="创建视图："></a>创建视图：</h3><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">create</span> <span class="keyword">view</span> 视图名 <span class="keyword">as</span> <span class="keyword">select</span>语句 go</span><br></pre></td></tr></table></figure><h3 id="查询视图："><a href="#查询视图：" class="headerlink" title="查询视图："></a>查询视图：</h3><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> 视图名</span><br></pre></td></tr></table></figure><h3 id="删除视图："><a href="#删除视图：" class="headerlink" title="删除视图："></a>删除视图：</h3><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">drop</span> <span class="keyword">view</span> 视图名</span><br></pre></td></tr></table></figure><h2 id="游标"><a href="#游标" class="headerlink" title="游标"></a>游标</h2><h3 id="分类："><a href="#分类：" class="headerlink" title="分类："></a>分类：</h3><ul><li>静态游标</li><li>动态游标</li><li>键集驱动游标</li></ul><h3 id="创建游标-scroll：滚动游标，没有scroll，只进"><a href="#创建游标-scroll：滚动游标，没有scroll，只进" class="headerlink" title="创建游标(scroll：滚动游标，没有scroll，只进)"></a>创建游标(scroll：滚动游标，没有scroll，只进)</h3><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">declare</span> 游标名 <span class="keyword">cursor</span> <span class="keyword">scroll</span> <span class="keyword">for</span> <span class="keyword">select</span> 字段名 <span class="keyword">from</span> 表名</span><br></pre></td></tr></table></figure><h3 id="打开游标"><a href="#打开游标" class="headerlink" title="打开游标"></a>打开游标</h3><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">open</span> 游标名</span><br></pre></td></tr></table></figure><h3 id="关闭游标"><a href="#关闭游标" class="headerlink" title="关闭游标"></a>关闭游标</h3><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">close</span> 游标名</span><br></pre></td></tr></table></figure><h3 id="删除游标"><a href="#删除游标" class="headerlink" title="删除游标"></a>删除游标</h3><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">deallocate</span> 游标名</span><br></pre></td></tr></table></figure><h3 id="提取数据"><a href="#提取数据" class="headerlink" title="提取数据"></a>提取数据</h3><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">fetch</span> <span class="keyword">first</span> <span class="keyword">from</span> 游标名         <span class="comment">--提取第一行</span></span><br><span class="line"><span class="keyword">fetch</span> <span class="keyword">last</span> <span class="keyword">from</span> 游标名   <span class="comment">--提取最后一行</span></span><br><span class="line"><span class="keyword">fetch</span> absolute <span class="number">2</span> <span class="keyword">from</span> 游标名    <span class="comment">--提取第二行</span></span><br><span class="line"><span class="keyword">fetch</span> relative <span class="number">2</span> <span class="keyword">from</span> 游标名    <span class="comment">--当前行下移两行</span></span><br><span class="line"><span class="keyword">fetch</span> next <span class="keyword">from</span> 游标名     <span class="comment">--下移一行</span></span><br><span class="line"><span class="keyword">fetch</span> prior <span class="keyword">from</span> 游标名    <span class="comment">--上移一行</span></span><br></pre></td></tr></table></figure><p>—提取游标数据存放入变量，进行查询所有列信息</p><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">declare</span> <span class="variable">@acc</span> <span class="type">varchar</span>(<span class="number">20</span>)</span><br><span class="line"><span class="keyword">fetch</span> absolute <span class="number">2</span> <span class="keyword">from</span> mycur <span class="keyword">into</span> <span class="variable">@acc</span></span><br><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> Menber <span class="keyword">where</span> MenberAccount<span class="operator">=</span><span class="variable">@acc</span></span><br></pre></td></tr></table></figure><p>—遍历游标</p><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line">  @<span class="variable">@fetch_status</span>  ：<span class="number">0</span>提取成功，<span class="number">-1</span>提取失败，<span class="number">-2</span>不存在</span><br><span class="line"><span class="keyword">declare</span> <span class="variable">@acc</span> <span class="type">varchar</span>(<span class="number">20</span>)</span><br><span class="line"><span class="keyword">fetch</span> absolute <span class="number">2</span> <span class="keyword">from</span> mycur <span class="keyword">into</span> <span class="variable">@acc</span></span><br><span class="line">while @<span class="variable">@fetch_status</span><span class="operator">=</span><span class="number">0</span></span><br><span class="line"><span class="keyword">begin</span></span><br><span class="line">print<span class="string">&#x27;提取成功：&#x27;</span><span class="operator">+</span><span class="variable">@acc</span></span><br><span class="line"><span class="keyword">fetch</span> next <span class="keyword">from</span> mycur <span class="keyword">into</span> <span class="variable">@acc</span></span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure><p>—利用游标进行数据的修改和删除</p><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">fetch</span> absolute <span class="number">2</span> <span class="keyword">from</span> mycur</span><br><span class="line"><span class="keyword">update</span> <span class="keyword">Member</span> <span class="keyword">set</span> MemberPwd <span class="operator">=</span> <span class="string">&#x27;654321&#x27;</span> <span class="keyword">where</span> <span class="keyword">current</span> <span class="keyword">of</span> mycur</span><br><span class="line"></span><br><span class="line"><span class="keyword">fetch</span> absolute <span class="number">2</span> <span class="keyword">from</span> mycur</span><br><span class="line"><span class="keyword">delete</span> <span class="keyword">from</span> <span class="keyword">Member</span> <span class="keyword">where</span> <span class="keyword">current</span> <span class="keyword">of</span> mycur</span><br></pre></td></tr></table></figure><p>—一行多列游标</p><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">declare</span> 游标名 <span class="keyword">cursor</span> <span class="keyword">scroll</span> <span class="keyword">for</span> <span class="keyword">select</span> 字段<span class="number">1</span>，字段<span class="number">2</span>，字段<span class="number">3</span> <span class="keyword">from</span> 表名</span><br></pre></td></tr></table></figure><h2 id="函数"><a href="#函数" class="headerlink" title="函数"></a>函数</h2><ol><li>系统函数</li><li>自定义函数<ol><li>标量值函数（返回单个值）</li><li>表值函数（返回查询结果）</li></ol></li></ol><h3 id="创建函数"><a href="#创建函数" class="headerlink" title="创建函数"></a>创建函数</h3><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">create</span> <span class="keyword">function</span> 函数名(@传入值 传入值类型) <span class="keyword">returns</span> 返回值类型</span><br><span class="line"><span class="keyword">as</span> </span><br><span class="line"><span class="keyword">begin</span></span><br><span class="line"><span class="keyword">declare</span> @返回值 返回值类型</span><br><span class="line"><span class="keyword">select</span> @返回值 <span class="operator">=</span> </span><br><span class="line"><span class="keyword">return</span> @返回值</span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure><h4 id="返回值类型的情况"><a href="#返回值类型的情况" class="headerlink" title="返回值类型的情况"></a>返回值类型的情况</h4><ol><li><p>可以有其他逻辑代码</p><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">create</span> <span class="keyword">function</span> 函数名(@传入值 传入值类型) <span class="keyword">returns</span> @返回值 <span class="keyword">table</span></span><br><span class="line">(</span><br><span class="line">字段<span class="number">1</span> 数据类型，</span><br><span class="line">字段<span class="number">2</span> 数据类型，</span><br><span class="line">字段<span class="number">3</span> 数据类型，</span><br><span class="line">字段<span class="number">4</span> 数据类型</span><br><span class="line">)</span><br><span class="line"><span class="keyword">as</span></span><br><span class="line"><span class="keyword">begin</span></span><br><span class="line"><span class="keyword">insert into</span> @返回值</span><br><span class="line">查询语句</span><br><span class="line"><span class="keyword">return</span></span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure></li><li><p>函数体只能有return+sql查询结果</p><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">create</span> <span class="keyword">function</span> 函数名(@传入值 传入值类型) <span class="keyword">returns</span> <span class="keyword">table</span></span><br><span class="line"><span class="keyword">as</span></span><br><span class="line"><span class="keyword">return</span></span><br><span class="line">查询语句</span><br><span class="line">go</span><br></pre></td></tr></table></figure><p>函数调用</p><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">select</span> dbo.函数名()        <span class="comment">--返回值为一个值</span></span><br><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> 函数名()     <span class="comment">--返回值为一个表</span></span><br></pre></td></tr></table></figure></li></ol><h3 id="函数删除"><a href="#函数删除" class="headerlink" title="函数删除"></a>函数删除</h3><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">drop</span> <span class="keyword">function</span> 函数名</span><br></pre></td></tr></table></figure><h2 id="触发器"><a href="#触发器" class="headerlink" title="触发器"></a>触发器</h2><h3 id="分类"><a href="#分类" class="headerlink" title="分类"></a>分类</h3><ol><li>instead of  事前触发器</li><li>after  事后触发器</li></ol><h3 id="创建触发器"><a href="#创建触发器" class="headerlink" title="创建触发器"></a>创建触发器</h3><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">create</span> <span class="keyword">trigger</span> 触发器名称 <span class="keyword">on</span> 表名 [after<span class="operator">|</span>instead <span class="keyword">of</span>] [<span class="keyword">insert</span><span class="operator">|</span><span class="keyword">delete</span><span class="operator">|</span><span class="keyword">update</span>]</span><br><span class="line"><span class="keyword">as</span> </span><br><span class="line">语句</span><br><span class="line">go</span><br><span class="line"></span><br><span class="line">   inserted插入的临时表</span><br><span class="line">   deleted删除的临时表</span><br></pre></td></tr></table></figure><p>   数据更新实际上是先删除再插入</p><h2 id="存储过程"><a href="#存储过程" class="headerlink" title="存储过程"></a>存储过程</h2><p>存储过程与函数的主要区别：存储过程可以被其他程序调用，函数一般用于select语句中</p><h3 id="创建存储过程"><a href="#创建存储过程" class="headerlink" title="创建存储过程"></a>创建存储过程</h3><h4 id="存储过程创建"><a href="#存储过程创建" class="headerlink" title="存储过程创建"></a>存储过程创建</h4><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">create</span> proc 存储过程名 @变量<span class="number">1</span>名 变量类型，@变量<span class="number">2</span>名 变量类型，@变量<span class="number">3</span>名 变量类型 output，@变量<span class="number">4</span>名 变量类型 output</span><br><span class="line"><span class="keyword">as</span></span><br><span class="line">语句</span><br><span class="line">go</span><br></pre></td></tr></table></figure><h4 id="存储过程的调用"><a href="#存储过程的调用" class="headerlink" title="存储过程的调用"></a>存储过程的调用</h4><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">exec</span> 存储过程名 变量值<span class="number">1</span>，变量值<span class="number">2</span>，变量名 output，变量名 output   <span class="comment">--前两个用于输入，后两个用于接收，如果变量名(第三四个)已经赋值了则具有输入输出功能</span></span><br></pre></td></tr></table></figure><h4 id="删除存储过程"><a href="#删除存储过程" class="headerlink" title="删除存储过程"></a>删除存储过程</h4><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">drop</span> proc 存储过程名</span><br></pre></td></tr></table></figure>]]></content:encoded>
      
      
      <category domain="https://blog.itrf.cn/tags/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</category>
      
      <category domain="https://blog.itrf.cn/tags/SQL-Server/">SQL Server</category>
      
    </item>
    
    <item>
      <title>Git 推送失败问题解决指南</title>
      <link>https://blog.itrf.cn/posts/9c16/</link>
      <guid>https://blog.itrf.cn/posts/9c16/</guid>
      <pubDate>2025-07-20T04:41:27.000Z</pubDate>
      
      <description>Git 推送失败问题解决指南</description>
      
      
      <enclosure url="https://t.alcy.cc/ycy?_r_=cec59ac4-5090-4eb2-4a2f-db0787f6bbc7" type="image"/>
      
      
      <content:encoded><![CDATA[<p>当执行 <code>git push -u origin master</code> 时遇到类似以下错误：</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">error: failed to push some refs to &#x27;http://xxxx.cn/vip/xxx.git&#x27;</span><br><span class="line">To http://xxxx.cn/vip/xxx.git</span><br><span class="line">!refs/heads/master:refs/heads/master[rejected] (fetch first)</span><br><span class="line">Done</span><br><span class="line">hint: Updates were rejected because the remote contains work that you do not</span><br><span class="line">hint: have locally. This is usually caused by another repository pushing to</span><br><span class="line">hint: the same ref. If you want to integrate the remote changes, use</span><br><span class="line">hint: &#x27;git pull&#x27; before pushing again.</span><br></pre></td></tr></table></figure><p>或简化版错误：<br><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">[rejected]   master -&gt; master (fetch first)    </span><br><span class="line">error: failed to push some refs</span><br></pre></td></tr></table></figure></p><p>这是 Git 常见的版本冲突问题，原因是远程仓库存在本地没有的更新，为防止覆盖远程变更而拒绝推送。</p><h2 id="推荐解决方案"><a href="#推荐解决方案" class="headerlink" title="推荐解决方案"></a>推荐解决方案</h2><p>最简洁高效的处理方式是执行以下命令：<br><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 拉取远程master分支并以rebase方式整合本地更改</span></span><br><span class="line">git pull --rebase origin master</span><br><span class="line"></span><br><span class="line"><span class="comment"># 完成整合后再次推送（-u参数会建立本地与远程分支的关联）</span></span><br><span class="line">git push -u origin master</span><br></pre></td></tr></table></figure><br>这种方式会：</p><ol><li>将远程最新更改下载到本地</li><li>以线性方式重新应用你的本地提交</li><li>建立本地与远程分支的关联关系</li></ol><h2 id="备选解决方案（适用于复杂冲突）"><a href="#备选解决方案（适用于复杂冲突）" class="headerlink" title="备选解决方案（适用于复杂冲突）"></a>备选解决方案（适用于复杂冲突）</h2><p>如果遇到复杂的代码冲突，可使用标准合并流程：</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 拉取远程更新并合并到本地</span></span><br><span class="line">git pull origin master</span><br><span class="line"></span><br><span class="line"><span class="comment"># 若出现冲突，编辑冲突文件后标记为已解决</span></span><br><span class="line">git add &lt;冲突的文件路径&gt;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 提交合并结果</span></span><br><span class="line">git commit -m <span class="string">&quot;合并远程更新并解决冲突&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 推送本地更改</span></span><br><span class="line">git push origin master</span><br></pre></td></tr></table></figure><h2 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h2><ul><li><code>git pull --rebase</code> 会改写提交历史，保持提交记录更整洁</li><li>多人协作时，推送前先拉取是良好习惯</li><li>解决冲突时需仔细检查代码，确保逻辑正确</li><li>首次推送分支时，<code>-u</code> 参数只需使用一次，后续可直接用 <code>git push</code><br>以上方法可解决绝大多数推送失败问题，推荐优先使用 <code>git pull --rebase</code> 方式，它能更优雅地处理分支同步问题。</li></ul>]]></content:encoded>
      
      
      <category domain="https://blog.itrf.cn/tags/Git/">Git</category>
      
      <category domain="https://blog.itrf.cn/tags/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</category>
      
    </item>
    
    <item>
      <title>正则表达式学习笔记</title>
      <link>https://blog.itrf.cn/posts/3a56/</link>
      <guid>https://blog.itrf.cn/posts/3a56/</guid>
      <pubDate>2025-07-20T04:27:11.000Z</pubDate>
      
      <description>正则表达式学习笔记</description>
      
      
      <enclosure url="https://t.alcy.cc/moez?_r_=c023dde6-b6aa-9412-68b3-7f8a94ce3383" type="image"/>
      
      
      <content:encoded><![CDATA[<blockquote><p>参考<a href="https://www.runoob.com/regexp/regexp-tutorial.html">正则表达式 – 教程 | 菜鸟教程</a></p></blockquote><h2 id="1-什么是正则表达式？"><a href="#1-什么是正则表达式？" class="headerlink" title="1.什么是正则表达式？"></a>1.什么是正则表达式？</h2><p><em>正则表达式（Regular Expression，简称 Regex 或 RegExp）是一种用来匹配字符串中字符组合的模式。</em></p><p>简单来说就是一种匹配内容的模板。</p><h2 id="2-正则表达式的作用"><a href="#2-正则表达式的作用" class="headerlink" title="2.正则表达式的作用"></a>2.正则表达式的作用</h2><ul><li><strong>查找：</strong> 在文本中找到特定模式的内容</li><li><strong>替换：</strong> 将符合某种模式的文本替换为其他内容</li><li><strong>验证：</strong> 检查输入的数据是否符合预期格式</li><li><strong>提取：</strong> 从复杂文本中提取需要的信息</li></ul><h2 id="3-正则表达式的模式"><a href="#3-正则表达式的模式" class="headerlink" title="3.正则表达式的模式"></a>3.正则表达式的模式</h2><ul><li>字面值字符：例如字母、数字、空格等，可以直接匹配它们自身。</li><li>特殊字符：例如点号 <code>.</code>、星号 <code>*</code>、加号 <code>+</code>、问号 <code>?</code> 等，它们具有特殊的含义和功能。</li><li>字符类：用方括号 <code>[ ]</code> 包围的字符集合，用于匹配方括号内的任意一个字符。</li><li>元字符：例如 <code>\d</code>、<code>\w</code>、<code>\s</code> 等，用于匹配特定类型的字符，如数字、字母、空白字符等。</li><li>量词：例如 <code>{n}</code>、<code>{n,}</code>、<code>{n,m}</code> 等，用于指定匹配的次数或范围。</li><li>边界符号：例如 <code>^</code>、<code>$</code>、<code>\b</code>、<code>\B</code> 等，用于匹配字符串的开头、结尾或单词边界位置。</li></ul><h2 id="4-正则表达式元字符和特性"><a href="#4-正则表达式元字符和特性" class="headerlink" title="4.正则表达式元字符和特性"></a>4.正则表达式元字符和特性</h2><h4 id="字符匹配"><a href="#字符匹配" class="headerlink" title="字符匹配"></a>字符匹配</h4><ul><li>普通字符：普通字符按照字面意义进行匹配，例如匹配字母 “a” 将匹配到文本中的 “a” 字符。</li><li>元字符：元字符具有特殊的含义，例如 <code>\d</code> 匹配任意数字字符，<code>\w</code> 匹配任意字母数字字符，<code>.</code> 匹配任意字符（除了换行符）等。</li></ul><h4 id="基本元字符"><a href="#基本元字符" class="headerlink" title="基本元字符"></a>基本元字符</h4><p><code>.</code> (点号)</p><ul><li><p>匹配除换行符(<code>\n</code>)外的任意单个字符</p><p>示例：<code>a.b</code> 匹配 “aab”, “a1b”, “a b” 等</p></li></ul><p><code>^</code> (脱字符)</p><ul><li>匹配字符串的开始位置</li><li>示例：<code>^abc</code> 匹配以 “abc” 开头的字符串</li></ul><p><code>$</code> (美元符)</p><ul><li>匹配字符串的结束位置</li><li>示例：<code>xyz$</code> 匹配以 “xyz” 结尾的字符串</li></ul><p><code>\</code> (反斜杠)</p><ul><li>转义字符，使后面的字符失去特殊含义</li><li>示例：<code>\.</code> 匹配实际的点号而不是任意字符</li></ul><h4 id="量词"><a href="#量词" class="headerlink" title="量词"></a>量词</h4><ul><li><code>*</code>：匹配前面的模式零次或多次。</li><li><code>+</code>：匹配前面的模式一次或多次。</li><li><code>?</code>：匹配前面的模式零次或一次。</li><li><code>{n}</code>：匹配前面的模式恰好 n 次。</li><li><code>{n,}</code>：匹配前面的模式至少 n 次。</li><li><code>{n,m}</code>：匹配前面的模式至少 n 次且不超过 m 次。</li></ul><h4 id="字符类"><a href="#字符类" class="headerlink" title="字符类"></a>字符类</h4><ul><li><code>[ ]</code>：匹配括号内的任意一个字符。例如，<code>[abc]</code> 匹配字符 “a”、”b” 或 “c”。</li><li><code>[^ ]</code>：匹配除了括号内的字符以外的任意一个字符。例如，<code>[^abc]</code> 匹配除了字符 “a”、”b” 或 “c” 以外的任意字符。</li><li><code>[A-Z]</code>：表示一个区间，匹配所有大写字母，[a-z] 表示所有小写字母。</li></ul><h4 id="边界匹配"><a href="#边界匹配" class="headerlink" title="边界匹配"></a>边界匹配</h4><ul><li><code>^</code>：匹配字符串的开头。</li><li><code>$</code>：匹配字符串的结尾。</li><li><code>\b</code>：匹配单词边界。</li><li><code>\B</code>：匹配非单词边界。</li></ul><h4 id="分组和选择元字符"><a href="#分组和选择元字符" class="headerlink" title="分组和选择元字符"></a>分组和选择元字符</h4><p><code>()</code> (圆括号)</p><ul><li>定义子表达式或捕获组</li><li>示例：<code>(ab)+</code> 匹配 “ab”, “abab” 等</li></ul><p><code>|</code> (竖线)</p><ul><li>表示”或”关系</li><li>示例：<code>cat|dog</code> 匹配 “cat” 或 “dog”</li></ul><h4 id="特殊字符"><a href="#特殊字符" class="headerlink" title="特殊字符"></a>特殊字符</h4><p><code>\d</code></p><ul><li>匹配任意数字，等价于 <code>[0-9]</code></li></ul><p><code>\D</code></p><ul><li>匹配任意非数字，等价于 <code>[^0-9]</code></li></ul><p><code>\w</code></p><ul><li>匹配任意单词字符(字母、数字、下划线)，等价于 <code>[a-zA-Z0-9_]</code></li></ul><p><code>\W</code></p><ul><li>匹配任意非单词字符，等价于 <code>[^a-zA-Z0-9_]</code></li></ul><p><code>\s</code></p><ul><li>匹配任意空白字符(空格、制表符、换行符等)</li></ul><p><code>\S</code></p><ul><li>匹配任意非空白字符</li></ul><h4 id="非打印符"><a href="#非打印符" class="headerlink" title="非打印符"></a>非打印符</h4><div class="table-container"><table><thead><tr><th style="text-align:left">字符</th><th style="text-align:left">描述</th></tr></thead><tbody><tr><td style="text-align:left">\cx</td><td style="text-align:left">匹配由x指明的控制字符。例如， \cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则，将 c 视为一个原义的 ‘c’ 字符。</td></tr><tr><td style="text-align:left">\f</td><td style="text-align:left">匹配一个换页符。等价于 \x0c 和 \cL。</td></tr><tr><td style="text-align:left">\n</td><td style="text-align:left">匹配一个换行符。等价于 \x0a 和 \cJ。</td></tr><tr><td style="text-align:left">\r</td><td style="text-align:left">匹配一个回车符。等价于 \x0d 和 \cM。</td></tr><tr><td style="text-align:left">\s</td><td style="text-align:left">匹配任何空白字符，包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。注意 Unicode 正则表达式会匹配全角空格符。</td></tr><tr><td style="text-align:left">\S</td><td style="text-align:left">匹配任何非空白字符。等价于 [ ^ \f\n\r\t\v]。</td></tr><tr><td style="text-align:left">\t</td><td style="text-align:left">匹配一个制表符。等价于 \x09 和 \cI。</td></tr><tr><td style="text-align:left">\v</td><td style="text-align:left">匹配一个垂直制表符。等价于 \x0b 和 \cK。</td></tr></tbody></table></div><h2 id="5-常用修饰符"><a href="#5-常用修饰符" class="headerlink" title="5.常用修饰符"></a>5.常用修饰符</h2><h4 id="1-i-ignore-case-忽略大小写"><a href="#1-i-ignore-case-忽略大小写" class="headerlink" title="1. i (ignore case) - 忽略大小写"></a>1. <code>i</code> (ignore case) - 忽略大小写</h4><ul><li>使匹配不区分大小写</li><li>示例：<code>/abc/i</code> 可以匹配 “abc”, “Abc”, “ABC” 等</li><li>支持语言：几乎所有正则表达式实现（JavaScript、PHP、Python等）</li></ul><h4 id="2-g-global-全局匹配"><a href="#2-g-global-全局匹配" class="headerlink" title="2. g (global) - 全局匹配"></a>2. <code>g</code> (global) - 全局匹配</h4><ul><li>查找所有匹配项，而不是在第一个匹配后停止</li><li>示例：在字符串 “ababab” 中，<code>/ab/g</code> 会匹配所有三个 “ab”</li><li>支持语言：JavaScript、PHP等</li></ul><h4 id="3-m-multiline-多行模式"><a href="#3-m-multiline-多行模式" class="headerlink" title="3. m (multiline) - 多行模式"></a>3. <code>m</code> (multiline) - 多行模式</h4><ul><li>改变 <code>^</code> 和 <code>$</code> 的行为，使其匹配每行的开头和结尾，而不仅是整个字符串的开头和结尾</li><li>示例：在多行字符串中，<code>/^abc/m</code> 会匹配每行开头的 “abc”</li><li>支持语言：JavaScript、PHP、Python、Perl等</li></ul><h4 id="4-s-single-line-dotall-单行模式"><a href="#4-s-single-line-dotall-单行模式" class="headerlink" title="4. s (single line/dotall) - 单行模式"></a>4. <code>s</code> (single line/dotall) - 单行模式</h4><ul><li>使点号 <code>.</code> 匹配包括换行符在内的所有字符</li><li>在JavaScript中称为”dotall”模式，使用 <code>/s</code> 修饰符</li><li>示例：<code>/a.b/s</code> 可以匹配 “a\nb”</li><li>支持语言：PHP、Perl、Python(作为<code>re.DOTALL</code>)、JavaScript(ES2018+)</li></ul><h4 id="5-u-unicode-Unicode模式"><a href="#5-u-unicode-Unicode模式" class="headerlink" title="5. u (unicode) - Unicode模式"></a>5. <code>u</code> (unicode) - Unicode模式</h4><ul><li>启用完整的Unicode支持</li><li>正确处理UTF-16代理对和Unicode字符属性</li><li>示例：<code>/\p{Script=Greek}/u</code> 可以匹配希腊字母</li><li>支持语言：JavaScript、PHP等</li></ul><h4 id="6-y-sticky-粘性匹配"><a href="#6-y-sticky-粘性匹配" class="headerlink" title="6. y (sticky) - 粘性匹配"></a>6. <code>y</code> (sticky) - 粘性匹配</h4><ul><li>从目标字符串的当前位置开始匹配（使用<code>lastIndex</code>属性）</li><li>类似于<code>^</code>锚点，但针对的是匹配的起始位置</li><li>示例：在JavaScript中，<code>/a/y</code> 会从<code>lastIndex</code>开始匹配 “a”</li><li>支持语言：JavaScript</li></ul><h4 id="7-x-extended-扩展模式"><a href="#7-x-extended-扩展模式" class="headerlink" title="7. x (extended) - 扩展模式"></a>7. <code>x</code> (extended) - 扩展模式</h4><ul><li>忽略模式中的空白和注释，使正则表达式更易读</li><li>示例：在PHP中，<code>/a b c/x</code> 等同于 <code>/abc/</code></li><li>支持语言：PHP、Perl、Python(作为<code>re.VERBOSE</code>)</li></ul><h2 id="6-贪婪与非贪婪量词"><a href="#6-贪婪与非贪婪量词" class="headerlink" title="6.贪婪与非贪婪量词"></a>6.贪婪与非贪婪量词</h2><p>默认情况下，量词(<code>*</code>, <code>+</code>, <code>?</code>, <code>{}</code>)是贪婪的，会尽可能多地匹配字符。在量词后加<code>?</code>可使其变为非贪婪(懒惰)模式：</p><ul><li><code>*?</code>：零次或多次，但尽可能少</li><li><code>+?</code>：一次或多次，但尽可能少</li><li><code>??</code>：零次或一次，但尽可能少</li><li><code>{n,m}?</code>：n到m次，但尽可能少</li></ul><p>示例：<code>&lt;.*?&gt;</code> 匹配HTML标签时不会跨标签匹配</p><h2 id="7-断言"><a href="#7-断言" class="headerlink" title="7.断言"></a>7.断言</h2><p>断言（Assertion）是正则表达式中用于指定匹配位置的元字符，它们不匹配任何实际字符，而是匹配字符之间的位置。</p><p>简单来说，断言就是”条件”，它要求目标字符串必须满足某些条件，但不会消耗字符。</p><h4 id="断言的特点"><a href="#断言的特点" class="headerlink" title="断言的特点"></a>断言的特点</h4><ol><li><strong>零宽度</strong>：不占用匹配字符的位置</li><li><strong>条件检查</strong>：只检查是否满足特定条件</li><li><strong>不影响匹配结果</strong>：仅作为匹配的约束条件</li></ol><h4 id="断言类型"><a href="#断言类型" class="headerlink" title="断言类型"></a>断言类型</h4><p><code>(?=...)</code> (正向肯定预查)</p><ul><li>匹配后面跟着特定模式的位置</li><li>示例：<code>Windows(?=95|98)</code> 匹配后面跟着95或98的”Windows”</li></ul><p><code>(?!...)</code> (正向否定预查)</p><ul><li>匹配后面不跟着特定模式的位置</li><li>示例：<code>Windows(?!95|98)</code> 匹配后面不跟着95或98的”Windows”</li></ul><p><code>(?&lt;=...)</code> (反向肯定预查)</p><ul><li>匹配前面是特定模式的位置</li><li>示例：<code>(?&lt;=95|98)Windows</code> 匹配前面是95或98的”Windows”</li></ul><p><code>(?&lt;!...)</code> (反向否定预查)</p><ul><li>匹配前面不是特定模式的位置</li><li>示例：<code>(?&lt;!95|98)Windows</code> 匹配前面不是95或98的”Windows”</li></ul><h2 id="8-运算符优先级"><a href="#8-运算符优先级" class="headerlink" title="8.运算符优先级"></a>8.运算符优先级</h2><p>以下是一些常见正则表达式运算符按照优先级从高到低的顺序：</p><ul><li><p><strong>转义符号：</strong> <code>\</code> 是用于转义其他特殊字符的转义符号。它具有最高的优先级。</p><p>示例：<code>\d</code>、<code>\.</code> 等，其中 <code>\d</code> 匹配数字，<code>\.</code> 匹配点号。</p></li><li><p><strong>括号：</strong> 圆括号 <code>()</code> 用于创建子表达式，具有高于其他运算符的优先级。</p><p>示例：<code>(abc)+</code> 匹配 “abc” 一次或多次。</p></li><li><p><strong>量词：</strong> 量词指定前面的元素可以重复的次数。</p><p>示例：<code>a*</code> 匹配零个或多个 “a”。</p></li><li><p><strong>字符类：</strong> 字符类使用方括号 <code>[]</code> 表示，用于匹配括号内的任意字符。</p><p>示例：<code>[aeiou]</code> 匹配任何一个元音字母。</p></li><li><p><strong>断言：</strong> 断言是用于检查字符串中特定位置的条件的元素。</p><p>示例：<code>^</code> 表示行的开头，<code>$</code> 表示行的结尾。</p></li><li><p><strong>连接：</strong> 连接在没有其他运算符的情况下表示字符之间的简单连接。</p><p>示例：<code>abc</code> 匹配 “abc”。</p></li><li><p><strong>管道：</strong> 管道符号 <code>|</code> 表示”或”关系，用于在多个模式之间选择一个。</p><p>示例：<code>cat|dog</code> 匹配 “cat” 或 “dog”。</p></li></ul><h2 id="9-分组和引用"><a href="#9-分组和引用" class="headerlink" title="9.分组和引用"></a>9.分组和引用</h2><div class="table-container"><table><thead><tr><th style="text-align:left">概念</th><th style="text-align:left">语法</th><th style="text-align:left">用途</th></tr></thead><tbody><tr><td style="text-align:left">捕获分组</td><td style="text-align:left"><code>(pattern)</code></td><td style="text-align:left">捕获匹配内容并分配编号</td></tr><tr><td style="text-align:left">非捕获分组</td><td style="text-align:left"><code>(?:pattern)</code></td><td style="text-align:left">分组但不捕获</td></tr><tr><td style="text-align:left">命名分组</td><td style="text-align:left"><code>(?P&lt;name&gt;pattern)</code> (Python)</td><td style="text-align:left">为分组指定名称</td></tr><tr><td style="text-align:left">反向引用</td><td style="text-align:left"><code>\1</code>, <code>\2</code> 等</td><td style="text-align:left">引用前面匹配的分组</td></tr><tr><td style="text-align:left">命名反向引用</td><td style="text-align:left"><code>(?P=name)</code> (Python)</td><td style="text-align:left">按名称引用分组</td></tr><tr><td style="text-align:left">替换引用</td><td style="text-align:left"><code>$1</code>, <code>$2</code> 或 <code>\1</code>, <code>\2</code></td><td style="text-align:left">在替换字符串中引用分组</td></tr></tbody></table></div>]]></content:encoded>
      
      
      <category domain="https://blog.itrf.cn/tags/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</category>
      
      <category domain="https://blog.itrf.cn/tags/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F/">正则表达式</category>
      
    </item>
    
  </channel>
</rss>