Python 与 MySQL 数据库:从入门到生产级实践的全方位指南

前言

在当今的数据驱动时代,数据库操作是软件开发的核心技能之一。Python,以其简洁优雅的语法和强大的生态系统,成为了处理数据的首选语言。而 MySQL,作为全球最流行的开源关系型数据库管理系统,与 Python 的结合堪称“黄金搭档”。无论是构建 Web 应用、数据分析管道,还是物联网数据采集系统,掌握 Python 操作 MySQL 的技能都至关重要。

本指南将深入浅出地带您走过从环境搭建到高级实战的每一个步骤。我们将覆盖两种最主流的驱动:官方推荐的 mysql-connector-python 和纯 Python 实现的 PyMySQL。您将不仅学会如何执行增删改查(CRUD),更将理解连接池、事务管理、安全防护等生产环境必备的实践。

第一章:基石搭建——环境准备与驱动选择

在开始编写任何代码之前,我们需要确保“地基”稳固。

1.1 数据库与 Python 环境

首先,您需要确保系统中已安装 MySQL 数据库和 Python 解释器。

  • MySQL: 建议使用 MySQL 8.0 或更高版本,它支持 JSON、窗口函数等新特性。您可以通过 mysql --version 命令检查版本。
  • Python: 建议使用 Python 3.8 或更高版本。通过 python --version 检查。

1.2 数据库驱动:连接的关键桥梁

Python 需要通过一个“数据库驱动”来与 MySQL 通信。目前最主流的选择有三个:

库名称类型最佳适用场景
mysql-connector-python官方驱动企业级应用,需要官方支持,追求最佳兼容性与稳定性。
PyMySQL纯 Python 实现通用场景,跨平台部署,环境无法安装 C 扩展时。
SQLAlchemyORM 框架复杂业务系统,需要对象关系映射(ORM)和高级抽象。

对于初学者和大多数项目,PyMySQL 因其安装简便、无需编译、兼容性极佳而备受推崇。而 mysql-connector-python 作为官方出品,在稳定性和功能完整度上同样出色。

安装命令(根据您的选择执行其一):

# 安装 MySQL 官方驱动
pip install mysql-connector-python

# 安装 PyMySQL
pip install pymysql

# 安装 SQLAlchemy (通常会与 PyMySQL 一起使用)
pip install sqlalchemy pymysql

1.3 初始化数据库与用户

为了使我们的代码能够成功连接,需要在 MySQL 中创建一个数据库和一个用户,并授予相应的权限。

-- 登录 MySQL (以 root 用户为例)
-- mysql -u root -p

-- 1. 创建数据库 (推荐使用 utf8mb4 字符集以支持 emoji 和生僻字)
CREATE DATABASE IF NOT EXISTS python_mysql_demo
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;

-- 2. 创建用户 (请替换为您的密码)
CREATE USER 'demo_user'@'localhost' IDENTIFIED BY '你的强密码';

-- 3. 授予权限
GRANT ALL PRIVILEGES ON python_mysql_demo.* TO 'demo_user'@'localhost';

-- 4. 刷新权限使设置生效
FLUSH PRIVILEGES;

安全提示:在生产环境中,应遵循最小权限原则,仅授予应用所需的权限(如 SELECT, INSERT, UPDATE, DELETE),而不是 ALL PRIVILEGES。同时,绝对不要把密码硬编码在代码中,应使用环境变量或专门的配置文件管理。

第二章:深入连接——建立 Python 与 MySQL 的通信

这是所有数据操作的第一步。我们将演示如何使用 mysql-connector-pythonPyMySQL 建立连接。

2.1 使用 mysql-connector-python 建立连接

这是官方推荐的驱动,通过 mysql.connector.connect() 方法实现。

import mysql.connector
from mysql.connector import Error

# 数据库配置(生产环境请从环境变量或配置文件读取)
db_config = {
    'host': 'localhost',
    'user': 'demo_user',
    'password': '你的强密码',
    'database': 'python_mysql_demo',
    'charset': 'utf8mb4'
}

