☰
硬盘杀手 Codex:logs_2.sqlite 高频刷盘,降低磁盘写入
2026/10/18 10:33:33 网站建设 项目流程

1. Codex 日志刷盘问题到底有多严重

如果你最近在用 Codex 做日常编码,可能会发现一个很隐蔽的问题:硬盘灯几乎没停过,~/.codex/logs_2.sqlite这个文件体积涨得飞快。我实测下来,连续跑几天之后,SSD 的累计写入量会明显高于正常开发场景,这不是错觉,而是日志模块的默认配置在“疯狂刷盘”。

先说清楚logs_2.sqlite是什么。Codex 在运行过程中会把会话、工具调用、底层通信细节写进一个本地 SQLite 数据库,路径通常在用户目录下的.codex文件夹里。它的作用是方便回溯和调试,但问题在于默认日志级别开到了 TRACE。TRACE 是最啰嗦的一档,会把 WebSocket 原始数据包、文件读写、内部状态变化全部记下来,其中大量内容对普通使用者没有价值。

写入放大是另一个关键点。SQLite 每次插入、删除都会触发页级操作,小块高频写入会让 SSD 实际物理写入量远超逻辑数据量。有用户反馈连续运行 21 天写入约 37TB,按这个速度一年就是几百 TB,而主流 1TB 消费级 SSD 的 TBW 通常在 600TB 左右。也就是说,放任不管,一块新盘可能在一年内被写废。

更麻烦的是,Codex 对标准的RUST_LOG环境变量并不完全买账,你没法用常规方式把日志级别调低。所以这篇教程的思路是两条腿走路:一是从 SQLite 层面直接拦截日志表的插入,二是从日志级别和运行方式上减少刷盘频率。下面我会给出可复制的 PRAGMA 配置、触发器写法,以及用系统工具验证写入下降的对比方法。适合所有在本地跑 Codex、关心 SSD 寿命的开发者。

2. 动手前的准备:TaoToken 与 Codex 环境确认

在改配置之前,先把运行环境理清楚。Codex 本身是一个编码 Agent 工具,它需要模型服务来驱动。如果你还在为模型调用发愁,可以用 TaoToken 这类兼容 OpenAI 接口的服务来承接请求,官网是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 地址是 https://taotoken.net/api 。它的作用是提供稳定的模型对话与编码能力,让你在本地跑 Codex 时不用折腾账号和网络。

确认 Codex 安装位置。不同系统下.codex目录不一样:

  • Windows:%USERPROFILE%\.codex\logs_2.sqlite
  • macOS / Linux:~/.codex/logs_2.sqlite

先确认这个文件存在,并且知道它当前多大。Windows PowerShell 里执行:

Get-Item "$env:USERPROFILE\.codex\logs_2.sqlite" | Select-Object FullName, Length

macOS / Linux 终端里执行:

ls -lh ~/.codex/logs_2.sqlite

记下当前大小,后面验证时会用到。接着确认sqlite3命令行工具是否可用:

sqlite3 --version

如果显示版本号,比如3.39.5,就可以直接进入下一步。如果提示command not found或'sqlite3' 不是内部或外部命令,需要先安装。macOS 用 Homebrew:brew install sqlite3;Debian/Ubuntu 用sudo apt install sqlite3;Windows 去 SQLite 官网下载sqlite3.exe,解压后放到C:\Windows目录,再开一个新的 CMD 窗口验证。

这里有个容易忽略的点:操作数据库前一定要先关闭 Codex。因为 SQLite 在进程持有连接时会加锁,你直接改表结构或建触发器可能报database is locked。关闭 Codex 后再执行下面的命令,触发器会立即生效,不需要重启电脑。

另外,如果你用的是 Cline、Claude Code 这类工具配合 Codex,建议把三件套记牢:Base URL 填https://taotoken.net/api,Key 在控制台生成,Model ID 按你订阅的模型填。这样排查问题时能快速定位是模型侧还是本地日志侧的问题。

