<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>yfscret (河西)</title>
    <link>https://ruby-china.org/yfscret</link>
    <description>持之以恒、坚持学习</description>
    <language>en-us</language>
    <item>
      <title>                                                                                                                                                       内容审核（头像审核、昵称审核）技术对比，阿里云内容安全审核 api 和大模型 LLM 的 PK 对比</title>
      <description>&lt;h2 id="一、背景与目标"&gt;一、背景与目标&lt;/h2&gt;&lt;h3 id="1.1 为什么需要做对比"&gt;1.1 为什么需要做对比&lt;/h3&gt;
&lt;p&gt;内容审核是社区、电商等业务的基础能力，需对用户上传的&lt;strong&gt;头像/图片&lt;/strong&gt;和&lt;strong&gt;昵称/文本&lt;/strong&gt;进行合规检测。我们之前一直使用的是阿里云的内容审核 api 来检测的，但是偶尔会有检测出错的情况，尤其是头像的广告引流，偶尔会有识别不出来的情况。因此考虑使用大模型能力来做个测试对比：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;传统审核 API&lt;/strong&gt;：如阿里云 profilePhotoCheck、text_scan 等&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LLM 多模态审核&lt;/strong&gt;：通过通义、GPT、Claude 等大模型的提示词限制来判断（我们是通过 Dify 工作流来提供的 api 接口来服务业务系统）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;不同方案在&lt;strong&gt;耗时、费用、准确度&lt;/strong&gt;上差异显著，需要系统对比后做出技术选型。&lt;/p&gt;
&lt;h3 id="1.2 对比目标"&gt;1.2 对比目标&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;梳理各方案（阿里云、通义、GPT、Claude 等）的&lt;strong&gt;价格与准确性&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;通过实际测试，验证&lt;strong&gt;阿里云 API&lt;/strong&gt; 与 &lt;strong&gt;Dify + LLM&lt;/strong&gt; 的耗时、费用、准确度&lt;/li&gt;
&lt;li&gt;给出最终选型与实施建议&lt;/li&gt;
&lt;/ul&gt;