try:
    # 建立连接
    connection = mysql.connector.connect(**db_config)
    if connection.is_connected():
        db_info = connection.get_server_info()
        print(f"成功连接到 MySQL 服务器,版本: {db_info}")
        # 在这里执行数据库操作...

except Error as e:
    print(f"连接或操作失败: {e}")
finally:
    # 确保关闭连接以释放资源
    if 'connection' in locals() and connection.is_connected():
        connection.close()
        print("MySQL 连接已关闭。")

2.2 使用 PyMySQL 建立连接

PyMySQL 的 API 与官方驱动非常相似,同样是调用 pymysql.connect()

import pymysql
from pymysql.cursors import DictCursor

# 数据库配置
db_config = {
    'host': 'localhost',
    'user': 'demo_user',
    'password': '你的强密码',
    'database': 'python_mysql_demo',
    'charset': 'utf8mb4',
    'cursorclass': DictCursor  # 推荐:使查询结果返回字典,方便按列名访问
}

try:
    # 建立连接
    connection = pymysql.connect(**db_config)
    print("成功连接到 MySQL 服务器 (PyMySQL)")
    # 在这里执行数据库操作...

except pymysql.MySQLError as e:
    print(f"连接或操作失败: {e}")
finally:
    # 确保关闭连接
    if 'connection' in locals() and connection.open:
        connection.close()
        print("MySQL 连接已关闭。")

2.3 使用 with 语句:更优雅的资源管理

在 Python 中,推荐使用 with 语句(上下文管理器),它可以自动管理资源,即使发生异常也能确保连接被正确关闭,使代码更简洁、安全。

PyMySQL 示例

import pymysql
from pymysql.cursors import DictCursor

db_config = {
    'host': 'localhost',
    'user': 'demo_user',
    'password': '你的强密码',
    'database': 'python_mysql_demo',
    'charset': 'utf8mb4',
    'cursorclass': DictCursor
}

# with 语句会确保 connection 和 cursor 被自动关闭
with pymysql.connect(**db_config) as conn:
    with conn.cursor() as cursor:
        cursor.execute("SELECT VERSION()")
        result = cursor.fetchone()
        print(f"MySQL 版本: {result['VERSION()']}")
    # 退出内层 with,游标关闭
# 退出外层 with,连接关闭

mysql-connector-python 示例

import mysql.connector

db_config = {
    'host': 'localhost',
    'user': 'demo_user',
    'password': '你的强密码',
    'database': 'python_mysql_demo',
    'charset': 'utf8mb4'
}

with mysql.connector.connect(**db_config) as conn:
    with conn.cursor() as cursor:
        cursor.execute("SELECT VERSION()")
        version = cursor.fetchone()
        print(f"MySQL 版本: {version[0]}")

第三章:核心操作 —— CRUD 全解析

CRUD(Create, Read, Update, Delete)是数据库操作的核心。我们将通过具体代码演示每一个操作。注意:后面的示例将主要使用 PyMySQL 并默认使用 DictCursor,因为它更直观方便。

3.0 准备工作:创建示例表

在操作数据前,我们需要一个表。以下 SQL 语句创建一个 users 表。

import pymysql
from pymysql.cursors import DictCursor

db_config = {
    # ... 您的配置
}

def create_table():
    """创建 users 表示例"""
    with pymysql.connect(**db_config) as conn:
        with conn.cursor() as cursor:
            sql = """
            CREATE TABLE IF NOT EXISTS users (
                id INT AUTO_INCREMENT PRIMARY KEY,
                name VARCHAR(50) NOT NULL,
                age TINYINT UNSIGNED,
                email VARCHAR(100) UNIQUE,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
            """
            cursor.execute(sql)
            conn.commit()  # DDL 语句也需要提交
    print("表 'users' 创建成功。")

# 调用函数创建表
create_table()

3.1 创建 (Create):插入数据