3. 可复制的 PRAGMA 配置与日志拦截方案

这一节是核心操作。目标有两个:降低 SQLite 的刷盘频率,以及直接阻止日志表被高频插入。先给出一份可复制的配置片段,你可以按系统选择。

3.1 SQLite PRAGMA 调优配置

SQLite 默认的synchronous=FULL会在每次事务提交时强制 fsync,对 SSD 写入压力很大。把日志库改成synchronous=NORMAL或OFF,并调整 journal 模式,可以显著减少物理写入。下面是一份 TOML 风格的配置参考,你可以把它写进自己的启动脚本或初始化逻辑里:

# codex-sqlite-tuning.toml [sqlite] journal_mode = "WAL" # 预写日志,减少随机写 synchronous = "NORMAL" # 降低 fsync 频率 temp_store = "MEMORY" # 临时表放内存 cache_size = -8000 # 约 8MB 页缓存 mmap_size = 268435456 # 256MB 内存映射 wal_autocheckpoint = 2000 # 每 2000 页做一次 checkpoint

如果你更习惯用 JSON 描述,也可以写成:

{ "sqlite": { "journal_mode": "WAL", "synchronous": "NORMAL", "temp_store": "MEMORY", "cache_size": -8000, "mmap_size": 268435456, "wal_autocheckpoint": 2000 } }

这些参数的含义:WAL 模式让读写分离,写入先落日志再合并;synchronous=NORMAL在 WAL 下已经足够安全,同时大幅减少 fsync;cache_size为负值表示 KB,-8000就是 8MB;mmap_size用内存映射减少 read 系统调用。实测下来,这一组配置能把日志库的写入频率压下来一大截。

3.2 用触发器直接拦截日志插入

如果只想快速止血,最直接的办法是在logs表上建一个 BEFORE INSERT 触发器,让所有插入被忽略。先关闭 Codex,然后执行对应系统的命令。

Windows PowerShell:

sqlite3 "$env:USERPROFILE\.codex\logs_2.sqlite" "CREATE TRIGGER IF NOT EXISTS block_log_inserts BEFORE INSERT ON logs BEGIN SELECT RAISE(IGNORE); END;"

macOS / Linux:

sqlite3 ~/.codex/logs_2.sqlite "CREATE TRIGGER IF NOT EXISTS block_log_inserts BEFORE INSERT ON logs BEGIN SELECT RAISE(IGNORE); END;"

执行后不会有输出,这是正常的。触发器立即生效,之后 Codex 再往logs表写数据会被直接忽略,不再产生新的磁盘写入。想确认触发器是否建好,可以查询:

sqlite3 ~/.codex/logs_2.sqlite "SELECT name FROM sqlite_master WHERE type='trigger';"

应该能看到block_log_inserts。如果以后想恢复日志,删除触发器即可:

sqlite3 ~/.codex/logs_2.sqlite "DROP TRIGGER IF EXISTS block_log_inserts;"

3.3 日志级别与启动方式调整

除了数据库层,还可以从日志级别入手。Codex 对RUST_LOG不完全响应,但你可以尝试在启动前设置:

export RUST_LOG=warn

Windows PowerShell:

$env:RUST_LOG="warn"

如果无效,就依赖上面的触发器方案。另外,把 Codex 的日志目录软链接到内存盘(如 Linux 的/dev/shm)也是一种思路,但重启会丢数据,适合临时调试。长期方案还是触发器加 PRAGMA 调优。

4. 验证请求与写入频率下降的对比方法

改完配置不能只看感觉,要用数据说话。这一节给出可复制的验证步骤,对比操作前后的写入量。

4.1 记录操作前的基线

先关闭 Codex,记录logs_2.sqlite的当前大小和系统累计写入。Linux 下可以用/proc/diskstats或iostat:

iostat -dx 5 3

关注kB_wrtn/s这一列。macOS 可以用iostat -d disk0 5。Windows 用性能监视器看PhysicalDisk\Disk Writes/sec。

同时记录文件大小:

stat -c "%s %n" ~/.codex/logs_2.sqlite

4.2 启动 Codex 跑一段固定任务

让 Codex 执行一段相同的编码任务,比如生成一个模块或跑一次对话,持续 5 分钟。期间用iostat采样,记录平均写入速率。

4.3 对比触发器生效后的数据

建好触发器后再跑同样的任务,重复采样。正常情况下,logs_2.sqlite的大小几乎不再增长,kB_wrtn/s会明显下降。下面是一个对照表模板,你可以填自己的实测值:

指标操作前操作后
logs_2.sqlite 大小持续增长基本不变
平均写入速率高显著降低
触发器状态无block_log_inserts

4.4 用 SQLite 自身统计确认

还可以查询页数和空闲页,判断写入是否被拦截:

sqlite3 ~/.codex/logs_2.sqlite "PRAGMA page_count; PRAGMA freelist_count;"

如果触发器生效,page_count在多次运行后应保持稳定。这一步能排除“文件没涨但 WAL 在涨”的假象,记得同时看-wal文件大小。

5. 本篇常见报错排查

操作过程中容易遇到几个典型报错,这里逐个说明。

database is locked:说明 Codex 还在运行,SQLite 连接被占用。解决方法是完全退出 Codex,包括后台进程。Windows 可以在任务管理器里结束相关进程,macOS/Linux 用pkill -f codex。

no such table: logs:说明数据库结构和你预期不一致,可能版本不同。先列出所有表:

sqlite3 ~/.codex/logs_2.sqlite ".tables"

如果表名不是logs,把触发器语句里的表名换成实际名称。

401 或 local proxy failed:这类报错通常出现在模型请求侧,不是日志问题。检查你的 Base URL 是否为https://taotoken.net/api,Key 是否有效,Model ID 是否填对。三件套缺一不可。如果用的是 Claude Code 或 Cline MCP,确认配置文件里的地址和密钥没有多余空格。

reading choices 报错:一般是响应格式不匹配,检查模型 ID 是否支持当前接口。可以在模型对话页面先单独验证一次请求,确认服务正常后再回到 Codex。

OAuth 相关报错:如果你用的是需要 OAuth 的客户端,确认授权是否过期。Codex 的auth.json里保存了凭证,路径通常在~/.codex/auth.json。检查里面的字段是否完整,必要时重新登录生成。

触发器建了但文件还在涨:可能是 WAL 文件在增长,或者有其他表在写入。用.tables看全部表,对高频写入的表都加触发器,或者直接对数据库设置PRAGMA journal_mode=DELETE临时观察。

sqlite3 命令找不到:回到第 2 节安装步骤,Windows 注意把sqlite3.exe放到 PATH 包含的目录,重开终端再试。

6. 长期编码与 Agent 场景的稳定接入

把日志问题压下去之后,Codex 才能长期稳定地跑在本地。对于需要长时间编码、跑 Agent 任务的场景,建议把模型接入也固定下来。TaoToken 的 Coding Plan 适合这种持续调用的需求,地址是 https://taotoken.net/api ,配合控制台生成的 Key 使用。控制台在 https://taotoken.net/console ,API Keys 管理在 https://taotoken.net/api-keys ,接入文档在 https://taotoken.net/doc ,模型对话验证在 https://taotoken.net/chat 。Claude Code 相关配置参考 https://taotoken.net/claude-code 。

实际使用时,把 Base URL、Key、Model ID 三件套写进对应客户端的配置文件。比如 Cline 的 MCP 配置、Codex 的auth.json、Claude Code 的 settings,都要保证地址一致。这样即使日志侧做了拦截,模型请求依然正常,不会因为排查日志而影响编码效率。

最后提醒一句:触发器方案是应急止血,等官方修复补丁发布后,建议删掉触发器恢复日志功能,同时保留 PRAGMA 调优参数,让 SQLite 在正常范围内工作。SSD 寿命是消耗品,但没必要浪费在无用的 TRACE 日志上。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询