当pnpm遇上机械硬盘:卡住不一定是网络问题。

6 阅读4分钟

本文所呈现的结论与解决方案,基于本人的亲身实践与手动排查。在排查和编写过程中,作者借助 AI 工具分析并获取了优化建议。

问题现象

在使用 pnpm 安装项目依赖时,进程长时间停滞在 Progress: resolved xxx, reused xxx, downloaded 0, added 0 状态,无法继续执行。

进度解析

pnpm 的安装过程中,Progress: resolved X, reused Y, downloaded Z, added W 是显示依赖处理进度的核心指标,分别对应依赖解析、复用已有包、下载新包、添加到项目四个关键阶段。

  • resolved:分析项目依赖树,确定每个依赖包的精确版本下载地址(如 npm registry)及依赖关系
  • reuseddownloaded:根据 resolved 阶段确定的包版本,计算包的唯一标识,在 store 目录中匹配是否存在该标识的包,确保包在 store 中存在,不存在则进行下载;
  • added:添加到项目node_modules,让项目能实际使用这些包。

我这边遇到的场景,经常卡在reused xxx或者added xxx阶段,理解这个进度的逻辑关系,有助于后续排查。

环境信息

  • 项目所在磁盘:E 盘(机械硬盘,HDD)
  • 操作系统:Windows

排查过程

  1. 监控磁盘活动:观察到项目所在的 E 盘(机械硬盘)磁盘占用率持续 100%,I/O 响应延迟极高。
  2. 检查 pnpm store 路径:通过 pnpm store path 确认默认存储路径为 E:/.pnpm-store(与项目同盘)。
  3. 分析 store 体积:发现随着项目增多,E:/.pnpm-store 目录已积累大量依赖包(体积达 9.5GB),包含海量小文件。
  4. 修改 store 路径:将 pnpm store path 迁移至 D 盘(固态硬盘,SSD)的 D:/.pnpm-store
  5. 验证效果:重新执行 pnpm install,安装过程流畅完成,磁盘占用率仅在 added 阶段上升至 100%。

原因分析

1. 机械硬盘(HDD)的 I/O 瓶颈与小文件随机访问低效

机械硬盘的性能受限于物理结构(磁头寻道+盘片旋转),对海量小文件的随机读写极为敏感:

  • 随机寻道开销pnpm 作为高效包管理器,其依赖解析过程强调硬链接和共享存储,生成海量小文件和元数据更新,机械硬盘需不断移动磁头定位文件,累积后导致读取时间线性增长。
  • store 体积膨胀加剧延迟:随着项目增多,pnpm-store 会持续积累依赖包(去重后仍可能因版本差异产生冗余),文件数量可达数十万级。机械硬盘对小文件的随机读取效率随文件数量增加呈指数级下降(文件碎片化加剧),最终导致“读取时间越来越长”,甚至因 I/O 队列阻塞表现为“进程停滞”。

store 与项目均在机械硬盘时,海量小文件的“读取 store+写入.pnpm”复制操作会触发机械硬盘的“随机读写双瓶颈”(磁头频繁寻道),最终导致磁盘 100%占用、进程停滞。

2. pnpm store 默认路径行为:按驱动器隔离存储与冗余风险

若未手动设置 store-dirpnpm 会采用 “每驱动器/文件系统独立存储” 策略:

  • 默认路径规则:在项目所在驱动器的根目录下创建 .pnpm-store(如项目在 E 盘,则路径为 E:/.pnpm-store;若在 F 盘,则创建 F:/.pnpm-store)。
  • 冗余与性能隐患:若项目分布在多个驱动器,每个驱动器会生成独立的 store,导致相同依赖包在多盘重复存储(“冗余包”)。同时,当项目与 store 为同一个机械硬盘时,所有依赖的读取/写入均集中在低效的机械 I/O 上,进一步放大性能问题。

解决方案

核心思路:将机械磁盘中的 pnpm store 迁移至高性能磁盘(如 SSD),减少低效 I/O;

尽管跨磁盘时存在文件复制开销,也因为复制导致占用磁盘并不会减少,但 “node_modules 中的所有文件均克隆或硬链接自单一存储位置” 这一高效管理能力依旧存在,依然能减少重复下载同版本依赖的请求。

操作步骤:

  1. 选择高性能磁盘作为 store 路径:推荐将 store 放在 SSD(如 D 盘),利用其高随机 I/O 性能加速小文件读取。
  2. 配置全局 store 路径
    # 设置store路径至SSD(示例:D盘根目录)
    pnpm config set store-dir D:\.pnpm-store
    
  3. (可选)清理旧 store:删除原机械盘 E:/.pnpm-store 释放空间(注意:确保无其他项目依赖此 store)。
  4. 重新安装依赖:在项目目录执行 pnpm install,此时依赖读取将以 SSD 为主,机械盘仅承担少量 junctions 创建(可忽略)及复制写入 I/O。

优化建议

若条件允许,将常维护的项目迁移至 SSD,并与 store 置于同一磁盘分区(避免跨盘复制感知,且 SSD 写入更快),可最小化 IO 瓶颈。