插入数据时,必须使用参数化查询%s 占位符)来防止 SQL 注入攻击。

def insert_single_user():
    """插入单条记录"""
    with pymysql.connect(**db_config) as conn:
        with conn.cursor() as cursor:
            sql = "INSERT INTO users (name, age, email) VALUES (%s, %s, %s)"
            data = ('Alice', 30, 'alice@example.com')
            cursor.execute(sql, data)
            conn.commit()  # 重要!增删改操作后必须提交事务
            print(f"插入成功!自增 ID: {cursor.lastrowid}, 影响行数: {cursor.rowcount}")

def insert_multiple_users():
    """批量插入多条记录(更高效)"""
    with pymysql.connect(**db_config) as conn:
        with conn.cursor() as cursor:
            sql = "INSERT INTO users (name, age, email) VALUES (%s, %s, %s)"
            data_list = [
                ('Bob', 25, 'bob@example.com'),
                ('Charlie', 28, 'charlie@example.com'),
                ('David', 32, 'david@example.com'),
            ]
            cursor.executemany(sql, data_list)
            conn.commit()
            print(f"批量插入成功!影响行数: {cursor.rowcount}")

提示executemany 比在循环中多次执行 execute 要快得多,因为它减少了客户端与服务器之间的网络往返次数。

3.2 读取 (Read):查询数据

PyMySQL 提供了三种获取结果的方法:fetchone(), fetchmany(), fetchall()

def query_all_users():
    """查询所有用户"""
    with pymysql.connect(**db_config) as conn:
        with conn.cursor() as cursor:
            cursor.execute("SELECT * FROM users")
            users = cursor.fetchall()  # 获取所有记录
            for user in users:
                print(f"ID: {user['id']}, Name: {user['name']}, Email: {user['email']}")
    print(f"共查询到 {len(users)} 条记录。")

def query_user_by_id(user_id):
    """根据 ID 查询单个用户"""
    with pymysql.connect(**db_config) as conn:
        with conn.cursor() as cursor:
            sql = "SELECT * FROM users WHERE id = %s"
            cursor.execute(sql, (user_id,))  # 参数是元组,即使只有一个元素也要加逗号
            user = cursor.fetchone()
            if user:
                print(f"找到用户: {user}")
            else:
                print(f"未找到 ID 为 {user_id} 的用户。")
    return user

def query_users_by_age_range(min_age, max_age):
    """根据年龄范围查询用户"""
    with pymysql.connect(**db_config) as conn:
        with conn.cursor() as cursor:
            sql = "SELECT * FROM users WHERE age BETWEEN %s AND %s ORDER BY age"
            cursor.execute(sql, (min_age, max_age))
            users = cursor.fetchall()
            print(f"年龄在 {min_age} 到 {max_age} 之间的用户有 {len(users)} 个。")
            for user in users:
                print(user['name'], user['age'])

性能提示:如果结果集非常庞大(例如百万级数据),使用 fetchall() 可能会消耗大量内存。此时,应该使用 fetchmany(size) 分批次获取,或使用服务器端游标(如 SSCursor)。

3.3 更新 (Update):修改数据

更新操作同样需要 commit() 来使更改生效。

def update_user_age(user_id, new_age):
    """更新指定用户的年龄"""
    with pymysql.connect(**db_config) as conn:
        with conn.cursor() as cursor:
            sql = "UPDATE users SET age = %s WHERE id = %s"
            affected_rows = cursor.execute(sql, (new_age, user_id))
            conn.commit()
            if affected_rows > 0:
                print(f"用户 {user_id} 的年龄已更新为 {new_age}。")
            else:
                print(f"未找到 ID 为 {user_id} 的用户。")

3.4 删除 (Delete):删除数据

执行 DELETE 语句后,务必提交。

