DBUtils是一個容許在多線程python應用和數據庫之間安全及高效鏈接的python模塊套件。python
DBUtils套件包含兩個模塊子集,一個適用於兼容DB-API 2接口的模塊,一個適用於PyGreSQL的模塊。mysql
該子集下的模塊依賴關係如圖:
sql
該子集下的模塊依賴關係如圖:
數據庫
DBUtils.SimplePooledDB
是池化數據庫鏈接中很是基礎的一種實現。相較於PooledDB
,它並不那麼複雜,且缺乏failover機制。SimplePooledDB應視爲一種概念演示,不要直接在生產環境使用。緩存
DBUtils.SteadyDB
基於兼容DB-API 2接口的數據庫模塊建立的普通鏈接,實現了"增強"鏈接。具體指當數據庫鏈接關閉、丟失或使用頻率超出限制時,將自動從新獲取鏈接。安全
典型的應用場景以下:在某個維持了某些數據庫鏈接的程序運行時重啓了數據庫,或在某個防火牆隔離的網絡中訪問遠程數據庫時重啓了防火牆。網絡
DBUtils.PersistentDB
實現了穩定,線程仿射(thread-affine
),持久化的數據庫鏈接。下圖顯式了使用PersistentDB進行鏈接時涉及的鏈接層:
session
某個線程第一次開啓一個數據庫鏈接時,該鏈接將用於此特定線程。即便在線程中關閉鏈接,鏈接也會保持打開狀態,以便同一個線程的下一次鏈接請求直接使用。線程結束時該鏈接會自動關閉。多線程
簡而言之:PersistentDB會回收數據庫鏈接從而在總體上增長多線程應用的數據庫訪問性能,它確保線程之間永遠不會共享鏈接。框架
所以即便底層的DB-API模塊不是connection級別的線程安全,PersistentDB也能夠完美實現線程安全,避免在其餘線程更改數據庫會話或執行跨多個SQL指令的事務時出現問題。
要使用PersistentDB模塊,首先傳遞如下參數建立PersistentDB實例:
(OperationalError, InternalError)
不能處理鏈接failover機制時使用ping()
方法可用,該值表示什麼時候使用ping()方法檢查鏈接(0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always, and all other bit combinations of these values
)threading.local
可能獲取鏈接的速度更快,但不必定適用於全部狀況(例如,mod_wsgi
會清空requests之間的threading.local數據)import pymysql from DBUtils.PersistentDB import PersistentDB persist = PersistentDB(creator=pymysql, user="root", passwd="123456", db="test") # conn的使用和常規DB-API 2接口相似 conn = persist.connection()
NOTE:須要在鏈接上調用begin()
方法明確開啓事務。這能夠確保a.只在事務完成時才從新打開鏈接b.鏈接被同一個線程重用時回滾。
DBUtils.PooledDB
實現了穩定、線程安全的緩存鏈接池。下圖顯式了使用PooledDB進行鏈接時涉及的鏈接層:
使用正整數的maxshared
參數和connection級別的線程安全的creator
參數建立鏈接池時,鏈接池中的鏈接默認是線程間共享的。但仍能夠請求非線程共享的專用數據庫鏈接。
除了共享鏈接池外,還能夠建立至少mincached
個,至多maxcached
個鏈接的空閒鏈接池,在共享鏈接池未滿(不太理解)或線程請求專用數據庫鏈接時使用。當某個線程關閉再也不共享的鏈接時,該鏈接將回收到空閒鏈接池以便再次使用。
若是底層的DB-API 2模塊非線程安全,將使用線程鎖確保PooledDB鏈接是線程安全的。但對於線程專用的鏈接,要當心更改數據庫會話或執行跨多個SQL指令的事務帶來的不良影響。
要使用PoolDB模塊,首先傳遞如下參數建立PoolDB實例:
When this maximum number is reached, connections are shared if they have been requested as shareable
begin()
開啓的事務,默認值爲True,出於安全考慮老是會回滾)import pymysql from DBUtils.PooledDB import PooledDB pool = PooledDB(creator=pymysql, 5, user="root", passwd="123456", db="test") # conn的使用和常規DB-API 2接口相似 conn = pool.connection()
對於線程共享的鏈接池,能夠用如下方式獲取線程專用鏈接:
conn = pool.connection(shareable=False) # 或者 conn = pool.dedicated_connection()
對於再也不使用的鏈接,調用close()方法回收到鏈接池。
在多線程環境中,不要寫如下代碼,這會致使鏈接過早釋放並被其餘線程重用,若是鏈接非線程安全可能致使程序出現嚴重錯誤:
pool.connection().cursor().execute(...)
NOTE:須要在鏈接上調用begin()
方法明確開啓事務。這能夠確保a.只在事務完成時才從新打開鏈接b.鏈接在返回鏈接池以前執行回滾c.鏈接不會被其餘線程共享
PooledDB和PersistentDB都經過回收數據庫鏈接,且即便數據庫鏈接中斷也能保持穩定性的方式從而達到提高數據庫訪問性能的目的。在現實場景中應該如何選擇呢?對於保持常量線程數且頻繁使用數據庫的應用,使用PersistentDB;對於頻繁開啓、結束線程的應用,使用PooledDB。
若是程序中使用了ORM框架,如SQLObject
或SQLAlchemy
,不須要使用DBUtils,由於這些框架自身維護了鏈接池。
數據庫線程安全級別:
好比pymysql
就是能夠共享模塊但不能共享鏈接,查看方式pymysql.threadsafety