&lt;hr&gt;
&lt;h2 id="二、先直接给出结论"&gt;二、先直接给出结论&lt;/h2&gt;&lt;table class="table table-bordered table-striped"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th&gt;场景&lt;/th&gt;
&lt;th&gt;选型&lt;/th&gt;
&lt;th&gt;理由&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;图片审核&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Dify + qwen-vl-flash&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;费用约 7 元/万张，低于阿里云 15 元；耗时优于 qwen-vl-plus；准确度满足需求&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;昵称审核&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Dify + qwen-plus-latest&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;费用约 2.6 元/万次，低于阿里云 7.5 元；对引流、联系方式等更敏感&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;兜底/强合规&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;阿里云&lt;/td&gt;
&lt;td&gt;延迟低、稳定，可作为主流程失败或高并发时的补充&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="三、方案调研"&gt;三、方案调研&lt;/h2&gt;&lt;h3 id="3.1 昵称/文本审核：准确性与价格"&gt;3.1 昵称/文本审核：准确性与价格&lt;/h3&gt;&lt;table class="table table-bordered table-striped"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th&gt;方案&lt;/th&gt;
&lt;th&gt;准确性&lt;/th&gt;
&lt;th&gt;价格 (元/万次)&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;通义 qwen-plus&lt;/td&gt;
&lt;td&gt;高&lt;/td&gt;
&lt;td&gt;约 3.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;通义 qwen-max&lt;/td&gt;
&lt;td&gt;最高&lt;/td&gt;
&lt;td&gt;约 50–60（按 250 input + 80 output token 估算）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;通义 qwen-turbo&lt;/td&gt;
&lt;td&gt;中高&lt;/td&gt;
&lt;td&gt;约 1.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek-V3&lt;/td&gt;
&lt;td&gt;中&lt;/td&gt;
&lt;td&gt;约 7.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-4o&lt;/td&gt;
&lt;td&gt;精确率高、召回率低&lt;/td&gt;
&lt;td&gt;约 4–5&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;h3 id="3.2 头像/图片审核：准确性与价格"&gt;3.2 头像/图片审核：准确性与价格&lt;/h3&gt;&lt;table class="table table-bordered table-striped"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th&gt;方案&lt;/th&gt;
&lt;th&gt;准确性&lt;/th&gt;
&lt;th&gt;价格 (元/万张)&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen-VL-Max&lt;/td&gt;
&lt;td&gt;最高&lt;/td&gt;
&lt;td&gt;约 19.8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;阿里云 profilePhotoCheck&lt;/td&gt;
&lt;td&gt;高（专项）&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen-VL-Plus&lt;/td&gt;
&lt;td&gt;中高&lt;/td&gt;
&lt;td&gt;约 9.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen-VL-Flash&lt;/td&gt;
&lt;td&gt;中高&lt;/td&gt;
&lt;td&gt;约 7（实测）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude 3.5 Sonnet&lt;/td&gt;
&lt;td&gt;高&lt;/td&gt;
&lt;td&gt;约 200–300（按单张约 $0.003–0.004 换算）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-4o vision&lt;/td&gt;
&lt;td&gt;高&lt;/td&gt;
&lt;td&gt;约 20–45（随分辨率与 token 波动）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;h3 id="3.3 阿里云现有方案（对比基准）"&gt;3.3 阿里云现有方案（对比基准）&lt;/h3&gt;&lt;table class="table table-bordered table-striped"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;价格 (元/万次或元/万张)&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;文本审核（增强版）&lt;/td&gt;
&lt;td&gt;约 7.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;图片/头像审核（profilePhotoCheck）&lt;/td&gt;
&lt;td&gt;约 15&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="四、我们的实际对比测试"&gt;四、我们的实际对比测试&lt;/h2&gt;&lt;h3 id="4.1 测试设计"&gt;4.1 测试设计&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;图片&lt;/strong&gt;：来自我们实际生产数据的用户头像&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;昵称&lt;/strong&gt;：30 组预设样本（含正常、敏感、边界）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="4.2 图片审核实测"&gt;4.2 图片审核实测&lt;/h3&gt;&lt;h4 id="4.2.1 qwen-vl-plus vs qwen-vl-flash（50 张图，统一 q100 jpg）"&gt;4.2.1 qwen-vl-plus vs qwen-vl-flash（50 张图，统一 q100 jpg）&lt;/h4&gt;
&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/yfscret/29ee1274-bbb8-49f1-975e-07e8233e57bc.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/yfscret/4c74040a-76be-48e2-b0f4-4e4b025f147b.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/yfscret/52fb40d1-ebad-4435-a994-41a49c99da1a.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;注意这张图，引流内容不容易被发现，阿里云 api 就检测通过了，大模型精准识别&lt;/li&gt;
&lt;/ul&gt;
&lt;table class="table table-bordered table-striped"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th&gt;方案&lt;/th&gt;
&lt;th&gt;总耗时 (ms)&lt;/th&gt;
&lt;th&gt;总费用 (元)&lt;/th&gt;
&lt;th&gt;单次平均耗时 (ms)&lt;/th&gt;
&lt;th&gt;万张预估费用 (元)&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;阿里云&lt;/td&gt;
&lt;td&gt;37,781&lt;/td&gt;
&lt;td&gt;0.075&lt;/td&gt;
&lt;td&gt;756&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;qwen-vl-plus-latest&lt;/td&gt;
&lt;td&gt;157,416&lt;/td&gt;
&lt;td&gt;0.048&lt;/td&gt;
&lt;td&gt;3,148&lt;/td&gt;
&lt;td&gt;9.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;qwen-vl-flash&lt;/td&gt;
&lt;td&gt;127,056&lt;/td&gt;
&lt;td&gt;0.035&lt;/td&gt;
&lt;td&gt;2,541&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;7&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;结论&lt;/strong&gt;：qwen-vl-flash 费用更低、耗时更短、准确率和 qwen-vl-plus-latest 相当，选型为图片审核方案。&lt;/p&gt;
&lt;h4 id="4.2.2 原图 vs 800px宽格式化为jpg(image/resize,w_800/quality,q_90/format,jpg"&gt;4.2.2 原图 vs 800px 宽格式化为 jpg(image/resize,w_800/quality,q_90/format,jpg&lt;/h4&gt;
&lt;p&gt;)（80 张图，同一 Dify 工作流）&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;做这个对比是因为现在用户的手机拍照随便都是 5M 起步，如果用原图的话，大模型先下载图片再转成输入 token 就比较大，比较慢了，所以先格式化为宽 800 的 jpg（十几 M 的图片变几百 K）用作大模型输入。
&lt;img src="https://l.ruby-china.com/photo/yfscret/4be68387-63c8-4a43-bb68-16f7124cedea.png!large" title="" alt=""&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;table class="table table-bordered table-striped"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th&gt;方案&lt;/th&gt;
&lt;th&gt;总耗时 (ms)&lt;/th&gt;
&lt;th&gt;总费用 (元)&lt;/th&gt;
&lt;th&gt;单次平均耗时 (ms)&lt;/th&gt;
&lt;th&gt;万张预估费用 (元)&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;阿里云&lt;/td&gt;
&lt;td&gt;66,126&lt;/td&gt;
&lt;td&gt;0.12&lt;/td&gt;
&lt;td&gt;827&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dify 原图&lt;/td&gt;
&lt;td&gt;253,539&lt;/td&gt;
&lt;td&gt;0.081&lt;/td&gt;
&lt;td&gt;3,169&lt;/td&gt;
&lt;td&gt;10.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dify 800px jpg(q90)&lt;/td&gt;
&lt;td&gt;281,114&lt;/td&gt;
&lt;td&gt;0.078&lt;/td&gt;
&lt;td&gt;3,514&lt;/td&gt;
&lt;td&gt;9.7&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt; &lt;strong&gt;说明：&lt;/strong&gt; 上面这个测试的原图我们保存时候已经转过格式压缩过了，所以这个对比其实是失败的，没有测试出 几兆那种原图和格式化后的图在费用上的差异，写出来只是给大家提供一下思路。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;结论&lt;/strong&gt;：最终采用 800px jpg q90 jpg 作为统一输入，兼顾清晰度与体积。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="4.3 昵称审核实测（30 组，使用 qwen-plus-latest）"&gt;4.3 昵称审核实测（30 组，使用 qwen-plus-latest）&lt;/h3&gt;
&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/yfscret/35880fb5-0018-4da3-bf26-575c67d50d24.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;table class="table table-bordered table-striped"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th&gt;方案&lt;/th&gt;
&lt;th&gt;总耗时 (ms)&lt;/th&gt;
&lt;th&gt;总费用 (元)&lt;/th&gt;
&lt;th&gt;单次平均耗时 (ms)&lt;/th&gt;
&lt;th&gt;万次预估费用 (元)&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;阿里云&lt;/td&gt;
&lt;td&gt;7,370&lt;/td&gt;
&lt;td&gt;0.023&lt;/td&gt;
&lt;td&gt;246&lt;/td&gt;
&lt;td&gt;7.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dify + qwen-plus-latest&lt;/td&gt;
&lt;td&gt;53,419&lt;/td&gt;
&lt;td&gt;0.008&lt;/td&gt;
&lt;td&gt;1,781&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2.6&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;h3 id="4.4 准确度对比"&gt;4.4 准确度对比&lt;/h3&gt;&lt;h4 id="图片审核"&gt;图片审核&lt;/h4&gt;&lt;table class="table table-bordered table-striped"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th&gt;能力&lt;/th&gt;
&lt;th&gt;阿里云&lt;/th&gt;
&lt;th&gt;LLM (qwen-vl)&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;政治敏感（领导人等）&lt;/td&gt;
&lt;td&gt;识别&lt;/td&gt;
&lt;td&gt;识别（含 inappropriate content 拒绝）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;平台水印/引流（抖音、小红书）&lt;/td&gt;
&lt;td&gt;识别&lt;/td&gt;
&lt;td&gt;识别&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;饰品/合规展示&lt;/td&gt;
&lt;td&gt;通过&lt;/td&gt;
&lt;td&gt;通过，可附带说明&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;复杂场景/边界&lt;/td&gt;
&lt;td&gt;规则为主&lt;/td&gt;
&lt;td&gt;语义理解更细&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;h4 id="昵称审核"&gt;昵称审核&lt;/h4&gt;&lt;table class="table table-bordered table-striped"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th&gt;测试用例&lt;/th&gt;
&lt;th&gt;阿里云&lt;/th&gt;
&lt;th&gt;Dify + qwen-plus-latest&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;小明、翠翠、珠宝爱好者&lt;/td&gt;
&lt;td&gt;通过&lt;/td&gt;
&lt;td&gt;通过&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VX123456、加我微信&lt;/td&gt;
&lt;td&gt;不通过&lt;/td&gt;
&lt;td&gt;不通过（联系方式引流）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;xhs_abc&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;通过&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;不通过&lt;/strong&gt;（含引流信息）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;闲鱼号 123&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;通过&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;不通过&lt;/strong&gt;（联系方式引流）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;结论&lt;/strong&gt;：qwen-plus-latest 对引流、联系方式类昵称更敏感，拦截更严格。&lt;/p&gt;