def delete_user_by_id(user_id):
    """根据 ID 删除用户"""
    with pymysql.connect(**db_config) as conn:
        with conn.cursor() as cursor:
            sql = "DELETE FROM users WHERE id = %s"
            affected_rows = cursor.execute(sql, (user_id,))
            conn.commit()
            if affected_rows > 0:
                print(f"用户 {user_id} 已删除。")
            else:
                print(f"未找到 ID 为 {user_id} 的用户。")

第四章:进阶必备 —— 事务控制与错误处理

事务是一组逻辑上不可分割的操作,要么全部成功,要么全部失败。

4.1 理解事务:ACID 特性

  • 原子性 (Atomicity):事务中的所有操作像一个原子,不可分割。
  • 一致性 (Consistency):事务执行前后,数据库的完整性约束没有被破坏。
  • 隔离性 (Isolation):并发的多个事务之间互不干扰。
  • 持久性 (Durability):一旦提交,结果就是永久性的。

4.2 PyMySQL 中的事务管理

PyMySQL 默认是 autocommit=False 的,这意味着所有的增删改操作都需要显式调用 commit() 才能永久保存,或者在出错时调用 rollback() 回滚。这给了我们精确控制事务的能力。

def transfer_funds(from_account_id, to_account_id, amount):
    """
    模拟资金转账:确保扣款和存款操作原子性。
    这是一个典型的事务示例。
    """
    connection = None
    try:
        connection = pymysql.connect(**db_config)
        # 关闭自动提交,开始一个事务
        connection.autocommit(False)

        with connection.cursor() as cursor:
            # 步骤1: 从账户扣款
            sql_debit = "UPDATE accounts SET balance = balance - %s WHERE id = %s AND balance >= %s"
            affected = cursor.execute(sql_debit, (amount, from_account_id, amount))
            if affected == 0:
                raise ValueError("扣款失败:余额不足或账户不存在。")

            # 步骤2: 向账户存款
            sql_credit = "UPDATE accounts SET balance = balance + %s WHERE id = %s"
            cursor.execute(sql_credit, (amount, to_account_id))

            # 如果以上两步都成功,提交事务
            connection.commit()
            print(f"转账成功:从账户 {from_account_id} 向 {to_account_id} 转账 {amount}。")

    except Exception as e:
        print(f"转账失败,正在回滚事务: {e}")
        if connection:
            # 回滚事务,撤销所有未提交的修改
            connection.rollback()
    finally:
        if connection:
            # 恢复自动提交模式(可选),然后关闭连接
            connection.autocommit(True)
            connection.close()

4.3 全面的错误处理模式

在实际项目中,一个健壮的错误处理模式是必不可少的。

import pymysql
from pymysql import MySQLError

def execute_safe_query(sql, params=None):
    """一个带有完整错误处理和资源管理的安全执行示例"""
    connection = None
    try:
        connection = pymysql.connect(**db_config)
        with connection.cursor() as cursor:
            cursor.execute(sql, params or ())
            connection.commit()
            print("操作成功。")
            # 如果是查询,返回结果
            if sql.strip().upper().startswith("SELECT"):
                return cursor.fetchall()
    except MySQLError as e:
        print(f"数据库错误 [错误码 {e.args[0]}]: {e.args[1]}")
        if connection:
            connection.rollback()
        # 根据需要可以重新抛出异常
        raise
    except Exception as e:
        print(f"其他错误: {e}")
        if connection:
            connection.rollback()
        raise
    finally:
        if connection:
            connection.close()

第五章:性能优化 —— 连接池、索引与批量操作

当您的应用需要处理高并发请求时,数据库性能的优化就变得至关重要。

5.1 连接池:告别频繁创建连接

每次数据库操作都创建和销毁连接的开销是非常巨大的。连接池维护着一组“活的”连接,应用程序可以重复使用它们,从而极大地提升了性能。

Python 中可以使用 DBUtils 库来轻松实现连接池。

# 安装: pip install DBUtils

from dbutils.pooled_db import PooledDB
import pymysql
from pymysql.cursors import DictCursor

