在多线程中使用相同的数据库连接时,后面发生了什么


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

  • 然后,我使用多处理和多线程编写了一个示例代码,我正在尝试解决它,但仍有一些困难需要解决。这是我得到的:

    多处理:

    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(。原因(我认为(是:尽管多处理并行运行,但process1仍然比process2更早被调用。对于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以解决此问题?

    我试过了。对于多处理来说,这似乎是可行的;但对多线程没有影响。理论上应该有效,不是吗?如果是这样的话,我无论如何都不会这么做,只是想办法解决它

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

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

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

相关内容

最新更新