负载测试脚本的线程或多处理

时间:2014-08-02 16:33:03

标签: python pyodbc

当用户同时运行查询时,我编写了一些用于测试数据库性能的代码。目标是了解经过的时间如何随着用户数量的增加而增加。该代码包含一个类User(如下所示),其对象是通过解析XML文件创建的。

class User(object):

    def __init__(self, id, constr):
        self.id = id
        self.constr = constr
        self.queryid = list()
        self.queries = list()

    def openConn(self):
        self.cnxn = pyodbc.connect(self.constr)
        logDet.info("%s %s"%(self.id,"Open connection."))

    def closeConn(self):
        self.cnxn.close()
        logDet.info("%s %s"%(self.id,"Close connection."))

    def executeAll(self):
        self.openConn()

        for n,qry in enumerate(self.queries):
            try:
                cursor = self.cnxn.cursor()
                logTim.info("%s|%s|beg"%(self.id, self.queryid[n]))
                cursor.execute(qry)
                logTim.info("%s|%s|end"%(self.id, self.queryid[n]))

            except Exception:
                cursor.rollback()
                logDet.exception("Error while running query.")

        self.closeConn()

pyODBC用于连接数据库。创建两个日志 - 一个详细(logDet)和一个只有时间(logTim)的日志。 User对象存储在列表中。每个用户的查询也在一个列表中(不在线程安全的队列中)。

为了模拟并行用户,我尝试了几种不同的方法:

def worker(usr):
    usr.executeAll()

选项1:multiprocessing.Pool

pool = Pool(processes=len(users))
pool.map(worker, users)

选项2:threading.Thread

for usr in users:
    t = Thread(target=worker, args=(usr,))
    t.start()

这两种方法都有效。在我的测试中,我尝试了#users = 2,6,..,60,每个用户有4个查询。考虑到如何捕获查询时间,在查询结束和下一个查询开始之间应该有不到一秒的延迟,即查询应该一个接一个地触发。这正是多处理所发生的情况,但是使用线程,在下一个查询之前会引入随机延迟。延迟可能超过一分钟(见下文)。 enter image description here

使用:python3.4.1,pyodbc3.0.7;客户端运行代码Windows 7 / RHEL 6.5

我真的更喜欢让它与线程一起使用。这是在线程方法中预期的还是我缺少一个命令?或者如何重写呢? THX。

1 个答案:

答案 0 :(得分:1)

当您使用基于threading的方法时,您将为每个用户启动一个线程,最多可以启动60个线程。所有这些线程必须争取在其I / O操作之间访问GIL。这引入了a ton of overhead。如果您使用ThreadPool仅限于较少数量的线程(可能2 * multiprocessing.cpu_count()?),即使用户数较多,您可能会看到更好的结果:

from multiprocessing.pool import ThreadPool
from multiprocessing import cpu_count

pool = ThreadPool(processes=cpu_count()*2)
pool.map(worker, users)

出于内存使用原因,您可能还希望限制运行的并发进程数。启动60个并发Python进程非常昂贵。