一,背景
最近工作中需要使用python脚本将一个xlsx文件中的数据全部导入到es中,由于文件中的内容丰富,数量又多。直接通过python文件读入之后在写入es中,时间比较长,总共接近两万条数据从执行程序,到最终入到es完成,差不多要花10分钟。
通过使用多进程的方式,极大地提高了数据导入es的性能。在这里记录一下对python多进程程序进行的一些分析,研究。
二,性能消耗在哪些地方
为了优化导入速度需要找到花费时间较长的地方在哪。一般来说,一个任务执行时间较长会在两个地方。
1.进行复杂的计算。
2.执行IO操作中存在阻塞,长时间等待IO。
三,程序功能说明
该程序有几个部分,首先是使用openpyxl读xlsx文件中的数据,然后放到一个list中。其次,构造es的数据格式,导入数据到es中。由此可以看出来,在这个程序中既包含了复杂计算比如向list中插入数据,又包含IO操作,所以,在这两个部分都存在耗费时间的问题,这就需要具体问题具体分析。
四,实验环境
测试环境一:
系统:单核,单CPU
数据量:2000
操作:读取xlsx,并发送到es中
首先在单核单CPU,单进程情况下:
main 361 ...Sat Nov 11 04:32:49 2017
main 363...Sat Nov 11 04:33:07 2017
---------------
耗时为18s
其次在单核单CPU,多进程情况下:
main 361 ...Sat Nov 11 04:34:47 2017
main 363...Sat Nov 11 04:35:08 2017
---------------
耗时为19s,反而耗时高了
测试环境二:
系统:2核,2CPU
数据量:2000条
2核2CPU的,单进程:
main 361 ...Sat Nov 11 04:40:15 2017
main 363...Sat Nov 11 04:40:33 2017
---------------
耗时18s
2核2CPU,4个进程:
main 361 ...Sat Nov 11 04:41:46 2017
main 363...Sat Nov 11 04:41:59 2017
---------------
耗时为13s
测试环境三:
系统:2核,2CPU
数据量:4000条
2核2CPU的,单进程:
main 361 ...Sat Nov 11 05:30:22 2017
main 363...Sat Nov 11 05:30:52 2017
---------------
耗时30s
2核2CPU,4个进程:
main 361 ...Sat Nov 11 05:29:08 2017
main 363...Sat Nov 11 05:29:28 2017
---------------
耗时20s
五,总结分析
id | 核心 | 数据量 | 进程 | 耗时 |
---|---|---|---|---|
1 | 单核 | 2000 | 单进程 | 18s |
2 | 单核 | 2000 | 4进程 | 19s |
3 | 4核 | 2000 | 单进程 | 18s |
4 | 4核 | 2000 | 4进程 | 13s |
5 | 4核 | 4000 | 单进程 | 30s |
6 | 4核 | 4000 | 4进程 | 20s |
1和2对比
结论:
1. 多进程并没有提高程序的性能,反而造成了一定的性能下降。
2. 有些情况多进程并不能提高程序的性能
产生原因:
1. 由于系统为单核系统,所以每次程序运行的时候只有一个进程在使用CPU。
2. 由于该程序并没有长时间IO阻塞的情况,只存在复杂的计算。所以时间都耗费在CPU进行复杂计算上。
3. 由于多进程存在进程间的切换,会消耗一定的时间,所以导致耗时反而比单进程时间还长。
1和3对比
结论
1. 多核对于单进程来说没有任何性能上的提升
产生的原因:
1. 多核单进程,实际上每次只有一个进程在使用CPU。
2和4对比
结论:
1. 多核可以明显提升程序的性能。
产生原因:
1. 每个cpu可以并行的执行程序,不像单核CPU那样是交替并发的执行。
5和6对比
结论
1. 数据量越大多核性能优势体现的越明显。
产生的原因:
1. 理想情况应该是多一个核,性能提升翻一倍,但是由于存在一些进程间的切换,所以没法达到理想情况。数据量越大消耗的时间越多,性能翻倍之后,消耗时间也会翻倍的减少,所以数量越大效果越明显。
六,附上程序源码
#coding=utf-8
from datetime import datetime
import threading
from elasticsearch import Elasticsearch
from elasticsearch import helpers
from openpyxl import load_workbook
import sys
import time
from multiprocessing import Process
reload(sys)
sys.setdefaultencoding('utf-8')
def insertApplication(content):
es = Elasticsearch(hosts=["xx.xx.xx.xx:9200"], timeout=5000)
actions = []
i=1
index = 0
#print "start generate index..."
for line in content:
try:
action={
"_index":"my_test_v1",
"_type":"office",
"_id":i,
"_source":{
u"id":unicode(line[0]),
u"proto":unicode(line[1]).decode('utf8'),
u"sip":unicode(line[2]).decode('utf8'),
u"sport":unicode(line[3]).decode('utf8'),
u"dip":unicode(line[4]).decode('utf8'),
u"dport":unicode(line[5]).decode('utf8'),
u"status":unicode(line[6]),
}
}
except:
print "======="
print line
print "+++++++"
i+=1
actions.append(action)
if(len(actions)==500):
success, _ = helpers.bulk(es, actions, raise_on_error=True)
actions = []
#del actions[0:len(actions)]
if (len(actions) > 0):
helpers.bulk(es, actions, raise_on_error=True)
def readFile():
wb = load_workbook(filename=r'~/myfile.xlsx')
sheets = wb.get_sheet_names() # 获取所有表格(worksheet)的名字
sheet0 = sheets[0] # 第一个表格的名称
ws = wb.get_sheet_by_name(sheet0) # 获取特定的 worksheet
# 获取表格所有行和列,两者都是可迭代的
rows = ws.rows
columns = ws.columns
return rows
def sendToEs(rows, start, end):
content = []
for row in rows[start:end]:
line = [col.value for col in row]
content.append(line)
insertApplication(content)
def task():
rows = readFile()
process_list = []
count = 1000
for i in range(0,4):
process_list.append(Process(target=sendToEs, args=(rows,i*count,(i+1)*count - 1)))
return process_list
def multiRun():
process_list = task()
for process in process_list:
process.start()
# 主进程中等待所有子进程退出
for process in process_list:
process.join()
def run():
rows = readFile()
count = 1000
for i in range(0,4):
sendToEs(rows,i*count,(i+1)*count - 1)
if __name__=='__main__':
print "main 361 ..." + time.ctime()
#单进程执行
run()
#多进程执行
multiRun()
print "main 363..." + time.ctime()