mysql where字段未加索引会导致全表扫描(type=all),应为where、join、order by字段建索引,注意复合索引最左前缀原则,并避免在索引字段上使用函数。

MySQL WHERE 条件字段没加索引,查得慢还看不出原因
很多 Python Web 项目上线后发现某个接口响应突然变长,EXPLAIN 一查发现 type 是 ALL,说明在全表扫描。根本原因常是 WHERE 里用的字段没建索引,比如 user_id、status、created_at 这类高频查询字段。
实操建议:
- 对所有 WHERE、JOIN、ORDER BY 中出现的字段,逐个检查是否已有索引;用
SHOW INDEX FROM table_name查 - 复合索引要注意最左前缀原则:如果建了
(a, b, c),那WHERE a = ? AND b = ?能命中,但WHERE b = ?就不行 - 避免在索引字段上做函数操作,比如
WHERE DATE(created_at) = '2024-01-01'会让索引失效,改用WHERE created_at >= '2024-01-01' AND created_at - 小表(
Django/Flask 里 Redis 缓存 key 设计容易撞车或失效
缓存 key 写成硬编码字符串,比如 "user_profile",多个用户共用一个 key,或者没带参数拼接,导致缓存互相覆盖。更常见的是 key 里漏了关键维度,比如没包含 user_id 或 tenant_id,结果 A 用户看到 B 用户的数据。
实操建议:
- key 必须包含所有影响结果的变量,推荐格式:
f"user:{user_id}:profile:v2",版本号v2方便后续失效整批 - 避免用 JSON 字符串或复杂对象直接做 key,序列化不稳定且难调试;用确定性拼接,比如
f"order:list:{user_id}:{page}:{per_page}" - 不依赖 Redis 的过期自动清理来兜底——业务逻辑该删的时候就得主动
delete,比如用户更新资料后立刻删掉对应user:{id}:profile - 本地开发时别关 Redis,用
redis-server --port 6380启个独立实例,避免和测试环境混用导致 key 冲突
Python ORM 查询 N+1 问题让 Redis 缓存也救不了
加了 Redis 缓存,接口还是慢,django-debug-toolbar 或 sqlalchemy-flask-sqlprofiler 一看,单次请求发了几十条相似 SQL,比如循环查每个订单的用户信息。这时候缓存再快也没用——瓶颈在数据库连接和 ORM 层。
实操建议:
- Django 用
select_related()(外键)或prefetch_related()(多对多/反向)提前加载关联数据 - Flask-SQLAlchemy 用
joinedload()或subqueryload(),别在 for 循环里反复调obj.user - 缓存粒度要匹配查询粒度:如果每次查的是“用户+其最近 5 个订单”,就缓存整个结构体,而不是只缓存用户、再单独缓存订单列表
- 警惕
__str__、__repr__或序列化函数里偷偷触发数据库查询,这种地方加了缓存也白搭
Redis 缓存穿透和雪崩在高并发下会直接打垮数据库
缓存没命中的请求直接穿透到 DB,如果大量请求查同一个不存在的 ID(比如恶意刷 /user/999999999),DB 瞬间被打满;或者缓存集体过期,所有请求同时涌向 DB,就是雪崩。
实操建议:
- 缓存穿透:对查不到的 key 也写入一个空值(如
None或"MISS")并设较短过期时间(比如 60 秒),避免重复穿透 - 缓存雪崩:给过期时间加随机偏移,比如基础 3600 秒,再
+ random.randint(0, 600),分散失效时间 - 别把 Redis 当唯一真相源——缓存只是加速层,DB 才是权威;缓存异常时降级为直连 DB,但要加熔断(如
tenacity库限流) - 用
SET key value EX 3600 NX命令原子性地写缓存,防止并发重建时多个进程同时查 DB
索引和缓存不是加了就完事,关键在“查什么”和“怎么查”是否对齐。最容易被忽略的是:缓存 key 的语义一致性、ORM 查询路径的可见性、以及缓存失效和 DB 更新的时序控制——这三个点一松动,性能优化就变成负优化。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











