欢迎加入QQ讨论群258996829
麦子学院 头像
苹果6袋
6
麦子学院

Django学习之数据库的链接详解

发布时间:2016-12-24 12:32  回复:0  查看:2942   最后回复:2016-12-24 12:32  

最近总会遇到 MySQL server has gone away 的报错,然后就看了一下django数据库连接这一块。

  django数据库连接

  ORM中数据库连接用到的 connections ,从 django.db 模块引入,属于 ConnectionHandler 对象。

  # django.db.__init__.py

  # django ORM中用到的数据库连接来源

  connections = ConnectionHandler()

  # 请求开始之前重置所有连接def reset_queries(**kwargs):

  for conn in connections.all():

  conn.queries_log.clear()

  signals.request_started.connect(reset_queries)

  # 请求开始结束之前遍历所有已存在连接,关闭不可用的连接def close_old_connections(**kwargs):

  for conn in connections.all():

  conn.close_if_unusable_or_obsolete()

  signals.request_started.connect(close_old_connections)

  signals.request_finished.connect(close_old_connections)

  我理解的 ConnectionHandler 类是一个数据库连接管理器,负责根据不同数据库后端创建数据库连接,保存连接,给应用方提供连接,以及关闭所有连接。 这里通过django信号的方式,在请求开始之前以及请求结束之后关闭失效数据库连接。

  # django.db.utils.py

  class ConnectionHandler(object):

  def __init__(self, databases=None):

  # 获取数据库配置

  self._databases = databases

  # 从当前线程变量获取所有数据库连接

  self._connections = local()

  # 获取数据库连接关键逻辑

  def __getitem__(self, alias):

  # 首先直接从当前线程变量获取

  if hasattr(self._connections, alias):

  return getattr(self._connections, alias)

  # 重新建立数据库连接并写入当前线程变量

  self.ensure_defaults(alias)

  self.prepare_test_settings(alias)

  db = self.databases[alias]

  backend = load_backend(db['ENGINE'])

  # django.db.backends.mysql.base.DatabaseWrapper

  conn = backend.DatabaseWrapper(db, alias)

  setattr(self._connections, alias, conn)

  return conn

  ConnectionHandler 中 _connections 表示当前数据库连接集合,是一个 ThreadLocal 对象,是和线程绑定在一起的。在整个线程生命周期内, _connections 属于全局变量,但是当线程一旦关闭, _connections 也消失了。

  关键逻辑在于 __getitem__ 方法,当通过别名获取数据库连接时,首先从当前线程变量中获取连接,获取不到就根据别名创建新的数据库连接,并将连接写入 ThreadLocal 。

  通过CONN_MAX_AGE设置连接存活时间

  django 1.6 开始支持持久数据库连接,通过参数 CONN_MAX_AGE 设置每个连接的最大存活时间。默认值是0,设置为None表示无限制的持久连接。

  # django.db.backends.base.base.py

  class BaseDatabaseWrapper(object):

  def connect(self):

  self.in_atomic_block = False

  self.savepoint_ids = []

  self.needs_rollback = False

  # 根据CONN_MAX_AGE参数设置连接的关闭时间

  max_age = self.settings_dict['CONN_MAX_AGE']

  self.close_at = None if max_age is None else time.time() + max_age

  ... ...

  def close_if_unusable_or_obsolete(self):

  if self.connection is not None:

  if self.get_autocommit() != self.settings_dict['AUTOCOMMIT']:

  self.close()

  return

  # 发生异常,检查连接是否可用,不可用关闭连接

  if self.errors_occurred:

  if self.is_usable():

  self.errors_occurred = False

  else:

  self.close()

  return

  # 设置了超时时间,并且连接超时,关闭连接

  if self.close_at is not None and time.time() >= self.close_at:

  self.close()

  return

  数据库连接在建立的时候会根据 CONN_MAX_AGE 参数设置连接的 close_at 属性,表示连接失效时间。再看上面:point_up_2: django.db.__init__.py 的代码,通过信号方式,每次请求开始以及结束的时候,会调用 close_if_unusable_or_obsolete 方法,判断当连接超时或者处在不可恢复状态时则关闭连接。

  总结

  1. django的数据库连接是保存到线程变量的数据库连接是全局的,但只存在于当前线程中,如果线程关闭,数据库连接也不存在了。

  2. 可以通过CONN_MAX_AGE参数配置数据库连接的存活时间即使设置了CONN_MAX_AGE参数,也是在线程依然存活的情况下,数据库连接能够存活的时间。

  需要注意的两点是:

  ·CONN_MAX_AGE 应该小于数据库本身的最大连接时间 wait_timeout ,否则应用程序可能会获取到连接超时的数据库连接,这时会出现 MySQL server has gone away 的报错。

  ·如果部署方式采用多线程,最大线程数不能大于最大数据库连接数。另外,开发模式下(runserver),由于每条请求都是创建一个新的 Thread ,就不要使用 CONN_MAX_AGE 参数了,这样在老的请求线程中保存的数据库连接根本不能复用。

来源:rainybowe

您还未登录,请先登录

热门帖子

最新帖子