python-pooledDB连接池性能问题
最近写工具时需要快速执行大量sql,之前因为db操作不是很多,所以底层实现是每次执行sql的时候,都会去新建立一个链接,这次就直接拿来用了,最后发现大概每秒只能执行300-400个的sql(还可以更高,因为只起了10个线程,但是如果线程数太高db可能会拒绝访问),所以想要改用连接池看看速度能有多块,但是换成链接池后,反而速度立马变成了100个sql/秒,当时还以为是连接池的配置不对,但是试了很多次都是这个速度,最后看了pooledDB的代码才知道对于高线程多并发的场景,pooledDB支持度有限。
并发时性能变差的原因
取出db链接时需要加锁测试存活性
首先,为了保证多线程不出问题,在存取链接到池子里面的时候,会对池子加锁。
而为了保证链接的可用性,在连接池取链接(pool.connection())的时候,pooledDB还会用该链接ping一下(ping_check()),来测试是否链接还可用,确认可用后再将链接返回并解锁队列,此时下一个线程才能来队列中拿链接。
因此在高并发的场景下,耗时一堆叠,就会越来越慢。
def connection(self, shareable=True): |
因此,只要不让db去检查链接存活性就可以大幅提升取连接的速度(可以通过初始化时指定ping=0来避免对连接的可用性进行检查), 减少阻塞时间, 但是同时也会导致可能出现不可用的链接,需要逻辑对这种场景进行兼容。
归还db连接时,会对没关机的链接进行回滚
除了取的时候会有问题,归还的时候,也会对pool的队列进行上锁,然后进行归还,pooledDB有个默认的操作,归还时进行reset,也就是回滚链接。
def cache(self, con): |
如果大量的链接同时归还,也会出现耗时越来越长的问题,最终速度上不去。
对于这个问题,可以在初始化时指定reset=False,避免其强制性回滚。
解决办法
无论是ping=0,还是reset=False,虽然能解决速度问题,但是牺牲了pooledDB为连接带来的健壮性。
因此最好的办法是创建多个链接池。把压力均摊到每个连接池上这样就可以在大量并发的同时,拥有更好的性能。