JSON 流式解析:处理 GB 级 JSON 数据
当 JSON 文件达到 GB 级别时,传统的一次性解析方式会导致内存溢出。流式解析是解决这个问题的关键技术。
传统解析 vs 流式解析
传统解析(一次性加载)
// 整个文件加载到内存
const data = JSON.parse(fs.readFileSync('huge.json', 'utf8'));
// 内存占用:文件大小 × 2-3 倍
流式解析(逐块处理)
// 逐条读取,内存占用极低
const stream = fs.createReadStream('huge.json')
.pipe(JSONStream.parse('*'))
.on('data', item => {
// 每次只处理一条数据
processItem(item);
});
流式解析原理
流式解析器(Streaming Parser)不构建完整的数据树,而是:
- 逐字符读取 JSON
- 识别 token({、}、[、]、:、,、字符串、数字)
- 触发事件回调
- 处理完即释放内存
JavaScript 流式解析
使用 stream-json
const { parser } = require('stream-json');
const { streamArray } = require('stream-json/streamers/StreamArray');
const { createReadStream } = require('fs');
const pipeline = createReadStream('data.json')
.pipe(parser())
.pipe(streamArray());
pipeline.on('data', ({ key, value }) => {
console.log(`Item ${key}:`, value);
});
pipeline.on('end', () => {
console.log('处理完成');
});
使用 oboe.js(前端)
oboe('/api/large-data')
.node('items.*', function(item) {
// 每收到一条数据就处理
renderItem(item);
})
.done(function(data) {
console.log('全部加载完成');
});
Java 流式解析
Jackson Streaming API
JsonFactory factory = new JsonFactory();
try (JsonParser parser = factory.createParser(new File("data.json"))) {
while (parser.nextToken() != null) {
if (parser.currentToken() == JsonToken.START_OBJECT) {
// 读取单个对象
MyObject obj = parser.readValueAs(MyObject.class);
processObject(obj);
}
}
}
逐字段读取(最省内存)
try (JsonParser parser = factory.createParser(new File("data.json"))) {
String currentField = null;
while (parser.nextToken() != null) {
if (parser.currentToken() == JsonToken.FIELD_NAME) {
currentField = parser.getCurrentName();
} else if ("name".equals(currentField)) {
String name = parser.getText();
// 只提取需要的字段
}
}
}
Python 流式解析
import ijson
# 流式读取大 JSON 文件
with open('data.json', 'rb') as f:
# 只读取 items 数组中的每个元素
for item in ijson.items(f, 'items.item'):
process_item(item)
JSON Lines 格式
JSON Lines 是流式处理的友好格式:
{"id": 1, "name": "Alice"}
{"id": 2, "name": "Bob"}
{"id": 3, "name": "Charlie"}
优势:
- 每行独立,可以逐行处理
- 支持 append 写入
- 可以用 grep、awk 处理
- 天然支持并行处理
性能对比
对 1GB JSON 文件的处理测试:
| 方法 | 内存占用 | 处理时间 |
|---|---|---|
| JSON.parse | 4.2GB | 45s |
| 流式解析 | 50MB | 60s |
| JSON Lines | 10MB | 35s |
最佳实践
- 设计 API 时考虑流式:返回 JSON Lines 或支持分页
- 选择合适的库:JavaScript 用 stream-json,Java 用 Jackson Streaming
- 只提取需要的字段:避免加载不必要的数据
- 设置超时和重试:大文件处理可能超时
总结
流式解析是处理大数据量 JSON 的关键技术。通过逐块读取和处理,可以在有限的内存中处理无限大的 JSON 文件。