MySQL连接器/Python没有显式关闭连接

3

我有以下内容:

class FooData(object):
    def __init__(self):
        ...
        try:
            self.my_cnf = os.environ['HOME'] + '/.my.cnf'
            self.my_cxn = mysql.connector.connect(option_files=self.my_cnf)
            self.cursor = self.my_cxn.cursor(dictionary=True)
        except mysql.connector.Error as err:
            if err.errno == 2003:
                self.my_cnf = None
                self.my_cxn = None
                self.cursor = None

我能够使用my_cxncursor而没有任何明显的失败。我从未明确终止连接,并观察到以下信息在我的mysql错误日志中...

2017-01-08T15:16:09.355190Z 132 [Note] Aborted connection 132 to db:
    'mydatabase' user: 'myusername' host: 'localhost'
    (Got an error reading communication packets)

我这样做是不是有问题?每次运行查询时,初始化连接器和游标是否更有效率?

在mysql配置中,我需要注意哪些内容以避免连接中断?

此外,我经常在错误日志中看到以下消息:

2017-01-06T15:28:45.203067Z 0 [Warning] Changed limits: max_open_files: 1024
    (requested 5000)
2017-01-06T15:28:45.205191Z 0 [Warning] Changed limits: table_open_cache: 431
    (requested 2000)

这与上述内容有关吗?它是什么意思,我该如何解决它?

我尝试了各种解决方案,包括/lib/systemd/system/mysql.service.d/limits.conf和其他配置设置,但是我无法让它们中的任何一个起作用。

2个回答

3
不是配置问题。当你完成一个连接时,应该通过显式调用 `close` 方法来关闭它。通常最佳实践是长时间保持连接,因为创建连接需要时间。从你的代码片段中无法确定何时关闭连接是最好的 - 这取决于每个人的“完成”时间; 可能在你的 `__main__` 方法结束时关闭。同样,在完成游标时应明确关闭它。通常这发生在每个查询之后。
因此,也许可以这样做:
class FooData(object):
    def __init__(self):
        ...
        try:
            self.my_cnf = os.environ['HOME'] + '/.my.cnf'
            self.my_cxn = mysql.connector.connect(option_files=self.my_cnf)

     def execute_some_query(self, query_info):
        """Runs a single query. Thus it creates a cursor to run the
           query and closes it when it's done."""

        # Note that cursor is not a member variable as it's only for the
        # life of this one query    
        cursor = self.my_cxn.cursor(dictionary=True)
        cursor.execute(...)

        # All done, close the cursor
        cursor.close()

    def close():
        """Users of this class should **always** call close when they are
           done with this class so it can clean up the DB connection."""
        self.my_cxn.close()

你还可以了解一下 Python 的 with 语句,这是一种保证所有内容都得到清理的好方法。 (点击此处)

谢谢您的评论。这澄清了我从未正确调用cursor.close(),并且还需要显式调用my_cxn.close()。有没有一种好的方法将mysql.connector连接集成到Python类中,以便不会创建不必要的连接到服务器,并负责关闭连接而无需明确指定?在使用 with 时,使用 __enter____exit__ 是实现 mysql.connector 类的良好思路,并确保适当清除吗? - Vishal
@Vishal 我认为这个问题没有一个单一的最佳解决方案。它取决于你的类将如何使用。经验法则是尽可能创建少量的连接,但在完成后始终关闭它们。因此,你的类的使用方式将决定最佳方法。当然,这个经验法则也有例外情况。 - Oliver Dain

0

我将我的类重写成了这样...

class FooData(object):
    def __init__(self):
        self.myconfig = {
            'option_files': os.environ['HOME'] + '/.my.cnf',
            'database': 'nsdata'
        }
        self.mysqlcxn = None

    def __enter__(self):
        try:
            self.mysqlcxn = mysql.connector.connect(**self.myconfig)
        except mysql.connector.Error as err:
            if err.errno == 2003:
                self.mysqlcxn = None
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        if self.mysqlcxn is not None and self.mysqlcxn.is_connected():
            self.mysqlcxn.close()

    def etl(self)
        ...

我可以使用 with ... as 来确保我进行了适当的清理。

with FooData() as obj:
    obj.etl()

Aborted connection消息因此被正确消除。

Oliver Dain的回复使我走上了正确的道路,解释Python的'__enter__'和'__exit__'对于理解实现我的类的正确方法非常有帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接