&lt;hr&gt;
&lt;h3 id="4.5 我们的 dify 工作流"&gt;4.5 我们的 dify 工作流&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;其实也可以不用 dify 工作流，直接在代码里调用大模型来识别，不过 dify 的好处就是切换大模型简单，可以随时更新其他大模型。
&lt;img src="https://l.ruby-china.com/photo/yfscret/287c59c3-b20e-4849-9125-3f57f4b49c0a.png!large" title="" alt=""&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;说明&lt;/strong&gt;：大模型的识别能力非常依赖提示词，但我们这种识别其实提示词很简单，后续如果有识别不出来的，把提示词修改一下即可。举例：比如昵称是”你们公司真下头“，这个调用传统内容审核 api 是通过的，但我们不想让他通过，使用传统内容审核就不好定制，但使用大模型的话，我们只用在提示词加一句，如果内容包含”下头“则视为审核不通过。大模型就会按审核不通过处理。是不是&lt;strong&gt;特别灵活&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id="4.6 关于费用怎么对比"&gt;4.6 关于费用怎么对比&lt;/h3&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;阿里云内容审核 api 有明确的文档说明每次调用多少钱&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;大模型是按输入输出 token 计费的，我们用来 format w800 jpg 格式的图片的话，那输入 token 就是图片和提示词，基本可以确定，输出我们也是严格限制了格式，所以也是确定的。另外 dify 每次调用都会返回 useage，我们保存到数据库即可。方便我们选型和统计
&lt;img src="https://l.ruby-china.com/photo/yfscret/a14162e0-7526-4ea1-8e8c-4e53f2ef3916.png!large" title="" alt=""&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="五、阿里云 API vs LLM 优劣势"&gt;五、阿里云 API vs LLM 优劣势&lt;/h2&gt;&lt;h3 id="5.1 阿里云内容审核 API"&gt;5.1 阿里云内容审核 API&lt;/h3&gt;&lt;table class="table table-bordered table-striped"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th&gt;优势&lt;/th&gt;
&lt;th&gt;劣势&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;延迟低&lt;/strong&gt;：图片约 800 ms，文本约 250 ms&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;费用高&lt;/strong&gt;：图片 15 元/万张，文本 7.5 元/万次&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;稳定&lt;/strong&gt;：成熟商用接口，SLA 保障&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;规则化&lt;/strong&gt;：依赖预设规则，边界场景易漏检&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;部署简单&lt;/strong&gt;：HTTP 调用即可&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;定制难&lt;/strong&gt;：无法按业务语义微调策略&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;合规背书&lt;/strong&gt;：符合监管要求&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;结果简单&lt;/strong&gt;：多为 pass/block，缺少详细说明&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;h3 id="5.2 LLM 多模态审核"&gt;5.2 LLM 多模态审核&lt;/h3&gt;&lt;table class="table table-bordered table-striped"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th&gt;优势&lt;/th&gt;
&lt;th&gt;劣势&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;成本低&lt;/strong&gt;：图片约 7 元/万张，文本约 2.6 元/万次，还有更低成本的大模型可选&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;延迟高&lt;/strong&gt;：图片 2.5–3 s，文本约 1.8 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;语义理解好&lt;/strong&gt;：能识别「饰品展示」「联系方式引流」等&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;依赖模型&lt;/strong&gt;：不当内容可能返回 400，需专门处理&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;结果更细&lt;/strong&gt;：可返回原因说明，便于运营排查&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;需自建工作流&lt;/strong&gt;：需维护 Dify 与模型配置&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;可定制&lt;/strong&gt;：prompt 可调整审核策略&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;稳定性&lt;/strong&gt;：依赖第三方模型服务可用性&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="六、最终选型与实施建议"&gt;六、最终选型与实施建议&lt;/h2&gt;&lt;h3 id="6.1 选型结论"&gt;6.1 选型结论&lt;/h3&gt;&lt;table class="table table-bordered table-striped"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th&gt;场景&lt;/th&gt;
&lt;th&gt;选型&lt;/th&gt;
&lt;th&gt;理由&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;图片审核&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Dify + qwen-vl-flash&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;费用最低（约 7 元/万张），耗时优于 plus，准确度满足需求&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;昵称审核&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Dify + qwen-plus-latest&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;费用低，对引流/联系方式更敏感&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;兜底/强合规&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;阿里云&lt;/td&gt;
&lt;td&gt;低延迟、稳定，可作为主流程失败或高并发时的补充&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;h3 id="6.2 实施建议"&gt;6.2 实施建议&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;图片输入&lt;/strong&gt;：统一使用 format q90 jpg（oss-process），平衡清晰度与体积&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;异常处理&lt;/strong&gt;：Dify 返回 400 且含「inappropriate content」时，按不通过处理&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;混合策略&lt;/strong&gt;：高并发或要求极低延迟时，可考虑阿里云；长尾审核、成本敏感时用 LLM&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;持续监控&lt;/strong&gt;：记录耗时、费用、通过率，定期对比不同方案表现&lt;/li&gt;
&lt;/ol&gt;</description>
      <author>yfscret</author>
      <pubDate>Fri, 13 Feb 2026 16:08:53 +0800</pubDate>
      <link>https://ruby-china.org/topics/44480</link>
      <guid>https://ruby-china.org/topics/44480</guid>
    </item>
    <item>
      <title>声网 ruby gem</title>
      <description>&lt;h2 id="声网 ruby gem ，地址：https://github.com/yfscret/agora-ruby"&gt;
