Python操作MySQL数据库

目录

简介

一、环境准备

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交互主要有两种主流库选择,以下是具体安装命令及详解:

1. 官方推荐库
pip install mysql-connector-python

命令解释

  • ​pip​​:Python的包管理工具,用于安装和管理第三方库。
  • ​install​​:pip的子命令,指示安装操作。
  • ​mysql-connector-python​​​:MySQL官方提供的Python连接库,由Oracle维护,支持最新的MySQL特性(如SSL加密、X协议)。
    适用场景:需要与MySQL数据库进行官方标准方式交互的场景(如企业级系统对接)。
2. 轻量级替代库
pip install pymysql

命令解释

  • ​pymysql​​​:纯Python实现的MySQL连接库,无需编译即可使用,兼容Python 3.6+版本。
    优势
  • 安装简单:无需依赖C语言扩展(对比​​mysqlclient​​等库)。
  • 社区活跃:GitHub星标超1.5万,问题响应及时。
    适用场景:大多数日常开发场景(如Web应用、数据分析脚本)的首选。

二、基础操作

1. 建立数据库连接

要操作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)。
2. 游标对象

连接建立后,需要通过游标(Cursor)对象执行SQL语句。游标是Python与MySQL交互的“中间人”,负责发送SQL指令并接收结果。

cursor = db.cursor()  # 创建游标对象

命令解释

  • ​db.cursor()​​​:调用连接对象的​​cursor()​​方法,创建一个游标实例。
  • 游标作用
  • 发送SQL语句到数据库服务器执行。
  • 接收并暂存查询结果(如​​SELECT​​的返回数据)。
  • 管理事务的提交或回滚(后续事务章节详细说明)。
3. 执行查询语句

执行SQL时,需通过游标对象的​​execute()​​方法。为避免SQL注入攻击,必须使用占位符传递参数

# 执行查询:获取年龄大于18岁的用户
cursor.execute("SELECT * FROM users WHERE age > %s", (18,))

逐行解析

  • ​cursor.execute()​​:游标对象的核心方法,用于执行SQL语句。
  • 第一个参数:SQL语句字符串。​​%s​​​是占位符(PyMySQL专用,不同于Python的​​%​​格式化),表示“此处需要替换为一个参数值”。
  • 第二个参数:元组形式的参数值,用于替换占位符。本例中​​(18,)​​​表示将​​%s​​替换为数字18。
  • 为什么用占位符?
    直接拼接字符串可能导致SQL注入攻击。例如,若用户输入​​​' OR 1=1 --​​​,拼接后的SQL会变成​​SELECT * FROM users WHERE age > ' OR 1=1 --​​​,导致查询所有用户(​​--​​是SQL注释符,会忽略后续条件)。使用占位符时,PyMySQL会自动对参数进行转义,确保安全性。
4. 获取查询结果

执行查询后,需通过游标对象的方法获取结果。常见方法有三种:

# 获取所有结果(元组列表)
results = cursor.fetchall()  

# 获取单条结果(第一个匹配的行)
row = cursor.fetchone()  

# 获取指定数量的结果(如前5条)
some_rows = cursor.fetchmany(5)

命令解释

  • ​fetchall()​​:返回所有匹配的行,结果为元组列表(每个元组代表一行数据)。适用于数据量较小的场景。
  • ​fetchone()​​:返回结果中的下一行(按查询顺序),适用于逐条处理大数据集(避免内存溢出)。
  • ​fetchmany(size)​​​:返回前​​size​​条结果,适用于分页查询(如前端每页显示10条数据)。
5. 关闭资源

操作完成后,必须关闭游标和连接,释放数据库资源。

cursor.close()  # 关闭游标,释放临时内存
db.close()      # 关闭数据库连接,释放TCP连接资源

为什么重要?

  • 数据库的连接数是有限的(MySQL默认最大连接数为151)。若大量连接未关闭,会导致后续请求无法建立连接(报错​​Too many connections​​)。
  • 游标会暂存查询结果,长期不关闭可能占用内存,影响程序性能。

三、数据操作

1. 插入数据(INSERT)

插入数据是最常见的写操作,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倍。

2. 更新数据(UPDATE)

