为什么连接池会减慢flask中的sql查询

2024-05-16 03:38:08 发布

您现在位置:Python中文网/ 问答频道 /正文

连接池应该能够提高postgres的吞吐量,或者至少这是每个人在谷歌搜索什么是池及其好处时所说的,然而,每当我在flask中尝试连接池时,结果总是比只在文件开头打开连接和光标而从不关闭它们要慢得多。如果我的webapp不断收到用户的请求,为什么我们甚至要关闭连接和游标,那么创建一次连接和游标,然后在收到请求时(无论是get请求还是POST请求)只使用现有的游标和连接不是更好吗。我是不是遗漏了什么?! 下面是每种方法的时间安排,下面是我运行以对每种方法进行基准测试的代码

用一个数据库连接完成100000个查询花费了16.537524700164795秒,我们在flask应用程序开始时打开了一次数据库连接,但从未关闭

使用psqycopg2池方法完成100000个查询花费了38.0747735003357秒

使用pgbouncer池方法完成100000个查询花费了52.307902574539185秒

此外,这里有一个视频运行测试的结果,以防它有任何帮助 https://youtu.be/V2kzKApDs8Y

我用来对每种方法进行基准测试的flask应用程序是

import psycopg2
import time
from psycopg2 import pool
from flask import Flask

app = Flask(__name__)

connection_pool = pool.SimpleConnectionPool(1, 50,host="localhost",database="test",user="postgres",password="test",port="5432")

connection = psycopg2.connect(host="127.0.0.1",database="test",user="postgres",password="test",port="5432")
cursor = connection.cursor()

pgbouncerconnection_pool = pool.SimpleConnectionPool(1, 50, host="127.0.0.1",database="test",user="postgres",password="test",port="6432")

@app.route("/poolingapproach")
def zero():
    start = time.time()
    for x in range(100000):
        with connection_pool.getconn() as connectionp:
            with connectionp.cursor() as cursorp:
                cursorp.execute("SELECT *  from tb1 where id = %s" , [x%100])
                result = cursorp.fetchone()
                connection_pool.putconn(connectionp)
    y = "it took " +  str(time.time() - start) + " seconds to finish 100000 queries with the pooling approach"
    return str(y) , 200

@app.route("/pgbouncerpooling")
def one():
        start = time.time()
        for x in range(100000):
                with pgbouncerconnection_pool.getconn() as pgbouncer_connection:
                        with pgbouncer_connection.cursor() as pgbouncer_cursor:
                                pgbouncer_cursor.execute("SELECT *  from tb1 where id = %s" , [x%100])
                                result = pgbouncer_cursor.fetchone()
                                pgbouncerconnection_pool.putconn(pgbouncer_connection)
        a = "it took " +  str(time.time() - start) + " seconds to finish 100000 queries with pgbouncer pooling approach"
        return str(a) , 200



@app.route("/oneconnection_at_the_begining")
def two():
    start = time.time()
    for x in range(100000):
        cursor.execute("SELECT * from tb1 where id = %s",[x%100])
        result = cursor.fetchone()
    end = time.time()
    x = 'it took ' + str(end - start)+ ' seconds to finish 100000 queries with one database connection that we don\'t close'
    return str(x) , 200
if  __name__=="__main__":
    app.run()


Tags: 方法fromtestimportappflasktimewith
1条回答
网友
1楼 · 发布于 2024-05-16 03:38:08

我不确定您是如何进行测试的,但连接池的想法基本上是解决两个问题:

  1. 您有更多的用户尝试连接到db可以直接处理的db(例如,您的db允许100个连接,同时有1000个用户尝试使用它),并且
  2. 节省打开和关闭连接所需的时间,因为它们始终处于打开状态

所以我相信你的测试必须集中在这两种情况上

如果您的测试只是一个请求大量查询的用户,那么连接池将只是一个开销,因为它将尝试打开您不需要的额外连接

如果测试总是返回相同的数据,那么DBMS可能会缓存结果和查询计划,因此会影响结果。事实上,为了扩展内容,您甚至可以从辅助缓存(如elasticsearch)中获益

现在,如果您想执行一个真实的测试,您必须混合读写操作,向其中添加一些随机变量(强制DB不缓存结果或查询计划),并尝试增量加载,这样您就可以在每次添加更多执行请求的同时客户端时看到系统的行为

由于这些客户机还增加了测试的CPU负载,您也可以考虑在不同的机器上运行客户端,而不是服务于您的DB,以保持结果公平。p>

相关问题 更多 >