&lt;a href="https://www.shengwang.cn/" rel="nofollow" target="_blank" title=""&gt;声网&lt;/a&gt; ruby gem，地址：&lt;a href="https://github.com/yfscret/agora-ruby" rel="nofollow" target="_blank"&gt;https://github.com/yfscret/agora-ruby&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;最近对接了&lt;a href="https://doc.shengwang.cn/doc/cloud-recording/restful/landing-page" rel="nofollow" target="_blank" title=""&gt;声网的云端视频录制功能&lt;/a&gt;（直播的时候，录主播和连麦用户的视频及声音），官方 sdk 中 ruby 语言只有生成 token 的代码，功能方面没有 sdk，就自己写了个 gem，其中 token 生成（含升级版 token2）是 复制的声网官方的代码，我加了个云端视频录制功能，欢迎大家使用和贡献更多功能&lt;/p&gt;</description>
      <author>yfscret</author>
      <pubDate>Fri, 16 May 2025 18:54:16 +0800</pubDate>
      <link>https://ruby-china.org/topics/44163</link>
      <guid>https://ruby-china.org/topics/44163</guid>
    </item>
    <item>
      <title>部署时产生 ActiveRecord::PreparedStatementCacheExpired 错误的原因及解决方案</title>
      <description>&lt;h2 id="一、问题："&gt;一、问题：&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;有时在 Postgres 上部署 Rails 应用程序时，可能会看到 ActiveRecord::PreparedStatementCacheExpired 错误。仅当在部署中运行迁移时才会发生这种情况。&lt;/li&gt;
&lt;li&gt;发生这种情况是因为 Rails 利用 Postgres 的缓存准备语句 (PreparedStatementCache) 功能来提高性能。这个功能在 rails 中默认是开启的。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="二、问题复现："&gt;二、问题复现：&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;我们可以用 rspec 来复现这个错误&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'not raise ActiveRecord::PreparedStatementCacheExpired'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
   &lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;
   &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_by_sql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ALTER TABLE users ADD new_metric_column integer;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transaction&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/yfscret/6bc94c36-fe85-451f-b1c5-d31751e5cee8.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="三、产生的原理："&gt;三、产生的原理：&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;rails 查询语句如&lt;code&gt;User.all&lt;/code&gt;被 active_record &lt;strong&gt;解析成 sql 语句&lt;/strong&gt;后，发送给数据库，&lt;strong&gt;先执行 PREPARE&lt;/strong&gt;预备语句，sql 语句会被解析、分析、优化并且重写。当后续&lt;strong&gt;发出一个 EXECUTE 命令&lt;/strong&gt;时，该预备语句会被规划并且执行。&lt;/li&gt;
&lt;li&gt; rails 会把查询语句存到&lt;code&gt;pg_prepared_statements&lt;/code&gt;中，以方便下次调用同类语句时候&lt;strong&gt;直接 execute&lt;/strong&gt; statements 中的语句，而不用再进行解析、分析、优化，避免重复工作，提高效率。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;
&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;
&lt;span class="c1"&gt;# 执行上面的2个查询后，用connection.instance_variable_get(:@statements)就可以看到缓存的准备语句&lt;/span&gt;
&lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance_variable_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:@statements&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;==&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ConnectionAdapters&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;PostgreSQLAdapter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;StatementPool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mh"&gt;0x00000001086b13c8&lt;/span&gt; 
&lt;span class="vi"&gt;@cache&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;78368&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$user&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, public-SELECT &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;users&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;.* FROM &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;users&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; ORDER BY &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;users&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; ASC LIMIT 
$1"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"a7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$user&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, public-SELECT &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;users&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;.* FROM &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;users&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; /* loading for inspect */ LIMIT $1"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"a8"&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
&lt;span class="vi"&gt;@statement_limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@connection&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c1"&gt;#&amp;lt;PG::Connection:0x00000001086b31a0&amp;gt;, @counter=8&amp;gt;&lt;/span&gt;

&lt;span class="c1"&gt;# 这个也可以看到，会在数据库中去查询&lt;/span&gt;
&lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'select * from pg_prepared_statements'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pg_prepared_statements&lt;/span&gt;
&lt;span class="o"&gt;==&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="s2"&gt;"a7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"SELECT &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;users&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;.* FROM &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;users&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; ORDER BY &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;users&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; ASC LIMIT $1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"2024-07-
11T07:03:06.891+00:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"{bigint}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"a8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"SELECT &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;users&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;.* FROM &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;users&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; /* loading for inspect 
*/ LIMIT $1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"2024-07-11T07:04:47.772+00:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"{bigint}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;在 Postgres 中，如果表的模式 (schema) 更改影响返回结果，则预准备语句缓存将失效。具体说就是给表增加、删除字段，或者修改字段的类型、长度等 ddl 操作。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如下面的例子，添加或删除字段后执行 SELECT 时，&lt;strong&gt;pg 数据库&lt;/strong&gt;就会抛出&lt;code&gt;cached plan must not change result type&lt;/code&gt;，rails 中&lt;strong&gt;active_record&lt;/strong&gt;获取到这个错误然后会抛出&lt;code&gt;ActiveRecord::PreparedStatementCacheExpired&lt;/code&gt;&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;ALTER&lt;/span&gt; &lt;span class="no"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="no"&gt;ADD&lt;/span&gt; &lt;span class="no"&gt;COLUMN&lt;/span&gt; &lt;span class="n"&gt;new_column&lt;/span&gt; &lt;span class="n"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="no"&gt;ALTER&lt;/span&gt; &lt;span class="no"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="no"&gt;DROP&lt;/span&gt; &lt;span class="no"&gt;COLUMN&lt;/span&gt; &lt;span class="n"&gt;old_column&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;添加或删除列&lt;/span&gt;&lt;span class="err"&gt;，&lt;/span&gt;&lt;span class="n"&gt;然后执行&lt;/span&gt; &lt;span class="no"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="n"&gt;删除&lt;/span&gt; &lt;span class="n"&gt;old_column&lt;/span&gt; &lt;span class="n"&gt;列然后执行&lt;/span&gt; &lt;span class="no"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;old_column&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;部署服务中运行增、减、修改字段的迁移时，用户发出的查询语句会从预准备语句缓存中直接拿 sql 直接进行 excute，但这时候因为表结构变化了，预准备语句缓存就失效了，&lt;strong&gt;pg 数据库&lt;/strong&gt;就会抛出&lt;code&gt;cached plan must not change result type&lt;/code&gt;错误&lt;/li&gt;
&lt;li&gt;查看 active_record 源码中的&lt;code&gt;exec_cache&lt;/code&gt;方法，发现 rails 对 pg 的这个错误处理方式是：

