Python 2亿级数据下基于DataFrame索引生成字典列表多进程方案优化咨询
当前多进程实现的根本问题
- 进程创建粒度完全不合理:你针对每个CustID键单独创建一个独立Process,2亿个键就意味着要创建2亿个进程,光进程创建、销毁的系统开销就远大于本身的计算量。Linux系统下每秒最多也就创建上百个进程,这种粒度下跑完全部任务的时间是不可接受的。
- Manager共享机制完全失效:通过
Manager生成的Namespace本质是跨进程的RPC代理,每次读写ns.df_data、ns.result都要把数据序列化、通过进程间管道传输,根本不是内存层面的共享。每次取对应CustID的行数据都要从主进程跨管道传数据,来回序列化的开销远大于本身的计算耗时,同时会在内存里留存大量临时序列化副本,直接导致内存占用过高。 - 代码本身存在多处运行时bug:
dict_sample没有在函数内初始化,多个进程同时写这个全局变量会出现值覆盖、结果错乱index_data本身是装了1个大字典的列表,直接调用.items()会触发AttributeError,根本跑不通- 漏算了需求里的
Number(Number2/Number1)、Phone字段,已经写的Time_Score和需求的Total_Score字段名不匹配
- 冗余计算拉低效率:题目里明确说明同一个CustID下,仅Score列存在差异,Number1、Number2、Phone都是固定值,你逐键切小DataFrame的方案里,这些固定值会被重复读取、重复计算,纯无意义的性能损耗。
更高效的实现方案
这类按CustID分组的聚合计算,完全不需要逐键循环,pandas内置的向量化聚合在亿级数据上的效率比手写循环高2-3个数量级,大部分场景下甚至不需要多进程就能跑完。
基础单进程实现(优先推荐)
# 先把嵌套结构的index_data拍平,取出真正存映射关系的大字典 custid_index_map = index_data[0] # 构建 行索引 -> 所属CustID 的映射,对齐df_data的行位置 index_to_custid = pd.Series( {row_idx: cust_id for cust_id, idx_list in custid_index_map.items() for row_idx in idx_list} ).sort_index() # 提前计算固定的Number列值,避免聚合时重复计算 df_data['Number'] = df_data['Number2'] / df_data['Number1'] # 一次性完成全量聚合,不需要逐行遍历 agg_res = df_data.groupby('CustID').agg( Total_Score=('Score', 'mean'), Number=('Number', 'first'), Phone=('Phone', 'first') ).reset_index() # 直接转成要求的字典列表结构 result = agg_res.to_dict('records')
如果单进程内存无法承载全量数据,再考虑按数据块拆分的多进程方案,绝对不要按单个CustID开进程:
- 把全量CustID切成和CPU核心数相等的固定大小块,每个进程负责处理一块CustID对应的行数据
- 用
multiprocessing.Pool的进程池管理任务,避免反复创建销毁进程的开销 - 子进程算完直接返回结果列表,不要用跨进程的Manager存共享状态,减少序列化传输开销
亿级数据的内存优化技巧
- 把大整数类型的CustID从默认的object格式转成
uint64类型,Phone这种固定值列转成category类型,能把DataFrame的内存占用压到原来的1/5-1/10,从根源上避免内存溢出 - 计算聚合值时,因为同CustID下非Score列全是固定值,直接取第一行值即可,不需要对这些列做均值、求和计算,能进一步减少计算量
内容的提问来源于stack exchange,提问作者Toan Nguyen Phuoc
相关产品推荐
相关产品推荐

