如何在内存中压缩、保存与加载大型数据?
解决方案:给压缩块加「长度标记」实现内存中分块读写
我太懂这种尴尬了——巨型字典把内存占得满满当当,连整体pickle或者压缩都做不了,好不容易找到分块写入内存的方法,结果读取的时候卡壳了,因为压缩块没分界根本拆不开!核心问题就是你之前的写入逻辑没给每个压缩块做明确的边界标记,导致读取时无法判断每个块的起止位置。下面给你一套完整的内存中分块读写方案:
第一步:补全带边界标记的分块写入逻辑
如果你的原有写入代码没有记录每个压缩块的长度,得先改成下面这种(不然现有数据没法正确读取):
import zlib import struct import pickle from io import BytesIO import numpy as np def write_large_dict_to_memory(large_dict): buffer = BytesIO() # 自定义分块大小,根据你的内存剩余空间调整,比如每1000个键值对为一块 chunk_size = 1000 items = list(large_dict.items()) for i in range(0, len(items), chunk_size): # 切分字典为小分块 chunk = dict(items[i:i+chunk_size]) # 序列化分块(用pickle转成字节流) serialized_chunk = pickle.dumps(chunk) # zlib压缩 compressed_chunk = zlib.compress(serialized_chunk) # 关键操作:先写入压缩块的长度(用struct打包成4字节无符号整数) # 这是读取时的"分界锚点" buffer.write(struct.pack('I', len(compressed_chunk))) # 再写入压缩后的块数据 buffer.write(compressed_chunk) # 将缓冲区指针移到开头,方便后续读取 buffer.seek(0) return buffer
第二步:对应的分块读取逻辑
有了块长度标记后,读取就很清晰了——循环读取长度标记,再读取对应长度的压缩数据,解压合并即可:
def read_large_dict_from_memory(buffer): large_dict = {} buffer.seek(0) while True: # 先读取4字节的块长度标记 len_bytes = buffer.read(4) if not len_bytes: # 读到缓冲区末尾,退出循环 break # 解析出压缩块的实际长度 compressed_len = struct.unpack('I', len_bytes)[0] # 读取对应长度的压缩块数据 compressed_chunk = buffer.read(compressed_len) # 解压+反序列化分块 serialized_chunk = zlib.decompress(compressed_chunk) chunk = pickle.loads(serialized_chunk) # 合并到总字典 large_dict.update(chunk) return large_dict
关键说明
- 边界标记的作用:用
struct.pack('I', ...)把每个压缩块的长度转成固定4字节的二进制数据,相当于给每个块加了一个"长度标签",读取时先读标签就知道接下来要读多少字节的压缩数据,完美解决块分界问题。 - 分块大小调整:
chunk_size可以根据你的内存情况灵活调整——如果单个分块解压后还是占太多内存,就把数值调小一点,反之可以调大提升效率。 - 序列化/压缩一致性:读写时必须用相同的工具链——比如写入用zlib压缩、pickle序列化,读取时也必须对应使用zlib解压、pickle反序列化,否则会报错。
注意事项
如果你的原有写入数据没有加这个长度标记,那很遗憾,现有数据可能无法正确拆分读取了——因为压缩后的字节流没有任何分界特征,无法判断一个块的结束位置。这种情况下只能重新用带长度标记的逻辑写入数据。
内容的提问来源于stack exchange,提问作者Slowpoke
相关产品推荐
相关产品推荐