更新操作需要指定更新条件,避免误改数据。

# 定义更新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(表示影响的行数)。
3. 删除数据(DELETE)

删除是不可逆操作,需严格限制删除条件。

# 定义删除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'​​),避免误删。
4. 高级查询技巧
(1)模糊查询(LIKE)
# 查询名字包含"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​​)。
(2)联合查询(JOIN)

当需要从多个表获取关联数据时,可使用​​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​​。

四、性能优化

1. 为什么需要连接池?

在高并发场景下(如Web服务器处理大量请求),频繁创建和销毁数据库连接会带来巨大开销:

  • 连接创建耗时:每次连接需要TCP三次握手、MySQL认证等步骤,耗时约10-100ms。
  • 资源浪费:短时间内大量连接可能超出MySQL的最大连接数限制(默认151),导致新请求失败。

连接池通过预先创建并管理一组连接,实现连接复用,解决上述问题。

2. 使用DBUtils实现连接池

​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​​:连接池满时,新请求会阻塞直到有连接释放(推荐生产环境使用)。
3. 从连接池获取连接
# 使用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()​​导致的连接泄漏。
  • 代码更简洁,符合Python的“简洁之美”原则。

也可以使用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()   #连接会自动归还给连接池

五、事务管理

1. 事务的基本概念

事务是一组SQL操作的集合,具有ACID特性

  • 原子性(Atomicity):事务中的操作要么全部成功,要么全部失败。
  • 一致性(Consistency):事务执行后,数据库从一个合法状态变为另一个合法状态(如转账后双方余额总和不变)。
  • 隔离性(Isolation):多个事务并发执行时,彼此互不干扰(通过隔离级别控制)。
  • 持久性(Durability):事务提交后,数据会永久保存(即使数据库崩溃)。
2. 事务操作实战
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()​​:回滚事务,撤销本次事务内的所有操作,保证数据一致性。
3. 事务隔离级别

MySQL支持四种事务隔离级别,决定了并发事务之间的相互影响程度。通过​​SET TRANSACTION​​命令设置:

# 设置当前会话的隔离级别为READ COMMITTED(提交读)
cursor.execute("SET TRANSACTION ISOLATION LEVEL READ COMMITTED")

常见隔离级别详解

隔离级别

脏读(Dirty Read)

不可重复读(Non-Repeatable Read)

幻读(Phantom Read)

适用场景

​READ UNCOMMITTED​

允许

允许

允许

对一致性要求极低的场景(如日志)

​READ COMMITTED​

禁止

允许

允许

大多数业务系统(默认级别)

​REPEATABLE READ​

禁止

禁止

允许

MySQL默认级别(如金融转账)

​SERIALIZABLE​

禁止

禁止

禁止

高一致性场景(如银行核心系统)

六、实践与常见问题

1. 资源管理最佳实践
  • 使用with语句:无论是连接、游标还是文件操作,​​with​​语句能自动释放资源,避免泄漏。
  • 限制连接持有时间:业务逻辑执行完成后,尽快关闭连接(避免长时间占用连接池资源)。
2. 错误处理策略
  • 捕获具体异常:优先捕获​​pymysql.MySQLError​​​的子类(如​​IntegrityError​​表示主键冲突),避免掩盖其他错误。
  • 记录详细日志:生产环境中,需将错误信息(如​​err.args​​)记录到日志文件,便于问题排查。
3. 性能优化建议
  • 合理使用索引:在查询频繁的字段(如​​users.name​​)上创建索引,可将查询时间从O(n)降至O(log n)。
  • 调整连接池大小:连接池的​​maxconnections​​建议设置为CPU核心数的2-4倍(如8核CPU设置为16-32)。

总结

本文系统讲解了Python操作MySQL数据库的核心技术,并逐条解释了关键命令的功能和设计原理。通过本文的学习,你将能够:

  • 安全高效地建立数据库连接
  • 实现各种数据操作(CRUD)并防止SQL注入
  • 通过连接池技术提升高并发场景下的性能
  • 使用事务保证数据一致性,并根据业务需求选择隔离级别

你可能感兴趣的:(MySQL,连接池,python,事务管理)