网盘直链下载助手完整教程:如何免费获取百度网盘、阿里云盘等8大平台高速下载链接
2026/8/2 10:05:39
“数据库连接不是越多越好,而是越稳越妙。”——写给每一位追求高可用架构的 Python 开发者
在日常开发中,数据库是后端系统的核心支柱之一。无论是 Web 应用、数据分析平台,还是自动化工具,几乎都离不开数据库的支撑。然而,很多初学者在构建系统时,常常忽视了一个关键问题:
数据库连接的创建是昂贵的操作。
每一次连接数据库,背后都涉及网络握手、认证、资源分配等多个步骤。如果在系统中频繁创建连接,不仅会拖慢性能,还可能导致连接池耗尽、服务崩溃。
那么,如何优雅地管理数据库连接,既保证性能,又避免资源浪费?这正是本文要探讨的核心:使用单例模式(Singleton)实现数据库连接管理器。
单例模式的核心思想是:
一个类只能有一个实例,并提供全局访问点。
这与数据库连接的需求天然契合:
在进入数据库实战之前,我们先快速回顾几种常见的 Python 单例实现方式。
Python 的模块本身就是单例的。
# db_connection.pyimportsqlite3 conn=sqlite3.connect("example.db")# main.pyfromdb_connectionimportconn cursor=conn.cursor()cursor.execute("SELECT * FROM users")适用于简单项目,但不易扩展和控制。
defsingleton(cls):instances={}defwrapper(*args,**kwargs):ifclsnotininstances:instances[cls]=cls(*args,**kwargs)returninstances[cls]returnwrapper@singletonclassConfig:def__init__(self):self.db_url="sqlite:///example.db"classSingleton:_instance=Nonedef__new__(cls,*args,**kwargs):ifnotcls._instance:cls._instance=super().__new__(cls)returncls._instance这种方式更灵活,适合复杂逻辑的封装。
我们以 SQLite 为例,构建一个可复用的数据库连接管理器。
importsqlite3classDatabase:_instance=Nonedef__new__(cls,db_path="example.db"):ifcls._instanceisNone:cls._instance=super().__new__(cls)cls._instance._conn=sqlite3.connect(db_path)returncls._instancedefget_connection(self):returnself._conndb1=Database().get_connection()db2=Database().get_connection()print(db1isdb2)# True,说明是同一个连接importsqlite3importthreadingclassThreadSafeDB:_instance=None_lock=threading.Lock()def__new__(cls,db_path="example.db"):ifcls._instanceisNone:withcls._lock:ifcls._instanceisNone:cls._instance=super().__new__(cls)cls._instance._conn=sqlite3.connect(db_path,check_same_thread=False)returncls._instancedefget_connection(self):try:self._conn.execute("SELECT 1")exceptsqlite3.ProgrammingError:self._conn=sqlite3.connect("example.db",check_same_thread=False)returnself._conn在实际项目中,我们可能需要支持多种数据库(如 SQLite、MySQL、PostgreSQL)。我们可以进一步抽象出一个通用的连接工厂。
importsqlite3importthreadingimportpymysqlimportpsycopg2classDBFactory:_instances={}_lock=threading.Lock()@classmethoddefget_connection(cls,db_type,**kwargs):key=(db_type,tuple(sorted(kwargs.items())))ifkeynotincls._instances:withcls._lock:ifkeynotincls._instances:ifdb_type=="sqlite":conn=sqlite3.connect(kwargs["db"])elifdb_type=="mysql":conn=pymysql.connect(**kwargs)elifdb_type=="postgres":conn=psycopg2.connect(**kwargs)else:raiseValueError("Unsupported DB type")cls._instances[key]=connreturncls._instances[key]conn1=DBFactory.get_connection("sqlite",db="example.db")conn2=DBFactory.get_connection("sqlite",db="example.db")print(conn1isconn2)# True我们将使用 Flask + SQLite + 单例数据库连接,构建一个简单的用户管理 API。
user_app/ ├── app.py ├── db.py └── models.pyimportsqlite3importthreadingclassDB:_instance=None_lock=threading.Lock()def__new__(cls,db_path="users.db"):ifcls._instanceisNone:withcls._lock:ifcls._instanceisNone:cls._instance=super().__new__(cls)cls._instance._conn=sqlite3.connect(db_path,check_same_thread=False)cls._instance._conn.row_factory=sqlite3.Rowreturncls._instancedefget_conn(self):returnself._connfromdbimportDBdefinit_db():conn=DB().get_conn()cursor=conn.cursor()cursor.execute(''' CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, email TEXT ) ''')conn.commit()defadd_user(name,email):conn=DB().get_conn()cursor=conn.cursor()cursor.execute("INSERT INTO users (name, email) VALUES (?, ?)",(name,email))conn.commit()defget_users():conn=DB().get_conn()cursor=conn.cursor()cursor.execute("SELECT * FROM users")returncursor.fetchall()fromflaskimportFlask,request,jsonifyfrommodelsimportinit_db,add_user,get_users app=Flask(__name__)init_db()@app.route("/users",methods=["POST"])defcreate_user():data=request.json add_user(data["name"],data["email"])return{"status":"success"}@app.route("/users",methods=["GET"])deflist_users():users=get_users()returnjsonify([dict(u)foruinusers])if__name__=="__main__":app.run(debug=True)with上下文管理器或手动关闭。check_same_thread=False)。随着异步编程的普及,像asyncpg、aiomysql等异步数据库库逐渐流行。我们也可以将单例模式与异步连接结合:
importasyncpgimportasyncioclassAsyncDB:_pool=None@classmethodasyncdefget_pool(cls):ifcls._poolisNone:cls._pool=awaitasyncpg.create_pool(database="test",user="user",password="pass")returncls._pool