前言
在当今的数据驱动时代,数据库操作是软件开发的核心技能之一。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 扩展时。 |
| SQLAlchemy | ORM 框架 | 复杂业务系统,需要对象关系映射(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-python 和 PyMySQL 建立连接。
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 服务器)时,异步编程可以显著提高吞吐量。aiomysql 是 PyMySQL 的异步版本,可与 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)
在开发过程中,您可能会遇到一些常见问题。
-
ModuleNotFoundError: No module named 'pymysql'- 原因:未安装该库。
- 解决:运行
pip install pymysql。
-
pymysql.err.OperationalError: (2003, "Can't connect to MySQL server...")- 原因:无法连接到 MySQL 服务器。可能是服务未启动、主机地址或端口号错误、防火墙阻止了连接。
- 解决:检查 MySQL 服务是否正在运行,确认
host和port参数是否正确。
-
插入中文时显示
???或报错Incorrect string value- 原因:字符集不一致。数据库、表或连接的字符集未设置为支持中文的
utf8mb4。 - 解决:确保连接参数设置了
charset='utf8mb4',并在创建数据库和表时也使用utf8mb4。
- 原因:字符集不一致。数据库、表或连接的字符集未设置为支持中文的
-
执行 INSERT、UPDATE、DELETE 后数据没有变化
- 原因:没有调用
conn.commit()。PyMySQL 默认处于手动提交模式。 - 解决:在所有修改操作之后,务必调用
conn.commit()。
- 原因:没有调用
-
pymysql.err.ProgrammingError: (1064, ...)- 原因:SQL 语法错误。
- 解决:仔细检查 SQL 语句的拼写、关键字和引号使用是否正确。
-
在多线程环境下连接报错
- 原因:PyMySQL 的
Connection对象本身不是线程安全的。 - 解决:不要在线程间共享同一个连接。使用连接池(如
DBUtils)为每个线程提供独立的连接。
- 原因:PyMySQL 的
总结
本指南系统地介绍了 Python 操作 MySQL 数据库的方方面面,从环境搭建、基础CRUD,到事务控制、性能优化、安全实践乃至ORM和异步编程。掌握这些知识,您将能够自信地构建健壮、高效、安全的数据库应用。
核心要点回顾:
- 驱动选择:
mysql-connector-python是官方推荐,PyMySQL是纯 Python 首选,SQLAlchemy用于 ORM。 - 永远参数化:使用
%s占位符防止 SQL 注入。 - 务必提交:CRUD 中的修改操作后,记得
commit()。 - 管理资源:使用
with语句自动关闭连接和游标。 - 拥抱连接池:在高并发场景下,连接池是性能的关键。
6万+

被折叠的 条评论
为什么被折叠?



