PyMySQL数据库连接与优化方式

Written by

in

文章目录
  • PyMySQL 是一个纯 Python 实现的 MySQL 客户端库,用于连接和操作 MySQL 数据库。它完全兼容 Python DB API 2.0 规范,提供了简单易用的接口来执行 SQL 查询和操作。 核心优势 纯 Python 实现:无需外部依赖,跨平台兼容性好 Python 3 全面支持:兼容最新 Python 特性和语法 线程安全:支持多线程并发操作 完整功能支持:事务、存储过程、预处理语句等 广泛兼容:支持 MySQL 5.5+ 和 MariaDB 安装方法 pip install pymysql
  • 连接管理 使用连接池管理数据库连接 合理配置连接池参数 及时释放连接回池 事务控制 明确控制事务边界 及时提交或回滚事务 处理并发场景下的数据一致性 错误处理 实现适当的重试机制 记录详细的错误日志 区分业务错误和系统错误 性能优化 使用预处理语句防止 SQL 注入 合理使用批量操作 监控连接池使用情况 以上为个人经验,希望能给大家一个参考,也希望大家多多支持风君子博客。 您可能感兴趣的文章: Python用Flask和PyMySQL实现MySQL数据库的增删改查API Python中使用pymysql连接MySQL数据库进行数据查询 Python中使用PyMySQL模块的方法详解 pymysql.err.DataError:(1264, ")异常的有效解决方法(最新推荐) pymysql.err.DataError:1366的报错解决 Python使用PyMySql增删改查Mysql数据库的实现
  • 目录
    • 一、PyMySQL 简介
    • 二、数据库连接配置
      • 基础连接方式
      • 完整连接示例
    • 三、数据库基础操作
      • 创建示例数据表
      • 数据库操作封装类
      • CRUD 操作示例
      • 事务处理示例
      • 批量操作
    • 四、连接池优化
      • 为什么需要连接池
      • 使用 DBUtils 实现连接池
    • 五、应用示例
      • Flask 集成示例
      • 连接池实践配置
      • 错误重试机制
    • 六、SQL事务操作对比
      • 事务影响
      • 事务特性
    • 七、总结

      本文将介绍如何使用 PyMySQL 连接和操作 MySQL 数据库,包括基本连接CRUD 操作事务处理以及如何在高并发环境下使用连接池优化性能。

      通过合理的连接池配置和错误处理机制,可以构建出稳定高效的数据库应用。

      PyMySQL 是一个纯 Python 实现的 MySQL 客户端库,用于连接和操作 MySQL 数据库。它完全兼容 Python DB API 2.0 规范,提供了简单易用的接口来执行 SQL 查询和操作。

      核心优势

      • 纯 Python 实现:无需外部依赖,跨平台兼容性好
      • Python 3 全面支持:兼容最新 Python 特性和语法
      • 线程安全:支持多线程并发操作
      • 完整功能支持:事务、存储过程、预处理语句等
      • 广泛兼容:支持 MySQL 5.5+ 和 MariaDB

      安装方法

      pip install pymysql
      

      import pymysql
      from pymysql.cursors import DictCursor
      
      # 推荐配置方式
      def create_connection():
          return pymysql.connect(
              host='localhost',      # 数据库地址
              user='username',       # 用户名
              password='password',   # 密码
              database='test_db',    # 数据库名
              port=3306,            # 端口,默认3306
              charset='utf8mb4',     # 字符集,推荐utf8mb4
              autocommit=False,     # 是否自动提交
              cursorclass=DictCursor # 返回字典格式结果
          )
      	
      

      连接参数说明

      参数 说明
      host 数据库服务器地址 ‘localhost’
      user 用户名 根据实际配置
      password 密码 根据实际配置
      database 数据库名称 项目数据库名
      charset 字符编码 ‘utf8mb4’(支持表情符号)
      autocommit 自动提交事务 False(建议手动控制)
      cursorclass 游标类型 DictCursor(结果以字典返回)

      cursorclass参数说明

      cursorclass 说明 返回结果格式 适用场景
      Cursor (默认) 普通游标 元组格式 (value1, value2, …) 基础查询,需要最高性能时
      DictCursor 字典游标 字典格式 {‘column’: value} 需要按列名访问数据时
      SSCursor 无缓冲游标 元组格式,流式读取 处理大量数据,内存有限时
      SSDictCursor 无缓冲字典游标 字典格式,流式读取 大量数据且需要按列名访问
      Cursor 子类 自定义游标 自定义格式 特殊数据处理需求

      import pymysql
      from pymysql.cursors import DictCursor
      
      def get_db_connection():
          """获取数据库连接"""
          return pymysql.connect(
              host='localhost',
              user='myuser',
              password='mypassword',
              database='mydatabase',
              charset='utf8mb4',
              autocommit=False,
              cursorclass=DictCursor,
              connect_timeout=10  # 连接超时10秒
          )
      
      # 使用示例
      def test_connection():
          conn = get_db_connection()
          try:
              with conn.cursor() as cursor:
                  cursor.execute("SELECT 1 as test")
                  result = cursor.fetchone()
                  print("连接测试成功:", result)
          finally:
              conn.close()
      
      test_connection()
      

      输出:

      连接测试成功: {'test': 1}
      

      CREATE TABLE mydb.users (
          id INT AUTO_INCREMENT PRIMARY KEY,
          name VARCHAR(100) NOT NULL,
          email VARCHAR(100) UNIQUE NOT NULL,
          age INT,
          created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
      );
      

      相关说明

      关键字 类型 说明
      INT 数据类型 整数类型,用于存储整数值
      AUTO_INCREMENT 约束/属性 自动递增,每次插入新记录时自动生成唯一ID
      PRIMARY KEY 约束 主键,唯一标识每条记录
      VARCHAR(100) 数据类型 可变长度字符串,最大100字符
      NOT NULL 约束 该字段不能为空,必须包含值
      UNIQUE 约束 确保每个值唯一,不允许重复
      TIMESTAMP 数据类型 时间戳类型,用于存储日期和时间
      DEFAULT CURRENT_TIMESTAMP 默认值 默认值为当前系统时间

      import pymysql
      from pymysql.cursors import DictCursor
      from typing import List, Dict, Any, Optional, Tuple
      
      class MySQLManager:
          """MySQL 数据库管理类"""
      
          def __init__(self, config: Dict[str, Any]):
              self.config = config
      
          def execute_query(self, sql: str, params: Tuple = None) -> List[Dict]:
              """执行查询语句(SELECT)"""
              conn = pymysql.connect(**self.config)
              try:
                  with conn.cursor(DictCursor) as cursor:
                      cursor.execute(sql, params or ())
                      return cursor.fetchall()
              finally:
                  conn.close()
      
          def execute_update(self, sql: str, params: Tuple = None) -> int:
              """执行更新语句(INSERT/UPDATE/DELETE)"""
              conn = pymysql.connect(**self.config)
              try:
                  with conn.cursor() as cursor:
                      affected_rows = cursor.execute(sql, params or ())
                      conn.commit()
                      return affected_rows
              except Exception as e:
                  conn.rollback()
                  raise e
              finally:
                  conn.close()			
      

      操作 英文 中文 对应 SQL 描述
      C Create 创建 INSERT 创建新记录
      R Read 读取 SELECT 查询/读取数据
      U Update 更新 UPDATE 修改现有记录
      D Delete 删除 DELETE 删除记录
      # 数据库配置
      db_config = {
          'host': 'localhost',
          'user': 'root',
          'password': 'password',
          'database': 'test_db',
          'charset': 'utf8mb4',
          'cursorclass': DictCursor 
      }
      
      db = MySQLManager(db_config)
      
      # 1. 插入数据
      def add_user(name: str, email: str, age: int) -> int:
          sql = "INSERT INTO users (name, email, age) VALUES (%s, %s, %s)"
          return db.execute_update(sql, (name, email, age))
      
      # 2. 查询数据
      def get_all_users() -> List[Dict]:
          return db.execute_query("SELECT * FROM users")
      
      # 3. 更新数据
      def update_user_email(user_id: int, new_email: str) -> int:
          sql = "UPDATE users SET email = %s WHERE id = %s"
          return db.execute_update(sql, (new_email, user_id))
      
      # 4. 删除数据
      def delete_user(user_id: int) -> int:
          return db.execute_update("DELETE FROM users WHERE id = %s", (user_id,))
      	
      if __name__ == '__main__':
          users = get_all_users()
          print(f"查询所有用户: {users}")
      	
          user_id = add_user("张三", "zhangsan@example.com", 25)
          print(f"执行:插入新用户")
      	
          users = get_all_users()
          print(f"查询所有用户: {users}")
      	
          user_id = users[0]['id']
          affected_rows = update_user_email(user_id, "zhangsan2@example.com")
          print(f"执行:更新邮箱,影响行数: {affected_rows}")
      	
          users = get_all_users()
          print(f"查询所有用户: {users}")
      	
          affected_rows = delete_user(user_id)
          print(f"执行:删除用户,影响行数: {affected_rows}")
      	
          users = get_all_users()
          print(f"查询所有用户: {users}")
      

      输出:

      查询所有用户: ()
      执行:插入新用户
      查询所有用户: [{'id': 3, 'name': '张三', 'email': 'zhangsan@example.com', 'age': 25, 'created_at': datetime.datetime(2025, 9, 23, 19, 25, 11)}]
      执行:更新邮箱,影响行数: 1
      查询所有用户: [{'id': 3, 'name': '张三', 'email': 'zhangsan2@example.com', 'age': 25, 'created_at': datetime.datetime(2025, 9, 23, 19, 25, 11)}]
      执行:删除用户,影响行数: 1
      查询所有用户: ()
      

      模拟简单的转账操作,从一个用户账户转移到另一个用户账户。

      def transfer_points(sender_id: int, receiver_id: int, points: int) -> bool:
          """转账操作(事务示例)"""
          conn = pymysql.connect(**db_config)
          try:
              with conn.cursor(DictCursor) as cursor:
                  # 检查发送者余额
                  cursor.execute("SELECT points FROM accounts WHERE user_id = %s", (sender_id,))
                  sender = cursor.fetchone()
                  
                  if not sender or sender['points'] < points:
                      raise ValueError("余额不足")
                  
                  # 执行转账
                  cursor.execute("UPDATE accounts SET points = points - %s WHERE user_id = %s", 
                               (points, sender_id))
                  cursor.execute("UPDATE accounts SET points = points + %s WHERE user_id = %s", 
                               (points, receiver_id))
                  
                  conn.commit()
                  return True
          except Exception as e:
              conn.rollback()
              raise e
          finally:
              conn.close()
      

      def batch_insert_users(users: List[tuple]) -> int:
          """批量插入用户数据"""
          sql = "INSERT INTO users (name, email, age) VALUES (%s, %s, %s)"
          conn = pymysql.connect(**db_config)
          try:
              with conn.cursor() as cursor:
                  affected_rows = cursor.executemany(sql, users)
                  conn.commit()
                  return affected_rows
          except Exception as e:
              conn.rollback()
              raise e
          finally:
              conn.close()
      
      # 使用示例
      users_data = [
          ('张三', 'zhangsan@example.com', 25),
          ('李四', 'lisi@example.com', 30)
      ]
      batch_insert_users(users_data)
      

      频繁创建和关闭数据库连接会导致:

      • 资源浪费(TCP 连接建立开销)
      • 性能下降(连接初始化时间)
      • 连接数耗尽(超过数据库最大连接数)
        连接池通过复用连接解决这些问题。

      安装方法

      pip install DBUtils
      

      实现示例

      from dbutils.pooled_db import PooledDB
      import pymysql
      import threading
      from typing import List, Dict, Any, Tuple
      from pymysql.cursors import DictCursor
      
      class ConnectionPool:
          """数据库连接池"""
          
          _instance = None
          _lock = threading.Lock()
          
          def __new__(cls, config: Dict[str, Any]):
              with cls._lock:
                  if cls._instance is None:
                      cls._instance = super().__new__(cls)
                      cls._instance.pool_config = config.copy()
                      cls._instance._pool = PooledDB(
                          creator=pymysql,
                          maxconnections=20,  # 最大连接数
                          mincached=2,  # 初始空闲连接
                          maxcached=10,  # 最大空闲连接
                          blocking=True,  # 连接耗尽时等待
                          ping=1,  # 使用时检查连接
                          **config
                      )
              return cls._instance
                  
          def get_connection(self):
              """从连接池获取连接"""
              return self._pool.connection()
      
      # 使用连接池的数据库管理器
      class PooledDBManager:
          def __init__(self, pool_config: Dict[str, Any]):
              self.pool = ConnectionPool(pool_config)
          
          def execute_query(self, sql: str, params: Tuple = None) -> List[Dict]:
              """执行查询"""
              conn = self.pool.get_connection()
              try:
                  with conn.cursor(DictCursor) as cursor:
                      cursor.execute(sql, params or ())
                      return cursor.fetchall()
              finally:
                  conn.close()  # 实际是放回连接池
          
          def execute_update(self, sql: str, params: Tuple = None) -> int:
              """执行更新"""
              conn = self.pool.get_connection()
              try:
                  with conn.cursor() as cursor:
                      affected_rows = cursor.execute(sql, params or ())
                      conn.commit()
                      return affected_rows
              except Exception as e:
                  conn.rollback()
                  raise e
              finally:
                  conn.close()
      

      ping 参数说明

      0 = 不检查
      1 = 每次请求时检查(推荐)
      2 = 每次游标创建时检查
      4 = 每次执行时检查
      7 = 1+2+4(所有检查)
      

      from dbutils.pooled_db import PooledDB
      from flask import Flask, request, jsonify
      from pymysql.cursors import DictCursor
      
      app = Flask(__name__)
      
      db_config = {
          'host': 'localhost',
          'user': 'root',
          'password': 'password',
          'database': 'test_db',
          'charset': 'utf8mb4',
          'cursorclass': pymysql.cursors.DictCursor
      }
      
      # 初始化连接池
      db_manager = PooledDBManager(db_config)
      
      @app.route('/users', methods=['GET'])
      def get_users():
          """获取所有用户"""
          try:
              users = db_manager.execute_query("SELECT * FROM users")
              return jsonify({'success': True, 'data': users})
          except Exception as e:
              return jsonify({'success': False, 'error': str(e)}), 500
      
      @app.route('/users', methods=['POST'])
      def create_user():
          """创建用户"""
          try:
              data = request.json
              sql = "INSERT INTO users (name, email, age) VALUES (%s, %s, %s)"
              result = db_manager.execute_update(sql, (data['name'], data['email'], data['age']))
              return jsonify({'success': True, 'affected_rows': result})
          except Exception as e:
              return jsonify({'success': False, 'error': str(e)}), 500
      
      if __name__ == '__main__':
          app.run(debug=True)
      

      # 优化后的连接池配置
      optimal_pool_config = {
          'maxconnections': 20,      # 根据并发量调整
          'mincached': 2,           # 减少初始资源占用
          'maxcached': 10,          # 控制最大空闲连接
          'blocking': True,         # 避免连接耗尽错误
          'ping': 1,                # 使用前检查连接健康
          **db_config              # 基础数据库配置
      }
      

      数据库操作重试装饰器:当数据库连接出现临时故障时,会自动进行最多3次重试,并且每次重试间隔时间按指数增长(1秒、2秒、4秒),提高程序的容错能力。

      import time
      from functools import wraps
      import pymysql
      
      def retry_on_failure(max_retries=3, initial_delay=1):
          """数据库操作重试装饰器"""
          def decorator(func):
              @wraps(func)
              def wrapper(*args, **kwargs):
                  for attempt in range(max_retries):
                      try:
                          return func(*args, **kwargs)
                      except (pymysql.OperationalError, pymysql.InterfaceError) as e:
                          if attempt == max_retries - 1:
                              raise e
                          time.sleep(initial_delay * (2 ** attempt))  # 指数退避
                  return None
              return wrapper
          return decorator
      
      # 使用示例
      @retry_on_failure(max_retries=3)
      def robust_query(sql, params=None):
          return db_manager.execute_query(sql, params)
      

      指数退避:当操作失败时,不立即重试,而是等待一段时间,且每次重试的等待时间呈指数级增长。等待 1 秒, 2 秒, 4 秒,8 秒…

      操作类型 语法示例 主要用途 返回值 事务影响 性能考虑 使用场景
      SELECT
      (查询)
      SELECT * FROM users WHERE age > 18; 从数据库中检索数据 结果集(0行或多行) 只读操作,不影响数据 索引优化很重要,避免全表扫描 数据查询、报表生成、数据分析
      UPDATE
      (更新)
      UPDATE users SET age = 20 WHERE id = 1; 修改现有记录 受影响的行数 需要事务控制,会锁定行 WHERE 条件要精确,避免锁表 修改用户信息、更新状态、调整数值
      INSERT
      (插入)
      INSERT INTO users (name, age) VALUES (‘张三’, 25); 添加新记录 插入的行数(通常是1) 需要事务控制 批量插入比单条插入高效 新增用户、创建订单、记录日志
      DELETE
      (删除)
      DELETE FROM users WHERE id = 1; 删除记录 受影响的行数 需要事务控制,谨慎使用 建议软删除,避免物理删除 删除用户、清理数据、撤销操作

      操作 是否自动提交 锁级别 回滚支持 并发影响
      SELECT 是(可设置) 共享锁 可回滚到快照 低(读写不阻塞)
      UPDATE 排他锁 完全支持 高(会阻塞其他写操作)
      INSERT 排他锁 完全支持 中(可能触发索引重建)
      DELETE 排他锁 完全支持 高(会阻塞其他操作)
      • 排他锁(X锁):写锁,一个事务独占资源,其他事务不能读写
      • 共享锁(S锁):读锁,多个事务可同时读取,但不能写入
      • 排他锁 = 独占,共享锁 = 共享读

      普通 SELECT 是完全无锁的,不会阻塞其他事务的写操作,也不会被写操作阻塞。只有显式加锁的SELECT才会影响并发。

      连接管理

      • 使用连接池管理数据库连接
      • 合理配置连接池参数
      • 及时释放连接回池

      事务控制

      • 明确控制事务边界
      • 及时提交或回滚事务
      • 处理并发场景下的数据一致性

      错误处理

      • 实现适当的重试机制
      • 记录详细的错误日志
      • 区分业务错误和系统错误

      性能优化

      • 使用预处理语句防止 SQL 注入
      • 合理使用批量操作
      • 监控连接池使用情况

      以上为个人经验,希望能给大家一个参考,也希望大家多多支持风君子博客。

      您可能感兴趣的文章:

      • Python用Flask和PyMySQL实现MySQL数据库的增删改查API
      • Python中使用pymysql连接MySQL数据库进行数据查询
      • Python中使用PyMySQL模块的方法详解
      • pymysql.err.DataError:(1264, ")异常的有效解决方法(最新推荐)
      • pymysql.err.DataError:1366的报错解决
      • Python使用PyMySql增删改查Mysql数据库的实现

      站内搜索