说明
Python还是有其自身局限的
我觉得python的一些对象设计应该有一些缺陷(没有验证),至少我碰到了几种情况,导致了内存膨胀,或者处理速度非常慢:
- 1 list。 当list对象里面的元素过多时,处理会非常慢,时间的延长不是线性的,而是指数的
- 2 request。本次我一次请求数据库中约一亿条数据的id时,内存直接膨胀超限。id本身很小,后续使用分块方式读出时,python程序和数据库占用内存不过5G, 而一次请求时数据时,内存膨胀超过70G还没有停止。
内容
使用一个通用程序来分块执行任务
# 1 根据最大最小id制作tuple list
def slice_list_by_batch1(min_idx, max_idx, batch_num):
batch_list =list(range(min_idx, max_idx + batch_num , batch_num))
res_list = []
for i in range(len(batch_list)-1):
res_list.append((batch_list[i],batch_list[i+1]))
return res_list
# 2 运行的封装
import tqdm
import pandas as pd
# 按照区块进行处理 | 注意,区块的数量最好不要超过1万个, Mongo建议以10万每次的数据读取;Mysql以每次1万条读取。
def process_by_blocks(min_idx, max_idx, batch_num , partial_block_process):
id_tuple_list = slice_list_by_batch1(min_idx, max_idx, batch_num)
df_list = []
fail_tuple_list = []
for id_tuple in tqdm.tqdm(id_tuple_list):
try:
tem_df = partial_block_process(id_tuple = id_tuple)
df_list.append(tem_df)
except:
fail_tuple_list.append(id_tuple)
print('Successful Blocks:' , len(df_list))
print('Fail Blocks:' , len(fail_tuple_list))
res_dict = {}
res_dict['data'] = pd.concat(df_list, ignore_index=True)
res_dict['fail_tuples'] = fail_tuple_list
return res_dict
# 3 具体的处理逻辑
import requests as req
import pandas as pd
mongo_agent_host = 'http://172.17.0.1:24011/'
def block_process(id_tuple = None, id_name = None, keep_cols = None):
start_id, end_id = id_tuple
start_id = int(start_id)
end_id = int(end_id)
limits = end_id - start_id + 1
task_id_series_data = req.post(mongo_agent_host+ 'query_recs_v2/', json = {'connection_hash':'61c73a1cacd7b216e71dcdb7c8f56f5f',
'filter_dict':{id_name : {'$gte':start_id, '$lt':end_id}},
'tier1':'xs','tier2':'ktggcn_raw',
'keep_cols' : keep_cols,
'limits':limits
}).json()
if task_id_series_data['status']:
if len(task_id_series_data['data']):
return pd.DataFrame(task_id_series_data['data'])
else:
return pd.DataFrame()
else:
raise Exception('请求失败', id_tuple)
具体的执行
id_min = 13220001
id_max = 98340000
from functools import partial
block_process_1 = partial(block_process, id_name='task_id', keep_cols=['task_id'])
task_id_series = process_by_blocks(id_min,id_max, 100000, block_process_1)
100%|██████████| 852/852 [05:15<00:00, 2.70it/s]
Successful Blocks: 852
Fail Blocks: 0
指定一个具体的处理逻辑,只要输入起始和终止id就可以进行任务,要用的时候进行偏置就可以了。
大约5分钟读取了一亿的数据,可以直接进行分析。