python 中数据库连接池的最佳解决方案是什么?

What is the best solution for database connection pooling in python?

提问人:John 提问时间:9/19/2008 最后编辑:John 更新时间:6/21/2022 访问量:68639

问:

我开发了一些自定义的类似 DAO 的类来满足我的项目的一些非常专业的要求,这是一个不在任何类型的框架中运行的服务器端进程。

该解决方案效果很好,除了每次发出新请求时,我都会通过 MySQLdb.connect 打开一个新连接。

将其切换到在 python 中使用连接池的最佳“插入式”解决方案是什么?我正在想象类似于 Java 的 commons DBCP 解决方案。

该进程运行时间很长,并且有许多线程需要发出请求,但不是同时发出请求......具体来说,在短暂地写出大部分结果之前,他们做了相当多的工作。

编辑添加: 经过一番搜索,我找到了 anitpool.py 看起来不错的,但由于我对 python 相对较新,我想我只是想确保我没有错过一个更明显/更惯用/更好的解决方案。

python mysql 连接池

评论


答:

18赞 Chris 9/19/2008 #1

包装连接类。

设置建立的连接数限制。 返回未使用的连接。 Intercept close 以释放连接。

更新: 我在 dbpool.py 中放了这样的东西:

import sqlalchemy.pool as pool
import MySQLdb as mysql
mysql = pool.manage(mysql)

评论

2赞 John 9/19/2008
克里斯,肯定有人已经建造了这个吗?最坏的情况是我可以自己编写它,但显然对于不使用现有 ORM/框架的人来说,这应该是一个相当普遍的要求,而且我敢肯定其他人已经创建了一个经过时间验证的解决方案?
0赞 Chris 9/19/2008
我以前在 Oracle 上做过这件事,我认为它总共涉及不到 50 行代码。基本上,使用id,字典,存储连接,存储使用状态等。 很简单吗?
5赞 Martin Konecny 10/8/2013
@Chris,按照这个逻辑链,我也应该开始自己实现我的哈希图和列表。
16赞 S.Lott 9/19/2008 #2

IMO,“更明显/更惯用/更好的解决方案”是使用现有的 ORM,而不是发明类似 DAO 的类。

在我看来,ORM 比“原始”SQL 连接更受欢迎。为什么?因为 Python OO,从 SQL 行到对象的映射绝对必要的。处理未映射到 Python 对象的 SQL 行的用例并不多。

我认为SQLAlchemySQLObject(以及相关的连接池)是更惯用的Pythonic解决方案。

池化作为单独的功能并不常见,因为纯 SQL(没有对象映射)对于受益于连接池的复杂、长时间运行的进程来说不是很受欢迎。是的,使用纯 SQL,但它始终用于更简单或更受控的应用程序,在这些应用程序中,池化没有帮助。

我认为您可能有两种选择:

  1. 修改类以使用 SQLAlchemy 或 SQLObject。虽然这乍一看很痛苦(所有的工作都浪费了),但您应该能够利用所有的设计和思想。它只是采用广泛使用的 ORM 和池化解决方案的练习。
  2. 使用您概述的算法推出您自己的简单连接池 - 您循环浏览的简单连接集或列表。

评论

0赞 Stanislav Bashkyrtsev 4/29/2023
a) DAO/存储库不违背 ORM。事实上,当你使用ORM或DB-API时,你应该创建这样的层。b) 数据库池必须存在于 ORM 之外 - 只是 Python 社区不知道更好。事实上,如果编写得当,两种类型的项目都应该使用它们——无论有没有 ORM。
21赞 mbac32768 9/19/2008 #3

在MySQL中?

我想说的是不要为连接池而烦恼。它们通常是麻烦的根源,而对于MySQL,它们不会给你带来你所希望的性能优势。从政治上讲,这条路可能需要付出很多努力,因为在这个领域有太多的最佳实践挥手和教科书式的措辞,关于连接池的优势。

连接池只是无状态应用程序(例如 HTTP 协议)的后 Web 时代和有状态的长寿命批处理应用程序的前 Web 时代之间的桥梁。由于在Web之前的数据库中,连接非常昂贵(因为过去没有人太关心建立连接需要多长时间),因此后Web应用程序设计了这种连接池方案,以便每次点击都不会在RDBMS上产生如此巨大的处理开销。

由于MySQL更像是Web时代的RDBMS,因此连接非常轻量级和快速。我编写了许多大容量的 Web 应用程序,这些应用程序根本不对 MySQL 使用连接池。

这是一个复杂的问题,只要没有政治障碍需要克服,你可能会从中受益。

评论

35赞 Joe 9/14/2016
这个答案发布 8 年后,汇集仍然具有相关性。如果运行流量较大的 Web 应用,则无论其是否无状态,都很容易遇到“连接过多”限制。池将通过等待免费连接而不是硬失败来帮助缓解这种情况。此外,如果要水平缩放应用服务器,则数据库可能不会位于同一台计算机上。在这种情况下,您很可能希望通过 HTTPS 连接到它,这会产生大量开销。游泳池也会在这里提供帮助。
1赞 Stanislav Bashkyrtsev 5/26/2023
@Joe,您可能指的是 TLS/SSL,而不是 HTTPS。
3赞 Willie 5/15/2009 #4