# 创建连接池实例
POOL = PooledDB(
    creator=pymysql,          # 使用 PyMySQL 作为驱动
    maxconnections=6,         # 连接池允许的最大连接数
    mincached=2,              # 初始化时,连接池至少空闲的连接数
    maxcached=5,              # 连接池中最多空闲的连接数
    maxusage=None,            # 一个连接最多被重复使用的次数,None 表示无限
    blocking=True,            # 连接池满时,是否等待,True 表示等待直到有空闲连接
    setsession=[],            # 开始会话前执行的 SQL 列表
    ping=4,                   # 连接池对连接进行心跳检测的间隔(秒),0 表示不检测
    **db_config               # 你的数据库配置字典
)

def get_connection_from_pool():
    """从连接池获取一个连接"""
    conn = POOL.connection()
    return conn

# 使用连接池
with get_connection_from_pool() as conn:
    with conn.cursor() as cursor:
        cursor.execute("SELECT * FROM users")
        data = cursor.fetchall()
# 使用完后,连接不会关闭,而是回到连接池供下次使用

5.2 索引的力量

为高频查询的列添加索引,可以显著提升查询速度。但请注意,索引会增加写入操作的开销,因此需要合理创建。

-- 为 name 列创建索引
ALTER TABLE users ADD INDEX idx_name (name);

-- 创建联合索引
ALTER TABLE orders ADD INDEX idx_customer_date (customer_id, order_date);

5.3 批量操作策略

  • 使用 executemany() 代替循环 execute():这是最直接有效的方式。
  • 合适的批量提交:在进行大量插入时,每 N 条数据提交一次,而不是每一条都提交。例如每 1000 条 commit() 一次。

第六章:安全至上 —— 防御 SQL 注入与使用最佳实践

安全性是所有数据库应用的生命线。

6.1 什么是 SQL 注入?

SQL 注入是指攻击者通过向应用程序的输入字段中插入恶意的 SQL 代码,从而欺骗数据库执行非预期的命令。例如:

危险写法(绝对禁止!)

user_input = "' OR '1'='1"
sql = f"SELECT * FROM users WHERE name = '{user_input}'"
cursor.execute(sql)  # 结果: SELECT * FROM users WHERE name = '' OR '1'='1' --> 返回所有用户!

6.2 防御之道:参数化查询

参数化查询将 SQL 语句的结构和数据分开。数据库首先编译 SQL 语句,然后将参数作为纯数据传入,因此任何恶意代码都无法改变 SQL 的结构,从根本上杜绝了注入。

# 安全的写法(在所有驱动中通用)
sql = "SELECT * FROM users WHERE name = %s AND email = %s"
cursor.execute(sql, (user_input_name, user_input_email))

# 在 PyMySQL 中,占位符是 %s
# 在 mysql-connector-python 中,占位符也是 %s

记住:永远不要使用字符串拼接构建 SQL 语句!永远使用参数化查询!

6.3 其他安全实践

  • 凭证管理:使用环境变量或安全的配置管理服务(如 HashiCorp Vault)存储数据库密码,永远不要硬编码。
  • 最小权限原则:为应用创建专门的数据库用户,并只授予其完成工作所必需的权限。
  • 输入验证:除了 SQL 注入,还应对用户输入进行必要的格式验证(如邮箱格式、数字范围等)。
  • HTTPS:确保应用与数据库服务器之间的通信是加密的。

第七章:超越基础 —— ORM 与异步编程

对于非常复杂的应用,直接编写 SQL 可能会变得繁琐。

7.1 SQLAlchemy:对象关系映射 (ORM) 的魅力

ORM 允许您用 Python 类和对象的方式来操作数据库表,大大提高了开发效率。

from sqlalchemy import create_engine, Column, Integer, String, DateTime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from datetime import datetime

# 定义模型基类
Base = declarative_base()

# 定义 User 类,对应 users 表
class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(50), nullable=False)
    age = Column(Integer)
    email = Column(String(100), unique=True)
    created_at = Column(DateTime, default=datetime.now)

