文章
共 61 篇 · 在这座岛上写下的思考
你好,LAN ISLAND
这是博客的第一篇文章,介绍这座小岛是怎么建起来的。
大模型应用的可观测性:日志、指标与追踪
大模型应用出问题时,"哪里错了"往往比传统服务更难定位——可能是检索没召回、可能是 prompt 有歧义、也可能是模型本身的随机性。可观测性决定了你能不能快速找到原因。
SSE 流式输出:让大模型边想边说
大模型生成一段长回答可能要十几秒,用户盯着空白页面会以为卡死。SSE 让内容边生成边推送,把"首字延迟"从十几秒压到一秒内。这篇文章讲清 SSE 的原理、实现和踩坑点。
提示词工程:从"能跑"到"稳定"
写一个能出结果的 prompt 很容易,写一个在几百种输入下都稳定的 prompt 很难。这篇文章讲清结构化提示的要素、输出格式约束的方法,以及怎么用版本管理让 prompt 的改动可回溯。
向量数据库选型:Milvus、Qdrant 与 pgvector
向量检索是 RAG 的地基,但选型不只是比性能。数据量级、是否需要混合检索、运维成本、能否复用现有数据库,这四个问题决定了答案。
大模型应用的 Token 成本怎么算与控制
大模型应用的成本不只是"每次调用多少钱",而是输入、输出、重试、上下文膨胀共同决定的。这篇文章拆解成本的构成,并给出工程上真正有效的六个控制手段。
分布式 ID 生成方案对比
订单号为什么不能用自增主键?因为自增 ID 会暴露业务量、容易被遍历。这篇文章对比 UUID、数据库自增、号段模式、雪花算法和 Redis 自增五种方案,重点讲清雪花算法的时钟回拨问题。
大模型应用响应慢怎么优化
首字延迟高,往往不是模型不够快,而是你把三次模型调用串成了一条线。减少往返次数比换更快的模型有效得多。这篇拆解「响应慢」的六个方向和「状态混乱」的五个方向,并给出三个核心重构动作。
用 LangGraph 构建客服状态机
把多轮流程写在提示词里,同一句话跑两次可能走两条不同路径,线上出问题还无法复现。LangGraph 用「显式状态 + 节点 + 条件边」把隐式流程变成可画、可测、可恢复的图,这篇讲清它的建模方式和分流判据该怎么选。
MCP:模型上下文协议解决了什么问题
在没有统一协议之前,每个 Agent 对接每个工具都要单独写一遍适配,M 个模型乘 N 个工具就是 M×N 份胶水代码。MCP 用一套开放标准把这件事收敛成 M+N,代价是引入了一层进程边界和新的上下文开销。
大模型的记忆系统怎么设计
大模型本身是无状态的,所谓的「记忆」全靠每次请求重新喂进去。但上下文窗口不是记忆系统,把所有历史都塞进去会带来成本、延迟和记忆漂移。这篇讲清工作记忆、短期记忆、长期记忆的分层设计,以及召回复核与时间衰减怎么做。
Function Calling 的实现原理
模型从来没有执行过你的函数,它只是输出了一段结构化 JSON。把 Function Calling 拆成六步来看,你会发现它的本质是「结构化输出 + 客户端分发」,理解了这点,并行调用、参数校验、失败重试的边界就都清楚了。
Agentic RAG:让检索从固定流程变成自主决策
传统 RAG 是一条「查询→检索→生成」的单向管道,简单问题够用,一旦遇到多跳推理就非常脆弱。Agentic RAG 把检索变成 Agent 可以反复调用的工具,自主决定检索什么、检索几次。这篇讲清三大瓶颈、三点转变和量化收益。
RAGAS 评测:怎么知道 RAG 答得好不好
光看答案对不对,分不清是检索的问题还是模型的问题。RAGAS 的真正价值不是给一个分数,而是把这两层拆开——它要你提供检索片段,所以能定位是哪一环的锅。这篇讲透指标分类、四分支诊断、修法和工具的边界。
怎么保证 RAG 的召回率
召回是 RAG 效果的天花板——没被捞回来的内容,后面再怎么重排、再怎么换大模型都救不回来。这篇拆开五种互补的召回手段,讲清「宁多勿漏」这个取舍的代价,以及为什么多路分数不能直接相加。
文档切块策略:从固定长度到语义切块
切块是 RAG 里最容易被拍脑袋决定的参数,「按 500 字切」抄下来只要一秒钟。但 chunk 是检索单元,它的设计目标是「能独立回答一个问题」。这篇从切口复现讲到结构感知、父子块和语义切块,给出参数怎么定的完整思路。
为什么要把大模型部署到本地
云端 API 调用简单、按量付费,看起来很划算,但长期使用成本、数据合规和稳定性这三笔账算下来,本地部署在特定场景下反而是更优解。
RAG 的核心流程:离线建库与在线推理
很多人把 RAG 理解成「向量库 + 大模型」,效果不好就换更强的模型。但一条链路上有十几个环节,模型只占两三个。这篇把离线建库和在线推理两条链路完整拆开,并说明为什么排障要按这两半切开。
ES 深度分页:问题、代价与 search_after
翻到第 1000 页时,ES 会把前 1000 页的数据全部捞出来重新排序,开销随 from 线性增长。这篇讲清 from + size 的分布式代价、1 万条默认限制的由来,以及 search_after + PIT 的用法与局限。
Redis 五大数据结构与底层实现
五种数据类型是 API 层面的名字,底层的编码远不止五种。同一个 key 可能用三种结构存,一个 List 可能是"链表套数组"。这篇从编码转换条件出发,把每种结构的实现、阈值和适用场景串起来。
消息堆积了怎么办:一套应急处理流程
堆积预警不是"加机器"就能解决的。这篇给出一套六步应急流程——定位原因、扩容、处理毒消息、限流生产者、优化消费逻辑、事后复盘,并补上扩容后必然出现的乱序问题与两种解法。
分布式事务的边界该怎么划:一个 2400 倍吞吐差距的教训
同一个 SKU、同样的 100 个并发请求、同一套 Seata 配置,只因为全局事务的边界多划了一段,单 SKU 成交量从 20 单/秒掉到 0.0083 单/秒。这篇用实测数据说明全局锁的持有时间意味着什么,以及事务边界该怎么划。
热搜榜怎么做:ZSet 累加与定时榜单
热搜榜的关键不是"怎么排序",而是"什么时候排序"。把每次搜索都变成一次 O(log N) 的累加,把排序交给定时任务,ZSet 就是为这个形状量身定做的结构;顺带说清 skiplist 和防刷设计。
EXPLAIN 与慢查询优化实战
一条 SQL 慢,通常不是"数据太多",而是优化器没找到好的执行路径。这篇讲清 EXPLAIN 里 type、key、rows、Extra 四个关键字段怎么读,以及从 filesort、temporary 到深分页的五类实战优化手段。
ES 与 MySQL 的数据一致性方案
业务只写 MySQL,用消息队列异步写 ES,这条链路从生产到消费每一环都可能丢数据。这篇按生产、传输、消费三个阶段拆解本地消息表、Confirm、手动 ACK、幂等与乱序治理,并给出定时对账兜底方案。
分布式事务与 Seata 的 AT/XA 模式
分布式事务的难点从来不是「怎么提交」,而是「怎么回滚」和「怎么隔离」。这篇对比 XA、AT、TCC、SAGA 四种模式的原理与代价,重点拆解 Seata AT 模式的两阶段流程、undo_log 补偿机制和它无法回避的脏写风险。
RabbitMQ 延迟消息:DLX 与延迟插件的取舍
订单超时取消是延迟消息最经典的场景。RabbitMQ 原生不支持延迟投递,DLX 方案简单但存在队头阻塞,延迟插件没有阻塞却要动运维。这篇把两种实现的原理解开,并说明消费者为什么必须先查库校验。
配置热更新:改配置不重启服务
把配置写死在代码或打包进镜像是常见的做法,但改一个限流阈值就要重新发布,成本太高。这篇文章讲清配置中心的两种接入方式、动态刷新的原理,以及哪些配置适合热更新。
InnoDB 与 MyISAM:存储引擎怎么选
MyISAM 查询常常更快,为什么现在几乎没人用它?因为存储引擎的选择不是比速度,而是比"出问题时能不能救回来"。这篇从事务、锁粒度、索引结构、崩溃恢复四个角度看两者的分野。
熔断、降级与雪崩效应
一个非核心的推荐服务变慢,最后拖垮了整个下单链路——雪崩通常不是被「打垮」的,而是被「等垮」的。这篇讲清熔断器的三态转换、Sentinel 与 Hystrix 的差异、舱壁隔离和超时该怎么设。
集群部署解决了什么,又带来了什么
集群是解决单点故障和容量瓶颈的标准手段,但它不是免费的——会话共享、分布式锁、定时任务重复执行、日志排查,这些都是从单机走向集群后必须补上的课。
Refresh Token 买的是体验,不是安全
很多人以为配了 refresh token 就更安全,其实安全窗口完全没变——它买的是「用户不用反复登录」。这篇文章对比两种方案的真实差距,并拆解 refresh token 引入后的三个代价与六个必答问题。
缓存一致性:为什么是删缓存而不是更新缓存
先改库再删缓存能成立,靠的不是"顺序对了",而是删除这个动作的幂等性。这篇把四种更新顺序逐一拆开,说明延迟双删、重试队列、binlog 订阅分别补的是哪个洞。
RBAC 权限模型:表结构设计与落地方案
RBAC 的核心思想是用角色做中转,不给用户直接分配权限。这篇文章拆解标准五张表的职责划分、多对多中间表的必要性,以及数据权限这类扩展场景怎么设计。
消息幂等:重复消费的五种解法
MQ 只能保证「至少一次」投递,所以重复消费是必然发生的。这篇把五种幂等方案(ID+去重表、唯一约束、SETNX、状态机、天然幂等)拆开对比,并说明生产环境为什么最终常常落到「唯一约束 + 状态机」的组合。
乐观锁的两种流派:version 字段 vs 条件更新
两个 SQL 都叫乐观锁,失败语义却完全相反:version 失败是"冲突",条件更新失败是"资源不足"。混用会让重试逻辑变成压垮数据库的重试风暴。这篇讲清两种流派的取舍与边界。
限流的四种算法:固定窗口、滑动窗口、漏桶、令牌桶
限流 1 秒 100 个请求,却在 0.2 秒内放行了 200 个——固定窗口的临界突刺是怎么发生的?这篇从四种算法的原理讲到 Redis + Lua 的分布式实现,把「为什么网关都用令牌桶」这件事说透。
IK 分词器实战:ik_smart、ik_max_word 与自定义词典
自定义词典解决的不是「搜不到」,而是分词边界错了导致精度和召回双输。这篇用三种分词粒度的实测数据说明:为什么单字索引精确率只有 40%,只收长词的索引召回率会掉到 0,以及为什么索引用 ik_max_word、查询用 ik_smart。
伪共享与 CPU 缓存行:一个被忽略的性能杀手
两个线程分别修改互不相干的两个变量,代码里没有任何共享,性能却掉了几倍——问题出在它们恰好落在同一条 64 字节缓存行上。这篇从 MESI 协议讲到 @Contended 与 Disruptor 的填充技巧,并说明它和 CAS 的关系。
一个迭代的完整开发流程
从需求评审到上线复盘,一个规范的迭代要经过七道关。这篇文章按时间顺序拆解每个阶段的具体动作、产出物,以及最容易出问题的环节。
消息不丢:生产者、Broker、消费者三端保障
消息丢失从来不是某一个组件的问题,而是生产、存储、消费三段里任何一段断了都会丢。这篇逐段拆开讲:本地消息表与 Confirm 怎么配合、Broker 要开哪些持久化、消费者为什么必须手动 ACK。
API 网关到底该做什么
网关不是「反向代理换个名字」。路由和负载均衡只是它的入场券,真正的价值在于把鉴权、限流、日志这些每个服务都要写一遍的横切逻辑收拢到一处。这篇讲清网关的六项职责、断言与过滤器模型,以及它和 Nginx 的分工。
缓存击穿、穿透、雪崩:三种失效场景与解法
这三个词经常被混着用,但它们失效的对象、触发的条件和该上的解法完全不同。这篇用一组 200 并发的实测数据把三者拆开,给出各自的方案对比表和可落地的代码。
ThreadLocal 的原理与内存泄漏
ThreadLocal 的内存泄漏,根因在于 ThreadLocalMap 的 key 是弱引用而 value 是强引用。这篇从数据结构画到引用链,讲清泄漏怎么发生、为什么 set(null) 不管用,以及线程池下为什么还会串数据。
建表规范:字段类型、范式与冗余
建表是后端最容易埋雷的一步。金额用 float、状态用 varchar、多对多拿逗号拼 ID,上线时都没事,出问题时都是大问题。这篇给出六步建表法,并说明范式什么时候该让位于冗余。
并发容器选型:从 ConcurrentHashMap 说起
并发容器的演进主线是「不断缩小锁的粒度」:从 Hashtable 的全表锁,到 ConcurrentHashMap 的分段锁,再到桶级锁。这篇讲清它的两代实现、CopyOnWriteArrayList 的适用边界,以及一套选型对照表。
倒排索引:为什么 ES 检索比 LIKE 快几个数量级
数万级 SKU 上 Elasticsearch,真正的原因不是 MySQL 扛不住数据量,而是「分词检索」这件事 MySQL 没有索引结构支持。这篇从 LIKE 的代价算起,讲清倒排索引的三层结构与它快在哪。
Redis 为什么能挡住超卖:根因不是并发写
超卖的根因不是「两个人同时写」,而是「有人拿着过期的读数做判断」。理解这一点,才能明白为什么单条命令原子解决不了问题,以及原子操作为什么比分布式锁更适合库存场景。
微服务架构:特点、收益与代价
微服务解决的从来不是技术问题,而是"大团队协作"和"独立演进"的问题。这篇文章拆解七个核心特点、它替代单体的真实动机,以及那些被低估的代价。
索引设计:从最左前缀到索引失效
索引不是建得越多越快,建错了反而拖慢写入。这篇从聚簇索引与回表讲起,梳理联合索引的列顺序该怎么排、最左前缀法则为什么成立,以及七类常见的索引失效场景。
RabbitMQ 交换机类型:Direct、Topic、Fanout 与 Headers
交换机不是消息的容器,而是路由规则本身。这篇把 Direct 与 Topic 的匹配差异、Fanout 广播、Headers 匹配、备用交换机与死信路由讲清楚,并给出一套选型决策表。
多线程的三种典型应用场景
多线程不是为了「看起来快」,它只在三类问题上真正有价值:抢资源的、跑管道的、别挡路的。这篇用秒杀库存、离线数据管道、异步日志三个场景,把线程安全、生产者-消费者、上下文传递三条主线一次讲清。
注册中心的原理:Nacos、Eureka 与 CP/AP 之争
服务之间真正发起调用的那条链路上,注册中心其实完全不参与。理解它「只做地址管理、不做请求转发」的定位,才能想明白推拉结合怎么配合、AP 与 CP 该怎么选,以及它整个集群挂掉之后业务为什么还能继续跑。
Redis 分布式锁:从 SETNX 到看门狗
分布式锁的难点不在"怎么加锁",而在"锁的主人已经换人了你还不知道"。这篇从最朴素的 SETNX 讲起,一路推到 Lua 释放、看门狗续期、Redisson 封装,最后说清主从切换丢锁这个绕不开的缺陷。
线程池参数到底怎么定:从公式到压测
核心线程数 8 还是 32?网上流传的「CPU 核数 × 2」既不是答案也不是起点。这篇给出一套四步推导法:公式定基线、压测找拐点、队列划阈值、配置中心热更新,并附上无界队列撑爆堆内存的真实教训。
JWT 的无状态困境:签发之后为什么收不回来
JWT 的全部优势来自服务端不存状态,代价也正来自这里——签发之后没有任何入口能撤回它。这篇文章用一个「幽灵窗口」实验讲清无状态与可撤销的矛盾,并给出四种撤销方案的取舍。
B+ 树与 B 树的区别,以及为什么数据库选 B+ 树
单看等值查询,B+ 树并不比 B 树快,有时还要多走一层。数据库选它,是因为磁盘 IO 的粒度和范围查询的需求。这篇从页和 IO 次数讲起,把 B+ 树的扇出优势、叶子链表和回表代价一次说清。
Spring 事务失效的六大场景
事务失效最麻烦的地方不是报错,而是静默失效——注解照写、代码照跑,数据却已经落库了。这篇从事务赖以生效的两块基石出发,把六类失效场景的成因、复现代码和修法一次讲清。
深入理解 CAS 与 ABA 问题
CAS 是无锁并发的基石,靠一条 CPU 原子指令完成「比较并交换」。但它有三个绕不开的坑——自旋开销、单变量限制和 ABA 问题,这篇一次讲透。
岛屿开放日
小岛正式开张,记录今天的心情。
Next.js 静态博客的工作流
从写 Markdown 到全球可访问,中间只隔着一次 git push。