【问题标题】:single database connection throughout the python application (following singleton pattern)整个 python 应用程序中的单个数据库连接(遵循单例模式)
【发布时间】:2016-11-10 10:40:45
【问题描述】:

我的问题是在整个应用程序中维护单个数据库连接的最佳方式是什么? 使用单例模式?怎么样?

需要注意的条件:

  1. 如果有多个请求,我应该使用同一个连接
  2. 如果连接关闭,请创建一个新连接
  3. 如果连接超时,我的代码应根据新请求创建新连接。

Django ORM 不支持我的数据库的驱动程序。由于同样的驱动相关问题,我使用pyodbc 连接到数据库。现在我有下面的类来创建和管理数据库连接:

class DBConnection(object):
    def __init__(self, driver, serve,
                 database, user, password):

        self.driver = driver
        self.server = server
        self.database = database
        self.user = user
        self.password = password

    def __enter__(self):
        self.dbconn = pyodbc.connect("DRIVER={};".format(self.driver) +\
                                     "SERVER={};".format(self.server) +\
                                     "DATABASE={};".format(self.database) +\
                                     "UID={};".format(self.user) +\
                                     "PWD={};".format(self.password) + \
                                     "CHARSET=UTF8",
                                     # "",
                                     ansi=True)

        return self.dbconn

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.dbconn.close()

但是这种方法的问题是它会为每个查询创建新的数据库连接。遵循单例模式的更好方法是什么?如果连接关闭,我能想到的方式将保留对连接的引用。比如:

 def get_database_connection():
     conn = DBConnection.connection
     if not conn:
          conn = DBConnection.connection = DBConnection.create_connection()
     return conn

实现这一目标的最佳方法是什么?有什么建议/想法/例子吗?

PS:我正在检查是否使用weakref,它允许创建对对象的弱引用。我认为使用weakref 和单例模式来存储连接变量是个好主意。这样,当 DB 不使用时,我就不必保持连接 alive。你们对此有何看法?

【问题讨论】:

  • pyocdb的第三方后端好像有几个,比如django-pyocdb。为什么不使用其中之一?
  • 我使用 IBM Netezza 作为 django-pyodbc 不支持的数据库
  • 您可以实现一个仅支持创建连接和游标的最小数据库后端。这样你就可以使用 Django 的连接管理来处理你的连接。我想这比自己实现连接处理要容易。
  • 我用工具查看了django.db.backends.mysql。有 6 个文件,分别是 base.py、client.py、compiler.py、creation.py、introspection.pyvalidation.py。我认为实施起来会有很多开销。可能是我没有完全理解最小数据库后端的定义。在我看来,我正在考虑创建一个 Singleton 类来维护单一连接(已经这样做了),但你的想法对我来说看起来更好。你知道图书馆或博客可以让我了解 Django 的后端是如何工作的吗?
  • 大部分都与 ORM 有关。我认为如果您在base.py 中实现DatabaseWrapper 类,并为其他类使用虚拟类,您可以使用连接和游标来执行原始查询,并依赖Django 的连接管理。恐怕我不知道任何指南或什么的,我自己也只知道一点。

标签: python django singleton database-connection pyodbc


【解决方案1】:

现在,我将继续使用单例类方法。任何看到其中潜在缺陷的人,都可以提及它们:)

DBConnector用于创建连接

class DBConnector(object):

   def __init__(self, driver, server, database, user, password):

        self.driver = driver
        self.server = server
        self.database = database
        self.user = user
        self.password = password
        self.dbconn = None

    # creats new connection
    def create_connection(self):
        return pyodbc.connect("DRIVER={};".format(self.driver) + \
                              "SERVER={};".format(self.server) + \
                              "DATABASE={};".format(self.database) + \
                              "UID={};".format(self.user) + \
                              "PWD={};".format(self.password) + \
                              "CHARSET=UTF8",
                              ansi=True)

    # For explicitly opening database connection
    def __enter__(self):
        self.dbconn = self.create_connection()
        return self.dbconn

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.dbconn.close()

DBConnection用于管理连接

class DBConnection(object):
    connection = None

    @classmethod
    def get_connection(cls, new=False):
        """Creates return new Singleton database connection"""
        if new or not cls.connection:
            cls.connection = DBConnector().create_connection()
        return cls.connection

    @classmethod
    def execute_query(cls, query):
        """execute query on singleton db connection"""
        connection = cls.get_connection()
        try:
            cursor = connection.cursor()
        except pyodbc.ProgrammingError:
            connection = cls.get_connection(new=True)  # Create new connection
            cursor = connection.cursor()
        cursor.execute(query)
        result = cursor.fetchall()
        cursor.close()
        return result

【讨论】:

  • 您应该确保线程之间没有共享连接,否则您可能会遇到一些令人惊讶且难以调试的行为。您可以将连接保存在 threading.local() 对象上,以便每个线程都有自己的单例连接。
  • @knbk:我只是共享连接对象,而是为execute_query 函数中的每个查询创建一个新的本地cursor。因此,由于查询/结果关系由游标维护,即使异步调用execute_query,我也看不到任何伤害。这又是我的假设。还是我错了?
  • 来自pyodbc docs: "threadsafety 整数1,表示线程可以共享模块但不能共享连接。注意连接和游标可能被不同的线程使用,只是不是同时。” -- 所以你不能在试图同时访问数据库的线程之间共享连接。
【解决方案2】:
类 DBConnector(对象): def __new__(cls): 如果没有 hasattr(cls, 'instance'): cls.instance = super(DBConnector, cls).__new__(cls) 返回 cls.instance def __init__(self): #构造函数中的数据库连接代码 con = DBConnector() con1 = DBConnector() con is con1 # 输出为真 希望上面的代码会有所帮助。

【讨论】:

  • 注意,如果不将*args, **kwargs 传递给__new__,这将无法让我到达任何地方。
猜你喜欢
  • 2019-03-06
  • 1970-01-01
  • 2018-10-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-04
  • 1970-01-01
相关资源
最近更新 更多