我正在尝试对我正在使用SQLAlchemy访问的SQlite数据库中的大约200,000个条目进行一些文本处理。我想并行化它(我正在看并行Python),但我不确定如何做到这一点。
我希望每次处理一个条目时都提交会话,这样如果我需要停止脚本,我就不会丢失它已经完成的工作。但是,当我尝试将session.commit()命令传递给回调函数时,它似乎不起作用。
from assignDB import *
from sqlalchemy.orm import sessionmaker
import pp, sys, fuzzy_substring
def matchIng(rawIng, ingreds):
maxScore = 0
choice = ""
for (ingred, parentIng) in ingreds.iteritems():
score = len(ingred)/(fuzzy_substring(ingred,rawIng)+1)
if score > maxScore:
maxScore = score
choice = ingred
refIng = parentIng
return (refIng, choice, maxScore)
def callbackFunc(match, session, inputTuple):
print inputTuple
match.refIng_id = inputTuple[0]
match.refIng_name = inputTuple[1]
match.matchScore = inputTuple[2]
session.commit()
# tuple of all parallel python servers to connect with
ppservers = ()
#ppservers = ("10.0.0.1",)
if len(sys.argv) > 1:
ncpus = int(sys.argv[1])
# Creates jobserver with ncpus workers
job_server = pp.Server(ncpus, ppservers=ppservers)
else:
# Creates jobserver with automatically detected number of workers
job_server = pp.Server(ppservers=ppservers)
print "Starting pp with", job_server.get_ncpus(), "workers"
ingreds = {}
for synonym, parentIng in session.query(IngSyn.synonym, IngSyn.parentIng):
ingreds[synonym] = parentIng
jobs = []
for match in session.query(Ingredient).filter(Ingredient.refIng_id == None):
rawIng = match.ingredient
jobs.append((match, job_server.submit(matchIng,(rawIng,ingreds), (fuzzy_substring,),callback=callbackFunc,callbackargs=(match,session))))
会话从assignDB
导入。我没有收到任何错误,只是没有更新数据库。
感谢您的帮助。
更新 这是fuzzy_substring
的代码def fuzzy_substring(needle, haystack):
"""Calculates the fuzzy match of needle in haystack,
using a modified version of the Levenshtein distance
algorithm.
The function is modified from the levenshtein function
in the bktree module by Adam Hupp"""
m, n = len(needle), len(haystack)
# base cases
if m == 1:
return not needle in haystack
if not n:
return m
row1 = [0] * (n+1)
for i in range(0,m):
row2 = [i+1]
for j in range(0,n):
cost = ( needle[i] != haystack[j] )
row2.append( min(row1[j+1]+1, # deletion
row2[j]+1, #insertion
row1[j]+cost) #substitution
)
row1 = row2
return min(row1)
我从这里得到的是Fuzzy Substring。就我而言,“针”是~8000种可能的选择之一,而haystack是我想要匹配的原始字符串。我循环遍历所有可能的“针”并选择得分最高的那针。
答案 0 :(得分:3)
不看你的具体代码,可以说:
是互不相容的欲望。请SQLite FAQ:
...但是,客户端/服务器数据库引擎(如PostgreSQL,MySQL, 或Oracle)通常支持更高级别的并发和允许 多个进程要同时写入同一个数据库 时间。这在客户端/服务器数据库中是可能的,因为存在 始终是一个可以协调的良好控制的服务器进程 访问。如果您的应用程序需要大量并发,那么 您应该考虑使用客户端/服务器数据库。但经验 表明大多数应用程序需要的并发性要比它们少得多 设计师想象。 ...
即使没有SQLAlchemy使用的任何门控和排序,这也是如此。根本不清楚 - 如果有的话 - 并行Python工作正在完成。
我的建议:让它正常工作首先,然后寻找优化。特别是pp
秘密酱可能根本不会给你带来太大的收益,即使它工作得很好。
在回复评论时添加:
如果fuzzy_substring
匹配是瓶颈,它似乎与数据库访问完全分离,您应该记住这一点。在没有看到fuzzy_substring
正在做什么的情况下,一个好的开始假设是你可以进行算法改进,这可以使单线程编程在计算上可行。 Approximate string matching是一个研究得很好的问题,选择正确的算法通常远比“投入更多处理器”好得多。
在这个意义上更好的是你有更清晰的代码,不浪费分割和重新组合问题的开销,最后有一个更可扩展和可调试的程序。
答案 1 :(得分:0)
@msw提供了an excellent overview of the problem,给出了考虑并行化的一般方法。
尽管有这些评论,但我最终还是要完成这项工作:
from assignDB import *
from sqlalchemy.orm import sessionmaker
import pp, sys, fuzzy_substring
def matchIng(rawIng, ingreds):
maxScore = 0
choice = ""
for (ingred, parentIng) in ingreds.iteritems():
score = len(ingred)/(fuzzy_substring(ingred,rawIng)+1)
if score > maxScore:
maxScore = score
choice = ingred
refIng = parentIng
return (refIng, choice, maxScore)
# tuple of all parallel python servers to connect with
ppservers = ()
#ppservers = ("10.0.0.1",)
if len(sys.argv) > 1:
ncpus = int(sys.argv[1])
# Creates jobserver with ncpus workers
job_server = pp.Server(ncpus, ppservers=ppservers)
else:
# Creates jobserver with automatically detected number of workers
job_server = pp.Server(ppservers=ppservers)
print "Starting pp with", job_server.get_ncpus(), "workers"
ingreds = {}
for synonym, parentIng in session.query(IngSyn.synonym, IngSyn.parentIng):
ingreds[synonym] = parentIng
rawIngredients = session.query(Ingredient).filter(Ingredient.refIng_id == None)
numIngredients = session.query(Ingredient).filter(Ingredient.refIng_id == None).count()
stepSize = 30
for i in range(0, numIngredients, stepSize):
print i
print numIngredients
if i + stepSize > numIngredients:
stop = numIngredients
else:
stop = i + stepSize
jobs = []
for match in rawIngredients[i:stop]:
rawIng = match.ingredient
jobs.append((match, job_server.submit(matchIng,(rawIng,ingreds), (fuzzy_substring,))))
job_server.wait()
for match, job in jobs:
inputTuple = job()
print match.ingredient
print inputTuple
match.refIng_id = inputTuple[0]
match.refIng_name = inputTuple[1]
match.matchScore = inputTuple[2]
session.commit()
基本上,我把问题分成了几块。在并行匹配30个子字符串后,将返回结果并将其提交到数据库。我有点随意选择30,所以在优化这个数字时可能会有所收获。它似乎加快了一点,因为我现在使用处理器中的所有3(!)内核。