当用户同时运行查询时,我编写了一些用于测试数据库性能的代码。目标是了解经过的时间如何随着用户数量的增加而增加。该代码包含一个类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个查询。考虑到如何捕获查询时间,在查询结束和下一个查询开始之间应该有不到一秒的延迟,即查询应该一个接一个地触发。这正是多处理所发生的情况,但是使用线程,在下一个查询之前会引入随机延迟。延迟可能超过一分钟(见下文)。
使用:python3.4.1,pyodbc3.0.7;客户端运行代码Windows 7 / RHEL 6.5
我真的更喜欢让它与线程一起使用。这是在线程方法中预期的还是我缺少一个命令?或者如何重写呢? THX。
答案 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进程非常昂贵。