对于一个简单的数据库应用,由于对于数据库的访问不是很频繁。这时可以简单地在需要访问数据库时,就新创建一个连接,用完后就关闭它,这样做也不会带来什么明显的性能上的开销。但是对于一个复杂的数据库应用,情况就完全不同了。频繁的建立、关闭连接,会极大的减低系统的性能,因为对于连接的使用成了系统性能的瓶颈。
连接复用。通过建立一个数据库连接池以及一套连接使用管理策略,使得一个数据库连接可以得到高效、安全的复用,避免了数据库连接频繁建立、关闭的开销。
对于共享资源,有一个很著名的设计模式:资源池。该模式正是为了解决资源频繁分配、释放所造成的问题的。把该模式应用到数据库连接管理领域,就是建立一个数据库连接池,提供一套高效的连接分配、使用策略,最终目标是实现连接的高效、安全的复用。
数据库连接池的基本原理是在内部对象池中维护一定数量的数据库连接,并对外暴露数据库连接获取和返回方法。如:
外部使用者可通过getConnection 方法获取连接,使用完毕后再通过releaseConnection 方法将连接返回,注意此时连接并没有关闭,而是由连接池管理器回收,并为下一次使用做好准备。
数据库连接池技术带来的优势:
1. 资源重用
由于数据库连接得到重用,避免了频繁创建、释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增进了系统运行环境的平稳性(减少内存碎片以及数据库临时进程/线程的数量)。
2. 更快的系统响应速度
数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而缩减了系统整体响应时间。
3. 新的资源分配手段
对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接的配置,实现数据库连接池技术,几年钱也许还是个新鲜话题,对于目前的业务系统而言,如果设计中还没有考虑到连接池的应用,那么…….快在设计文档中加上这部分的内容吧。某一应用最大可用数据库连接数的限制,避免某一应用独占所有数据库资源。
4. 统一的连接管理,避免数据库连接泄漏
在较为完备的数据库连接池实现中,可根据预先的连接占用超时设定,强制收回被占用连接。从而避免了常规数据库连接操作中可能出现的资源泄漏。一个最小化的数据库连接池实现:
1 import pymysql 2 import os 3 import configparser 4 from pymysql.cursors import DictCursor 5 from dbutils.pooled_db import PooledDB 6 7 8 class Config(object): 9 """ 10 # Config().get_content("user_information") 11 配置文件里面的参数 12 [dbMysql] 13 host = 192.168.1.180 14 port = 3306 15 user = root 16 password = 123456 17 """ 18 19 def __init__(self, config_filename="config.ini"): 20 file_path = os.path.join(os.path.dirname(__file__), config_filename) 21 self.cf = configparser.ConfigParser() 22 self.cf.read(file_path) 23 24 def get_sections(self): 25 return self.cf.sections() 26 27 def get_options(self, section): 28 return self.cf.options(section) 29 30 def get_content(self, section): 31 result = {} 32 for option in self.get_options(section): 33 value = self.cf.get(section, option) 34 result[option] = int(value) if value.isdigit() else value 35 return result 36 37 38 class BasePymysqlPool(object): 39 def __init__(self, host, port, user, password, db_name): 40 self.db_host = host 41 self.db_port = int(port) 42 self.user = user 43 self.password = str(password) 44 self.db = db_name 45 self.conn = None 46 self.cursor = None 47 48 49 class MyPymysqlPool(BasePymysqlPool): 50 """ 51 MYSQL数据库对象,负责产生数据库连接 , 此类中的连接采用连接池实现 52 获取连接对象:conn = Mysql.getConn() 53 释放连接对象;conn.close()或del conn 54 """ 55 # 连接池对象 56 __pool = None 57 58 def __init__(self, conf_name=None): 59 self.conf = Config().get_content(conf_name) 60 super(MyPymysqlPool, self).__init__(**self.conf) 61 # 数据库构造函数,从连接池中取出连接,并生成操作游标 62 self._conn = self.__getConn() 63 self._cursor = self._conn.cursor() 64 65 def __getConn(self): 66 """ 67 @summary: 静态方法,从连接池中取出连接 68 @return MySQLdb.connection 69 """ 70 if MyPymysqlPool.__pool is None: 71 __pool = PooledDB(creator=pymysql, 72 mincached=1, 73 maxcached=20, 74 host=self.db_host, 75 port=self.db_port, 76 user=self.user, 77 passwd=self.password, 78 db=self.db, 79 use_unicode=True, 80 charset="utf8", 81 cursorclass=DictCursor) 82 # print("12211212") 83 return __pool.connection() 84 85 def getAll(self, sql, param=None): 86 """ 87 @summary: 执行查询,并取出所有结果集 88 @param sql:查询SQL,如果有查询条件,请只指定条件列表,并将条件值使用参数[param]传递进来 89 @param param: 可选参数,条件列表值(元组/列表) 90 @return: result list(字典对象)/boolean 查询到的结果集 91 """ 92 if param is None: 93 count = self._cursor.execute(sql) 94 else: 95 count = self._cursor.execute(sql, param) 96 if count > 0: 97 result = self._cursor.fetchall() 98 else: 99 result = False 100 return result 101 102 def getOne(self, sql, param=None): 103 """ 104 @summary: 执行查询,并取出第一条 105 @param sql:查询SQL,如果有查询条件,请只指定条件列表,并将条件值使用参数[param]传递进来 106 @param param: 可选参数,条件列表值(元组/列表) 107 @return: result list/boolean 查询到的结果集 108 """ 109 if param is None: 110 count = self._cursor.execute(sql) 111 else: 112 count = self._cursor.execute(sql, param) 113 if count > 0: 114 result = self._cursor.fetchone() 115 else: 116 result = False 117 return result 118 119 def getMany(self, sql, num, param=None): 120 """ 121 @summary: 执行查询,并取出num条结果 122 @param sql:查询SQL,如果有查询条件,请只指定条件列表,并将条件值使用参数[param]传递进来 123 @param num:取得的结果条数 124 @param param: 可选参数,条件列表值(元组/列表) 125 @return: result list/boolean 查询到的结果集 126 """ 127 if param is None: 128 count = self._cursor.execute(sql) 129 else: 130 count = self._cursor.execute(sql, param) 131 if count > 0: 132 result = self._cursor.fetchmany(num) 133 else: 134 result = False 135 return result 136 137 def insertMany(self, sql, values): 138 """ 139 @summary: 向数据表插入多条记录 140 @param sql:要插入的SQL格式 141 @param values:要插入的记录数据tuple(tuple)/list[list] 142 @return: count 受影响的行数 143 """ 144 count = self._cursor.executemany(sql, values) 145 return count 146 147 def __query(self, sql, param=None): 148 if param is None: 149 count = self._cursor.execute(sql) 150 else: 151 count = self._cursor.execute(sql, param) 152 return count 153 154 def update(self, sql, param=None): 155 """ 156 @summary: 更新数据表记录 157 @param sql: SQL格式及条件,使用(%s,%s) 158 @param param: 要更新的 值 tuple/list 159 @return: count 受影响的行数 160 """ 161 return self.__query(sql, param) 162 163 def insert(self, sql, param=None): 164 """ 165 @summary: 更新数据表记录 166 @param sql: SQL格式及条件,使用(%s,%s) 167 @param param: 要更新的 值 tuple/list 168 @return: count 受影响的行数 169 """ 170 return self.__query(sql, param) 171 172 def delete(self, sql, param=None): 173 """ 174 @summary: 删除数据表记录 175 @param sql: SQL格式及条件,使用(%s,%s) 176 @param param: 要删除的条件 值 tuple/list 177 @return: count 受影响的行数 178 """ 179 return self.__query(sql, param) 180 181 def begin(self): 182 """ 183 @summary: 开启事务 184 """ 185 self._conn.autocommit(0) 186 187 def end(self, option='commit'): 188 """ 189 @summary: 结束事务 190 """ 191 if option == 'commit': 192 self._conn.commit() 193 else: 194 self._conn.rollback() 195 196 def dispose(self, isEnd=1): 197 """ 198 @summary: 释放连接池资源 199 """ 200 if False not in isEnd: 201 self.end('commit') 202 else: 203 print("sql错误,rollback") 204 self.end('rollback') 205 self._cursor.close() 206 self._conn.close() 207 208 209 # 调用方法 210 if __name__ == '__main__': 211 mysql = MyPymysqlPool("dbMysql") 212 sqlAll = "show tables;" 213 result = mysql.getAll(sqlAll) 214 print(result) 215 # 释放资源 216 mysql.dispose(isEnd=[result, result])
配置mysql参数
1 [dbMysql] 2 host = 192.168.1.2 3 port = 3306 4 user = root 5 password = root 6 db_name = test