You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

百万级嵌套JSON文件对比性能优化请求(单文件4GB)

大JSON文件对比脚本优化方案

优化后的代码

import json
import time
import logging
import argparse
import orjson
from multiprocessing import Pool, cpu_count
from itertools import zip_longest

# 配置日志
def setup_logger(log_file):
    logger = logging.getLogger()
    logger.setLevel(logging.INFO)
    formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
    
    # 移除默认处理器避免重复输出
    if logger.handlers:
        for handler in logger.handlers:
            logger.removeHandler(handler)
    
    # 文件处理器
    file_handler = logging.FileHandler(log_file, mode='w', encoding='utf-8')
    file_handler.setFormatter(formatter)
    logger.addHandler(file_handler)
    
    # 控制台处理器
    console_handler = logging.StreamHandler()
    console_handler.setFormatter(formatter)
    logger.addHandler(console_handler)
    
    return logger

execution_times = {}

def time_function(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        elapsed_time = time.time() - start_time
        execution_times[func.__name__] = elapsed_time
        return result
    return wrapper

def parse_single_line(line):
    """用orjson解析单条JSON记录,速度远快于标准json库"""
    try:
        return orjson.loads(line.strip())
    except orjson.JSONDecodeError as e:
        logging.error(f"解析JSON行失败: {e}")
        return None

@time_function
def parse_json_file(file_path):
    """逐行读取并解析JSON文件,返回生成器避免内存占用"""
    with open(file_path, 'r', encoding='utf-8') as f:
        for line in f:
            record = parse_single_line(line)
            if record is not None:
                yield record

def compare_records_batch(batch):
    """批量对比记录,用于多进程处理"""
    batch_diffs = []
    for idx, (record1, record2) in batch:
        diffs = compare_json_records(record1, record2)
        if diffs:
            batch_diffs.append({"record_index": idx, "differences": diffs})
    return batch_diffs

@time_function
def compare_json_records(record1, record2):
    """迭代式对比嵌套JSON,比递归更高效"""
    differences = {}
    stack = [("root", record1, record2)]
    
    while stack:
        path, val1, val2 = stack.pop()
        
        # 处理字典类型
        if isinstance(val1, dict) and isinstance(val2, dict):
            all_keys = val1.keys() | val2.keys()
            for key in all_keys:
                stack.append((f"{path}.{key}", val1.get(key), val2.get(key)))
            continue
        
        # 处理列表类型(仅当长度相同时逐元素对比)
        if isinstance(val1, list) and isinstance(val2, list):
            if len(val1) != len(val2):
                differences[path] = {"old": val1, "new": val2}
                continue
            for i, (item1, item2) in enumerate(zip(val1, val2)):
                stack.append((f"{path}[{i}]", item1, item2))
            continue
        
        # 基础类型对比
        if val1 != val2:
            differences[path] = {"old": val1, "new": val2}
    
    return differences

@time_function
def compare_large_json_files(file1, file2, batch_size, output_file, log_file):
    logger = setup_logger(log_file)
    logger.info(f"开始对比文件: {file1} 和 {file2}")
    
    # 生成两个文件的记录迭代器
    records1 = parse_json_file(file1)
    records2 = parse_json_file(file2)
    
    # 准备带索引的批量任务
    task_batches = []
    current_batch = []
    for record_idx, (r1, r2) in enumerate(zip_longest(records1, records2)):
        # 处理记录数不一致的情况
        if r1 is None:
            current_batch.append((record_idx, (None, r2)))
        elif r2 is None:
            current_batch.append((record_idx, (r1, None)))
        else:
            current_batch.append((record_idx, (r1, r2)))
        
        if len(current_batch) >= batch_size:
            task_batches.append(current_batch)
            current_batch = []
    if current_batch:
        task_batches.append(current_batch)
    
    logger.info(f"共生成 {len(task_batches)} 个处理批次,每个批次 {batch_size} 条记录")
    
    # 多进程处理,进程数设为CPU核心数
    start_time = time.time()
    total_diffs = []
    with Pool(processes=cpu_count()) as pool:
        for batch_result in pool.imap_unordered(compare_records_batch, task_batches):
            total_diffs.extend(batch_result)
    
    # 按记录索引排序差异结果
    total_diffs.sort(key=lambda x: x["record_index"])
    
    # 保存差异结果
    with open(output_file, 'w', encoding='utf-8') as f:
        # 用orjson序列化更快,输出兼容标准JSON
        f.write(orjson.dumps(total_diffs, option=orjson.OPT_INDENT_2).decode('utf-8'))
    
    total_time = time.time() - start_time
    logger.info(f"对比完成,总耗时: {total_time:.2f} 秒")
    logger.info(f"共发现 {len(total_diffs)} 条差异记录")
    logger.info(f"差异结果已保存至: {output_file}")

@time_function
def main():
    parser = argparse.ArgumentParser(description="高效对比两个大JSON文件(每行一条JSON记录)")
    parser.add_argument("file1", help="第一个JSON文件路径")
    parser.add_argument("file2", help="第二个JSON文件路径")
    parser.add_argument("--batch_size", type=int, default=2000, help="每个进程处理的记录批次大小,默认2000")
    parser.add_argument("--output_file", required=True, help="差异结果输出文件路径")
    parser.add_argument("--log_file", required=True, help="日志文件路径")
    
    args = parser.parse_args()
    
    compare_large_json_files(args.file1, args.file2, args.batch_size, args.output_file, args.log_file)
    
    # 输出各函数执行时间
    logger = logging.getLogger()
    logger.info("\n各函数执行时间统计:")
    for func_name, elapsed in execution_times.items():
        logger.info(f"{func_name}: {elapsed:.4f} 秒")
    
    print("\n各函数执行时间统计:")
    for func_name, elapsed in execution_times.items():
        print(f"{func_name}: {elapsed:.4f} 秒")

if __name__ == "__main__":
    main()

核心优化点

  1. 移除预格式化步骤
    原代码需要生成4GB级别的中间JSON数组文件,既耗时又占磁盘空间。优化后直接逐行解析原始文件,完全规避中间文件的IO开销。

  2. 替换解析库为orjson
    orjson是Rust实现的JSON库,解析/序列化速度是标准json库的5-10倍,大幅降低文件解析的时间占比。

  3. 对比函数改为迭代实现
    原递归对比存在大量Python函数调用开销,改为基于栈的迭代遍历,减少函数调用次数,提升对比效率。

  4. 高效多进程处理

    • 进程数设置为CPU核心数,最大化利用多核资源
    • 批量提交任务而非单条提交,减少进程间通信(IPC)开销,解决之前多进程效率低下的问题
    • 使用imap_unordered异步获取结果,避免等待单个批次完成,提升整体处理速度
  5. 内存优化
    全程使用生成器读取记录,避免一次性加载百万条记录到内存,降低内存占用,避免系统换页导致的性能下降。

  6. 新增记录数不一致处理
    自动识别两个文件记录数不匹配的情况,并将缺失记录标记为差异。

使用说明

  1. 安装依赖:pip install orjson
  2. 运行示例:
    python compare_json.py old_system_output.json new_system_output.json --batch_size 2000 --output_file diffs.json --log_file compare.log
    
  3. 批次大小建议:根据CPU核心数调整,核心数越多可适当增大(如4核用2000,8核用4000)

内容的提问来源于stack exchange,提问作者Prakash Kumar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 08:55:55