【问题标题】:Getting too many deadlock errors while updating MSSQL table with pyodbc in parallel with multiprocessing使用 pyodbc 与多处理并行更新 MSSQL 表时出现太多死锁错误
【发布时间】:2015-09-17 15:51:41
【问题描述】:

我正在尝试打开其中包含数据的 pickle 文件,然后使用该数据更新 MSSQL 表。更新 1,000,000 行需要花费 10 天的时间。所以我写了一个脚本以获得更多的并行性。我运行它的进程越多,我得到的错误就越多

(<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>)  

正如你在我的代码中看到的那样,我一直在尝试处理更新直到成功,甚至在这里休眠一秒钟

while True:
    try:
        updated = cursor.execute(update,'Yes', fileName+'.'+ext, dt, size,uniqueID )
        break
    except:
        time.sleep(1)
        print sys.exc_info() 

这是因为当您在 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()

我使用的是 Windows 7 和 python 2.7 Anaconda Distribution

编辑 下面使用行锁的答案阻止了错误的发生。但是,更新仍然很慢。原来,主键上的旧式索引需要 100 倍加速

【问题讨论】:

  • 我不知道是什么导致了死锁,但是您可以尝试在UpDater 的开头(for pi 循环之前)连接到数据库一次,然后使用相同的连接运行查询。它应该使功能更快。 r 也可以在循环之前编译,因为您在每次迭代中都使用相同的正则表达式。
  • 不,我已经遇到过这个问题。您不能在多个进程之间共享一个连接。您只能传递“可挑选”的对象,而数据库连接不是其中之一。
  • 好的,这给了我一个巨大的错误列表,说连接关闭......
  • 啊,现在我知道那里做了什么(抱歉,一开始没有仔细阅读)。您是否为每个文件更新使用一个过程?您可以尝试使用一个进程进行大量更新吗?我不太了解python,但我想说的是,连接到数据库相当繁重(身份验证等),因此每个查询的连接和断开连接可能会影响性能。

标签: python-2.7 multiprocessing pyodbc python-multiprocessing


【解决方案1】:

有几件事可以尝试。使用睡眠是一个坏主意。首先,您可以尝试行级锁定吗?

    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服务器]关键字“TRANSAC TION”附近的语法不正确。(156) (SQLExecDirectW); [42000] [Microsoft][ODBC SQL Server Driver][SQL Server]无法准备语句。(8180)") , )
  • 第一个我得到这个:((class 'pyodbc.ProgrammingError'>, , PProgram mingError('42000', "[42000] [Microsoft ][ODBC SQL Server 驱动程序][SQL Server]关键字'with'附近的语法不正确。如果此语句是公共表表达式、xmlnamespaces 子句或更改跟踪上下文子句,则前面的语句必须以分号。(319) (SQLExecDirectW); [42000] [Microsoft][ODBC SQL Server Driver][SQL Server]无法准备语句。
  • 好吧,你已经得到了最好的答案。第一个只需要改成这样:update = ("""UPDATE DocumentList WITH (ROWLOCK) SET Downloaded=?, DownLoadedAs=?,DownLoadedWhen=?,DownLoadedSizeKB=? WHERE DocNumberSequence=? """) 但是,我改用 UPDLOCK ,它似乎运行得更快。仍然没有我喜欢的那么快,但我不再遇到这个错误,而且它是可持续的。谢谢!我也打开了 autocommit=True
  • 啊,很好,很抱歉!我一次做了三件事,只是语法倒退了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-29
相关资源
最近更新 更多