&lt;ol&gt;
&lt;li&gt;事务 transaction 中，会直接抛出 &lt;code&gt;raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;事务外的会把缓存&lt;a href="/statements" class="user-mention" title="@statements"&gt;&lt;i&gt;@&lt;/i&gt;statements&lt;/a&gt;中的&lt;strong&gt;这句删除并 try&lt;/strong&gt;，重试后会重新解析、分析、优化 sql 语句并执行&lt;code&gt;prepare_statement&lt;/code&gt;方法放入预准备语句缓存中&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;ActiveRecord&lt;/span&gt;
  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;ConnectionHandling&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;exec_cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;binds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;materialize_transactions&lt;/span&gt;
      &lt;span class="n"&gt;mark_transaction_written_if_write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;update_typemap_for_default_timezone&lt;/span&gt;

      &lt;span class="n"&gt;stmt_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;prepare_statement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;binds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;type_casted_binds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;type_casted_binds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;binds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;binds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;type_casted_binds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stmt_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Dependencies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;interlock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;permit_concurrent_loads&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
          &lt;span class="vi"&gt;@connection.exec_prepared&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stmt_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;type_casted_binds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;StatementInvalid&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
      &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;is_cached_plan_failure?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="c1"&gt;# Nothing we can do if we are in a transaction because all commands&lt;/span&gt;
      &lt;span class="c1"&gt;# will raise InFailedSQLTransaction&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;in_transaction?&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;PreparedStatementCacheExpired&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cause&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="vi"&gt;@lock.synchronize&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
          &lt;span class="c1"&gt;# outside of transactions we can simply flush this query and retry&lt;/span&gt;
          &lt;span class="vi"&gt;@statements.delete&lt;/span&gt; &lt;span class="n"&gt;sql_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="k"&gt;retry&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;所以出现在事务 transaction 中的这个错误，就会导致事务回滚，对业务来说就是请求失败了，需要我们自己来处理&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="四、解决方法："&gt;四、解决方法：&lt;/h2&gt;&lt;h3 id="1. 禁用缓存准备语句功能（不推荐）"&gt;1. 禁用缓存准备语句功能（不推荐）&lt;/h3&gt;
&lt;p&gt;rails6 以上可以把 database 中 prepared_statements 设为 false 来禁用这个功能&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="ss"&gt;default: &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;
  &lt;span class="ss"&gt;adapter: &lt;/span&gt;&lt;span class="n"&gt;postgresql&lt;/span&gt;
  &lt;span class="ss"&gt;encoding: &lt;/span&gt;&lt;span class="n"&gt;unicode&lt;/span&gt;
  &lt;span class="ss"&gt;prepared_statements: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;rails6 以下没测试，如果上面的不行可以试试新建个初始化文件&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/initializers/disable_prepared_statements.rb:&lt;/span&gt;
&lt;span class="n"&gt;db_configuration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configurations&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;db_configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'prepared_statements'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;establish_connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db_configuration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;验证：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;
&lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'select * from pg_prepared_statements'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;
&lt;span class="o"&gt;==&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;结论：小型项目中其实禁用这个功能无所谓，性能几乎不影响，但是大型项目中，用户越多，越复杂的查询语句，这个功能带来的受益越大，所以可以根据实际情况来决定是否禁用&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="2. 使select * 变为 select id, name这样的具体字段， rails7中的官方解决方案就是这样的，但只能解决新增字段引起的报错"&gt;2. 使&lt;code&gt;select *&lt;/code&gt; 变为 &lt;code&gt;select id, name&lt;/code&gt;这样的具体字段，rails7 中的&lt;a href="https://github.com/rails/rails/pull/41718" rel="nofollow" target="_blank" title=""&gt;官方解决方案&lt;/a&gt;就是这样的，但只能解决新增字段引起的报错&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;rails7 中 enumerate_columns_in_select_statements 设为 true&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/application.rb&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;MyApp&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Application&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Application&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;active_record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enumerate_columns_in_select_statements&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;rails7 以下没有这个配置，可以用 ignored_columns 来实现&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApplicationRecord&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abstract_class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="c1"&gt;#__fake_column__是自定义的，不要是某个表中的字段就行，如果是[:id],那么 User.all就会被解析为select name from users,没有id了&lt;/span&gt;
  &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ignored_columns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:__fake_column__&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; 
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;结论：这个方案存在的问题是，增加字段可以完美解决，但是删除字段，还会出现报错，比如删除 name 字段后，预准备语句 select id, name from users 中的 name 不存在了，就会报错，删除字段可以在 User.rb 中增加 self.ignored_columns = [:name], 然后先重启服务，再进行部署，部署时候要最好把 self.ignored_columns = [:name] 删掉，避免以后再加回 name 字段后，select 不到，rails7 官方的方案也存在这个问题，所以这个方案感觉很麻烦&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="3. 重启rails应用"&gt;3. 重启 rails 应用&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;预准备语句缓存的生命周期只存在于一个数据库会话中，关闭数据库连接（重启应用会关闭原连接，重新建立新连接）那原来的预准备语句缓存就会清空，重启后的 sql 请求就会重新缓存预准备语句，就能正常拿到数据。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;结论：重启应用会出现短暂服务 502 不可用，当然部署应用时候也是要重启服务的，也会出现 502，所以最好是没人访问的时候（半夜？）进行部署，这样就会尽可能少的出现&lt;code&gt;PreparedStatementCacheExpired&lt;/code&gt;报错&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;### 4. 重写 &lt;code&gt;transaction&lt;/code&gt; 方法&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApplicationRecord&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;retried&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
      &lt;span class="k"&gt;super&lt;/span&gt;
    &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;PreparedStatementCacheExpired&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;retried&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="n"&gt;retried&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
        &lt;span class="k"&gt;retry&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;重写后代码里写事务的地方改为使用 &lt;code&gt;ApplicationRecord.transaction do ... end&lt;/code&gt; 或者 &lt;code&gt;MyModel.transaction&lt;/code&gt;或者 &lt;code&gt;MyModel.transaction&lt;/code&gt;或者&lt;code&gt;obj.transaction&lt;/code&gt;, 只要不用&lt;code&gt;ActiveRecord::Base.transaction&lt;/code&gt;就行&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;结论：重要提示：如果在事务中有发送电子邮件、post 到 API 或执行其他与外界交互的操作，这可能会导致其中一些操作偶尔发生两次。这就是为什么 Rails 官方不会自动执行重试，而是将其留给应用程序开发人员。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;我本人测试这个方法还是会继续报错&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="5. 重写 transaction 方法结合手动清除预准备语句缓存"&gt;5. 重写 &lt;code&gt;transaction&lt;/code&gt; 方法结合手动清除预准备语句缓存&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;既然只有在事务中产生的这个报错会影响业务，那就把事务方法重写一下，让先清除预准备语句缓存，然后执行事务代码，这样就不会报错了&lt;/li&gt;
