Python 负载测试脚本的线程或多处理
我编写了一些代码,用于在用户同时对数据库运行查询时测试数据库的性能。目的是了解运行时间是如何随着用户数量的增加而增加的。代码包含一个类User(如下所示),其对象是通过解析XML文件创建的Python 负载测试脚本的线程或多处理,python,pyodbc,Python,Pyodbc,我编写了一些代码,用于在用户同时对数据库运行查询时测试数据库的性能。目的是了解运行时间是如何随着用户数量的增加而增加的。代码包含一个类User(如下所示),其对象是通过解析XML文件创建的 class User(object): def __init__(self, id, constr): self.id = id self.constr = constr self.queryid = list() self.queri
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)。用户对象存储在列表中。每个用户的查询也在列表中(不在线程安全队列中)
为了模拟并行用户,我尝试了两种不同的方法:
def worker(usr):
usr.executeAll()
选项1:多处理池
pool = Pool(processes=len(users))
pool.map(worker, users)
选项2:线程。线程
for usr in users:
t = Thread(target=worker, args=(usr,))
t.start()
这两种方法都有效。在我的测试中,我尝试了#users=2,6,…,60,每个用户有4个查询。考虑到如何捕获查询时间,在查询结束和下一个查询开始之间应有不到一秒的延迟,即应一个接一个地触发查询。这正是多处理的情况,但对于线程,在下一次查询之前会引入随机延迟。延迟可能超过一分钟(见下文)
使用:python3.4.1、pyodbc3.0.7;运行代码为Windows 7/RHEL 6.5的客户端
我真的更愿意让它与线程一起工作。这是线程方法中所期望的,还是缺少一个命令?或者怎样才能重新书写?Thx.当您使用基于
线程的方法时,您将为每个用户启动一个线程,最多60个线程。所有这些线程都必须在I/O操作之间争取对GIL的访问。这介绍了。如果使用的ThreadPool
仅限于少量线程(可能是2*多处理.cpu\u count()
?),即使用户数量较高,也可能会看到更好的结果:
from multiprocessing.pool import ThreadPool
from multiprocessing import cpu_count
pool = ThreadPool(processes=cpu_count()*2)
pool.map(worker, users)
出于内存使用的原因,您可能也想限制运行的并发进程的数量。启动60个并发Python进程非常昂贵。谢谢@dano。我对你的评论有点困惑,“你可能也想限制你运行的并发进程的数量”。如果我使用您的线程池想法,我不需要这样做,是吗?@ironv如果您使用线程池
,则不需要。但是如果您选择使用常规的多处理.Pool
,您可能希望使用少于len(用户)
的进程来减少内存开销。对于这么多的进程,您可能对CPU的负担也比您真正想要的要大。我观察到的另一种情况甚至是它的原始形式(从我的OP中),线程解决方案在Windows上运行良好,而多处理。池解决方案在RHEL上运行良好。在您发布之前,我正在对“nt”或“posix”进行os.name检查,并且正在调用其中一个。我现在正在Windows上测试线程池,然后在Linux上尝试,看看是否有延迟。另一个问题是我的RHEL盒有96个核心。我还应该使用cpu_count()*2吗?或者做一个最小值(SOMELIMIT,cpu_count()*2)?96个内核?!我嫉妒。有这么多内核,在基于进程池的方法中,我担心2*cpu\u count()
会使内存负担过重。您的最大用户数为60个,因此无论如何,您将不会使用超过60个进程。你可能会做得很好,甚至更少。确实很难说什么数字对你来说是最合适的,这是一种你只能通过测试不同值来验证的东西。阅读了你之前提到的Beazley演示,我有一个问题。在我的脚本中,线程所做的唯一工作是(i)使用pyODBC抓取并启动一个查询,然后等待它完成(ii)将结束时间写入日志文件。重复一遍。问题:是在线程等待查询完成时释放GIL,还是在查询完成后才释放GIL?