# 创建数据库引擎
# 格式: dialect+driver://username:password@host:port/database
engine = create_engine(
    'mysql+pymysql://demo_user:你的强密码@localhost:3306/python_mysql_demo?charset=utf8mb4',
    echo=True,  # 打印执行的 SQL 语句,调试用
    pool_size=5,  # 连接池大小
    pool_pre_ping=True  # 检查连接是否有效
)

# 创建会话
Session = sessionmaker(bind=engine)
session = Session()

# 使用 ORM 创建用户
new_user = User(name='Eve', age=24, email='eve@example.com')
session.add(new_user)
session.commit()

# 查询用户
user = session.query(User).filter_by(name='Eve').first()
print(user.name, user.email)

7.2 aiomysql:拥抱异步 I/O

在处理高并发的 I/O 密集型应用(如 Web 服务器)时,异步编程可以显著提高吞吐量。aiomysqlPyMySQL 的异步版本,可与 asyncio 配合使用。

import asyncio
import aiomysql

async def async_query():
    pool = await aiomysql.create_pool(
        host='localhost', port=3306,
        user='demo_user', password='你的强密码',
        db='python_mysql_demo', charset='utf8mb4',
        minsize=1, maxsize=10
    )
    async with pool.acquire() as conn:
        async with conn.cursor() as cur:
            await cur.execute("SELECT * FROM users")
            results = await cur.fetchall()
            print(results)
    pool.close()
    await pool.wait_closed()

# 运行异步函数
# asyncio.run(async_query())

第八章:实战案例 —— 构建一个简单的用户管理系统

现在,让我们把所学知识综合起来,构建一个控制台应用,实现对用户的管理。

import pymysql
from pymysql.cursors import DictCursor
from dbutils.pooled_db import PooledDB
import os

# 从环境变量读取配置(生产环境推荐)
DB_CONFIG = {
    'host': os.environ.get('DB_HOST', 'localhost'),
    'user': os.environ.get('DB_USER', 'demo_user'),
    'password': os.environ.get('DB_PASSWORD', '你的强密码'),
    'database': os.environ.get('DB_NAME', 'python_mysql_demo'),
    'charset': 'utf8mb4',
    'cursorclass': DictCursor
}

# 创建全局连接池
POOL = PooledDB(creator=pymysql, maxconnections=5, **DB_CONFIG)

def get_conn():
    """从连接池获取连接"""
    return POOL.connection()

class User:
    def __init__(self, name, age, email):
        self.name = name
        self.age = age
        self.email = email

class UserService:
    """用户服务层,封装数据库操作"""

    @staticmethod
    def create_user(user):
        """创建用户"""
        with get_conn() as conn:
            with conn.cursor() as cursor:
                sql = "INSERT INTO users (name, age, email) VALUES (%s, %s, %s)"
                cursor.execute(sql, (user.name, user.age, user.email))
                conn.commit()
                return cursor.lastrowid

    @staticmethod
    def get_all_users():
        """获取所有用户"""
        with get_conn() as conn:
            with conn.cursor() as cursor:
                cursor.execute("SELECT * FROM users")
                return cursor.fetchall()

    @staticmethod
    def get_user_by_id(user_id):
        """根据ID获取用户"""
        with get_conn() as conn:
            with conn.cursor() as cursor:
                cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
                return cursor.fetchone()

    @staticmethod
    def update_user_email(user_id, new_email):
        """更新用户邮箱"""
        with get_conn() as conn:
            with conn.cursor() as cursor:
                sql = "UPDATE users SET email = %s WHERE id = %s"
                cursor.execute(sql, (new_email, user_id))
                conn.commit()
                return cursor.rowcount

    @staticmethod
    def delete_user(user_id):
        """删除用户"""
        with get_conn() as conn:
            with conn.cursor() as cursor:
                cursor.execute("DELETE FROM users WHERE id = %s", (user_id,))
                conn.commit()
                return cursor.rowcount

