目录
简介
一、环境准备
1. 官方推荐库
2. 轻量级替代库
二、基础操作
1. 建立数据库连接
2. 游标对象
3. 执行查询语句
4. 获取查询结果
5. 关闭资源
三、数据操作
1. 插入数据(INSERT)
2. 更新数据(UPDATE)
3. 删除数据(DELETE)
4. 高级查询技巧
(1)模糊查询(LIKE)
(2)联合查询(JOIN)
四、性能优化
1. 为什么需要连接池?
2. 使用DBUtils实现连接池
3. 从连接池获取连接
五、事务管理
1. 事务的基本概念
2. 事务操作实战
3. 事务隔离级别
六、实践与常见问题
1. 资源管理最佳实践
2. 错误处理策略
3. 性能优化建议
总结
在现代应用开发中,数据库作为数据存储和管理的核心组件,其与应用程序的交互效率直接影响系统性能。MySQL作为最流行的关系型数据库之一,与Python的结合使用更是Web开发、数据分析等场景中的常见技术组合。
在开始编码前,我们需要先安装必要的依赖库。Python与MySQL交互主要有两种主流库选择,以下是具体安装命令及详解:
pip install mysql-connector-python
命令解释:
pip
:Python的包管理工具,用于安装和管理第三方库。install
:pip的子命令,指示安装操作。mysql-connector-python
:MySQL官方提供的Python连接库,由Oracle维护,支持最新的MySQL特性(如SSL加密、X协议)。pip install pymysql
命令解释:
pymysql
:纯Python实现的MySQL连接库,无需编译即可使用,兼容Python 3.6+版本。mysqlclient
等库)。要操作MySQL数据库,首先需要建立Python程序与数据库的连接。以下是完整的连接示例及逐行解析:
import pymysql # 导入PyMySQL库
# 创建数据库连接对象
db = pymysql.connect(
host="localhost", # 数据库服务器地址(本地部署为localhost,远程为IP地址)
user="root", # 数据库用户名(如root)
password="password", # 数据库密码(安装MySQL时设置的密码)
database="testdb", # 要连接的数据库名称(需提前在MySQL中创建)
charset="utf8mb4" # 可选参数:指定字符集(支持emoji的utf8mb4)
)
逐行解析:
import pymysql
:导入PyMySQL库,使Python程序能调用MySQL操作的相关函数。pymysql.connect()
:核心函数,用于创建数据库连接对象。host
:数据库服务器的地址。本地开发时填localhost
或127.0.0.1
;远程服务器填IP地址(如192.168.1.100
)。user
:登录MySQL的用户名(默认是root
,拥有最高权限)。password
:该用户的登录密码(生产环境需通过配置文件或环境变量存储,禁止明文硬编码)。database
:要操作的数据库名称(需提前通过CREATE DATABASE testdb;
在MySQL中创建)。charset
:可选参数,指定连接使用的字符集。utf8mb4
是推荐值,支持存储emoji和生僻字(普通utf8
不支持emoji)。连接建立后,需要通过游标(Cursor)对象执行SQL语句。游标是Python与MySQL交互的“中间人”,负责发送SQL指令并接收结果。
cursor = db.cursor() # 创建游标对象
命令解释:
db.cursor()
:调用连接对象的cursor()
方法,创建一个游标实例。SELECT
的返回数据)。执行SQL时,需通过游标对象的execute()
方法。为避免SQL注入攻击,必须使用占位符传递参数。
# 执行查询:获取年龄大于18岁的用户
cursor.execute("SELECT * FROM users WHERE age > %s", (18,))
逐行解析:
cursor.execute()
:游标对象的核心方法,用于执行SQL语句。%s
是占位符(PyMySQL专用,不同于Python的%
格式化),表示“此处需要替换为一个参数值”。(18,)
表示将%s
替换为数字18。' OR 1=1 --
,拼接后的SQL会变成SELECT * FROM users WHERE age > ' OR 1=1 --
,导致查询所有用户(--
是SQL注释符,会忽略后续条件)。使用占位符时,PyMySQL会自动对参数进行转义,确保安全性。执行查询后,需通过游标对象的方法获取结果。常见方法有三种:
# 获取所有结果(元组列表)
results = cursor.fetchall()
# 获取单条结果(第一个匹配的行)
row = cursor.fetchone()
# 获取指定数量的结果(如前5条)
some_rows = cursor.fetchmany(5)
命令解释:
fetchall()
:返回所有匹配的行,结果为元组列表(每个元组代表一行数据)。适用于数据量较小的场景。fetchone()
:返回结果中的下一行(按查询顺序),适用于逐条处理大数据集(避免内存溢出)。fetchmany(size)
:返回前size
条结果,适用于分页查询(如前端每页显示10条数据)。操作完成后,必须关闭游标和连接,释放数据库资源。
cursor.close() # 关闭游标,释放临时内存
db.close() # 关闭数据库连接,释放TCP连接资源
为什么重要?:
Too many connections
)。插入数据是最常见的写操作,PyMySQL支持单条插入和批量插入两种方式。
单条插入
# 定义插入SQL(%s为占位符)
sql_insert = "INSERT INTO users (name, age) VALUES (%s, %s)"
# 执行插入(参数为元组)
cursor.execute(sql_insert, ("Alice", 25))
# 提交事务(重要!)
db.commit()
逐行解析:
sql_insert
:SQL语句模板,%s
分别对应name
和age
字段的值。cursor.execute(sql_insert, ("Alice", 25))
:将参数"Alice"
(字符串)和25
(数字)替换到占位符位置,执行插入。db.commit()
:提交事务。PyMySQL默认关闭自动提交(autocommit=False
),所有写操作(INSERT/UPDATE/DELETE)必须调用commit()
才会真正写入数据库;否则数据仅存在于事务日志中,重启数据库后会丢失。批量插入:提升写入效率
当需要插入大量数据(如1000条)时,使用executemany()
比循环调用execute()
效率高得多。
# 批量数据(列表中的每个元素是一个元组)
batch_data = [("Bob", 30), ("Charlie", 35), ("David", 28)]
# 批量执行插入(第一个参数是SQL模板,第二个是数据列表)
cursor.executemany(sql_insert, batch_data)
# 提交事务
db.commit()
为什么高效?:
executemany()
会将多条SQL合并为一条(如INSERT INTO users ... VALUES (a),(b),(c)
),减少与数据库的交互次数。测试显示,插入1000条数据时,executemany()
比循环execute()
快约10倍。
更新操作需要指定更新条件,避免误改数据。
# 定义更新SQL(将name为Alice的用户年龄改为26)
sql_update = "UPDATE users SET age = %s WHERE name = %s"
# 执行更新(参数顺序与占位符顺序一致)
cursor.execute(sql_update, (26, "Alice"))
# 提交事务
db.commit()
关键注意点:
WHERE
子句必须明确(如name = %s
),否则会更新全表数据(生产环境中可能导致严重事故)。WHERE
条件无匹配行,execute()
不会报错,但cursor.rowcount
属性会返回0(表示影响的行数)。删除是不可逆操作,需严格限制删除条件。
# 定义删除SQL(删除name为Alice的用户)
sql_delete = "DELETE FROM users WHERE name = %s"
# 执行删除
cursor.execute(sql_delete, ("Alice",))
# 提交事务
db.commit()
安全建议:
is_deleted
字段实现逻辑删除(标记为已删除,保留原始数据)。SELECT * FROM users WHERE name = 'Alice'
),避免误删。# 查询名字包含"a"的用户(不区分大小写)
sql_like = "SELECT * FROM users WHERE name LIKE %s"
cursor.execute(sql_like, ("%a%",)) # %是通配符,表示任意字符(包括空)
results = cursor.fetchall()
参数说明:
%a%
:匹配所有包含字母a
的名字(如Alice
、Boba
)。a%
:匹配以a
开头的名字(如Alice
、Andy
)。%a
:匹配以a
结尾的名字(如Luna
、Zora
)。当需要从多个表获取关联数据时,可使用JOIN
操作。
# 查询用户及其订单金额(INNER JOIN)
sql_join = """
SELECT
users.name, # 选择users表的name字段
orders.amount # 选择orders表的amount字段
FROM users # 主表
INNER JOIN orders # 关联表
ON users.id = orders.user_id # 关联条件(用户ID相等)
"""
cursor.execute(sql_join)
results = cursor.fetchall()
JOIN类型说明:
INNER JOIN
:仅返回两表都有匹配的行(最常用)。LEFT JOIN
:返回主表所有行,关联表无匹配时字段值为NULL
。RIGHT JOIN
:返回关联表所有行,主表无匹配时字段值为NULL
。在高并发场景下(如Web服务器处理大量请求),频繁创建和销毁数据库连接会带来巨大开销:
连接池通过预先创建并管理一组连接,实现连接复用,解决上述问题。
DBUtils
是Python中常用的连接池库,支持与PyMySQL集成。以下是连接池的创建和使用示例:
from dbutils.pooled_db import PooledDB # 导入连接池库
import pymysql # 导入PyMySQL
# 数据库配置(与直接连接时相同)
db_config = {
"host": "localhost",
"user": "root",
"password": "password",
"database": "testdb",
"charset": "utf8mb4"
}
# 创建连接池(核心配置)
connection_pool = PooledDB(
creator=pymysql, # 指定使用PyMySQL创建连接
maxconnections=5, # 最大连接数(超过时新请求等待)
mincached=2, # 最小闲置连接数(启动时预先创建)
maxcached=5, # 最大闲置连接数(超过时关闭多余连接)
blocking=True, # 连接池满时是否阻塞(True:等待;False:报错)
**db_config # 其他数据库配置(如host、user等)
)
参数详解:
creator=pymysql
:指定连接的创建工具(此处用PyMySQL)。maxconnections=5
:连接池的最大活跃连接数。若同时有6个请求,第6个请求会等待(blocking=True
时)。mincached=2
:连接池启动时自动创建2个闲置连接,避免首次请求耗时。blocking=True
:连接池满时,新请求会阻塞直到有连接释放(推荐生产环境使用)。# 使用with语句自动获取/释放连接(推荐方式)
with connection_pool.connection() as db_connection: # 从连接池获取连接
with db_connection.cursor() as cursor: # 创建游标
cursor.execute("SELECT * FROM users") # 执行查询
results = cursor.fetchall() # 获取结果
for row in results:
print(row)
# with块结束后,连接自动返回连接池
with
语句的优势:
close()
导致的连接泄漏。也可以使用connection()的方式,每次获取的连接都可以直接执行数据库操作。
db_connection=connection_pool.connection()
#从数据库连接池(connection_pool)中获取一个可用的数据库连接对象
cursor=db_connection.cursor()
#通过数据库连接对象(db_connection)创建一个游标(cursor)
cursor.execute("select * from users")
#使用游标执行一条 SQL 查询语句(select * from users)
results=cursor.fetchall()
#获取游标执行查询后的所有结果数据
for row in results:
print(row)
cursor.close()
#遍历查询结果(results),逐行打印每一条用户数据
db_connection.close() #连接会自动归还给连接池
事务是一组SQL操作的集合,具有ACID特性:
import pymysql #调用模块
# 连接数据库(关闭自动提交)
conn = pymysql.connect(
host="localhost",
user="root",
password="password",
database="testdb",
autocommit=False # 关闭自动提交(默认False,可显式设置)
)
cursor = conn.cursor()
try:
# 操作1:插入用户
cursor.execute("INSERT INTO users (name, age) VALUES ('Eve', 22)")
# 操作2:插入该用户的订单(依赖操作1的结果)
cursor.execute("""
INSERT INTO orders (user_id, amount)
VALUES ((SELECT id FROM users WHERE name = 'Eve'), 120.58)
""")
# 所有操作成功,提交事务
conn.commit()
print("事务提交成功")
except pymysql.MySQLError as err:
# 发生错误,回滚事务(撤销所有操作)
conn.rollback()
print(f"事务回滚,错误原因:{err}")
finally:
# 无论是否异常,都关闭游标和连接
cursor.close()
conn.close()
解析:
autocommit=False
:关闭自动提交,所有写操作需手动调用commit()
。try...except...
:捕获数据库操作中的异常(如主键冲突、字段类型错误)。conn.commit()
:提交事务,将内存中的修改写入磁盘。conn.rollback()
:回滚事务,撤销本次事务内的所有操作,保证数据一致性。MySQL支持四种事务隔离级别,决定了并发事务之间的相互影响程度。通过SET TRANSACTION
命令设置:
# 设置当前会话的隔离级别为READ COMMITTED(提交读)
cursor.execute("SET TRANSACTION ISOLATION LEVEL READ COMMITTED")
常见隔离级别详解:
隔离级别 |
脏读(Dirty Read) |
不可重复读(Non-Repeatable Read) |
幻读(Phantom Read) |
适用场景 |
|
允许 |
允许 |
允许 |
对一致性要求极低的场景(如日志) |
|
禁止 |
允许 |
允许 |
大多数业务系统(默认级别) |
|
禁止 |
禁止 |
允许 |
MySQL默认级别(如金融转账) |
|
禁止 |
禁止 |
禁止 |
高一致性场景(如银行核心系统) |
with
语句:无论是连接、游标还是文件操作,with
语句能自动释放资源,避免泄漏。pymysql.MySQLError
的子类(如IntegrityError
表示主键冲突),避免掩盖其他错误。err.args
)记录到日志文件,便于问题排查。users.name
)上创建索引,可将查询时间从O(n)降至O(log n)。maxconnections
建议设置为CPU核心数的2-4倍(如8核CPU设置为16-32)。本文系统讲解了Python操作MySQL数据库的核心技术,并逐条解释了关键命令的功能和设计原理。通过本文的学习,你将能够: