记录最近一个大文件数据处理入库任务,需求大致是这样:
数据处理需求:
需要定期拉取一个千万行级的大型文件,文件内容是按照指定分隔符进行分割的数据表内容.
每行结束按照\n换行,类似下面这种格式:
1;female;Micky;19746;
2;male;Tom;749573;
3;male;Bob;465926;
...
字段名分别为:id、gender、name、code
分析思路
在进行代码编写之前,先整理思路,需要考虑的问题。
整体思路倒是简单:读取文件-->将每行转换key:value字典格式-->插入MongoDB;
但是这里不得不考虑两个问题,内存问题、效率问题。
大文件一般不能直接读取到内存,也许你的开发机器足够好能刚好承受,但你的代码如果直接放到生产上,可能直接就读取失败或者让生产机器的内存爆掉了。。
写入效率第一点考虑,在insert_one和insert_many中必然是优先选用insert_many,即一次将一个列表的dict数据插入到MongoDB,这样的效率远高于逐条遍历的insert_one。
写入效率第二点考虑,需考虑将数据并发写入MongoDB,具体究竟该使用协程、多线程还是多进程的方式呢?我们后面再说。
我的整体代码在最后部分,不想看中间啰嗦部分的可直接跳到最后~
本地数据库测试效率:1千万行数据,68秒写入完成
1. 生成器方式读取大文件
自定义一个生成器函数如下,使用next()方法每次可获取一行文件数据。
每行数据需要转换为字典格式返回,以便于可直接插如MongoDB。
FILEDS = ["id", "gender", "name", "code"]
def gen_file(filepath):
"""将大型文件转化为生成器对象,每次返回一行对应的字典格式数据"""
with open(filepath, "r", encoding="utf-8") as f:
while True:
line = f.readline()
if not line:
break
filed_list = line.split(";")[:-1]
doc_dict = {k: v for k, v in zip(FILEDS, filed_list)
yield doc_dict
可使用如下方式获取指定一行数据:
# example
gen = gen_file("./example_file.dat")
# 获取一行字典数据
doc = next(gen)
2.将数据批量写入MongoDB
每次读取1000行数据,使用insert_many批量插入MongoDB,你也可以自定义一次插入多少数据量。最好不要使用inser