with conn.cursor() as cursor: sql = """ INSERT INTO users (username, email, age) VALUES (%s, %s, %s) """ cursor.execute(sql, ("tom", "tom@example.com", 18)) conn.commit()
插入后拿自增主键:
1
user_id = cursor.lastrowid
11. 查询数据 SELECT
11.1 查全部字段
1
SELECT*FROM users;
11.2 只查部分字段
1
SELECT id, username FROM users;
11.3 带条件查询
1 2 3
SELECT id, username FROM users WHERE age >=18;
11.4 排序
1 2 3
SELECT id, username FROM users ORDERBY created_at DESC;
11.5 分页
1 2 3 4
SELECT id, username FROM users ORDERBY id DESC LIMIT 10OFFSET0;
也常写成:
1
LIMIT 0, 10;
12. 更新数据 UPDATE
1 2 3
UPDATE users SET email ='new@example.com' WHERE id =1;
一定要注意:
UPDATE 不写 WHERE,会更新整张表。
Python 示例:
1 2 3 4 5
with conn.cursor() as cursor: sql = "UPDATE users SET email = %s WHERE id = %s" affected_rows = cursor.execute(sql, ("new@example.com", 1)) conn.commit() print(affected_rows)
13. 删除数据 DELETE
1 2
DELETEFROM users WHERE id =1;
同样要注意:
DELETE 不写 WHERE,会删整张表的数据。
如果你是想删整张表但保留结构,也可以:
1
TRUNCATETABLE users;
14. 条件查询、排序、分组
这些是 SQL 里非常高频的能力。
14.1 WHERE
1
SELECT*FROM users WHERE age >=18;
14.2 ORDER BY
1
SELECT*FROM users ORDERBY id DESC;
14.3 LIMIT
1
SELECT*FROM users LIMIT 20;
14.4 GROUP BY
1 2 3
SELECT status, COUNT(*) AS total FROM tasks GROUPBY status;
14.5 HAVING
HAVING 通常配合分组结果使用:
1 2 3 4
SELECT status, COUNT(*) AS total FROM tasks GROUPBY status HAVING total >10;
15. JOIN 关联查询
这是关系型数据库最重要的优势之一。
假设:
users 表存用户
documents 表存文档
查询“每篇文档属于哪个用户”:
1 2 3
SELECT d.id, d.title, u.username FROM documents d JOIN users u ON d.user_id = u.id;
常见 JOIN:
JOIN / INNER JOIN:两边都能匹配到才返回
LEFT JOIN:左表全部保留,右表匹配不到则为 NULL
例如:
1 2 3
SELECT u.id, u.username, d.title FROM users u LEFTJOIN documents d ON u.id = d.user_id;
16. 索引 INDEX
索引的作用可以粗暴理解成:
让查询更快,但会增加写入成本和占用空间。
例如给用户名加索引:
1
CREATE INDEX idx_users_username ON users(username);
适合加索引的字段通常有:
经常出现在 WHERE 里的字段
经常用于排序的字段
经常用于 JOIN 的字段
唯一约束字段
常见误区:
不是索引越多越好
很小的表不一定需要索引
更新频繁的字段加太多索引会拖慢写入
17. 外键 FOREIGN KEY
外键用于表达表和表之间的引用关系。
例如:
1 2 3 4 5 6 7
CREATETABLE documents ( id BIGINTPRIMARY KEY AUTO_INCREMENT, user_id BIGINTNOTNULL, title VARCHAR(200) NOTNULL, CONSTRAINT fk_documents_user FOREIGN KEY (user_id) REFERENCES users(id) );
它的作用是:
保证引用关系合法
避免出现“文档指向一个不存在用户”的情况
但很多实际项目里,也会选择在业务层保证关系,而不是强依赖数据库外键。
18. 事务 commit 和 rollback
你原来的笔记里提到“事务提交”,这部分非常重要。
事务适合用于:
多条 SQL 必须一起成功
中间某一步失败时要整体撤回
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
try: with conn.cursor() as cursor: cursor.execute( "INSERT INTO accounts (name, balance) VALUES (%s, %s)", ("alice", 1000), ) cursor.execute( "INSERT INTO accounts (name, balance) VALUES (%s, %s)", ("bob", 500), ) conn.commit() except Exception: conn.rollback() raise
try: with conn.cursor() as cursor: cursor.execute( """ CREATE TABLE IF NOT EXISTS users ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(100) NOT NULL UNIQUE, age INT NOT NULL DEFAULT 0, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ) """ )
data = {"id": article_id, "title": "Redis Intro"} r.set(cache_key, json.dumps(data), ex=300) return data
21.3 缓存设计里要注意什么
一定要设计 TTL
不要无脑缓存超大对象
key 命名要稳定
更新数据库时要考虑缓存同步
22. 缓存穿透、击穿、雪崩
这是 Redis 学习里很常见的三个词。
22.1 缓存穿透
请求的数据本来就不存在:
Redis 没有
数据库也没有
每次都打到后端
常见处理:
参数校验
对空结果也做短期缓存
布隆过滤器
22.2 缓存击穿
某个热点 key 失效瞬间,大量请求同时打到后端。
常见处理:
热点 key 不要同时过期
加锁重建缓存
提前刷新
22.3 缓存雪崩
大量 key 在同一时间过期,导致后端压力暴涨。
常见处理:
TTL 加随机值
分批过期
多级缓存
23. 限流
Redis 很适合做接口限流,因为:
INCR 是原子操作
配合 TTL 很方便
23.1 最简单的固定窗口限流
思路:
某个用户请求一次就 INCR
第一次请求时设置过期时间
超过阈值就拒绝
Python 示例:
1 2 3 4 5 6 7 8 9 10
import redis
r = redis.Redis(host="127.0.0.1", port=6379, db=0, decode_responses=True)
defallow_request(user_id: str, limit: int = 10, window: int = 60) -> bool: key = f"rate_limit:{user_id}" current = r.incr(key) if current == 1: r.expire(key, window) return current <= limit
from langchain_community.document_loaders import TextLoader from langchain_community.vectorstores import FAISS from langchain_core.output_parsers import StrOutputParser from langchain_core.prompts import ChatPromptTemplate from langchain_core.runnables import RunnableLambda, RunnablePassthrough from langchain_huggingface import HuggingFaceEmbeddings from langchain_openai import ChatOpenAI from langchain_text_splitters import RecursiveCharacterTextSplitter