Python 2.7 在使用pyodbc与多处理并行更新MSSQL表时获取太多死锁错误
我试图打开包含数据的pickle文件,然后用这些数据更新MSSQL表。更新1000000行需要10天的时间。所以我写了一个脚本来提高并行性。我运行的进程越多,出现的错误就越多Python 2.7 在使用pyodbc与多处理并行更新MSSQL表时获取太多死锁错误,python-2.7,multiprocessing,pyodbc,python-multiprocessing,Python 2.7,Multiprocessing,Pyodbc,Python Multiprocessing,我试图打开包含数据的pickle文件,然后用这些数据更新MSSQL表。更新1000000行需要10天的时间。所以我写了一个脚本来提高并行性。我运行的进程越多,出现的错误就越多 (<class 'pyodbc.Error'>, Error('40001', '[40001] [Microsoft][ODBC SQL Server Dri ver][SQL Server]Transaction (Process ID 93) was deadlocked on lock resource
(<class 'pyodbc.Error'>, Error('40001', '[40001] [Microsoft][ODBC SQL Server Dri
ver][SQL Server]Transaction (Process ID 93) was deadlocked on lock resources wit
h another process and has been chosen as the deadlock victim. Rerun the transact
ion. (1205) (SQLExecDirectW)'), <traceback object at 0x0000000002791808>)
这是因为在windows中使用多处理模块时,它使用os.spawn而不是os.fork吗
有没有一种方法可以提供更高的速度
我被告知,该表可以处理比此更多的事务
#!C:/Python/python.exe -u
import pyodbc,re,pickle,os,glob,sys,time
from multiprocessing import Lock, Process, Queue, current_process
def UpDater(pickleQueue):
for pi in iter(pickleQueue.get, 'STOP'):
name = current_process().name
f=pi
cnxn = pyodbc.connect('DRIVER={SQL Server};SERVER=database.windows.net;DATABASE=DB;UID=user;PWD=pwd');
cursor = cnxn.cursor()
update = ("""UPDATE DocumentList
SET Downloaded=?, DownLoadedAs=?,DownLoadedWhen=?,DownLoadedSizeKB=?
WHERE DocNumberSequence=?""")
r = re.compile('\d+')
pkl_file = open(pi, 'rb')
meta = pickle.load(pkl_file)
fileName = meta[0][0]
pl = r.findall(fileName)
l= int(len(pl)-1)
ext = meta[0][1]
url = meta[0][2]
uniqueID = pl[l]
dt = meta[0][4]
size = meta[0][5]
while True:
try:
updated = cursor.execute(update,'Yes', fileName+'.'+ext, dt, size,uniqueID )
break
except:
time.sleep(1)
print sys.exc_info()
print uniqueID
cnxn.commit()
pkl_file.close()
os.remove(fileName+'.pkl')
cnxn.close()
if __name__ == '__main__':
os.chdir('Pickles')
pickles = glob.glob("*.pkl")
pickleQueue=Queue();processes =[];
for item in pickles:
pickleQueue.put(item)
workers = int(sys.argv[1]);
for x in xrange(workers):
p = Process(target=UpDater,args=(pickleQueue,))
p.start()
processes.append(p)
pickleQueue.put('STOP')
for p in processes:
p.join()
我使用的是Windows7和Python2.7Anaconda发行版
编辑
下面使用行锁的答案阻止了错误的发生。但是,更新仍然很慢。原来主键上的旧时尚索引需要100倍的加速一些尝试。使用睡眠是个坏主意。首先,您可以尝试行级锁定吗
update = ("""UPDATE DocumentList WITH (ROWLOCK)
SET Downloaded=?, DownLoadedAs=?,DownLoadedWhen=?,DownLoadedSizeKB=?
WHERE DocNumberSequence=? """)
另一种选择是在事务中包装每一项:
update = ("""
BEGIN TRANSACTION my_trans;
UPDATE DocumentList
SET Downloaded=?, DownLoadedAs=?,DownLoadedWhen=?,DownLoadedSizeKB=?
WHERE DocNumberSequence=?;
END TRANSACTION my_trans;
""")
这两种解决方案中的任何一种都适用于您吗?对于第二种方案,我会出现以下错误:(,ProgrammingError('42000'),“[42000][Microsof t][ODBC SQL Server Driver][SQL Server]关键字“Transaction”附近的语法不正确。”(156)(SQLExecDirectW);[42000][Microsoft][ODBC SQL Server Driver][SQL Server]语句无法准备。(8180)”,)对于第一个语句,我得到了以下结果:(,PProgram mingError('42000'),“[42000][Microsoft][ODBC SQL Server Driver][SQL Server]关键字“with”附近的Incor-rect语法。如果此语句是公共表表达式、xmlnamespaces子句或更改跟踪上下文子句,则前面的语句必须以分号终止。(319)(SQLExecDirectW);[42000][M icrosoft][ODBC SQL Server驱动程序][SQL Server]语句无法准备。好的,您已经得到了最好的答案。第一个答案只需更改为:update=(“update DocumentList WITH(ROWLOCK))即可设置Downloaded=?,DownLoadedAs=?,DownLoadedWhen=?,DownLoadedSizeKB=?其中DocNumberSequence=?“”)但是,我改用了UPDLOCK,它似乎运行得更快。仍然没有我喜欢的那么快,但我不会再出现这种错误,它是可持续的。谢谢你!我还将autocommit=TrueAh,很好,很抱歉!我一次做了三件事,只是语法颠倒了。我不知道是什么导致死锁,但你知道吗可以在
更新程序
开始时尝试连接到数据库一次(在for pi
循环之前)并使用相同的连接运行查询。这会使函数更快。r
也可以在循环之前编译,因为您在每次迭代中使用相同的正则表达式。不,我已经遇到了这个问题。您不能在多个进程之间共享一个连接。您只能传递“可拾取”的对象“数据库连接不是其中之一。好吧,这给了我一个巨大的错误列表,说连接关闭了……啊,现在我知道那里做了什么了(对不起,一开始还没有仔细阅读)。是否为每个文件更新使用一个进程?是否可以尝试为一个更新块使用一个进程?我不太了解python,但我想说的是,连接到数据库相当繁重(身份验证等),因此每个查询的连接和断开连接可能会影响性能。
update = ("""
BEGIN TRANSACTION my_trans;
UPDATE DocumentList
SET Downloaded=?, DownLoadedAs=?,DownLoadedWhen=?,DownLoadedSizeKB=?
WHERE DocNumberSequence=?;
END TRANSACTION my_trans;
""")