跳至内容

MySQL 常用命令

常用 SQL、Python 调用与连接池。

  • MySQL
  • aiomysql
  • SQL
  • Python
  • 连接池

MySQL 是数据库管理系统,SQL 是操作数据库的语言。 在 Python 中,PyMySQL、aiomysql 负责建立连接、发送 SQL,并把执行结果交回程序。

MySQL 连接示例:

实际运行示例需要已有的 MySQL 服务和可登录账号;安装 Python 驱动不会安装 MySQL 服务。脚本配置与调用方式见第 5 节。

1. 数据库、表和字段

1.1 数据如何组织

一个 MySQL 服务可以管理多个数据库,每个数据库里可以放多张表。表由行和列组成:一行是一条记录,一列是一个字段。

例如,用 intl_sixin_data 表保存会话记录。一行保存一次输入和回复,user_query 是输入字段,answer 是回复字段:

iddate_numuser_idsession_iduser_queryanswer
1202610201001session_a你好你好
2202610201001session_a测试回复NULL
3202610201002session_b测试连接连接正常

字段的名称、类型和约束合起来叫表结构。表结构规定能存什么数据,表中的记录则是实际存进去的内容。

1.2 创建数据库和表

CREATE DATABASE 新建数据库,后面跟库名。下面创建名为 sql_practice 的练习库,utf8mb4 是支持中文和表情等字符的字符集:

代码块 Sql
1
CREATE DATABASE sql_practice CHARACTER SET utf8mb4;

创建后,用 USE sql_practice; 为当前连接选择它;Python 连接也可以通过 database="sql_practice" 指定库。

使用连接池时,在创建池时指定库名,让池内所有连接使用同一个库。Python 的连接与执行方式见第 5 节。

CREATE TABLE 用来创建表。 括号内每一项定义一个字段,基本顺序是“字段名、数据类型、附加规则”。下面是支持本文示例的精简结构,只需在练习库创建一次:

代码块 Sql
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
CREATE TABLE intl_sixin_data (
    id BIGINT NOT NULL AUTO_INCREMENT,
    date_num INT NOT NULL,
    user_id BIGINT NOT NULL,
    session_id VARCHAR(255),
    user_request_id VARCHAR(255),
    user_query TEXT,
    answer LONGTEXT,
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id, date_num)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

相比前面的数据表,这里还定义了 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 表名; 来查看这些信息。

例如,查看上面这张表:

代码块 Sql
1
DESCRIBE intl_sixin_data;

返回结果中,每一行介绍一个字段。常见结果列的含义如下:

结果列看什么
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 条:

代码块 Sql
1
2
3
4
5
SELECT id, user_id, user_query, answer
FROM intl_sixin_data
WHERE date_num = 20261020 AND user_id = 1001
ORDER BY id DESC
LIMIT 20 OFFSET 0;
写法作用
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 别名给结果列或表起别名

例如,统计当天每个用户的记录数,只保留至少两条的用户:

代码块 Sql
1
2
3
4
5
SELECT user_id, COUNT(*) AS record_count
FROM intl_sixin_data
WHERE date_num = 20261020
GROUP BY user_id
HAVING COUNT(*) >= 2;

WHERE 先筛记录,HAVING 再筛分组。 示例结果为用户 1001、record_count = 2;record_count 是计数列的别名。

JOIN 用于关联两侧数据,ON 指定匹配条件:

  • INNER JOIN:只保留匹配成功的行。
  • LEFT JOIN:保留左侧全部记录;右侧匹配不到的列填 NULL。

3. 新增、修改和删除

3.1 新增记录

INSERT INTO 新增记录,字段与 VALUES 中的值按位置对应。 多组值用逗号分隔;下面写入前面的三条示例数据:

代码块 Sql
1
2
3
4
INSERT INTO intl_sixin_data (date_num, user_id, session_id, user_query, answer)
VALUES (20261020, 1001, 'session_a', '你好', '你好'),
       (20261020, 1001, 'session_a', '测试回复', NULL),
       (20261020, 1002, 'session_b', '测试连接', '连接正常');

id 自动生成,create_time 默认取当前时间。新建空表的编号从 1 开始;已有数据时,以实际编号为准。

3.2 修改和删除记录

UPDATE 修改记录,SET 指定新值。 下面修改指定记录的回复:

代码块 Sql
1
UPDATE intl_sixin_data SET answer = '已补充的回复' WHERE id = 2 AND date_num = 20261020;

DELETE FROM 删除满足条件的记录:

代码块 Sql
1
DELETE FROM intl_sixin_data WHERE id = 3 AND date_num = 20261020;

两者不写 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. 提交和事务

事务让多条修改一起生效,或在提交前一起撤销。 例如,一并修改两条测试记录的回复:

代码块 Sql
1
2
3
4
START TRANSACTION;
UPDATE intl_sixin_data SET answer = '已校正的回复' WHERE id = 1 AND date_num = 20261020;
UPDATE intl_sixin_data SET answer = '已校正的回复' WHERE id = 2 AND date_num = 20261020;
COMMIT;

这些语句需要在同一条连接上依次执行。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、读取结果。创建游标不会另外建立数据库连接。

