MySQL 常用命令
常用 SQL、Python 调用与连接池。
MySQL 是数据库管理系统,SQL 是操作数据库的语言。 在 Python 中,PyMySQL、aiomysql 负责建立连接、发送 SQL,并把执行结果交回程序。
MySQL 连接示例:
实际运行示例需要已有的 MySQL 服务和可登录账号;安装 Python 驱动不会安装 MySQL 服务。脚本配置与调用方式见第 5 节。
1. 数据库、表和字段
1.1 数据如何组织
一个 MySQL 服务可以管理多个数据库,每个数据库里可以放多张表。表由行和列组成:一行是一条记录,一列是一个字段。
例如,用 intl_sixin_data 表保存会话记录。一行保存一次输入和回复,user_query 是输入字段,answer 是回复字段:
| id | date_num | user_id | session_id | user_query | answer |
|---|---|---|---|---|---|
| 1 | 20261020 | 1001 | session_a | 你好 | 你好 |
| 2 | 20261020 | 1001 | session_a | 测试回复 | NULL |
| 3 | 20261020 | 1002 | session_b | 测试连接 | 连接正常 |
字段的名称、类型和约束合起来叫表结构。表结构规定能存什么数据,表中的记录则是实际存进去的内容。
1.2 创建数据库和表
CREATE DATABASE 新建数据库,后面跟库名。下面创建名为 sql_practice 的练习库,utf8mb4 是支持中文和表情等字符的字符集:
| |
创建后,用 USE sql_practice; 为当前连接选择它;Python 连接也可以通过 database="sql_practice" 指定库。
使用连接池时,在创建池时指定库名,让池内所有连接使用同一个库。Python 的连接与执行方式见第 5 节。
CREATE TABLE 用来创建表。 括号内每一项定义一个字段,基本顺序是“字段名、数据类型、附加规则”。下面是支持本文示例的精简结构,只需在练习库创建一次:
| |
相比前面的数据表,这里还定义了 user_request_id,用于保存请求编号;create_time 用于保存插入时间。
字段类型决定能存什么值:
| 类型 | 用途 | 本文中的字段 |
|---|---|---|
INT | 整数 | date_num |
BIGINT | 整数,范围比 INT 更大 | id、user_id |
VARCHAR(255) | 最多 255 个字符的字符串 | session_id、user_request_id |
TEXT、LONGTEXT | 长文本 | user_query、answer |
DATETIME | 日期和时间 | create_time |
再看字段后面的规则:
| 写法 | 含义 |
|---|---|
NOT NULL | 该字段不能存空值 NULL |
AUTO_INCREMENT | 插入时可以不填编号,由数据库生成 |
DEFAULT CURRENT_TIMESTAMP | 没有提供时间时,使用数据库当前时间 |
PRIMARY KEY (id, date_num) | 将这两个字段的组合作为主键,用它唯一标识一行 |
最后的 ENGINE=InnoDB 指定使用 InnoDB 存储引擎,后文的事务示例以它为基础。这里的主键包含两列,因此按主键定位记录时同时使用 id 和 date_num。
1.3 查看表结构
面对一张已有的表,通常要先确认它有哪些字段、能存什么类型的数据。MySQL 提供 DESCRIBE 表名; 来查看这些信息。
例如,查看上面这张表:
| |
返回结果中,每一行介绍一个字段。常见结果列的含义如下:
| 结果列 | 看什么 |
|---|---|
Field | 字段名,例如 id |
Type | 字段类型,例如 bigint |
Null | 是否允许空值,NO 表示不允许 |
Key | 索引标记,PRI 表示属于主键 |
Default | 默认值,例如 create_time 默认取当前时间 |
Extra | 附加信息,例如 auto_increment 表示自动生成编号 |
这里看到的是字段定义;要查看“你好”“测试回复”这样的记录内容,需要使用下一节的 SELECT。
1.4 其他查看命令
除了查看字段,还可以确认当前连接在哪个库、库里有哪些表:
| 要做什么 | SQL |
|---|---|
| 列出有权限看到的数据库 | SHOW DATABASES; |
| 查看当前选中的数据库 | SELECT DATABASE(); |
| 列出当前库中的表 | SHOW TABLES; |
| 查看已有表的完整建表语句 | SHOW CREATE TABLE intl_sixin_data; |
SQL 示例末尾的 ; 表示一条语句结束。在 Python 中执行时,每次传入一条 SQL 即可。
2. 查询
2.1 基本查询
SELECT 读取记录,WHERE 筛选记录。 下面查询用户 1001 在指定日期的记录,按编号倒序取前 20 条:
| |
| 写法 | 作用 |
|---|---|
SELECT 字段列表 | 选择返回的列;SELECT * 返回全部列 |
FROM 表名 | 指定从哪张表查询 |
WHERE 条件 | 过滤记录;不写时不按条件过滤 |
ORDER BY 字段 DESC | 降序排列;ASC 是升序 |
LIMIT 20 OFFSET 0 | 最多取 20 条、跳过 0 条;下一页用 OFFSET 20 |
对前面的三条数据,这条查询依次返回编号 2、1。需要写入示例数据时,使用 3.1 的
INSERT。
没有 ORDER BY 时,不保证返回顺序。 本例固定日期后按编号排序;跨日期可按 date_num DESC, id DESC 排序。
2.2 常用条件
| 写法 | 作用 |
|---|---|
user_id = 1001 | 等于指定值;其他比较有 <>、>、>=、<、<= |
条件 A AND 条件 B | 两个条件都满足;OR 表示至少满足一个 |
user_id IN (1001, 1002) | 匹配列表中的任一值 |
date_num BETWEEN 20261001 AND 20261020 | 匹配范围,包含两端 |
user_query LIKE '测试%' | 输入以“测试”开头,% 匹配任意长度的字符序列 |
answer IS NULL / IS NOT NULL | 判断有没有值,不能用 = NULL |
answer = '' | 匹配空字符串,与 NULL 不同 |
SQL 字符串用单引号。按时间查整天可写 create_time >= '2026-10-20' AND create_time < '2026-10-21',即包含当天零点、不包含次日零点。
create_time 与日期编号 date_num 独立保存,填写一个不会自动改变另一个。
2.3 去重、统计和关联
| 命令或函数 | 作用 |
|---|---|
DISTINCT | 去除重复结果,例如 SELECT DISTINCT user_id FROM intl_sixin_data; |
GROUP BY user_id | 按用户编号分组 |
COUNT(*) / COUNT(answer) | 统计行数 / 只统计 answer 不为 NULL 的行 |
SUM、AVG、MIN、MAX | 求和、平均值、最小值、最大值 |
HAVING 条件 | 筛选分组后的结果 |
AS 别名 | 给结果列或表起别名 |
例如,统计当天每个用户的记录数,只保留至少两条的用户:
| |
WHERE 先筛记录,HAVING 再筛分组。 示例结果为用户 1001、record_count = 2;record_count 是计数列的别名。
JOIN 用于关联两侧数据,ON 指定匹配条件:
INNER JOIN:只保留匹配成功的行。LEFT JOIN:保留左侧全部记录;右侧匹配不到的列填NULL。
3. 新增、修改和删除
3.1 新增记录
INSERT INTO 新增记录,字段与 VALUES 中的值按位置对应。 多组值用逗号分隔;下面写入前面的三条示例数据:
| |
id 自动生成,create_time 默认取当前时间。新建空表的编号从 1 开始;已有数据时,以实际编号为准。
3.2 修改和删除记录
UPDATE 修改记录,SET 指定新值。 下面修改指定记录的回复:
| |
DELETE FROM 删除满足条件的记录:
| |
两者不写 WHERE 都会作用于整张表。 执行前可用相同条件的 SELECT 确认范围。
| 命令 | 删除什么 | InnoDB 中能否靠普通事务回滚 |
|---|---|---|
DELETE FROM 表名 WHERE 条件 | 满足条件的记录 | 尚未提交时可以 |
TRUNCATE TABLE 表名 | 全部记录,重置自增计数,保留表定义 | 不能 |
DROP TABLE 表名 | 表和全部数据 | 不能 |
DROP DATABASE 库名 | 整个数据库及其中的表 | 不能 |
3.3 修改字段和索引
ALTER TABLE 修改表结构;索引是帮助查找数据的辅助结构。 下表是独立的命令示例:
| 用途 | SQL |
|---|---|
| 增加字段 | ALTER TABLE intl_sixin_data ADD COLUMN remark VARCHAR(200); |
| 删除字段 | ALTER TABLE intl_sixin_data DROP COLUMN remark; |
| 创建普通索引 | CREATE INDEX idx_user_id ON intl_sixin_data (user_id); |
| 查看索引 | SHOW INDEX FROM intl_sixin_data; |
| 删除索引 | DROP INDEX idx_user_id ON intl_sixin_data; |
普通索引允许重复值,唯一索引限制重复值。 一个用户可以有多条记录,因此 user_id 适合普通索引。
索引需要存储空间,也会增加写入时的维护工作;数据库会根据查询决定是否使用它。
4. 提交和事务
事务让多条修改一起生效,或在提交前一起撤销。 例如,一并修改两条测试记录的回复:
| |
这些语句需要在同一条连接上依次执行。START TRANSACTION 开始事务,COMMIT 提交;提交前改用 ROLLBACK 可以撤销修改。两个编号均为演示值。
在 Python 中,对应的是 conn.begin()、conn.commit() 和 conn.rollback();异步连接调用时加 await。配套客户端已经封装了这段过程,见 6.3。
autocommit 控制独立语句是否自动提交:
| 配置 | 如何理解 |
|---|---|
autocommit=True | 没有显式事务时,每条写语句自动提交 |
autocommit=False | 需要调用 commit() 或 rollback() 结束事务 |
PyMySQL 和 aiomysql 默认关闭自动提交;本文的连接池脚本主动开启它,但写入方法会显式开启事务,再提交或回滚。
关闭自动提交后,查询 InnoDB 表也可能开启事务。查询结束后同样应提交或回滚,避免后面的操作无意中继续沿用这次事务。
已经提交的修改不能再用 ROLLBACK 撤销。 ALTER TABLE、TRUNCATE TABLE 等操作会隐式提交,也不能套用普通数据修改的回滚方式。
5. pymysql 与 aiomysql
5.1 两者的区别
连接是与 MySQL 的一次会话;游标负责执行 SQL、读取结果。创建游标不会另外建立数据库连接。
| 比较项 | PyMySQL | aiomysql |
|---|---|---|
| 调用方式 | 普通函数调用 | 在协程中 await 数据库操作 |
| 等待响应时 | 当前线程等待 | 当前协程暂停,事件循环可推进其他任务 |
| 适用代码 | 同步脚本、同步服务 | 已使用 asyncio 的服务和并发 I/O 任务 |
| 连接池 | 本文使用额外的 DBUtils | 库内提供 create_pool() |
使用时记住这三个区别:
- 异步重叠的是等待时间。 它不会让单条 SQL 在数据库里执行得更快。
- 连续两次
await仍按顺序执行。 并发需要调度多个任务,每个任务各自借用连接。 async def不会改造同步调用。 在里面直接调用 PyMySQL,仍会阻塞事件循环所在的线程。
5.2 安装与配置
使用 Python 连接 MySQL,需要数据库地址、端口、账号、密码和库名。两个连接示例分别提供完整客户端,使用哪一版,就安装对应依赖:
| 版本 | 安装命令 |
|---|---|
| PyMySQL | python -m pip install "PyMySQL[rsa]" DBUtils |
| aiomysql | python -m pip install aiomysql "PyMySQL[rsa]" |
脚本适用于 Python 3.11+,从环境变量读取以下配置;可以在终端或 IDE 的运行配置中设置。
| 环境变量 | 填什么 |
|---|---|
MYSQL_HOST | 测试数据库地址 |
MYSQL_PORT | 端口,不填时使用 3306 |
MYSQL_USER / MYSQL_PASSWORD | 数据库账号 / 密码 |
MYSQL_DATABASE | 要连接的库名,例如前面创建的 sql_practice |
必填配置缺失时脚本直接报错。文件名使用连接示例中的 pymysql_client.py 或 aiomysql_client.py,不要命名为 pymysql.py、aiomysql.py,否则会遮蔽安装的库。
5.3 pymysql
连接示例提供的 MySQLClient 是对 PyMySQL 的封装。用数据库配置创建实例后,下文将这个对象记为 client,通过它执行 SQL。
这个客户端内部使用 DBUtils 连接池,可以反复调用,程序结束时才执行 client.close()。
| 方法 | 用途 |
|---|---|
query(sql, params) | 查询,返回字典列表;没有记录时返回 [] |
execute(sql, params) | 执行一条写入并提交,返回影响行数 |
executemany(sql, params) | 用多组参数批量写入,在一次事务中提交 |
transaction() | 让多条操作共用一个事务,见 6.3 |
SELECT、DESCRIBE 等有返回结果的语句,以字符串传给 query();建库、建表和写入用 execute()。多条 SQL 要一起提交时,使用 6.3 的 transaction()。
初次建库时,可以先传 database=None 创建客户端,执行 CREATE DATABASE;账号需有建库权限。完成后关闭这个客户端,再用新库名创建客户端并执行建表语句。
批量写入 date_num、user_id、user_query 时,参数形如 [(20261020, 1001, "测试输入一"), (20261020, 1001, "测试输入二")]。每个元组对应 SQL 中的一组占位符。
UPDATE 把字段改成原值时,影响行数可能为 0;不能仅凭 0 判断执行失败。大量查询结果应分页读取,避免一次全部载入内存。
5.4 aiomysql
异步版对应的封装是 AsyncMySQLClient,用 await AsyncMySQLClient.create(...) 创建。查询、写入和关闭也需要 await,方法用途及返回值与同步版相同。
- 独立脚本由
asyncio.run(main())启动;已有事件循环的应用直接在协程中调用。 - 多条查询需要并发时,用
TaskGroup等方式调度;并发数超过池上限时,后续任务会排队。 - 所有任务结束后,再执行
await client.close();同一个池始终在同一事件循环中使用。
pandas.read_sql() 是同步接口。需要 DataFrame 时,先异步取回记录,再用 pandas.DataFrame(rows) 构造。
5.5 SQL 与参数分开传
按连接示例初始化 client 后,把 SQL 和参数分别传入。下面查询会话 session_a 的记录:
| |
这是同步版调用片段,异步版在 client.query(...) 前加 await。
%s是参数占位符,整数和字符串都能用,不要再给它加引号。("session_a",)是单元素元组,末尾的逗号不能省。- 驱动负责编码和转义参数,不要用 f-string 拼接输入。
占位符只用于值,不能替换表名、列名或 ASC / DESC。查询返回的字典、数字、时间也应保留原始类型,需要展示时再格式化。
两次独立查询 A、B,比较等待数据库期间的执行顺序。
当前线程等待 A 返回后,才开始查询 B。
任务 A 在 I/O 处等待时让出执行机会,事件循环可以推进任务 B。
并发前提:异步侧已调度两个任务,分别使用一条连接。连续两次 await 仍可顺序执行;PyMySQL 也可以借助多线程并发调用。
6. 连接池怎么用
6.1 建池、借用和归还
连接池保留已经建立的数据库连接,供后续 SQL 复用。 它保存的是连接,不缓存查询结果。
配套脚本将池上限设为 5,并在池满时等待。已有 5 个操作占用了连接时,第 6 个操作就要等待;某个操作归还连接后,它才能继续借用。
一个池通常跟随应用或工作进程存活:
- 启动时建池,设置数据库配置和连接数上限。
- 执行 SQL 时借用连接,操作结束后归还。
- 退出时关闭池,先等待所有操作结束。
| 操作 | PyMySQL + DBUtils | aiomysql |
|---|---|---|
| 创建池 | PooledDB(creator=pymysql, ...) | await aiomysql.create_pool(...) |
| 借用连接 | pool.connection() | async with pool.acquire() as conn |
| 归还连接 | 池代理连接的 conn.close() | 离开 acquire() 上下文 |
| 关闭池 | pool.close() | pool.close(),再 await pool.wait_closed() |
归还连接不等于提交事务。 应先提交或回滚,再归还;配套客户端会完成这段处理。
DBUtils 代理连接的 close() 通常是归还。aiomysql 的 conn.close() 则会关闭物理连接,正常归还应使用上表中的上下文。
不要每执行一条 SQL 就创建、关闭整个池,否则下一次操作仍要重新建连。
6.2 连接池参数
| 目的 | DBUtils PooledDB | aiomysql create_pool |
|---|---|---|
| 初始连接数 | mincached | minsize |
| 空闲连接上限 | maxcached | 无独立参数 |
| 总连接数上限 | maxconnections | maxsize |
| 池满时 | blocking=True 等待,False 报错 | 等待可用连接 |
| 处理失效连接 | ping 等检查机制 | pool_recycle 等回收机制 |
mincached 只控制初始空闲数。aiomysql 的 minsize 还用于获取连接时补足池规模,但不保证始终有这么多条空闲连接。
可以从较小的有限上限开始,结合排队时间和数据库负载调整。部分参数用 0 表示“不限量”,具体含义要查对应参数,不能直接理解成关闭池。
6.3 多条 SQL 共用一个事务
配套脚本中,每次 client.execute() 都会单独提交。连续调用两次,即使碰巧借到同一条连接,也仍然是两个事务。
第一条更新成功,第二条报错:分别调用
execute()时,第一条已经提交,无法随第二条一起撤销。 如果要求两条一起成功或一起失败,就把它们放进transaction()。
下面是同步客户端的事务调用片段,两个编号替换为测试记录的实际 id:
| |
这段代码的过程是:借一条连接 → 开始事务 → 执行两条更新 → 正常结束时提交 → 归还连接。
执行过程中发生普通异常,脚本先回滚,再把异常抛给调用方。异步版使用 async with client.transaction(),块内的 cursor.execute() 加 await。
事务内始终使用它提供的 cursor。 不要换成 client.execute(),后者会另借连接、另开事务;池只有一条连接时还会一直等不到可用连接。
异步任务被取消时,脚本会关闭当前连接,避免把未读完响应的连接交给下一个任务。写入或提交期间断线时,结果可能不确定,不应直接重试写入。
6.4 池大小和超时
池满时,先看连接被什么占用:并发请求增多、SQL 变慢、事务过长或连接未归还,都可能造成排队。
每个工作进程一个池,上限为 5;4 个进程最多使用 20 条池连接。部署 3 个相同实例,上限就是 60 条,还要给其他服务留余量。
不同超时对应不同阶段:
| 阶段 | 需要区分的设置 |
|---|---|
| 等待池连接 | blocking=True 只表示等待,不代表设好了等待时限 |
| 新建物理连接 | connect_timeout |
| 等待数据收发 | PyMySQL 的 read_timeout、write_timeout |
| 限制异步调用总时间 | asyncio.timeout(...),可覆盖借连接和查询的等待 |
| 回收空闲连接 | pool_recycle,不是 SQL 执行超时 |
查询变慢时,可以在 SELECT 前加 EXPLAIN,查看 MySQL 计划如何查找记录、是否使用索引:
| |
EXPLAIN 展示优化器选择的计划,不是实际耗时。 数据库已繁忙时,继续增加连接数还可能加重排队。
接口细节可查阅 PyMySQL 游标、 aiomysql 连接池 和 DBUtils 使用指南。
评论
使用 GitHub 登录后参与讨论,评论将公开保存在 GitHub。
站长管理删除会同步到 GitHub,无法撤销。
阅读到这里时加载评论。