Codex 会不会写爆 SSD?
我查了本机日志
朋友圈在转一条警告:Codex 会在后台高频写 logs_2.sqlite,文件看着不大,实际写入可能很高。我先以为是夸张标题,查完本机日志后改了判断。
①网上在传什么
我看到两类说法:一类是转发截图,只说 Codex 在烧盘;另一类是 lencx 的源码和 issue 复盘。
转发版的说法是:装了 Codex CLI 后,它会往 ~/.codex/logs_2.sqlite 高频写数据;文件看着没变大,SSD 写入量却在涨。
证据版来自 lencx(浮之静)的 《Codex 日志正在燃烧你的 SSD》。他翻了 Codex 源码和一串 GitHub issue,把问题指向 SQLite 日志层的默认配置:TRACE 级网络事件、遥测镜像和依赖库噪声被写进持久化数据库,文件大小不能代表实际写入量。
我按这个方向查了自己的机器。下面只放本机命令和能核对的 issue,不靠转述下结论。
②我在自己机器上测到了什么
先看文件:
$ ls -lh ~/.codex/logs_2.sqlite*
logs_2.sqlite 126M
logs_2.sqlite-wal 4.9M
logs_2.sqlite-shm 32K
库里 66% 是 TRACE。内容不是业务日志,而是 websocket 轮询读写的底层打点:
$ sqlite3 ~/.codex/logs_2.sqlite "SELECT level,count(*) FROM logs GROUP BY level ORDER BY 2 DESC"
TRACE 65744 -- 占 66%
INFO 17968
DEBUG 13459
WARN 1974
ERROR 8 -- 当时合计约 9.9 万行
最新几条长这样:
TRACE log WouldBlock
TRACE log tokio-tungstenite/compat.rs:157 Read read -> poll_read
TRACE codex_api::endpoint::responses_websocket ...
只看当前行数还不够。这个库会边写边删,体积会把人骗过去。
在写的是桌面版,不是你敲的 CLI
原帖把问题说成"CLI 在写"。我查了日志里的 process_uuid,最近 500 行都来自同一个进程:
$ ps -p 2950 -o command
/Applications/Codex.app/Contents/Resources/codex app-server
写入来自 Codex 桌面版(Codex.app)的 app-server。当时我没有打开交互式 codex 终端。更准确的说法不是"CLI 在烧盘",而是"开着的 Codex 进程会写这个共享 SQLite 日志层",桌面版、TUI、编辑器扩展都可能参与。
文件只有 126 MB,但它 10 天写了 845 万行
主键是自增 id。我查了一下当前的 id 区间和实际行数:
$ sqlite3 ~/.codex/logs_2.sqlite "SELECT min(id), max(id), count(*) FROM logs"
min(id) 206,551,383
max(id) 215,003,791 -- 自增已到 2.15 亿
count(*) 101,406 -- 但只剩 10 万行
区间跨度 845 万,实际只剩 10 万 => 835 万行已经被删掉了
这是"插入-修剪"循环:表里只保留最近约 10 天的行(我这边最老一行正好是 10 天前),旧行会被删掉。ls 看到文件稳定在 126 MB,不等于没写。自增 id 显示,10 天里实际插入了 845 万行,删掉了其中 835 万。
这里推翻了我的第一版判断。我原本拿"残留的 10 万行"估算,得出"约 7 MB/天",还说这是标题党。按实际插入的 845 万行算,是 约 600 MB/天的日志正文,我少算了快 90 倍。这还只是逻辑写入;WAL、4 个索引、checkpoint、删除本身都会再写盘。
上游 issue 的量级更大:#17320 在流式输出时测到 WAL 写入 约 5–16 MiB/s,一次 50 token 回复让 MAX(id) 跳了约 5000 行;#28224 的报告者称 21 天写了约 37 TB,外推约 640 TB/年。我那次"6 秒只涨 109 行"的采样,只抓到了空闲时段;模型输出时才是高写入窗口。
"更新版本就好了"?本机还没完全好
这句要分开说。#28224 在 2026-06-22 关闭,报告者说两条 PR 合并后,自己的日志量少了约 85%。但我这台 CLI 0.134.0、桌面版自动更新后,2026-06-23 仍写了 18,017 条 TRACE,最近 200 行也全是 TRACE。也就是说,上游已经做了降噪,但我这台机器上还不能把它当成"已彻底修好"。
③到底是真是假
逐条对:
| 说法 | 实测 | 判定 |
|---|---|---|
偷偷往 logs_2.sqlite 写 |
真实存在,126 MB | 真 ✓ |
| 写了"几万条"废话 | 低估了。10 天插入 845 万行,66% 是 websocket TRACE | 真(还不止)✓ |
| 哪怕你什么都不干也在写 | 基本属实。我没开 CLI,后台桌面版 app-server 仍在写;只要有 Codex 进程开着,websocket 轮询就可能持续打 TRACE | 真 ✓ |
| 文件看着没变大,底层却在狂写 | 正是病灶本身。滚动删除让体积稳定,物理写入没停 | 真 ✓ |
| 一年就把你的 SSD 烧光 | 看用量。重度用户要管;轻度用户没必要恐慌 | 夸大 ≈ |
病根在哪(lencx 翻的源码)
app-server 和 TUI 启动时都会挂一个 SQLite 日志层,默认过滤器是:
.with_filter(Targets::new().with_default(Level::TRACE))
也就是说,这个落盘 sink 默认接收 TRACE,而且有自己的过滤链,不受 RUST_LOG=warn 约束。#17320 的报告者已经设了 RUST_LOG=warn,logs_2.sqlite 里仍然持续出现 TRACE。这个 issue 开于 2026-04-10,核对时仍是 Open。
SSD 风险要看写入量
SSD 的 TBW 是保修写入量,不是写到就坏的死亡线。风险取决于使用强度、盘容量和剩余空间:
- 重度用户:像 #28224 那样 640 TB/年,落到一块焊死的 256 GB Mac SSD(标称约 150 TBW)上,几个月就超保修写入量。这种情况值得处理。
- 轻度用户:写入量低一两个数量级,盘还能用很多年,没必要恐慌。
还有一个误解:它不会"烧穿同一小块"。SSD 有 FTL 和磨损均衡,写同一个文件名不等于反复擦同一批闪存单元。真正该看的是总主机写入量,以及磁盘太满时的写放大。
还有一层隐私风险容易被忽略:lencx 指出多个 issue 提到这个库可能落盘 websocket/SSE 的原始 payload,也就是可能夹带对话片段。所以别把 logs_2.sqlite 当成"无害缓存"随手上传、备份或公开。
我的结论:机制真实,上游有据,本机仍能复现;但它不是"人人一年烧光 SSD"。这取决于你开着哪些 Codex 进程、一天用多久、机器的 SSD 容量和剩余空间。我第一版只看残留行数,把它判成 FUD,是错的。
④怎么办
下面这些大多来自 lencx 的复盘,我在本机核对过。动手前先完全退出 Codex,否则会踩 #22444 的句柄问题:进程还占着已删除的 WAL,du 看着降了,df 却不释放空间。
先确认没进程占用
pgrep -fl 'Codex|codex|app-server'
lsof -nP | grep -E '\.codex/.+logs_2\.sqlite'
清理回收体积(治标)
# 确认 Codex 已退出后
sqlite3 ~/.codex/logs_2.sqlite 'DELETE FROM logs; VACUUM;'
这能把文件从 126 MB 缩回几 MB,但拦不住它重新开始写。清理 ≠ 限流。
临时止血:拦住新写入(治标,但管用)
sqlite3 ~/.codex/logs_2.sqlite \
"CREATE TRIGGER IF NOT EXISTS block_log_inserts
BEFORE INSERT ON logs BEGIN SELECT RAISE(IGNORE); END;"
# 回滚
sqlite3 ~/.codex/logs_2.sqlite "DROP TRIGGER IF EXISTS block_log_inserts;"
触发器会忽略所有插入,效果直接。代价是 feedback_log_body 会变空,报 bug 时缺诊断附件;升级若重建表,触发器可能被删,升级后要复查。
把数据库挪到别的盘(权宜方案)
# ~/.codex/config.toml
sqlite_home = "/Volumes/ExternalDisk/codex-sqlite"
# 或临时: CODEX_SQLITE_HOME=/Volumes/ExternalDisk/codex-sqlite codex
这比软链单个文件干净,但只是搬走写入,不会减少写入。外置盘断开或休眠会影响 Codex。注意它迁的是整个 SQLite home,state、logs、goals、memories 都会跟着走。
三个别踩的坑
/tmp软链在 macOS 没用:macOS 的/tmp指向/private/tmp,在 APFS 系统盘上,不是内存盘。软链过去只是从 SSD 一个目录挪到另一个目录,对写入毫无帮助。readlink /tmp; mount | grep -i tmpfs自己验。RUST_LOG=warn管不住这个 sink:它能压低 stderr,但这个 SQLite 落盘层有独立过滤链(见 #17320)。[feedback] enabled = false不是开关:它只关反馈提交流程,启动时照样挂日志层。
顺手:备份排除 WAL/SHM
--exclude='*.sqlite-wal'
--exclude='*.sqlite-shm'
WAL 和 SHM 是运行时文件,几十 GB 同步到另一台机器多半只是在复制问题。
上游还要继续收口
这些都只是止血。Codex 需要继续收窄落盘 sink 的默认级别,不默认落 websocket 原始 payload,给 WAL 和总大小加上限,并提供明确开关(比如 sqlite_log_level)。#28224 已因降噪 PR 关闭,但 #17320、#22444、#24275、#28997 核对时仍是 Open。
自己花一分钟判断该不该管:先 ls -lh ~/.codex/logs_2.sqlite* 看体积,再跑一次 SELECT max(id),count(*) FROM logs。如果 max(id) 是几千万上亿、行数却只有十几万,你这台机器也在"写了删、删了写"。重度用、又是焊死 SSD 的 Mac,建议处理;偶尔用用,知道有这回事即可。

微信
支付宝
评论
评论发布后会立即公开,如触发规则可能被审核下架。