比较项PyMySQLaiomysql
调用方式普通函数调用在协程中 await 数据库操作
等待响应时当前线程等待当前协程暂停,事件循环可推进其他任务
适用代码同步脚本、同步服务已使用 asyncio 的服务和并发 I/O 任务
连接池本文使用额外的 DBUtils库内提供 create_pool()

使用时记住这三个区别:

  • 异步重叠的是等待时间。 它不会让单条 SQL 在数据库里执行得更快。
  • 连续两次 await 仍按顺序执行。 并发需要调度多个任务,每个任务各自借用连接。
  • async def 不会改造同步调用。 在里面直接调用 PyMySQL,仍会阻塞事件循环所在的线程。

5.2 安装与配置

使用 Python 连接 MySQL,需要数据库地址、端口、账号、密码和库名。两个连接示例分别提供完整客户端,使用哪一版,就安装对应依赖:

版本安装命令
PyMySQLpython -m pip install "PyMySQL[rsa]" DBUtils
aiomysqlpython -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

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

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 的记录:

代码块 Python
1
2
3
4
rows = client.query(
    "SELECT id, date_num, user_query FROM intl_sixin_data WHERE session_id = %s LIMIT 20",
    ("session_a",),
)

这是同步版调用片段,异步版在 client.query(...) 前加 await。

  • %s 是参数占位符,整数和字符串都能用,不要再给它加引号。
  • ("session_a",) 是单元素元组,末尾的逗号不能省。
  • 驱动负责编码和转义参数,不要用 f-string 拼接输入。

占位符只用于值,不能替换表名、列名或 ASC / DESC。查询返回的字典、数字、时间也应保留原始类型,需要展示时再格式化。

同步等待与异步调度

两次独立查询 A、B,比较等待数据库期间的执行顺序。

发送请求 / 处理结果 等待数据库响应
PyMySQL单线程顺序调用

当前线程等待 A 返回后,才开始查询 B。

aiomysql同一事件循环 · 两个任务

任务 A 在 I/O 处等待时让出执行机会,事件循环可以推进任务 B。

并发前提:异步侧已调度两个任务,分别使用一条连接。连续两次 await 仍可顺序执行;PyMySQL 也可以借助多线程并发调用。

6. 连接池怎么用

6.1 建池、借用和归还

连接池保留已经建立的数据库连接,供后续 SQL 复用。 它保存的是连接,不缓存查询结果。

配套脚本将池上限设为 5,并在池满时等待。已有 5 个操作占用了连接时,第 6 个操作就要等待;某个操作归还连接后,它才能继续借用。

一个池通常跟随应用或工作进程存活:

  1. 启动时建池,设置数据库配置和连接数上限。
  2. 执行 SQL 时借用连接,操作结束后归还。
  3. 退出时关闭池,先等待所有操作结束。
操作PyMySQL + DBUtilsaiomysql
创建池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 PooledDBaiomysql create_pool
初始连接数mincachedminsize
空闲连接上限maxcached无独立参数
总连接数上限maxconnectionsmaxsize
池满时blocking=True 等待,False 报错等待可用连接
处理失效连接ping 等检查机制pool_recycle 等回收机制

mincached 只控制初始空闲数。aiomysql 的 minsize 还用于获取连接时补足池规模,但不保证始终有这么多条空闲连接。

可以从较小的有限上限开始,结合排队时间和数据库负载调整。部分参数用 0 表示“不限量”,具体含义要查对应参数,不能直接理解成关闭池。

6.3 多条 SQL 共用一个事务

配套脚本中,每次 client.execute() 都会单独提交。连续调用两次,即使碰巧借到同一条连接,也仍然是两个事务。

第一条更新成功,第二条报错:分别调用 execute() 时,第一条已经提交,无法随第二条一起撤销。 如果要求两条一起成功或一起失败,就把它们放进 transaction()。

下面是同步客户端的事务调用片段,两个编号替换为测试记录的实际 id:

代码块 Python
1
2
3
with client.transaction() as cursor:
    cursor.execute("UPDATE intl_sixin_data SET answer = %s WHERE id = %s AND date_num = %s", ("已校正的回复", 1, 20261020))
    cursor.execute("UPDATE intl_sixin_data SET answer = %s WHERE id = %s AND date_num = %s", ("已校正的回复", 2, 20261020))

这段代码的过程是:借一条连接 → 开始事务 → 执行两条更新 → 正常结束时提交 → 归还连接。

执行过程中发生普通异常,脚本先回滚,再把异常抛给调用方。异步版使用 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 计划如何查找记录、是否使用索引:

代码块 Sql
1
EXPLAIN SELECT id, date_num, session_id FROM intl_sixin_data WHERE date_num = 20261020 ORDER BY id DESC;

EXPLAIN 展示优化器选择的计划,不是实际耗时。 数据库已繁忙时,继续增加连接数还可能加重排队。

接口细节可查阅 PyMySQL 游标、 aiomysql 连接池 和 DBUtils 使用指南。

评论

使用 GitHub 登录后参与讨论,评论将公开保存在 GitHub。

阅读到这里时加载评论。