• django原生支持是不支持 以连接池方式连接数据库的

概述

在使用 Django 进行 Web 开发时, 我们避免不了与数据库打交道。 当并发量低的时候, 不会有任何问题。 但一旦并发量达到一定数量, 就会导致 数据库的连接数会被瞬时占满。

这将导致一个严重的后果 --其他应用, 或者 Django 本身的其他服务都无法访问数据库。 这是不可容忍的!

造成这样的结果的原因之一是 Django 底层与数据库的连接方式并不是连接池。

它会为每一次的数据库访问建立连接。 这样当并发量上来以后, 数据库的连接数就不够用了。

所以本篇博客介绍一种修改 Django 底层与数据库的连接方式为连接池的形式, 避免出现这种灾难性的后果。 (本文数据库采用 MySQL)

安装 djorm-ext-pool

可以使用命令行的方式安装

pip install djorm-ext-pool

也可以直接使用 Pycharm 进行搜索后安装。

创建 APP

创建一个名为 djorm_pool 的 APP, 在 init.py 文件中写如下语句(原封不动的复制进去就可以, 此 APP 中只有一个 init.py 文件就可以):

# -*- coding: utf-8 -*-

import logging
from functools import partial from django.core.exceptions import ImproperlyConfigured
from django.conf import settings from sqlalchemy import exc
from sqlalchemy import event
from sqlalchemy.pool import manage
from sqlalchemy.pool import Pool
from sqlalchemy.event import listens_for POOL_PESSIMISTIC_MODE = getattr(settings, "DJORM_POOL_PESSIMISTIC", False)
POOL_SETTINGS = getattr(settings, 'DJORM_POOL_OPTIONS', {})
POOL_SETTINGS.setdefault("recycle", 3600) logger = logging.getLogger('djorm.pool') @event.listens_for(Pool, "checkout")
def _on_checkout(dbapi_connection, connection_record, connection_proxy):
logger.debug("connection retrieved from pool") if POOL_PESSIMISTIC_MODE:
cursor = dbapi_connection.cursor()
try:
cursor.execute("SELECT 1")
except:
# raise DisconnectionError - pool will try
# connecting again up to three times before raising.
raise exc.DisconnectionError()
finally:
cursor.close() @event.listens_for(Pool, "checkin")
def _on_checkin(*args, **kwargs):
logger.debug("connection returned to pool") @event.listens_for(Pool, "connect")
def _on_connect(*args, **kwargs):
logger.debug("connection created") def patch_mysql():
class hashabledict(dict):
def __hash__(self):
return hash(tuple(sorted(self.items()))) class hashablelist(list):
def __hash__(self):
return hash(tuple(sorted(self))) class ManagerProxy(object):
def __init__(self, manager):
self.manager = manager def __getattr__(self, key):
return getattr(self.manager, key) def connect(self, *args, **kwargs):
if 'conv' in kwargs:
conv = kwargs['conv']
if isinstance(conv, dict):
items = []
for k, v in conv.items():
if isinstance(v, list):
v = hashablelist(v)
items.append((k, v))
kwargs['conv'] = hashabledict(items)
if 'ssl' in kwargs:
ssl = kwargs['ssl']
if isinstance(ssl, dict):
items = []
for k, v in ssl.items():
if isinstance(v, list):
v = hashablelist(v)
items.append((k, v))
kwargs['ssl'] = hashabledict(items)
return self.manager.connect(*args, **kwargs) try:
from django.db.backends.mysql import base as mysql_base
except (ImproperlyConfigured, ImportError) as e:
return if not hasattr(mysql_base, "_Database"):
mysql_base._Database = mysql_base.Database
mysql_base.Database = ManagerProxy(manage(mysql_base._Database, **POOL_SETTINGS)) def patch_postgresql():
try:
from django.db.backends.postgresql_psycopg2 import base as pgsql_base
except (ImproperlyConfigured, ImportError) as e:
return if not hasattr(pgsql_base, "_Database"):
pgsql_base._Database = pgsql_base.Database
pgsql_base.Database = manage(pgsql_base._Database, **POOL_SETTINGS) def patch_sqlite3():
try:
from django.db.backends.sqlite3 import base as sqlite3_base
except (ImproperlyConfigured, ImportError) as e:
return if not hasattr(sqlite3_base, "_Database"):
sqlite3_base._Database = sqlite3_base.Database
sqlite3_base.Database = manage(sqlite3_base._Database, **POOL_SETTINGS) def patch_all():
patch_mysql()
patch_postgresql()
patch_sqlite3() patch_all()