&lt;li&gt;使用了&lt;code&gt;gem 'rails-settings-cached'&lt;/code&gt;，部署前先打开 &lt;code&gt;Setting.clear_prepared_statements_cache&lt;/code&gt;，部署完毕后再关掉，这样性能也基本不会有任何影响&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApplicationRecord&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abstract_class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;

  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clear_cache!&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;Setting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clear_prepared_statements_cache&lt;/span&gt;
      &lt;span class="k"&gt;super&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="五、最终答案"&gt;五、最终答案&lt;/h2&gt;
&lt;p&gt;经过我们的测试，最终使用 &lt;strong&gt;方案 5&lt;/strong&gt; 完美解决这个问题，已使用到生产环境，可以把&lt;code&gt;Setting.clear_prepared_statements_cache&lt;/code&gt;这个开关集成到部署脚本中就更方便了。&lt;/p&gt;</description>
      <author>yfscret</author>
      <pubDate>Thu, 11 Jul 2024 18:10:01 +0800</pubDate>
      <link>https://ruby-china.org/topics/43807</link>
      <guid>https://ruby-china.org/topics/43807</guid>
    </item>
    <item>
      <title>wiki 里缓存介绍链接错误</title>
      <description>&lt;p&gt;&lt;a href="/huacnlee" class="user-mention" title="@huacnlee"&gt;&lt;i&gt;@&lt;/i&gt;huacnlee&lt;/a&gt; 
&lt;img src="https://l.ruby-china.com/photo/yfscret/bb6e188c-7166-4c33-b4e5-e4a8019d37c8.png!large" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>yfscret</author>
      <pubDate>Wed, 09 Jun 2021 10:17:01 +0800</pubDate>
      <link>https://ruby-china.org/topics/41355</link>
      <guid>https://ruby-china.org/topics/41355</guid>
    </item>
    <item>
      <title>求解关于 log 中 set NAMES utf-8, @@SESSION.wati_timeout，EXPLAIN 的含义和解决方法</title>
      <description>&lt;p&gt;以前在本地开发，启动 rails s 时候，log 中会有这么一行 set  NAMES utf-8, 然后开发过程中，不会再出现这个，最近发现，过个十几秒没操作页面的话，再刷新一下页面或者任意发个 get , put, post 等请求，log 都会在发出请求后卡住一会，然后显示 set NAMES 这一行，比如：
&lt;img src="https://l.ruby-china.com/photo/2020/ef6ca9a1-607d-4aeb-afe0-3b8a3a7ec32f.png!large" title="" alt=""&gt;
有时会显示多行这 &lt;img src="https://l.ruby-china.com/photo/2020/11939e47-afe3-4f71-a404-1dc7498742ec.png!large" title="" alt=""&gt;
中间可能还会夹杂着 EXPLAIN，比如：&lt;img src="https://l.ruby-china.com/photo/2020/529c568d-8e1b-4fef-8043-b69944d1547e.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h4 id="请教以下问题："&gt;请教以下问题：&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;1. set NAMEs 这一行是干啥的&lt;/li&gt;
&lt;li&gt;2. EXPLAIN 这一行是干啥的&lt;/li&gt;
&lt;li&gt;3. 怎么能让 log 中不出现些，也就是让发送请求后直接读取数据库，而不是卡住先去 set NAMES 这些，不让 EXPLAIN&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="/huacnlee" class="user-mention" title="@huacnlee"&gt;&lt;i&gt;@&lt;/i&gt;huacnlee&lt;/a&gt; &lt;a href="/Rei" class="user-mention" title="@Rei"&gt;&lt;i&gt;@&lt;/i&gt;Rei&lt;/a&gt; &lt;a href="/rocLv" class="user-mention" title="@rocLv"&gt;&lt;i&gt;@&lt;/i&gt;rocLv&lt;/a&gt; 求大佬指导&lt;/p&gt;</description>
      <author>yfscret</author>
      <pubDate>Wed, 23 Sep 2020 11:29:15 +0800</pubDate>
      <link>https://ruby-china.org/topics/40428</link>
      <guid>https://ruby-china.org/topics/40428</guid>
    </item>
    <item>
      <title>[西安][市内可远程] 诚聘 ruby 初中级工程师一名</title>
      <description>&lt;h3 id="良山科技诚邀 Ruby 初中级工程师一名"&gt;良山科技诚邀 Ruby 初中级工程师一名&lt;/h3&gt;
