美团面试里问“为什么要用分布式缓存?本地缓存呢?多级缓存一致性如何保证?”时,很多人能背出缓存穿透、雪崩、击穿,却把三者的定位混在一张图里。我用 Codex 做面试陪练时,第一步不是让它写业务代码,而是先把请求走 TaoToken:打开 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_content= 注册并创建 Key,再把 Codex 的 Base URL 填 https://taotoken.net/api。这样 Codex 只负责整理分布式缓存、本地缓存、多级缓存一致性的解释,不会去实现缓存一致性,也不会碰你的生产库。接下来按面试追问的顺序,把三问拆开,再对照它的返回结果补自己的回答。
1. 美团三连问卡在哪:分布式缓存、本地缓存、多级缓存不是三张皮
1.1 先看数据放在哪:共享层、进程层、持久层
面试官问“为什么要用分布式缓存”,并不是想听你把 Redis 的 QPS 背一遍。他在确认你知不知道分布式缓存解决的是一类很具体的问题:多个应用实例需要看到同一份热点数据,而单机内存扛不住容量和带宽。比如商品详情、用户会话、秒杀库存、排行榜,这些数据如果每个实例各存一份,很容易出现 A 实例看到库存 10、B 实例看到库存 8 的情况。分布式缓存把这份共享状态提到应用进程之外,所有实例访问同一个逻辑缓存层,扩容时也不会因为新增实例就多出一份不一致的数据。
本地缓存则是另一层东西。它活在 JVM 进程内,或者 Go 进程的堆里,访问路径最短,没有网络往返,也没有序列化开销。Caffeine、Guava Cache、Ehcache 都属于这一类。它适合极热、变更少、能容忍短时间不一致的数据,比如字典表、配置项、城市列表、权限位。面试官接着问“本地缓存呢”,重点不是让你说“本地缓存快”,而是看你有没有意识到它的代价:每个实例一份,更新时很难同步,堆内存有限,GC 压力大,重启后冷启动。你如果只回答“本地缓存快、分布式缓存大”,基本会被追问到崩。
多级缓存就是把这两层和数据库串起来:请求先查本地缓存,未命中查分布式缓存,再未命中查数据库,然后逐层回填。读路径听起来简单,真正难的是写路径。数据库更新后,本地缓存和分布式缓存怎么失效?先删哪个?删失败怎么办?多个实例的本地缓存怎么同时知道数据变了?这些问题答不清楚,分布式缓存、本地缓存、多级缓存就会变成三张互不相关的皮。
1.2 面试官为什么追着一致性问
缓存一致性不是一个“是或否”的问题,而是“在什么时间窗口内,允许读到什么程度的数据”。面试官追着问,通常想看你有没有分层思维:
- 强一致:更新后任何读都必须看到新值,多级缓存下成本极高,通常只在极少数库存扣减、支付状态场景用锁或版本号强控。
- 最终一致:允许毫秒到秒级的旧值,靠失效通知、TTL 兜底、binlog 订阅收敛。
- 读多写少:适合 Cache Aside,更新数据库后删除缓存,而不是更新缓存。
- 写多读少:缓存收益低,可能直接落库或用短 TTL。
如果面试官问“多级缓存一致性如何保证”,你可以先给结论:多级缓存通常不追求强一致,而是用“数据库为准 + 失效广播 + 短 TTL 兜底”做到最终一致。然后再展开本地缓存和分布式缓存各自的失效方式。这个层次一出来,对方就知道你不是只背了概念。
2. 把 Codex 接到 TaoToken:~/.codex/config.toml 里的 base_url 怎么填
2.1 在官网创建 Key 并确认模型 ID
让 Codex 帮你整理面试回答之前,先解决模型调用通道。打开 TaoToken 注册账号,在控制台创建 API Key。Key 不要写进代码仓库,也不要贴到聊天记录里,后面用环境变量注入。模型 ID 不要凭记忆写,去 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_content= 的模型广场看当时列表,选一个适合长文本解释和代码片段的模型。模型广场里显示什么 ID,就填什么 ID,不要自己加日期后缀,也不要编造不存在的模型名。
这一步的产物只有两样:一把YOUR_API_KEY,一个YOUR_MODEL_ID。TaoToken 在这里的角色是统一 API 入口,只给 Codex 供 Key 和 Base URL,它不参与你的缓存架构,也不会替你保证缓存一致性。缓存一致性仍然由你的应用代码、失效策略和存储层负责。
2.2 config.toml 可复制配置
Codex 读取的是~/.codex/config.toml。Windows 下通常是C:\Users\你的用户名\.codex\config.toml。把 provider 指到 TaoToken 的兼容通道,Base URL 写https://taotoken.net/api,末尾不要加/v1。可复制配置如下:
model = "YOUR_MODEL_ID" model_provider = "taotoken" [model_providers.taotoken] name = "TaoToken" base_url = "https://taotoken.net/api" env_key = "TAOTOKEN_API_KEY" wire_api = "chat"这里有几个容易填错的位置。base_url不是官网落地页,不要写成带utm_source的地址;https://taotoken.net/api是给工具调用的接口根地址。env_key写的是环境变量名,不是 Key 本身。model必须和模型广场里的 ID 对得上,否则 Codex 可能返回模型不存在。
2.3 环境变量与重启 Codex
macOS 或 Linux 终端里设置:
export TAOTOKEN_API_KEY=YOUR_API_KEYWindows PowerShell 里设置:
$env:TAOTOKEN_API_KEY="YOUR_API_KEY"设置完不要立刻在旧窗口里启动,先关掉终端重开,或者新开一个标签页,让环境变量生效。然后在项目目录执行:
codex如果 Codex 启动后仍然报鉴权失败,先检查当前 shell 里TAOTOKEN_API_KEY是否真的有值。可以用echo $TAOTOKEN_API_KEY(macOS/Linux)或$env:TAOTOKEN_API_KEY(PowerShell)确认。注意不要把这个值输出到公开日志里。
3. 让 Codex 分别解释三问:整理请求怎么写
3.1 第一问:为什么要用分布式缓存
配置通后,先发一条整理请求。提示词要明确:只做解释和整理,不连接数据库,不执行命令。可以这样写:
请只做文字整理,不要连接任何数据库、Redis 或生产环境,也不要执行命令。 请分别解释: 1. 为什么要用分布式缓存? 2. 本地缓存怎么选? 3. 多级缓存一致性如何保证? 输出对比表、典型失效与更新顺序、面试追问点。Codex 返回第一问时,通常会提到容量、共享、扩展性、热点数据集中管理。你要对照自己的面试回答检查三点:有没有说清“多个实例共享同一份逻辑数据”;有没有提到网络开销和序列化成本;有没有把缓存击穿、穿透、雪崩和分布式缓存本身区分开。分布式缓存不是因为“快”才存在,而是因为多个进程需要共享状态,同时数据库扛不住热点读。
3.2 第二问:本地缓存怎么选
第二问的返回结果里,你应该重点看它有没有把本地缓存选型拆成指标。选 Caffeine 还是 Guava,不是看哪个名字熟,而是看:
| 维度 | 要问自己的问题 |
|---|---|
| 命中率 | 热点是否足够集中,命中率能不能覆盖内存成本 |
| 容量 | 单实例堆内存能分多少给缓存,会不会影响 GC |
| 过期策略 | 能否接受 TTL 兜底,还是需要主动失效 |
| 更新频率 | 数据一天变几次,还是每秒都在变 |
| 一致性容忍 | 多实例之间允许旧值存在多久 |
| 回源成本 | 未命中时打数据库的代价有多高 |
如果数据变更频繁、多实例要求秒级一致,本地缓存就不是首选;如果数据极少变、读极多,本地缓存能挡掉大量分布式缓存访问。面试回答里最好补一句:本地缓存要设置容量上限和过期时间,不能当成无限 Map 用。
3.3 第三问:多级缓存一致性如何保证
第三问最容易被 Codex 整理成“先删缓存再更新数据库”一句话,你要继续追问它展开。多级缓存一致性通常按写路径拆:
- 更新数据库。
- 删除分布式缓存。
- 通过消息队列、Redis Pub/Sub 或配置中心广播失效事件。
- 各应用实例收到事件后删除本地缓存。
- 本地缓存保留短 TTL,作为广播丢失时的兜底。
这里的关键不是“删得干净”,而是“接受短暂不一致,并让不一致窗口可控”。你可以让 Codex 输出一个时序图文字版,再自己对照面试回答。注意,Codex 只生成解释和伪代码,真正的缓存清理命令、Redis 操作、数据库更新,必须由你在本地或测试环境执行,不要把生产库连接信息丢给模型。
4. 用返回结果对照面试回答:失效与更新顺序继续追问
4.1 先更新 DB 还是先删缓存
Codex 整理完三问后,继续追问:“先更新数据库再删缓存,和先删缓存再更新数据库,分别有什么并发问题?”这是面试官很爱追的第二层。常见结论是 Cache Aside 采用先更新数据库、再删除缓存,因为更新缓存容易产生无效写和并发覆盖。但先更新数据库再删缓存也有窗口:读请求在数据库更新前读到旧值,然后写请求更新数据库并删除缓存,读请求再把旧值写回缓存。这个窗口需要靠延迟双删、版本号或短 TTL 收敛。
如果先删缓存再更新数据库,窗口可能更大:删缓存后、数据库更新前,另一个读请求把旧值加载回缓存,之后数据库才更新,缓存里留下旧值。所以很多团队会选择先更新数据库再删缓存,再用延迟双删兜底。面试时不要只给顺序,要给原因和窗口。
4.2 延迟双删、binlog 订阅、消息队列的边界
延迟双删的伪代码可以这样理解:
public void updateProduct(Product product) { cache.delete("product:" + product.getId()); db.update(product); mq.publish("cache:invalidate", "product:" + product.getId()); scheduler.schedule(() -> { cache.delete("product:" + product.getId()); }, 500, TimeUnit.MILLISECONDS); }这段代码只表示思路,不要直接复制到生产。延迟时间要根据主从同步延迟、业务读耗时决定,不是固定 500 毫秒。binlog 订阅适合把数据库变更可靠地转成失效事件,消息队列适合跨服务广播,Redis Pub/Sub 更轻但可能丢消息。它们解决的是“通知”问题,不解决“强一致”问题。面试回答里要说明:最终一致依赖重试、幂等和 TTL 兜底。
4.3 本地缓存广播失效与 TTL 兜底
多级缓存里最难的是本地缓存。分布式缓存删一次,所有实例下次读会回源;本地缓存每个实例一份,必须广播。常见做法是应用订阅失效频道,收到消息后调用本地缓存的invalidate。如果广播丢失,TTL 就是最后一道防线。TTL 不能太长,否则旧值窗口大;也不能太短,否则本地缓存命中率下降,压力全打到分布式缓存和数据库。
你可以让 Codex 帮你列出“本地缓存失效失败”的排查清单:实例是否订阅成功、消息是否重复、Key 格式是否一致、序列化是否报错、TTL 是否被误设成永不过期。然后你把这些点补进自己的面试回答,比单纯背“延迟双删”更扎实。
5. 跑通之后验证用量:这次 Codex 请求有没有记上
5.1 调用成功的判断
当 Codex 返回了分布式缓存、本地缓存、多级缓存一致性的整理内容,说明 Key 和 Base URL 基本配通。这时不要只看它答得对不对,还要确认调用是否成功计费。回到 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_content= 看控制台用量,确认刚才那次整理请求有没有记上。如果页面里没有新增调用,先检查 Codex 是否真的走了taotokenprovider,而不是还在用默认 provider。
你也可以在 模型对话 里用同一把 Key 发一条短消息,确认模型 ID 和通道都正常。模型对话返回正常,但 Codex 没记录,通常是 Codex 配置文件没被读取,或者环境变量只在另一个终端里生效。
5.2 本篇容易遇到的配置报错
第一种是 401。先确认TAOTOKEN_API_KEY是否在启动 Codex 的同一个 shell 里,再确认 Key 没有多余空格。第二种是模型不存在。model必须从模型广场复制,不要写YOUR_MODEL_ID就启动。第三种是地址多了/v1。base_url只写https://taotoken.net/api,不要写https://taotoken.net/api/v1。如果改了配置,重启 Codex 让config.toml重新加载。
还有一个容易忽略的点:不要把官网落地页填进base_url。官网地址用于注册、创建 Key、看模型广场和看用量;接口地址才是https://taotoken.net/api。两者混用,轻则 404,重则你以为调用成功,实际请求没有落到正确通道。
6. 下一步:把 Codex 当面试陪练,别当缓存执行器
6.1 面试回答模板怎么收口
整理请求跑通后,你可以让 Codex 把三问压成一段 90 秒回答:先给结论,再分层展开。结论是“分布式缓存解决多实例共享和容量问题,本地缓存解决进程内极热数据,多级缓存一致性通常走最终一致,靠数据库为准、失效广播和 TTL 兜底”。中间展开读路径和写路径,最后补一句“不允许强一致场景不会硬套多级缓存”。这段回答比背八股更稳。
继续追问“失效与更新顺序”时,让 Codex 分别列出先更新 DB 再删缓存、先删缓存再更新 DB、延迟双删、binlog 订阅的适用边界。你把它返回的表格对照自己的项目:如果项目读多写少,Cache Aside 够用;如果跨服务,消息队列更合适;如果本地缓存多实例,广播失效不能少。
6.2 配完 Codex 后要做的三件事
配完 Codex 后,先回 模型对话 用同一把 Key 发一条缓存三连问,确认模型返回正常;再打开 控制台 API Keys 看这次调用有没有记上;如果后面要长期用 Codex 做面试陪练,可以看 Coding Plan 是否够用。若之后还要接 Claude Code,再对照 Claude Code 接入文档,但缓存一致性本身仍然由你的应用代码负责,Codex 只帮你生成解释、伪代码和追问清单,真正的 Redis 删除、数据库更新、本地缓存失效验证,都要在你自己的本地或测试环境里执行。