我一直在寻找同样的东西。

我找到了pysqlpoolsqlalchemy池模块

2赞 flexo 8/23/2011 #5

如果您的应用程序决定开始使用多线程,那么创建自己的连接池是一个坏主意。为多线程应用程序创建连接池比为单线程应用程序创建连接池要复杂得多。在这种情况下,您可以使用类似 PySQLPool 的东西。

如果您正在寻找性能,使用 ORM 也是一个坏主意。

如果您要处理必须处理大量选择、插入、 同时更新和删除,那么您将需要性能,这意味着您需要编写自定义 SQL 来优化查找和锁定时间。使用 ORM,您通常没有这种灵活性。

所以基本上,是的,你可以创建自己的连接池并使用 ORM,但前提是你确定你不需要我刚才描述的任何内容。

9赞 metaperture 7/15/2014 #6

旧线程,但对于通用池(连接或任何昂贵的对象),我使用类似的东西:

def pool(ctor, limit=None):
    local_pool = multiprocessing.Queue()
    n = multiprocesing.Value('i', 0)
    @contextlib.contextmanager
    def pooled(ctor=ctor, lpool=local_pool, n=n):
        # block iff at limit
        try: i = lpool.get(limit and n.value >= limit)
        except multiprocessing.queues.Empty:
            n.value += 1
            i = ctor()
        yield i
        lpool.put(i)
    return pooled

它构造懒惰,有一个可选的限制,并且应该泛化到我能想到的任何用例。当然,这是假设你真的需要任何资源的池化,而对于许多现代SQL来说,你可能不需要。用法:

# in main:
my_pool = pool(lambda: do_something())
# in thread:
with my_pool() as my_obj:
    my_obj.do_something()

这确实假设 ctor 创建的任何对象在需要时都具有适当的析构函数(某些服务器不会终止连接对象,除非它们被显式关闭)。

评论

1赞 socketpair 7/5/2016
你忘记了两件事:1.可能会引发异常,所以你应该用try...除了。2.可能以错误状态返回对象(如数据库连接与打开的事务)yield ilpool.put(i)
0赞 metaperture 7/6/2016
异常生成实际上应该由上下文管理器处理。无论上下文如何退出(异常或其他),函数的其余部分都将运行。但是,是的,如果您正在对数据库进行有状态操作,那么在函数的后屈服位中处理它将是一个好主意。
0赞 metaperture 7/6/2016
在实践中,在 Chris 编辑的帖子中使用 pool 对象可能更好,但对于那些希望学习如何实现池的人来说,我认为这是一个很好的例子。
3赞 kilokahn 6/11/2017 #7

回复一个旧线程,但上次我检查时,MySQL提供连接池作为其驱动程序的一部分。

您可以在以下位置查看它们:

https://dev.mysql.com/doc/connector-python/en/connector-python-connection-pooling.html

从 TFA 中,假设您想显式打开连接池(如 OP 所述):

dbconfig = {  "database": "test", "user":"joe" }
cnxpool = mysql.connector.pooling.MySQLConnectionPool(pool_name = "mypool",pool_size = 3, **dbconfig)

然后,通过 get_connection() 函数从池中请求来访问此池。

cnx1 = cnxpool.get_connection()
cnx2 = cnxpool.get_connection()
1赞 ospider 11/11/2018 #8

使用方便,简单可靠。DBUtils

pip install DBUtils
0赞 mahesh langote 6/21/2022 #9

我为 OpenSearch 做了它,所以你可以参考它。

    from opensearchpy import OpenSearch

    def get_connection():
                    connection = None
                    try:
                        connection = OpenSearch(
                            hosts=[{'host': settings.OPEN_SEARCH_HOST, 'port': settings.OPEN_SEARCH_PORT}],
                            http_compress=True,
                            http_auth=(settings.OPEN_SEARCH_USER, settings.OPEN_SEARCH_PASSWORD),
                            use_ssl=True,
                            verify_certs=True,
                            ssl_assert_hostname=False,
                            ssl_show_warn=False,
                        )
                    except Exception as error:
                        print("Error: Connection not established {}".format(error))
                    else:
                        print("Connection established")
                    return connection

    class OpenSearchClient(object):
        connection_pool = []
        connection_in_use = []

        def __init__(self):
            if OpenSearchClient.connection_pool:
                pass
            else:

                OpenSearchClient.connection_pool = [get_connection() for i in range(0, settings.CONNECTION_POOL_SIZE)]

        def search_data(self, query="", index_name=settings.OPEN_SEARCH_INDEX):
            available_cursor = OpenSearchClient.connection_pool.pop(0)
            OpenSearchClient.connection_in_use.append(available_cursor)
            response = available_cursor.search(body=query, index=index_name)
            available_cursor.close()
            OpenSearchClient.connection_pool.append(available_cursor)
            OpenSearchClient.connection_in_use.pop(-1)
            return response

评论

0赞 dsillman2000 6/21/2022
您的答案可以通过额外的支持信息得到改进。请编辑以添加更多详细信息,例如引文或文档,以便其他人可以确认您的答案是正确的。您可以在帮助中心找到有关如何写出好答案的更多信息。