配置 settings.py

打开 settings.py 文件, 按照如下要求进行配置:

INSTALLED_APPS = [
...
'djorm_pool', # 将刚创建的项目添加进 INSTALLED_APPS
] DJORM_POOL_OPTION = {
'pool_size': 20, # 本连接池的最大连接个数
'max_overflow': 0,
'recycle': 3600
} DJORM_POOL_PESSIMISTIC = True

这样 settings.py 就配置好了。

修改 MySQL 配置文件

将 MySQL 的配置文件 mysqld.cnf 按如下参数进行配置:

#
# The MySQL database server configuration file.
#
# You can copy this to one of:
# - "/etc/mysql/my.cnf" to set global options,
# - "~/.my.cnf" to set user-specific options.
#
# One can use all long options that the program supports.
# Run program with --help to get a list of available options and with
# --print-defaults to see which it would actually understand and use.
#
# For explanations see
# http://dev.mysql.com/doc/mysql/en/server-system-variables.html # This will be passed to all mysql clients
# It has been reported that passwords should be enclosed with ticks/quotes
# escpecially if they contain "#" chars...
# Remember to edit /etc/mysql/debian.cnf when changing the socket location. # Here is entries for some specific programs
# The following values assume you have at least 32M ram [mysqld_safe]
socket = /var/run/mysqld/mysqld.sock
nice = 0 [mysqld]
#
# * Basic Settings
#
user = mysql
pid-file = /var/run/mysqld/mysqld.pid
socket = /var/run/mysqld/mysqld.sock
port = 3306
basedir = /usr
datadir = /var/lib/mysql
tmpdir = /tmp
lc-messages-dir = /usr/share/mysql
skip-external-locking
open_files_limit = 10240
back_log = 600
external-locking = FALSE
#
# Instead of skip-networking the default is now to listen only on
# localhost which is more compatible and is not less secure.
#bind-address = 127.0.0.1
#
# * Fine Tuning
#
key_buffer_size = 32M
max_allowed_packet = 32M
thread_stack = 192K
thread_cache_size = 300
# This replaces the startup script and checks MyISAM tables if needed
# the first time they are touched
myisam-recover-options = BACKUP
max_connections = 3000
max_connect_errors = 6000
#thread_concurrency = 10
#
# * Query Cache Configuration
#
query_cache_limit = 4M
query_cache_size = 64M
query_cache_min_res_unit = 2k
#default-storage-engine = MyISAM
#
# * Logging and Replication
#
# Both location gets rotated by the cronjob.
# Be aware that this log type is a performance killer.
# As of 5.1 you can enable the log at runtime!
#general_log_file = /var/log/mysql/mysql.log
#general_log = 1
#
# Error log - should be very few entries.
#
log_error = /var/log/mysql/error.log
#
# Here you can see queries with especially long duration
#log_slow_queries = /var/log/mysql/mysql-slow.log
#long_query_time = 2
#log-queries-not-using-indexes
#
# The following can be used as easy to replay backup logs or for replication.
# note: if you are setting up a replication slave, see README.Debian about
# other settings you may need to change.
#server-id = 1
#log_bin = /var/log/mysql/mysql-bin.log
expire_logs_days = 10
max_binlog_size = 100M
#binlog_do_db = include_database_name
#binlog_ignore_db = include_database_name
#
# * InnoDB
#
# InnoDB is enabled by default with a 10MB datafile in /var/lib/mysql/.
# Read the manual for more InnoDB related options. There are many!
#
# * Security Features
#
# Read the manual, too, if you want chroot!
# chroot = /var/lib/mysql/
#
# For generating SSL certificates I recommend the OpenSSL GUI "tinyca".
#
# ssl-ca=/etc/mysql/cacert.pem
# ssl-cert=/etc/mysql/server-cert.pem
# ssl-key=/etc/mysql/server-key.pem transaction_isolation = READ-COMMITTED
# 设定默认的事务隔离级别.可用的级别如下:
# READ-UNCOMMITTED, READ-COMMITTED, REPEATABLE-READ, SERIALIZABLE
# 1.READ UNCOMMITTED-读未提交2.READ COMMITTE-读已提交3.REPEATABLE READ -可重复读4.SERIALIZABLE -串行 tmp_table_size = 256M
# tmp_table_size 的默认大小是 32M。如果一张临时表超出该大小,MySQL产生一个 The table tbl_name is full 形式的错误,如果你做很多高级 GROUP BY 查询,增加 tmp_table_size 值。如果超过该值,则会将临时表写入磁盘。
max_heap_table_size = 256M
long_query_time = 2
#log-bin = /data/3306/mysql-bin
binlog_cache_size = 4M
max_binlog_cache_size = 8M
max_binlog_size = 512M expire_logs_days = 7
key_buffer_size = 2048M
#批定用于索引的缓冲区大小,增加它可以得到更好的索引处理性能,对于内存在4GB左右的服务器来说,该参数可设置为256MB或384MB。 read_buffer_size = 1M
# MySql读入缓冲区大小。对表进行顺序扫描的请求将分配一个读入缓冲区,MySql会为它分配一段内存缓冲区。read_buffer_size变量控制这一缓冲区的大小。如果对表的顺序扫描请求非常频繁,并且你认为频繁扫描进行得太慢,可以通过增加该变量值以及内存缓冲区大小提高其性能。和sort_buffer_size一样,该参数对应的分配内存也是每个连接独享。 read_rnd_buffer_size = 16M
# MySql的随机读(查询操作)缓冲区大小。当按任意顺序读取行时(例如,按照排序顺序),将分配一个随机读缓存区。进行排序查询时,MySql会首先扫描一遍该缓冲,以避免磁盘搜索,提高查询速度,如果需要排序大量数据,可适当调高该值。但MySql会为每个客户连接发放该缓冲空间,所以应尽量适当设置该值,以避免内存开销过大。 bulk_insert_buffer_size = 64M
#批量插入数据缓存大小,可以有效提高插入效率,默认为8M myisam_sort_buffer_size = 128M
# MyISAM表发生变化时重新排序所需的缓冲 myisam_max_sort_file_size = 10G
# MySQL重建索引时所允许的最大临时文件的大小 (当 REPAIR, ALTER TABLE 或者 LOAD DATA INFILE).
# 如果文件大小比此值更大,索引会通过键值缓冲创建(更慢) myisam_repair_threads = 1
# 如果一个表拥有超过一个索引, MyISAM 可以通过并行排序使用超过一个线程去修复他们.
# 这对于拥有多个CPU以及大量内存情况的用户,是一个很好的选择. #自动检查和修复没有适当关闭的 MyISAM 表
lower_case_table_names = 1
#这个参数用来设置 InnoDB 存储的数据目录信息和其它内部数据结构的内存池大小,类似于Oracle的library cache。这不是一个强制参数,可以被突破。
innodb_buffer_pool_size = 256M
# 这对Innodb表来说非常重要。Innodb相比MyISAM表对缓冲更为敏感。MyISAM可以在默认的 key_buffer_size 设置下运行的可以,然而Innodb在默认的 innodb_buffer_pool_size 设置下却跟蜗牛似的。由于Innodb把数据和索引都缓存起来,无需留给操作系统太多的内存,因此如果只需要用Innodb的话则可以设置它高达 70-80% 的可用内存。一些应用于 key_buffer 的规则有 — 如果你的数据量不大,并且不会暴增,那么无需把 innodb_buffer_pool_size 设置的太大了 #innodb_data_file_path = ibdata1:1024M:autoextend
#表空间文件 重要数据 innodb_thread_concurrency = 8
#服务器有几个CPU就设置为几,建议用默认设置,一般为8. innodb_flush_log_at_trx_commit = 2
# 如果将此参数设置为1,将在每次提交事务后将日志写入磁盘。为提供性能,可以设置为0或2,但要承担在发生故障时丢失数据的风险。设置为0表示事务日志写入日志文件,而日志文件每秒刷新到磁盘一次。设置为2表示事务日志将在提交时写入日志,但日志文件每次刷新到磁盘一次。 innodb_log_buffer_size = 16M
#此参数确定些日志文件所用的内存大小,以M为单位。缓冲区更大能提高性能,但意外的故障将会丢失数据.MySQL开发人员建议设置为1-8M之间 innodb_log_file_size = 128M
#此参数确定数据日志文件的大小,以M为单位,更大的设置可以提高性能,但也会增加恢复故障数据库所需的时间 innodb_log_files_in_group = 3
#为提高性能,MySQL可以以循环方式将日志文件写到多个文件。推荐设置为3M innodb_max_dirty_pages_pct = 90
#推荐阅读 http://www.taobaodba.com/html/221_innodb_max_dirty_pages_pct_checkpoint.html
# Buffer_Pool中Dirty_Page所占的数量,直接影响InnoDB的关闭时间。参数innodb_max_dirty_pages_pct 可以直接控制了Dirty_Page在Buffer_Pool中所占的比率,而且幸运的是innodb_max_dirty_pages_pct是可以动态改变的。所以,在关闭InnoDB之前先将innodb_max_dirty_pages_pct调小,强制数据块Flush一段时间,则能够大大缩短 MySQL关闭的时间。 innodb_lock_wait_timeout = 120
# InnoDB 有其内置的死锁检测机制,能导致未完成的事务回滚。但是,如果结合InnoDB使用MyISAM的lock tables 语句或第三方事务引擎,则InnoDB无法识别死锁。为消除这种可能性,可以将innodb_lock_wait_timeout设置为一个整数值,指示 MySQL在允许其他事务修改那些最终受事务回滚的数据之前要等待多长时间(秒数) innodb_file_per_table = 0