&lt;hr&gt;
&lt;h4 id="待遇"&gt;待遇&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;8k 起&lt;/li&gt;
&lt;li&gt;其余面谈&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="工作地点"&gt;工作地点&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;西安&lt;/li&gt;
&lt;li&gt;西安市内可远程，不用打卡，每周视情况组织一到两次会议&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="岗位要求"&gt;岗位要求&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;2 年以上 Ruby on Rails 开发经验；&lt;/li&gt;
&lt;li&gt;熟悉 mysql 等关系型数据库；&lt;/li&gt;
&lt;li&gt;熟悉 HTML, CSS, Javascript, Jquery 等前端知识；&lt;/li&gt;
&lt;li&gt;会使用 Git&lt;/li&gt;
&lt;li&gt;良好的团队精神，能适应快节奏的工作环境；&lt;/li&gt;
&lt;li&gt;具有电子商务相关项目经验者优先考虑。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="工作职责"&gt;工作职责&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;后端编码开发&lt;/li&gt;
&lt;li&gt;编写自动化测试&lt;/li&gt;
&lt;li&gt;其他&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="联系方式"&gt;联系方式&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;微信：15829399791&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>yfscret</author>
      <pubDate>Wed, 11 Dec 2019 20:21:41 +0800</pubDate>
      <link>https://ruby-china.org/topics/39317</link>
      <guid>https://ruby-china.org/topics/39317</guid>
    </item>
    <item>
      <title>新手记录一次自动部署过程！mina+puma+nginx</title>
      <description>&lt;p&gt;第一次发帖，还不太会用 Markdown&lt;img title=":joy:" alt="😂" src="https://twemoji.ruby-china.com/2/svg/1f602.svg" class="twemoji"&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;为什么用 mina+puma？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;最开始一直是手动 ssh 到服务器进行部署，我也觉得挺好的，然后吧时间长了，就觉得好烦，然后就开始尝试自动部署。之前有跟着论坛的帖子用 Capistrano 和 Passenger 进行部署，是用&lt;code&gt;sudo apt-get install  nginx-extras passenger&lt;/code&gt;安装的 nginx，也是成功的，但是服务器加了 rtmp 服务，得用编译模式安装 nginx，然后 passenger 就不知道怎么搞到 nginx 里去了，还有一个原因是我看大家都说 mina 简单（作为新手我就需要简单的&lt;img title=":joy:" alt="😂" src="https://twemoji.ruby-china.com/2/svg/1f602.svg" class="twemoji"&gt; ），而且我一直觉 puma 是默认自带的，肯定有自带的好处啊，为啥那么多帖子都让用 Passenger，整不明白。所以果断就换了。废话不多说，开始吧！&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;安装 mina 和 mina-puma&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;在本地rails项目Gemfile中添加
gem 'mina'      #当前版本为1.2.3
gem 'mina-puma', require: false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后 bundle install&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;创建 Mina 的配置文件
cd 进入自己的本地 Rails 项目目录&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mina init
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;会生成 deploy.rb 文件
进行配置&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require 'mina/rails'
require 'mina/git'
require 'mina/puma'
require 'mina/rbenv'  # ruby版本控制用rbenv，rvm的话还要设置gemset

set :application_name, 'rails_app'    #给项目起名字，随便起
set :domain, 'username@ip'    #用户名@ip,用来ssh 登录服务器用的
set :deploy_to, '/home/ruihai/rails_app'    #服务器里想把项目部署的位置，可以没有rails_app那个文件夹，会自动创建
set :repository, 'git@xxxxxxxxt'   #项目git地址，也可以用https开头的
set :branch, 'master'     #想部署的分支

set :forward_agent, true     #使用本地的`SSH秘钥`去服务器执行`git pull`，这样`Git`上就不用设置`部署公钥`

#如果rails是5.2以上版本，就把下面的secrets.yml改成master.key
set :shared_files, fetch(:shared_files, []).push('config/database.yml', 'config/secrets.yml')

task :remote_environment do
  invoke :'rbenv:load'
end

task :setup do
  # command %{rbenv install 2.3.0 --skip-existing}
end

desc "Deploys the current version to the server."
task :deploy do
  deploy do
    invoke :'git:clone'
    invoke :'deploy:link_shared_paths'
    invoke :'bundle:install'
    invoke :'rails:db_migrate'
    invoke :'rails:assets_precompile'
    invoke :'deploy:cleanup'

    on :launch do
      in_path(fetch(:current_path)) do
        command %{mkdir -p tmp/}
        command %{touch tmp/restart.txt}
      end
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;服务器生成 Mina 的相关目录&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在本地&lt;code&gt;mina setup&lt;/code&gt;&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-----&amp;gt; Loading rbenv
-----&amp;gt; Setting up /home/ruihai/rails_app
       /home/ruihai
       总用量 24
       drwxrwxr-x  6 ruihai ruihai 4096 5月   5 18:23 .
       drwxr-xr-x 15 ruihai ruihai 4096 5月   7 13:41 ..
       lrwxrwxrwx  1 ruihai ruihai   34 5月   5 18:23 current -&amp;gt; /home/ruihai/rails_app/releases/3
       drwxrwxr-x  8 ruihai ruihai 4096 5月   5 18:23 releases
       drwxrwxr-x  7 ruihai ruihai 4096 5月   5 14:34 scm
       drwxrwxr-x  7 ruihai ruihai 4096 5月   5 14:37 shared
       drwxrwxr-x  2 ruihai ruihai 4096 5月   5 18:23 tmp
       # gitee.com:22 SSH-2.0-Basalt-1.2
       Connection to xxx.xxx.xxx.xxx closed.

       Elapsed time: 1.48 seconds

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里的遇到个坑，运行完这个命令终端里会提示远程服务器关闭，还是红色字体提示，我以为是报错了，其实没有报错。&lt;/p&gt;

&lt;p&gt;然后你需要 SSH 到服务器上，在生成的项目下 /shared/config目录中，新建以下两个文件&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;database.yml，直接把本地 database.yml 的内容拷进去&lt;/li&gt;
&lt;li&gt;secrets.yml，也是一样把本地 secrets.yml 的内容拷进去&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;在/shared/tmp 目录下 mkdir 新建 sockets 和 pids 两个目录（gem 'mina-puma'需要）&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;部署&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;本地运行&lt;code&gt;mina deploy&lt;/code&gt;&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-----&amp;gt; Creating a temporary build path
-----&amp;gt; Loading rbenv
-----&amp;gt; Fetching new git commits
-----&amp;gt; Using git branch 'master'
       正克隆到 '.'...
       完成。
-----&amp;gt; Using this git commit
       rushboy (cf7907e):
       &amp;gt; change Grindplayer.swf location
