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

反复读取Parquet文件时内存持续上涨的问题求助

解决Parquet文件反复读取时内存持续上涨的问题

问题根源分析

你遇到的内存上涨并非真正的Python对象泄漏,核心原因在于:

  1. PyArrow默认jemalloc内存池策略:jemalloc为提升性能,会保留已释放的内存块(dirty/muzzy页)供后续复用,不会立即还给操作系统,导致RSS(驻留内存)持续上涨。
  2. Parquet元数据缓存:PyArrow读取Parquet时默认缓存文件元数据,多次读取会累积占用内存。
  3. macOS内存管理特性:系统会将未主动回收的内存作为文件缓存,导致RSS数值看起来持续增长,但实际属于可回收资源。

具体解决方案

1. 强制jemalloc立即释放内存给操作系统

在脚本最开头设置jemalloc环境变量,调整内存回收策略:

import os
# 配置jemalloc立即回收脏页、开启后台回收线程
os.environ['MALLOC_CONF'] = 'dirty_decay_ms:0,muzzy_decay_ms:0,background_thread:true'

# 后续导入PyArrow等库
import pyarrow as pa
# ... 其他导入代码

同时在每次内存释放后,补充调用:

pa.default_memory_pool().release_unused()
# 强制jemalloc执行一次主动回收
pa.jemalloc_purge()

2. 禁用Parquet元数据缓存

读取Parquet时显式关闭元数据缓存,避免重复占用内存:

def test_func_pyarrow():
    # 读取时禁用元数据缓存
    table = pq.read_table("/Users/test.parquet", metadata_cache=None)
    # 先置空再删除,确保无隐式引用残留
    del table
    table = None
    gc.collect()
    pa.default_memory_pool().release_unused()
    pa.jemalloc_purge()

或者使用上下文管理器显式管理ParquetFile资源:

def test_func_pyarrow():
    with pq.ParquetFile("/Users/test.parquet", metadata_cache=None) as pf:
        table = pf.read()
    # 上下文结束后自动释放文件关联资源
    del table
    table = None
    gc.collect()
    pa.default_memory_pool().release_unused()
    pa.jemalloc_purge()

3. 区分真实内存占用与系统缓存

修改print_memory_usage函数,同时监控PyArrow实际分配的内存,避免被系统缓存误导:

def print_memory_usage():
    process = psutil.Process()
    mem_info = process.memory_info()
    print(f"RSS 驻留内存: {mem_info.rss / 1024 / 1024:.2f} MB")
    # 查看PyArrow实际占用的内存(真实内存使用量)
    print(f"PyArrow 已分配内存: {pa.total_allocated_bytes() / 1024 / 1024:.2f} MB")

如果PyArrow已分配内存不再上涨,说明只是系统未回收RSS缓存,无需过度担忧;若仍上涨,再排查是否有隐式引用残留。

4. 分块读取超大Parquet文件

如果文件体积过大,一次性读取整表会产生大量内存碎片,建议按行组分块读取处理:

def test_func_pyarrow():
    with pq.ParquetFile("/Users/test.parquet", metadata_cache=None) as pf:
        for row_group_idx in range(pf.num_row_groups):
            # 逐行组读取数据
            table = pf.read_row_group(row_group_idx)
            # 这里添加你的日期过滤逻辑
            # ...
            # 立即释放当前行组内存
            del table
            table = None
            gc.collect()
    pa.default_memory_pool().release_unused()
    pa.jemalloc_purge()

5. Pandas/Polars的适配方案

由于Pandas和Polars底层依赖PyArrow读取Parquet,上述内存池配置同样生效。读取时可显式指定PyArrow引擎并禁用缓存:

  • Pandas:
    pd.read_parquet("/Users/test.parquet", engine="pyarrow", metadata_cache=None)
    
  • Polars:
    pl.read_parquet("/Users/test.parquet", use_pyarrow=True, pyarrow_options={"metadata_cache": None})
    

验证效果

修改后运行脚本,观察PyArrow已分配内存是否稳定。若该数值不再增长,说明内存已正常回收,RSS的波动属于系统缓存机制,不影响实际内存使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 11:35:11