json.parse()无法安全解析几百mb文件,因v8需全量加载字符串并构建ast,易超内存;应改用stream-json流式解析,按需提取字段,避免内存溢出。
json.parse() 直接解析几百mb文件必卡死
浏览器或node.js里用 json.parse() 一次性读入并解析超大json(比如500mb日志、导出数据),进程会卡住甚至崩溃——这不是代码写错了,是内存模型决定的。v8引擎需要把整个字符串先载入内存,再构建完整ast,中间没有暂停点。
- Node.js中默认堆内存上限约1.4GB,实际能安全处理的纯JSON字符串远小于此(还要留空间给解析过程)
- 浏览器端更敏感,200MB JSON就可能触发页面无响应(
ERR_OUT_OF_MEMORY或直接白屏) - 别试图调高
--max-old-space-size硬扛,治标不治本,且线上环境通常不允许
用stream-json做流式解析,边读边处理
真正可行的方案是跳过“全量字符串→全量对象”这一步,用支持流式解析的库逐段提取关键字段。推荐 stream-json,它不构建完整对象树,只按需产出token或路径匹配结果。
- 安装:
npm install stream-json - 适合场景:你其实只关心JSON数组里的某几类字段(如
users[].name、logs[].timestamp),不需要整个结构 - 关键配置:
streamObject或streamArray可直接绑定到数组项,避免手动遍历path事件 - 示例片段(提取大数组中每个对象的
id和status):
const { streamArray } = require('stream-json/streamers/StreamArray');
const { chain } = require('stream-chain');
const fs = require('fs');
<p>const pipeline = chain([
fs.createReadStream('huge.json'),
streamArray(),
(data) => {
// data.value 是当前数组项,可直接取字段
console.log(data.value.id, data.value.status);
}
]);</p>
分批导入数据库时,别在循环里反复connect / close
很多人把流式解析后的数据分块(如每1000条一组),然后在for循环里对每批都执行db.insert(...),却没意识到连接开销可能比SQL本身还重。
- MySQL/PostgreSQL客户端默认开启连接池,但若每次手动
new Client()再.connect(),等于绕过池子,频繁握手+TLS协商拖慢整体速度 - 批量写入优先用原生批量语法:
INSERT INTO t VALUES (...), (...), (...),而不是1000次单行INSERT - SQLite特别注意:
BEGIN TRANSACTION必须包住整批,否则每条INSERT都是独立事务,I/O放大1000倍 - Node.js里用
Promise.allSettled()并发提交多批,但并发数别超过数据库连接池大小(通常设为CPU核心数×2)
前端上传大JSON前,先用Blob.slice()分片校验
用户拖一个800MB JSON文件进网页?别等FileReader.readAsText()跑完才报错。前端该在上传前快速判断是否真JSON、有无明显格式错误,避免白等几分钟。
- 用
Blob.slice(0, 1024 * 1024)只读前1MB,调用JSON.parse()验证开头是否合法(如是否以[或{起始) - 检查
Content-Type是否为application/json只是弱提示,真实校验得看内容 - 若后端支持分片上传(如TUS协议),前端应按
Uint8Array切片,而非转成字符串再切——避免UTF-8多字节字符被截断导致解析失败 - 别在主线程做任何
JSON.parse(),用Worker隔离,否则UI彻底冻结
最易忽略的是错误恢复:流式解析中途出错(如某行字段类型突变),stream-json默认终止整个流。必须监听error事件并手动destroy(),否则残留句柄会泄漏。还有,所有ReadStream记得加on('error', ...),Node.js里未捕获的流错误不会抛异常,只会静默失败。










