在多线程中使用相同的数据库连接时发生了什么情况?

时间:2018-07-10 09:28:02

标签: mysql multithreading python-3.x multiprocessing

  • 在提出这个问题之前,我已经搜索了很多有关“在线程之间共享相同的数据库连接”的内容。我得到的大多数答案都是否定的,而是使用连接池,但是很少有人详细解释为什么我们不能这样做。

  • 然后,我使用多处理和多线程编写了示例代码,我想弄清楚它,但是仍然存在一些难以解决的问题。这是我得到的:

      

    多重处理:

    import multiprocessing as multiprocessing
    import pymysql
    conn = pymysql.connect(host='localhost', port=3306, user='root', passwd='123456', db='test')
    
    def operate(sql):
        cur = conn.cursor()
        cur.execute(sql)
        cur.close()
        return cur.fetchall()
    
    pool = multiprocessing.Pool(4)
    res = []
    
    for seq in range(1, 3):
        sql = "SELECT * FROM `user` WHERE `id` = %d" % seq
        p = pool.apply_async(operate, [sql, ])
        res.append(p)
    
    
    pool.close()
    pool.join()
    
    conn.close()
    for j in res:
        print(j.get())
    

    按预期,此代码无法正常工作:

    ((None, None, 1, 'ttt', 'hhh', 1, 0, ''),)   # process1
    ((None, None, 2, 'zzz', '1256', 1, 0, ''),)  # process2  (1), this is normal output and should be what we want.
    

    ((None, None, 2, 'zzz', '1256', 1, 0, ''),)
    ((None, None, 1, 'ttt', 'hhh', 1, 0, ''),)  # (2), this is incorrect output.
    

    并运行了很多次,得到(1)或(2)。原因(我认为)是: 尽管多处理并行运行,但是仍然比process2更早地调用process1。对于mysql,不确定哪个进程的查询首先完成。如果更方便地完成了process2的处理,因为它们共享相同的连接,则该连接将按照调用的顺序返回数据,因此得到了情况(2),否则得到了情况(1)。我说的对吗?

      

    多线程

    import multiprocessing.dummy as multithread
    import pymysql
    from queue import Queue
    
    conn = pymysql.connect(host='localhost', port=3306, user='root', passwd='123456', db='test')
    
    def operate(sql):
        cur = conn.cursor()
        cur.execute(sql)
        cur.close()
        res.put(cur.fetchall())
    
    pool = multithread.Pool(4)
    res = Queue()
    
    for seq in range(1, 3):
        sql = "SELECT * FROM `user` WHERE `id` = %d" % seq
        p = pool.apply_async(operate, [sql, ])
    
    pool.close()
    pool.join()
    
    conn.close()
    print(res.get())
    

    最初,我认为此代码的结果与多处理相同,但结果却有所不同。它打印了process1或process2,甚至卡在了那里。为什么?

  • 将数据库隔离级别更改为SERIALIZABLE即可解决此问题?

    我尝试过。对于多处理,似乎可行;但对多线程没有影响。它应该在理论上起作用,不是吗?如果是这样,我将永远不会这样做,而只是尝试找到一种解决方法。

1 个答案:

答案 0 :(得分:1)

当python程序从多个线程内部使用单个conn对象时,它将在单个数据库连接上混合来自多个线程的消息流量(线程<==> MySql)。那行不通™。

MySQL连接对象不是线程安全的。每个需要访问MySQL的线程都必须打开自己的连接。 (尽管MySQL连接 pool 对象 是线程安全的,所以每个线程都可以安全地从池中请求,使用和释放连接。)

专业提示:当复杂的长期软件(例如pymysql)的开发人员告诉您它不是线程安全的时,请相信他们。