"""Database access with a PyMySQL connection pool. Each fetch/execute borrows a pooled connection and returns it on close(). Pool settings are read from environment (DB_POOL_*), so nothing here depends on config.py and the pool can be shared safely across threads via DBUtils. """ import os import logging import threading import pymysql from dbutils.pooled_db import PooledDB from . import config logger = logging.getLogger(__name__) def _pool_args(database): return dict( mincached=int(os.environ.get("DB_POOL_MIN_CACHED", "1")), maxcached=int(os.environ.get("DB_POOL_MAX_CACHED", "5")), maxconnections=int(os.environ.get("DB_POOL_MAX_CONNECTIONS", "10")), blocking=True, maxusage=int(os.environ.get("DB_POOL_MAX_USAGE", "100")), ping=1, host=config.MYSQL_HOST, port=config.MYSQL_PORT, user=config.MYSQL_USER, password=config.MYSQL_PASSWORD, database=database, charset="utf8mb4", connect_timeout=5, read_timeout=5, ) _pools = {} _pools_lock = threading.Lock() def _get_pool(database): key = database or config.MYSQL_DB with _pools_lock: pool = _pools.get(key) if pool is None: pool = PooledDB(creator=pymysql, **_pool_args(key)) _pools[key] = pool return pool def _connect(db=None): """Borrow a pooled connection (close() returns it to the pool).""" return _get_pool(db).connection() def fetch_one(sql, args=None, db=None): conn = _connect(db) try: with conn.cursor(pymysql.cursors.DictCursor) as cur: cur.execute(sql, args or ()) return cur.fetchone() finally: conn.close() def fetch_all(sql, args=None, db=None): conn = _connect(db) try: with conn.cursor(pymysql.cursors.DictCursor) as cur: cur.execute(sql, args or ()) return cur.fetchall() finally: conn.close() def execute(sql, args=None, db=None): conn = _connect(db) try: with conn.cursor() as cur: cur.execute(sql, args or ()) lastrowid = cur.lastrowid conn.commit() return lastrowid finally: conn.close() def close_all(): """Close every pooled connection (used by tests / reload hooks).""" with _pools_lock: for key in list(_pools): try: _pools.pop(key).close() except Exception: logger.exception("关闭数据库连接池失败: %s", key)