<?xml version="1.0" encoding="utf-8" ?><rss version="2.0"><channel><title><![CDATA[itmigrate的博客]]></title><description><![CDATA[]]></description><link>https://blog.csdn.net/itmigrate</link><language>zh-cn</language><generator>https://blog.csdn.net/</generator><copyright><![CDATA[Copyright &copy; itmigrate]]></copyright><item><title><![CDATA[向量检索不是万能药：聊聊 Embedding 检索系统里容易被忽略的工程陷阱]]></title><link>https://blog.csdn.net/itmigrate/article/details/169524332</link><guid>https://blog.csdn.net/itmigrate/article/details/169524332</guid><author>itmigrate</author><pubDate>Fri, 30 Oct 2026 08:02:14 +0800</pubDate><description><![CDATA[库里存量向量是旧版本模型产出，新的查询向量是新版本，整个向量空间对齐被破坏，召回效果悄悄变差，而且这个变化是渐进的，不会一下子报错，很难感知。但结构化解析本身成本很高，PDF、扫描件、富文本、Markdown 格式混杂的时候，文档解析失败、乱码、表格丢失，都会直接污染入库的向量数据。正确的做法，应该把向量召回当成粗筛，先召回一批候选，再通过元数据做过滤，再走重排模型做精细化排序，把业务规则提前加进去，而不是全部丢给大模型自己分辨。也有的切片很短，只包含零散关键词，向量特征很弱，很容易和别的文档混淆。]]></description><category></category></item><item><title><![CDATA[配置中心的坑：配置热更新，远不止监听回调那么简单]]></title><link>https://blog.csdn.net/itmigrate/article/details/169429391</link><guid>https://blog.csdn.net/itmigrate/article/details/169429391</guid><author>itmigrate</author><pubDate>Thu, 29 Oct 2026 08:38:03 +0800</pubDate><description><![CDATA[更麻烦的是中间件地址变更：很多客户端只会在初始化的时候建立连接，回调里只改地址变量，客户端不会主动断开旧连接，旧连接还在持续收发请求，新连接才开始创建，一段时间内新旧地址同时在调用，一部分请求打到老实例上。没有版本号比对的话，服务不知道收到的是不是最新配置，被动覆盖，出现莫名的配置回退。配置中心是按 key 单独推送，有可能地址先更新成功，密钥还没推送过来，短时间内地址是新的，密钥是旧的，所有请求鉴权失败。热更新不是加分项，是需要严格管控的能力，能少热更就少热更，复杂的资源类配置，重启虽然笨，但是最稳。]]></description><category></category></item><item><title><![CDATA[本地开发环境，别让 “能跑” 掩盖和线上的差异]]></title><link>https://blog.csdn.net/itmigrate/article/details/169329599</link><guid>https://blog.csdn.net/itmigrate/article/details/169329599</guid><author>itmigrate</author><pubDate>Wed, 28 Oct 2026 08:29:17 +0800</pubDate><description><![CDATA[经常出现的情况：开发改了业务逻辑，顺带修改了配置项，本地手动改了配置文件验证通过，但是没有同步到配置中心，上线之后服务读取旧配置，行为完全不对。还有操作系统自带的底层库，像 glibc，线上服务器版本老旧，本地新版本编译出来的二进制，直接跑会提示版本不兼容。时区切换、跨零点、时钟回拨、文件大小写、信号退出这些很难在本地手动复现的场景，交给单元测试、集成测试覆盖，而不是靠人工跑一遍主流程。本地和线上的差异点，分散在很多不容易留意的地方，而且大多不是代码 bug，是运行环境带来的隐性偏差。]]></description><category></category></item><item><title><![CDATA[Rust 里的所有权误区：不是不写 clone 就叫高性能]]></title><link>https://blog.csdn.net/itmigrate/article/details/169232086</link><guid>https://blog.csdn.net/itmigrate/article/details/169232086</guid><author>itmigrate</author><pubDate>Tue, 27 Oct 2026 08:33:36 +0800</pubDate><description><![CDATA[很多开发者包完 Arc，又套 Mutex，层层包装，最后写出来的代码，借用检查是过了，但锁竞争变多，性能还不如简单把数据 clone 一份。，觉得只要出现 clone，就是写得不好、有不必要的内存拷贝，追求全链路引用传递。换个思路，不是想着怎么躲开 clone 和 Arc，而是理解每一份数据的所有者到底是谁，生命周期到哪里结束，很多编译报错自然就有清晰的解法。当时需求是解析一行行结构化日志，提取里面的用户标识、事件类型，过滤之后，把符合条件的数据塞进一个 channel，交给后面的协程做聚合入库。]]></description><category></category></item><item><title><![CDATA[接口超时配置，大部分项目都配得很随意]]></title><link>https://blog.csdn.net/itmigrate/article/details/169133429</link><guid>https://blog.csdn.net/itmigrate/article/details/169133429</guid><author>itmigrate</author><pubDate>Mon, 26 Oct 2026 08:42:33 +0800</pubDate><description><![CDATA[假设超时 5s，重试 3 次，每次重试之间还有间隔，很容易出现：第一次请求 2.5s 超时，立刻重试第二次，又 2.5s，第三次还没发出去，总的时间已经超过给上游承诺的超时时间。因为网络传输、序列化、B 自身逻辑都要消耗时间，再加上重试的时间窗口，如果 A 的超时和 B 内部调用 C 的超时拉平，下游还在处理，上游已经超时放弃，上游重试的请求和还在跑的老请求并行，资源就会双倍消耗。短期看报错少了，问题被掩盖，但是一旦下游抖动，调用方大量线程阻塞堆积，堆积到一定程度，新请求进不来，服务直接假死。]]></description><category></category></item><item><title><![CDATA[前端静态资源缓存策略，不是加个 Cache-Control 就完事]]></title><link>https://blog.csdn.net/itmigrate/article/details/169037622</link><guid>https://blog.csdn.net/itmigrate/article/details/169037622</guid><author>itmigrate</author><pubDate>Sun, 25 Oct 2026 08:22:35 +0800</pubDate><description><![CDATA[如果 CDN 或者源站响应慢，大量并发请求上来，静态资源都要回源校验，回源带宽直接被打高，CDN 的加速效果大打折扣。同时配合 ETag 做弱校验，CDN 侧单独配置，区分 html 和静态资源的缓存规则，不要一刀切。如果旧版本的静态资源已经被清理，或者 CDN 节点已经过期淘汰，就算切回旧的 html，里面的资源地址访问 404，页面直接白屏。之前做页面改版，踩过一个很常见但又很磨人的坑：后端接口已经上新，页面打包也发了，可一部分用户打开还是旧页面，清浏览器缓存才正常。浏览器的刷新行为也会带来差异。]]></description><category></category></item><item><title><![CDATA[为什么很多重试逻辑，只在测试环境能跑通]]></title><link>https://blog.csdn.net/itmigrate/article/details/168955052</link><guid>https://blog.csdn.net/itmigrate/article/details/168955052</guid><author>itmigrate</author><pubDate>Sat, 24 Oct 2026 09:35:44 +0800</pubDate><description><![CDATA[等到重试的时候，直接拿同一个 body 对象去发第二次请求，流是空的，下游拿到空报文，返回和第一次一样的报错，从日志上看就像重试没有生效。连续三次无间隔重试，在下游短暂抖动的时候还好，如果下游是过载限流，连续重试等于瞬间放大流量，把下游直接打垮，本来只是短暂故障，重试之后直接演变成雪崩。另外还有链路追踪的问题。很多项目的 trace id 只在第一次请求的时候生成，重试请求没有更新 span，排查的时候只能看到第一次失败记录，后面几次重试的日志散落在别的地方，排查的时候很难把多次请求串起来。]]></description><category></category></item><item><title><![CDATA[关于接口幂等，很多方案都卡在边界条件上]]></title><link>https://blog.csdn.net/itmigrate/article/details/168838471</link><guid>https://blog.csdn.net/itmigrate/article/details/168838471</guid><author>itmigrate</author><pubDate>Fri, 23 Oct 2026 08:21:58 +0800</pubDate><description><![CDATA[最早踩的一个坑，是前端重复提交。前端收不到成功结果，自动重试，又拿新的 token 发请求，后端判定是新请求，直接生成第二条记录。代码捕获到唯一索引报错，不能直接返回 “重复提交”，要解析错误信息，解析逻辑在不同数据库版本、不同驱动下表现不一样，很容易误判。而且状态机只约束状态流转，如果上游回调短时间并发打到同一个订单，不加控制的话，两段业务逻辑还是可能并行执行，引发超卖、重复入账。当一个幂等请求已经处理成功，后续相同流水号过来，不能再去走一遍完整业务逻辑，最好缓存处理结果，直接返回上次成功响应。]]></description><category></category></item><item><title><![CDATA[别把 “可观测” 等同于日志多]]></title><link>https://blog.csdn.net/itmigrate/article/details/168731211</link><guid>https://blog.csdn.net/itmigrate/article/details/168731211</guid><author>itmigrate</author><pubDate>Thu, 22 Oct 2026 08:34:06 +0800</pubDate><description><![CDATA[ERROR 日志才是重点，报错的时候，除了错误堆栈，只带上能定位问题的最小上下文：traceId、用户标识、请求 ID、核心业务参数，无关字段全部砍掉。想观察趋势、做告警，比如 P99 耗时、错误率、队列堆积长度，交给监控指标，指标轻量化，聚合之后占用资源很小，适合长期观察和告警。但面向用户的核心链路，流量高，每多一行日志都是成本，就要克制。之前和同事复盘故障的时候聊起一个现象：很多项目上线之后，日志疯狂打，INFO 级别铺满磁盘，告警通道不停弹消息，但真的出事的时候，翻半天找不到有用信息。]]></description><category></category></item><item><title><![CDATA[前后端联调，别总把问题卡在接口文档上]]></title><link>https://blog.csdn.net/itmigrate/article/details/168625976</link><guid>https://blog.csdn.net/itmigrate/article/details/168625976</guid><author>itmigrate</author><pubDate>Wed, 21 Oct 2026 08:34:33 +0800</pubDate><description><![CDATA[还有枚举值，只给了数字，没有对应的业务含义，前端不知道 0、1、2 分别代表什么状态，只能反复去问后端。我们之前也试过不少方案，一开始是用 Word、Markdown 手写接口文档，后端改了参数忘了同步，前端照着文档写，调接口一直报错，来回反复核对，浪费很多时间。文档只是一个契约，不能替代沟通。遇到复杂业务接口，光看文字很难理解场景，我们会简单开几分钟小会，说清楚这个接口是在什么场景调用，有没有限流、缓存之类的隐藏逻辑。好的联调状态，不是文档写得有多华丽，而是双方拿到文档之后，对同一个接口的理解是一致的。]]></description><category></category></item><item><title><![CDATA[浙江地区起名注意事项——盘点专业靠谱的姓名学老师]]></title><link>https://blog.csdn.net/itmigrate/article/details/168557425</link><guid>https://blog.csdn.net/itmigrate/article/details/168557425</guid><author>itmigrate</author><pubDate>Tue, 20 Oct 2026 17:38:51 +0800</pubDate><description><![CDATA[优先匹配擅长领域新生儿取名，可以优先看综合型从业者；注册公司、做品牌，优先考虑擅长商业命名的；喜欢古风诗词名字，可以选择主打诗词典故方向的。跨领域咨询，很容易出现考虑不周的情况。远离焦虑营销只要一上来就放大负面问题、恐吓必须额外花钱化解的，尽量避开。靠谱的从业者，会客观分析名字优缺点，理性看待姓名的作用，不会神化姓名的影响力。客户来源可以作为参考维度短视频粉丝量只能当作参考，对比流量大号，长期依靠老客户推荐、深耕多年口碑的从业者，风格往往更加务实。]]></description><category></category></item><item><title><![CDATA[前端项目里，Object.is 并不是 === 的平替]]></title><link>https://blog.csdn.net/itmigrate/article/details/168533566</link><guid>https://blog.csdn.net/itmigrate/article/details/168533566</guid><author>itmigrate</author><pubDate>Tue, 20 Oct 2026 09:55:22 +0800</pubDate><description><![CDATA[写在这里也算记录一下：不要无脑跟风替换相等判断，先想清楚，业务到底要不要区分。很多教程一笔带过，实际业务里很容易忽略这个差异带来的问题。排查的时候一开始没往这方面想，日志打印看着都是 0，断点仔细看才发现一个是。出事的场景是这样的：我们有一个进度计算，后端返回的进度在边界条件下会算出。，本意是想规避 0 和 -0 的坑，结果在一处状态对比上出了小 bug。这个坑不算大，属于 JS 语言底层的历史特性。最近重构老项目的时候，看到同事写的一段判断逻辑，直接把。，一旦遇上，这种隐蔽的差异特别耗排查时间。]]></description><category></category></item><item><title><![CDATA[接口签名踩坑：时间戳校验不是越短越好]]></title><link>https://blog.csdn.net/itmigrate/article/details/168421164</link><guid>https://blog.csdn.net/itmigrate/article/details/168421164</guid><author>itmigrate</author><pubDate>Mon, 19 Oct 2026 09:15:24 +0800</pubDate><description><![CDATA[但还有个小细节，对接方有定时任务批量调用，任务启动的时候一瞬间发几百个请求，机器 CPU 打高，本地缓存的 nonce 校验逻辑锁竞争，也偶尔出现异常，后面把 nonce 的存储换成了 redis 轻量化 set，只存 key，过期自动清理，不再做复杂的计数。30 秒的窗口，扣掉链路耗时，留给业务本身的容错空间就只剩十几秒，稍微排队就直接过期。之前对接过一家，客户端传的是毫秒，服务端按秒解析，时间差直接放大一千倍，本地测试怎么都复现不了，上测试环境直接全挂，这种低级问题排查起来特别耗精力。]]></description><category></category></item><item><title><![CDATA[应用程序的优雅退出，远不止加一个 shutdown hook]]></title><link>https://blog.csdn.net/itmigrate/article/details/168330531</link><guid>https://blog.csdn.net/itmigrate/article/details/168330531</guid><author>itmigrate</author><pubDate>Sun, 18 Oct 2026 10:43:09 +0800</pubDate><description><![CDATA[20s 之后，不管任务有没有跑完，都放弃等待，主动退出，避免被 SIGKILL 粗暴收割，同时日志打印卡住的任务上下文，方便事后排查。标准做法是：收到终止信号第一件事，立刻把就绪探针置为失败，等待端点从负载均衡摘除，等待一小段 draining 时间，消化已经在链路里的存量请求，之后不再接收新请求。我们团队后来增加了专门的验证方式：向容器进程发送 SIGTERM，同时观察访问链路、消息消费、日志，检查有没有任务中断、重复、丢失，并且在压测并发的情况下复现终止流程。解决信号传递之后，新的问题来了：关闭顺序。]]></description><category></category></item><item><title><![CDATA[别盲目追求连接池更大，很多慢服务是连接池配错拖垮的]]></title><link>https://blog.csdn.net/itmigrate/article/details/168221839</link><guid>https://blog.csdn.net/itmigrate/article/details/168221839</guid><author>itmigrate</author><pubDate>Sat, 17 Oct 2026 09:23:04 +0800</pubDate><description><![CDATA[MySQL 里，每一个连接都要分配线程栈、内存，连接过多，数据库内核的线程调度开销上升，上下文切换变多，哪怕没有 SQL 在跑，数据库本身响应也会变慢。低峰期大量连接空挂着，高峰期这些连接还容易长时间不活动，被数据库侧的 wait_timeout 断掉，应用这边拿到已经失效的连接，执行 SQL 抛异常。池多了之后，数据库侧连接总数又会上涨，而且代码里要区分不同场景选择数据源，对开发有约束，一旦有人写错数据源，隔离就失效了。到现在我自己的习惯是，新服务上线，数据库连接池先往保守了配，而不是留很大余量。]]></description><category></category></item><item><title><![CDATA[分布式锁失效，很多时候根本不是锁本身的问题]]></title><link>https://blog.csdn.net/itmigrate/article/details/168097328</link><guid>https://blog.csdn.net/itmigrate/article/details/168097328</guid><author>itmigrate</author><pubDate>Fri, 16 Oct 2026 08:16:54 +0800</pubDate><description><![CDATA[我第一反应，是不是 Redis 主从同步延迟，主节点拿到锁之后还没同步到从节点，主节点宕机，从节点升主，新主上没有锁记录，第二个请求直接拿到锁。还有超时配置的本末倒置。很多文章把分布式锁的讨论重心放在 “怎么实现一把不会丢的锁”，却很少提：锁只是一个辅助手段，真正决定数据安全的，是临界区的边界、线程上下文、超时约束和业务状态设计。后来那个库存业务，我们没有继续换锁方案，而是把跨线程执行的逻辑改掉，临界区收敛，所有数据库写操作都在拿到锁的同一个线程里同步完成，第三方通知移到扣减成功之后、锁释放之后异步发送。]]></description><category></category></item><item><title><![CDATA[容器日志采集踩坑：容器删了，日志跟着丢]]></title><link>https://blog.csdn.net/itmigrate/article/details/167996273</link><guid>https://blog.csdn.net/itmigrate/article/details/167996273</guid><author>itmigrate</author><pubDate>Thu, 15 Oct 2026 09:31:03 +0800</pubDate><description><![CDATA[容器模式完全不一样，容器的日志文件本身在容器的可写层，Pod 销毁，整个文件系统直接被清理。每一个 Pod 多跑一个 Filebeat，集群里 Pod 数量一多，资源开销上涨很明显，而且配置分散，每个业务 yaml 都要维护 sidecar、挂载卷，后续统一改采集规则很麻烦，运维成本上来了。改完上线，偶尔还是丢。一次性 Job、短生命周期的 Pod，统一走 sidecar 采集，emptyDir 挂载，并且给 sidecar 增加延迟退出逻辑，等 Filebeat 把缓冲区的数据上报完毕，再结束进程。]]></description><category></category></item><item><title><![CDATA[PHP 导出大 CSV 时内存溢出的一次排查]]></title><link>https://blog.csdn.net/itmigrate/article/details/167879872</link><guid>https://blog.csdn.net/itmigrate/article/details/167879872</guid><author>itmigrate</author><pubDate>Wed, 14 Oct 2026 08:36:05 +0800</pubDate><description><![CDATA[跑了几次，数据量越大越慢，到后面 offset 很大的时候，数据库查询耗时暴涨。两个处理方式，一个是单独给导出接口加长超时，另一个是如果数据量超大，不实时在线导出，改成异步任务，后台生成文件存到对象存储，生成完给用户发通知去下载。fputcsv 本身会自动处理逗号、换行、引号，不要自己手动拼接字段、加转义，很容易把数据搞坏，单元格内容一旦自带换行，表格直接错位。另外，非缓冲查询有局限，一个连接同一时刻只能有一个未读完的结果集，游标没读完之前，不能再执行别的 SQL，这点在复杂业务里很容易踩雷。]]></description><category></category></item><item><title><![CDATA[静态资源版本号，藏在页面加载里的暗坑]]></title><link>https://blog.csdn.net/itmigrate/article/details/167780829</link><guid>https://blog.csdn.net/itmigrate/article/details/167780829</guid><author>itmigrate</author><pubDate>Tue, 13 Oct 2026 09:30:46 +0800</pubDate><description><![CDATA[浏览器依据 url 缓存文件，只要地址不变，它就优先读本地缓存，不会去拉服务器上新的文件。就算资源链接带了哈希，如果 CDN 节点缓存时间设置过长，节点没回源，用户拿到的依旧是旧文件。所有 js、css 每次打开页面都重新下载，首屏加载速度直接掉一截，带宽消耗也涨上去，缓存本来用来提速的优势，直接废掉。别一上来就归咎于用户浏览器，顺着缓存链路，从页面、资源地址、CDN 一层层捋，很快就能定位。缓存不是简单加个参数就能搞定，它是一整条链路的配合，哪一环疏漏，线上就会冒出这种断断续续、很难稳定复现的 bug。]]></description><category></category></item><item><title><![CDATA[过度封装，比代码重复更可怕的架构隐患]]></title><link>https://blog.csdn.net/itmigrate/article/details/167668316</link><guid>https://blog.csdn.net/itmigrate/article/details/167668316</guid><author>itmigrate</author><pubDate>Mon, 12 Oct 2026 09:46:22 +0800</pubDate><description><![CDATA[它不会直接引发报错，不会导致服务崩溃，却会持续降低开发效率，让简单需求变得复杂，让普通迭代层层受阻。最终写出一套高度抽象、层层嵌套、通用性极强，但业务适配极差的代码架构。原本三行代码可以写完的独立逻辑，为了适配公共框架，需要重写适配类、实现接口、重写方法、配置参数。在代码规范的普及下，绝大多数开发者都谨记一句话：杜绝代码冗余，尽量抽离封装、复用通用逻辑。公共方法为了适配A场景、B场景、C场景，不断堆砌if判断、参数兼容、特殊兜底。新人接手项目，看不到直观的业务逻辑，满眼都是抽象封装、通用适配、公共兜底。]]></description><category></category></item></channel></rss>