Unix下独立启动的Python进程共享大内存缓存方案问询
跨独立Python进程共享大内存缓存的解决方案
针对你这种通过独立shell脚本启动、非父子关系的Python进程,要共享5-10GB缓存数据的需求,我给你整理了几个实用的生产级方案:
1. 操作系统级共享内存(mmap/Posix IPC)
这是最底层、内存开销最低的方案,直接利用操作系统提供的共享内存机制,不依赖进程间的亲缘关系。
核心思路
通过**内存映射文件(mmap)**把一个磁盘文件映射到多个进程的虚拟内存空间,所有进程操作的都是同一块物理内存。对于大体积数据,建议用文件-backed的映射,避免耗尽系统物理内存。
代码示例
写入缓存的进程
import mmap import os import pickle # 预先创建一个足够大的文件作为共享载体(10GB) cache_size = 10 * 1024 * 1024 * 1024 with open("shared_cache.bin", "wb") as f: f.seek(cache_size - 1) f.write(b"\x00") # 映射文件到内存 with open("shared_cache.bin", "r+b") as f: shm = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_WRITE) # 序列化缓存数据(这里用pickle,也可以用msgpack提升性能) cache_data = { "model_weights": [i for i in range(10**7)], "config": {"threshold": 0.8, "batch_size": 32} } serialized_data = pickle.dumps(cache_data) # 先写入数据长度(方便读取时解析) shm.seek(0) shm.write(len(serialized_data).to_bytes(8, byteorder='big')) # 写入实际缓存数据 shm.write(serialized_data) shm.close()
读取缓存的进程
import mmap import pickle with open("shared_cache.bin", "r+b") as f: shm = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) # 读取数据长度 len_bytes = shm.read(8) data_length = int.from_bytes(len_bytes, byteorder='big') # 读取并反序列化数据 serialized_data = shm.read(data_length) cache_data = pickle.loads(serialized_data) print("读取到缓存配置:", cache_data["config"]) shm.close()
注意事项
- 并发同步:需要自己处理读写冲突,比如用
fcntl(Linux/macOS)或win32file(Windows)实现文件锁,或者用Posix信号量做同步。 - 内存优化:Linux下可以把共享文件放在
/dev/shm(tmpfs文件系统,内存级存储),避免占用磁盘空间。
2. Redis 内存数据库
如果不想处理底层共享内存的细节,Redis是最省心的选择——它是一个独立的内存服务,所有进程通过网络连接访问同一个实例,天然支持跨进程、跨机器共享数据。
核心优势
- 自带数据结构(哈希、列表、字符串等),不用自己序列化复杂对象。
- 支持缓存过期、持久化,还有完善的并发控制。
- 无需关心进程间的同步问题,Redis内部已经处理。
代码示例
先安装依赖:pip install redis
写入缓存的进程
import redis import pickle # 连接本地Redis实例(可以配置远程地址) r = redis.Redis(host='localhost', port=6379, db=0) # 序列化大对象并存入Redis cache_data = { "large_dataset": [i for i in range(10**7)], "metadata": {"created_at": "2024-05-20"} } r.set("shared_cache", pickle.dumps(cache_data))
读取缓存的进程
import redis import pickle r = redis.Redis(host='localhost', port=6379, db=0) # 读取并反序列化数据 serialized_data = r.get("shared_cache") cache_data = pickle.loads(serialized_data) print("读取到数据集长度:", len(cache_data["large_dataset"]))
注意事项
- 确保Redis配置了足够的内存(修改
redis.conf中的maxmemory参数,比如设置为12GB)。 - 对于超大数据,序列化和网络传输会有轻微开销,但远低于每个进程单独缓存一份的内存浪费。
3. LMDB 内存映射键值库
LMDB是一个轻量级、高性能的本地内存映射数据库,不需要单独的服务进程,所有进程通过同一个磁盘文件共享数据,自带事务和并发锁。
核心优势
- 性能接近原生共享内存,比Redis更轻量(无额外服务开销)。
- 支持TB级别的数据存储,适合大缓存场景。
- 内置并发控制,不用自己实现锁机制。
代码示例
先安装依赖:pip install lmdb
写入缓存的进程
import lmdb import pickle # 初始化LMDB环境,设置最大映射大小(10GB) env = lmdb.open("shared_lmdb", map_size=10 * 1024 * 1024 * 1024) # 写入缓存数据 with env.begin(write=True) as txn: cache_data = { "embeddings": [i for i in range(10**7)], "params": {"dim": 128, "version": "v1"} } txn.put(b"shared_cache", pickle.dumps(cache_data)) env.close()
读取缓存的进程
import lmdb import pickle env = lmdb.open("shared_lmdb", map_size=10 * 1024 * 1024 * 1024) # 读取缓存数据 with env.begin() as txn: serialized_data = txn.get(b"shared_cache") cache_data = pickle.loads(serialized_data) print("读取到嵌入维度:", cache_data["params"]["dim"]) env.close()
注意事项
map_size必须设置为大于预期缓存大小的值,后续无法动态扩容(需要重新初始化)。- LMDB的文件会自动管理内存映射,无需手动处理内存释放。
内容的提问来源于stack exchange,提问作者Varun Maurya