# 简单的控制台菜单
def main():
    service = UserService()
    while True:
        print("\n--- 用户管理系统 ---")
        print("1. 创建用户")
        print("2. 查看所有用户")
        print("3. 查找用户")
        print("4. 更新用户邮箱")
        print("5. 删除用户")
        print("0. 退出")
        choice = input("请选择操作: ")

        if choice == '1':
            name = input("姓名: ")
            age = int(input("年龄: "))
            email = input("邮箱: ")
            user = User(name, age, email)
            new_id = service.create_user(user)
            print(f"用户创建成功,ID: {new_id}")
        elif choice == '2':
            users = service.get_all_users()
            for u in users:
                print(f"ID: {u['id']}, Name: {u['name']}, Age: {u['age']}, Email: {u['email']}")
        elif choice == '3':
            uid = int(input("输入用户ID: "))
            user = service.get_user_by_id(uid)
            if user:
                print(f"找到用户: {user}")
            else:
                print("用户不存在。")
        elif choice == '4':
            uid = int(input("输入用户ID: "))
            new_email = input("新邮箱: ")
            rows = service.update_user_email(uid, new_email)
            print(f"更新了 {rows} 行。")
        elif choice == '5':
            uid = int(input("输入用户ID: "))
            confirm = input(f"确认删除用户 {uid}? (y/n): ")
            if confirm.lower() == 'y':
                rows = service.delete_user(uid)
                print(f"删除了 {rows} 行。")
        elif choice == '0':
            print("再见!")
            break
        else:
            print("无效输入。")

if __name__ == "__main__":
    main()

第九章:常见问题与排错 (FAQ)

在开发过程中,您可能会遇到一些常见问题。

  1. ModuleNotFoundError: No module named 'pymysql'

    • 原因:未安装该库。
    • 解决:运行 pip install pymysql
  2. pymysql.err.OperationalError: (2003, "Can't connect to MySQL server...")

    • 原因:无法连接到 MySQL 服务器。可能是服务未启动、主机地址或端口号错误、防火墙阻止了连接。
    • 解决:检查 MySQL 服务是否正在运行,确认 hostport 参数是否正确。
  3. 插入中文时显示 ??? 或报错 Incorrect string value

    • 原因:字符集不一致。数据库、表或连接的字符集未设置为支持中文的 utf8mb4
    • 解决:确保连接参数设置了 charset='utf8mb4',并在创建数据库和表时也使用 utf8mb4
  4. 执行 INSERT、UPDATE、DELETE 后数据没有变化

    • 原因:没有调用 conn.commit()。PyMySQL 默认处于手动提交模式。
    • 解决:在所有修改操作之后,务必调用 conn.commit()
  5. pymysql.err.ProgrammingError: (1064, ...)

    • 原因:SQL 语法错误。
    • 解决:仔细检查 SQL 语句的拼写、关键字和引号使用是否正确。
  6. 在多线程环境下连接报错

    • 原因:PyMySQL 的Connection对象本身不是线程安全的。
    • 解决:不要在线程间共享同一个连接。使用连接池(如 DBUtils)为每个线程提供独立的连接。

总结

本指南系统地介绍了 Python 操作 MySQL 数据库的方方面面,从环境搭建、基础CRUD,到事务控制、性能优化、安全实践乃至ORM和异步编程。掌握这些知识,您将能够自信地构建健壮、高效、安全的数据库应用。

核心要点回顾

  • 驱动选择mysql-connector-python 是官方推荐,PyMySQL 是纯 Python 首选,SQLAlchemy 用于 ORM。
  • 永远参数化:使用 %s 占位符防止 SQL 注入。
  • 务必提交:CRUD 中的修改操作后,记得 commit()
  • 管理资源:使用 with 语句自动关闭连接和游标。
  • 拥抱连接池:在高并发场景下,连接池是性能的关键。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浩瀚之水_csdn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值