-----&amp;gt; Symlinking shared paths
-----&amp;gt; Installing gem dependencies using Bundler
       Using rake 12.3.2
       Using concurrent-ruby 1.1.3
       Using i18n 1.2.0
       Using minitest 5.11.3
       Using thread_safe 0.3.6
       Using tzinfo 1.2.5
       Using activesupport 5.1.6.1
       Using builder 3.2.3
       Using erubi 1.7.1
       Using mini_portile2 2.3.0
       Using nokogiri 1.8.5
       Using rails-dom-testing 2.0.3
       Using crass 1.0.4
       Using loofah 2.2.3
       Using rails-html-sanitizer 1.0.4
       Using actionview 5.1.6.1
       Using rack 2.0.6
       Using rack-test 1.1.0
       Using actionpack 5.1.6.1
       Using nio4r 2.3.1
       Using websocket-extensions 0.1.3
       Using websocket-driver 0.6.5
       Using actioncable 5.1.6.1
       Using globalid 0.4.1
       Using activejob 5.1.6.1
       Using mini_mime 1.0.1
       Using mail 2.7.1
       Using actionmailer 5.1.6.1
       Using activemodel 5.1.6.1
       Using arel 8.0.0
       Using activerecord 5.1.6.1
       Using bcrypt 3.1.12
       Using bundler 2.0.1
       Using cancancan 2.3.0
       Using mime-types-data 3.2018.0812
       Using mime-types 3.2.2
       Using carrierwave 1.3.1
       Using climate_control 0.2.0
       Using terrapin 0.6.0
       Using cocaine 0.6.0
       Using orm_adapter 0.5.0
       Using ckeditor 4.2.4
       Using coffee-script-source 1.12.2
       Using execjs 2.7.0
       Using coffee-script 2.4.1
       Using method_source 0.9.2
       Using thor 0.20.3
       Using railties 5.1.6.1
       Using coffee-rails 4.2.2
       Using ffi 1.9.25
       Using multi_json 1.13.1
       Using jbuilder 2.8.0
       Using kaminari-core 1.1.1
       Using kaminari-actionview 1.1.1
       Using kaminari-activerecord 1.1.1
       Using kaminari 1.1.1
       Using mini_magick 4.9.2
       Using mysql2 0.5.2
       Using puma 3.12.0
       Using sprockets 3.7.2
       Using sprockets-rails 3.2.1
       Using rails 5.1.6.1
       Using rails-i18n 5.1.2
       Using rb-fsevent 0.10.3
       Using rb-inotify 0.9.10
       Using sass-listen 4.0.0
       Using sass 3.7.2
       Using tilt 2.0.9
       Using sass-rails 5.0.7
       Using turbolinks-source 5.2.0
       Using turbolinks 5.2.0
       Using uglifier 4.1.20
       Bundle complete! 23 Gemfile dependencies, 72 gems now installed.
       Gems in the groups development and test were not installed.
       Bundled gems are installed into `./vendor/bundle`
-----&amp;gt; DB migrations unchanged; skipping DB migration
-----&amp;gt; Precompiling asset files
yarn install v1.15.2
info No lockfile found.
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
Done in 0.16s.
-----&amp;gt; Cleaning up old releases (keeping 5)
       /home/ruihai/rails_app/tmp/build-155731703215529
-----&amp;gt; Deploy finished
-----&amp;gt; Building
-----&amp;gt; Moving build to /home/ruihai/rails_app/releases/4
-----&amp;gt; Build finished
-----&amp;gt; Launching
-----&amp;gt; Updating the /home/ruihai/rails_app/current symlink
       /home/ruihai/rails_app/current
-----&amp;gt; Done. Deployed version 4
       Connection to xxx.xxx.xxx.xxx closed.

       Elapsed time: 6.94 seconds
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;成功后会显示 Done. Deployed version x  然后连接你远程服务器关闭。也是红色显示（- - ！）。
至此，自动部署完毕。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nginx 配置
在 nginx.conf 里添加&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;upstream rails_app {
   server unix:///home/ruihai/rails_app/shared/tmp/sockets/puma.sock;
}

server {
   listen 80;
       server_name 你的域名或者ip;
       root /home/ruihai/rails_app/current/public;

   location / {
     proxy_pass http://rails_app;
     #下面这两个是为rtmp设置的，因为rtmp在同一台机器的nginx.conf里配置，不加下面的就播放不出来，我也不知道为什么
      proxy_set_header Origin http://$Host;
      proxy_set_header Host $Host:$server_port;
   }

   location ~ ^/assets/ {
       expires 1y;
       add_header Cache-Control public;
       add_header ETag "";
       break;
   }     
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后重启 nginx，不管你的服务器是 ubuntu 还是 centos，不管你是用 apt-get install nginx 还是 yum install nginx 或者是./configure 编译安装的 nginx，请都用这个命令/etc/nginx/sbin/nginx -s reload 重启 nginx，通用。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;最后浏览器打开你的域名或者 ip 就可以看到网站啦！&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;本人还是个新手小白，发帖是记录一下自己的部署过程，也给需要的朋友提供个参考，其实有很多不明白的地方，也可能有错误的地方，望大家批评指导&lt;/strong&gt;&lt;/p&gt;</description>
      <author>yfscret</author>
      <pubDate>Wed, 08 May 2019 20:37:55 +0800</pubDate>
      <link>https://ruby-china.org/topics/38496</link>
      <guid>https://ruby-china.org/topics/38496</guid>
    </item>
    <item>
      <title>求助：rails 中使用 kindeditor 富文本编辑器网络上传没问题，本地上传错误</title>
      <description>&lt;p&gt;直接使用的'rails_kindeditor' gem，在本地 local:3000 使用本地上传图片和网络上传图片都没问题，在线上上传图片时候只能用网络上传，本地上传错误，传不上去，求大神帮助&lt;/p&gt;</description>
      <author>yfscret</author>
      <pubDate>Sat, 04 Aug 2018 20:39:30 +0800</pubDate>
      <link>https://ruby-china.org/topics/37281</link>
      <guid>https://ruby-china.org/topics/37281</guid>
    </item>
    <item>
      <title>新手求《Rails 5 敏捷开发中文版》，英文的看不懂，已崩溃</title>
      <description>&lt;p&gt;&lt;img title=":sob:" alt="😭" src="https://twemoji.ruby-china.com/2/svg/1f62d.svg" class="twemoji"&gt; &lt;img title=":sob:" alt="😭" src="https://twemoji.ruby-china.com/2/svg/1f62d.svg" class="twemoji"&gt; &lt;img title=":sob:" alt="😭" src="https://twemoji.ruby-china.com/2/svg/1f62d.svg" class="twemoji"&gt; 英语水平比较次，看的崩溃了，哪位哥哥有用完的中文版，求一本&lt;img src="https://l.ruby-china.com/photo/2018/67590fee-265c-4072-9b05-2fca9301840a.png!large" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>yfscret</author>
      <pubDate>Thu, 07 Jun 2018 00:16:32 +0800</pubDate>
      <link>https://ruby-china.org/topics/36916</link>
      <guid>https://ruby-china.org/topics/36916</guid>
    </item>
  </channel>
</rss>
