MySQL 学习笔记
MySQL 学习笔记
1. MySQL 是什么
MySQL 是一个关系型数据库,用来存储:
- 用户信息
- 订单数据
- 文档元数据
- 任务记录
- 聊天记录
- 各种需要长期保存的结构化数据
先抓住它和 Redis 的分工差异:
MySQL 更适合存“长期、结构化、可查询的数据”,Redis 更适合存“高频、临时、快速访问的数据”。
2. 为什么项目里经常会有 MySQL
因为很多业务数据都需要:
- 长期保存
- 有明确字段结构
- 支持条件查询
- 支持关联查询
- 保证数据一致性
例如:
- 一个用户有哪些会话
- 一个文档属于哪个用户
- 一条任务记录当前是什么状态
- 某段时间创建了多少条消息
这些都很适合用 MySQL 来存。
3. 在 AI 项目里 MySQL 常见用法
在 AI 模型开发场景里,MySQL 常见用途有:
- 存用户表
- 存会话表
- 存消息表
- 存文档表
- 存任务记录表
- 存模型调用日志
- 存权限和配置
放回整套系统里看,各组件的分工大致是:
- FastAPI / Drogon:接请求
- Redis:做缓存、状态层、中间层
- Celery:处理后台任务
- MySQL:存长期结构化数据
4. MySQL 和 Redis 的区别
| 维度 | MySQL | Redis |
|---|---|---|
| 数据模型 | 关系型表结构 | 键值型 |
| 持久化 | 强,适合长期存储 | 可持久化,但常用于短期数据 |
| 查询能力 | 强,支持 SQL | 主要按 key 操作 |
| 适合场景 | 用户、订单、文档、消息 | 缓存、计数器、会话、队列 |
| 事务 | 支持 | 支持有限的原子操作,不是传统关系事务 |
简单理解:
- 用户资料、文档记录、消息历史更适合 MySQL
- 缓存结果、限流计数、短期任务状态更适合 Redis
5. 安装和启动
如果本地已经装好了 MySQL,可以直接启动服务。
如果只是临时学习,Docker 会更省事:
1 | docker run -d \ |
进入 MySQL:
1 | mysql -h 127.0.0.1 -P 3306 -u root -p |
查看数据库:
1 | SHOW DATABASES; |
选择数据库:
1 | USE demo; |
6. Python 连接 MySQL
Python 项目里很常见的是 pymysql。
安装:
1 | pip install pymysql |
最基础的连接方式:
1 | import pymysql |
几个常见参数:
host:数据库地址port:端口,MySQL 默认通常是3306user/password:用户名密码database:要连接的库charset="utf8mb4":推荐写上,避免中文和 emoji 编码问题autocommit=False:默认手动提交事务
7. 连接和游标 cursor
你原来笔记里提到“游标对象的具体作用是什么”,可以这样理解:
连接
conn负责连数据库,游标cursor负责执行 SQL 和取结果。
最常见写法:
1 | cursor = conn.cursor() |
更推荐:
1 | with conn.cursor() as cursor: |
cursor 主要负责:
- 执行 SQL
- 传递参数
- 获取查询结果
- 提供
lastrowid、rowcount等信息
常见方法:
cursor.execute():执行一条 SQLcursor.executemany():批量执行cursor.fetchone():取一条cursor.fetchmany(n):取前n条cursor.fetchall():取全部
如果你希望查询结果按字典返回,而不是元组,可以这样写:
1 | import pymysql.cursors |
这样查出来的数据会更接近:
1 | {"id": 1, "name": "tom"} |
8. 建表和常见字段类型
MySQL 最基础的 SQL 之一就是建表。
示例:
1 | CREATE TABLE users ( |
常见字段类型:
INT:整数BIGINT:更大的整数VARCHAR(n):变长字符串TEXT:长文本DATETIME:日期时间DATE:日期DECIMAL(10,2):精确小数BOOLEAN:布尔,底层通常可看成TINYINT
常见约束:
PRIMARY KEY:主键AUTO_INCREMENT:自增NOT NULL:不能为空UNIQUE:唯一DEFAULT:默认值FOREIGN KEY:外键
9. 表结构相关 SQL
这部分在实际开发里很常用。
9.1 创建表
1 | CREATE TABLE users ( |
9.2 如果表已存在就跳过
你前面提到的“表已存在怎么办”,对应的就是这个场景。
如果担心“已经有同名表,再创建会报错”,可以写:
1 | CREATE TABLE IF NOT EXISTS users ( |
这样当表已存在时,会直接跳过,不会因为同名表报错。
9.3 修改表
1 | ALTER TABLE users ADD COLUMN email VARCHAR(200); |
9.4 删除表
1 | DROP TABLE users; |
如果担心表不存在报错:
1 | DROP TABLE IF EXISTS users; |
9.5 查看建表语句
1 | SHOW CREATE TABLE users; |
这个命令非常实用,因为它能直接看到当前表的真实结构、索引、字符集等信息。
10. 插入数据 INSERT
最基础的插入写法:
1 | INSERT INTO users (username, email, age) |
在 Python 里更推荐参数化写法:
1 | with conn.cursor() as cursor: |
插入后拿自增主键:
1 | user_id = cursor.lastrowid |
11. 查询数据 SELECT
11.1 查全部字段
1 | SELECT * FROM users; |
11.2 只查部分字段
1 | SELECT id, username FROM users; |
11.3 带条件查询
1 | SELECT id, username |
11.4 排序
1 | SELECT id, username |
11.5 分页
1 | SELECT id, username |
也常写成:
1 | LIMIT 0, 10; |
12. 更新数据 UPDATE
1 | UPDATE users |
一定要注意:
UPDATE不写WHERE,会更新整张表。
Python 示例:
1 | with conn.cursor() as cursor: |
13. 删除数据 DELETE
1 | DELETE FROM users |
同样要注意:
DELETE不写WHERE,会删整张表的数据。
如果你是想删整张表但保留结构,也可以:
1 | TRUNCATE TABLE users; |
14. 条件查询、排序、分组
这些是 SQL 里非常高频的能力。
14.1 WHERE
1 | SELECT * FROM users WHERE age >= 18; |
14.2 ORDER BY
1 | SELECT * FROM users ORDER BY id DESC; |
14.3 LIMIT
1 | SELECT * FROM users LIMIT 20; |
14.4 GROUP BY
1 | SELECT status, COUNT(*) AS total |
14.5 HAVING
HAVING 通常配合分组结果使用:
1 | SELECT status, COUNT(*) AS total |
15. JOIN 关联查询
这是关系型数据库最重要的优势之一。
假设:
users表存用户documents表存文档
查询“每篇文档属于哪个用户”:
1 | SELECT d.id, d.title, u.username |
常见 JOIN:
JOIN/INNER JOIN:两边都能匹配到才返回LEFT JOIN:左表全部保留,右表匹配不到则为NULL
例如:
1 | SELECT u.id, u.username, d.title |
16. 索引 INDEX
索引的作用可以粗暴理解成:
让查询更快,但会增加写入成本和占用空间。
例如给用户名加索引:
1 | CREATE INDEX idx_users_username ON users(username); |
适合加索引的字段通常有:
- 经常出现在
WHERE里的字段 - 经常用于排序的字段
- 经常用于 JOIN 的字段
- 唯一约束字段
常见误区:
- 不是索引越多越好
- 很小的表不一定需要索引
- 更新频繁的字段加太多索引会拖慢写入
17. 外键 FOREIGN KEY
外键用于表达表和表之间的引用关系。
例如:
1 | CREATE TABLE documents ( |
它的作用是:
- 保证引用关系合法
- 避免出现“文档指向一个不存在用户”的情况
但很多实际项目里,也会选择在业务层保证关系,而不是强依赖数据库外键。
18. 事务 commit 和 rollback
你原来的笔记里提到“事务提交”,这部分非常重要。
事务适合用于:
- 多条 SQL 必须一起成功
- 中间某一步失败时要整体撤回
示例:
1 | try: |
常见规则:
- 成功后
conn.commit() - 失败时
conn.rollback()
如果连接时配置:
1 | autocommit=True |
就表示每条 SQL 默认自动提交。
学习阶段可以用,但项目里要清楚:
一旦涉及多步写操作,通常还是要认真控制事务边界。
19. 查询结果获取
这是你原来笔记里已经提到的内容,这里整理成更清晰的版本。
19.1 fetchone()
只取一条:
1 | row = cursor.fetchone() |
19.2 fetchmany(n)
取前 n 条:
1 | rows = cursor.fetchmany(10) |
19.3 fetchall()
取全部:
1 | rows = cursor.fetchall() |
19.4 lastrowid
拿最后插入的自增 ID:
1 | new_id = cursor.lastrowid |
19.5 rowcount
看本次 SQL 影响了多少行:
1 | count = cursor.rowcount |
20. execute() 和 executemany()
20.1 execute()
执行一条 SQL:
1 | cursor.execute( |
20.2 executemany()
批量执行:
1 | data = [ |
适合:
- 批量插入
- 批量更新同类数据
21. 一段比较完整的 PyMySQL 示例
1 | import pymysql |
22. FastAPI 里怎么用 MySQL
在 FastAPI 项目里,MySQL 常见用途有:
- 存用户
- 存任务记录
- 存会话和消息
- 存文档元数据
最简单的同步示例:
1 | import pymysql |
不过实际项目里通常会再往前走一步:
- 使用连接池
- 抽 Repository / Service 层
- 使用 SQLAlchemy 等更完整的数据库层
23. AI 项目里比较常见的表设计
如果你在做文档问答、RAG、聊天系统,常见会有这些表:
userssessionsmessagesdocumentsdocument_chunkstasks
例如:
documents:存文档标题、路径、状态、所属用户tasks:存异步任务 ID、状态、错误信息messages:存对话消息和角色
也就是说:
MySQL 更适合存“事实记录”和“长期业务数据”,不适合拿来做高频缓存层。
24. 常见坑
24.1 用字符串拼 SQL
错误示例:
1 | sql = f"SELECT * FROM users WHERE username = '{username}'" |
这会有 SQL 注入风险。
正确做法:
1 | cursor.execute( |
24.2 忘记提交事务
执行了 INSERT / UPDATE / DELETE 之后,如果没有 commit(),数据可能不会真正写入。
24.3 UPDATE / DELETE 不带 WHERE
这会影响整张表。
24.4 滥用 SELECT *
开发初期问题不大,但项目里更推荐明确字段,避免:
- 取了不需要的数据
- 表结构变动后影响接口
- 传输和解析成本增加
24.5 不建索引或乱建索引
都不对。
应该根据查询模式来设计索引。
24.6 字符集没统一
推荐统一使用:
- 数据库字符集:
utf8mb4 - Python 连接:
charset="utf8mb4"
24.7 把 MySQL 当缓存用
这会让数据库承担不适合它的高频压力。
25. 一套比较实用的学习顺序
建议这样学:
- 先学会创建库、建表、插入、查询、更新、删除
- 学会
WHERE、ORDER BY、LIMIT - 学会
GROUP BY和JOIN - 学会索引和事务
- 学会用 PyMySQL 连接和执行参数化 SQL
- 最后再接入 FastAPI、Celery、Redis
26. 总结
MySQL 的核心价值在于:
- 存长期结构化数据
- 支持 SQL 查询
- 支持关联查询
- 支持事务
- 适合承载业务主数据
对于 AI 项目,可以把它理解成:
- 用户数据存储层
- 会话和消息持久化层
- 文档元数据层
- 任务记录层
如果把你前面几份笔记连起来看,可以把它们理解成一条链:
- FastAPI / Drogon:接请求
- Redis:缓存 / 状态 / 中间层
- Celery:后台异步任务
- MySQL:长期持久化数据
27. 参考
- MySQL Official Docs: https://dev.mysql.com/doc/
- PyMySQL Docs: https://pymysql.readthedocs.io/
- SQL Syntax Reference: https://dev.mysql.com/doc/refman/8.0/en/sql-statements.html