對於一個簡單的數據庫應用,由於對於數據庫的訪問不是很頻繁。這時可以簡單地在需要訪問數據庫時,就新創建一個連接,用完后就關閉它,這樣做也不會帶來什么明顯的性能上的開銷。但是對於一個復雜的數據庫應用,情況就完全不同了。頻繁的建立、關閉連接,會極大的減低系統的性能,因為對於連接的使用成了系統性能的瓶頸。
連接復用。通過建立一個數據庫連接池以及一套連接使用管理策略,使得一個數據庫連接可以得到高效、安全的復用,避免了數據庫連接頻繁建立、關閉的開銷。
對於共享資源,有一個很著名的設計模式:資源池。該模式正是為了解決資源頻繁分配、釋放所造成的問題的。把該模式應用到數據庫連接管理領域,就是建立一個數據庫連接池,提供一套高效的連接分配、使用策略,最終目標是實現連接的高效、安全的復用。
數據庫連接池的基本原理是在內部對象池中維護一定數量的數據庫連接,並對外暴露數據庫連接獲取和返回方法。如:
外部使用者可通過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