我们可以采用高并发的方式测试, 博主采用的是每秒写入 20 条数据。 经过一晚上的测试(大概 10 个小时), 数据库中已存入几万条数据, 但是连接数一直保持在 13 个。

大功告成!

Git 地址: https://github.com/djangonauts/djorm-ext-pool.git

Django ORM 以连接池方式连接底层连接数据库方法的更多相关文章

  1. HttpClient连接池的连接保持、超时和失效机制

    HTTP是一种无连接的事务协议,底层使用的还是TCP,连接池复用的就是TCP连接,目的就是在一个TCP连接上进行多次的HTTP请求从而提高性能.每次HTTP请求结束的时候,HttpClient会判断连 ...

  2. commons-pool与commons-pool2连接池(Hadoop连接池)

    commons-pool和commons-pool2是用来建立对象池的框架,提供了一些将对象池化必须要实现的接口和一些默认动作.对象池化之后可以通过pool的概念去管理其生命周期,例如对象的创建,使用 ...

  3. java原生程序redis连接(连接池/长连接和短连接)选择问题

    最近遇到的连接问题我准备从重构的几个程序(redis和mysql)长连接和短连接,以及连接池和单连接等问题用几篇博客来总结下. 这个问题的具体发生在java原生程序和redis的交互中.这个问题对我最 ...

  4. JDBC连接池-自定义连接池

    JDBC连接池 java JDBC连接中用到Connection   在每次对数据进行增删查改 都要 开启  .关闭  ,在实例开发项目中 ,浪费了很大的资源 ,以下是之前连接JDBC的案例 pack ...

  5. 同过增强Connection类[重写了close的方法]实现的从连接池取出连接并放回连接的简单的实现流程

    package tk.dong.connection.util; import java.io.IOException;import java.io.InputStream;import java.i ...

  6. 通常每个套接字地址(协议/网络地址/端口)只允许使用一次。 数据库连接不释放测试 连接池 释放连接 关闭连接 有关 redis-py 连接池会导致服务器产生大量 CLOSE_WAIT 的再讨论以及一个解决方案

    import pymysqlfrom redis import Redisimport time h, pt, u, p, db = '192.168.2.210', 3306, 'root', 'n ...

  7. JDBC连接池-C3P0连接

    JDBC连接池-C3P0连接 c3p0连接池的学习英语好的看英文原版      c3p0 - JDBC3 Connection and Statement Pooling 使用c3p0连接池  三种方 ...

  8. 网络协议 finally{ return问题 注入问题 jdbc注册驱动问题 PreparedStatement 连接池目的 1.2.1DBCP连接池 C3P0连接池 MYSQL两种方式进行实物管理 JDBC事务 DBUtils事务 ThreadLocal 事务特性 并发访问 隔离级别

    1.1.1 API详解:注册驱动 DriverManager.registerDriver(new com.mysql.jdbc.Driver());不建议使用 原因有2个: >导致驱动被注册2 ...

  9. jdbc连接MySQL数据库+简单实例(普通JDBC方法实现和连接池方式实现)

    jdbc连接数据库 总结内容 1. 基本概念 jdbc的概念 2. 数据库连接 数据库的连接 DAO层思想 重构设计 3. 事务 概念 事务的ACID属性 事务的操作 4. 连接池 为什么要使用连接池 ...

随机推荐

  1. k8s管理存储资源

    1. Kubernetes 如何管理存储资源 理解volume 首先我们学习 Volume,以及 Kubernetes 如何通过 Volume 为集群中的容器提供存储:然后我们会实践几种常用的 Vol ...

  2. 1.2 管理 NetBackup 许可证

    关于管理 NetBackup 许可证 NetBackup许可证密钥是在安装软件时添加的.对于需要单独购买的选件,可以稍 后在"许可证密钥"对话框中添加许可证. 注意:在进行任何许可 ...

  3. php迭代器模式(iterator pattern)

    ... <?php /* The iterator pattern is used to traverse a container and access its elements. In oth ...

  4. Java精通并发-Lock锁机制深入详解

    从这次开始接触Java1.5推出的并发包中的东东,先看一下jdk中的并发包: 接下来咱们则会集中对这些并发包中的核心进行深入了解,不光要学会怎么用这些并发包中的类,而且还得知道这些功能背后运行的原理, ...

  5. python应用-工资计算

    """ 工资计算 Author:罗万财 Date:2017-6-8 """ salary=float(input('请输入工资:')) in ...

  6. Spring Cloud Task 知识点

    Spring Cloud Task的目标是为Spring Boot应用程序提供创建短期运行微服务的功能. 出处:https://blog.csdn.net/peterwanghao/article/d ...

  7. 在函数内部定义的变量加与不加var的区别,匿名函数和有名函数内声明变量的区别

    2014年4月21日,14:49分: 原创:http://www.cnblogs.com/liujinyu/p/3678523.html 今天写天气网英文版的产品频道,maps页面的js时,偶然意识到 ...

  8. mysql 执行存储过程报错Prepared statement needs to be re-prepared

    今日思语:不喜欢再见 说再见,因为有时明知道下一次再见已是遥遥无期或是不再见 错误如下: ERROR 1615 (HY000) at line 406 in file: 'update-mysql.s ...

  9. html css div固定底部

    <div id="father"> <footer></footer> </div> #father{ position:relat ...

  10. 文件搜索命令locate

    与find命令不同,locate命令不会去搜索某一个分区或硬盘,而是直接在资料库中搜索,因此,搜索的速度要比find命令快得多,linux系统会定期更新资料库. 利用locate locate命令可以 ...