php缓存无需数据库式索引,关键在设计可预测、可复用、可失效的key;应结合业务参数拼接带含义的key,配合命名空间与主动失效策略保障一致性。

PHP数据库查询结果缓存要不要设“索引”?
PHP本身不给数据库查询结果缓存“设索引”——$_SESSION、apcu_store()、redis->set() 这些缓存机制里,键(key)就是你手动拼的字符串,不是数据库那种 B+ 树索引。所谓“缓存索引”,其实是你设计缓存 key 的策略问题。重点不在“建索引”,而在“让 key 可预测、可复用、可失效”。
怎么生成带业务含义的缓存 key?
直接用 md5("select * FROM users WHERE status=1") 做 key 看似简单,但没法按条件批量清理,也不带版本或租户上下文。实际要结合查询参数、表名、状态字段、数据版本等拼接:
-
"user_list_status_1_v2"—— 明确作用域 + 条件 + 版本号,方便整体刷新 -
"user_profile_{$uid}_{$lang}"—— 含动态变量,注意提前过滤/转义$uid防注入或 key 冲突 - 避免用原始 sql 作 key,SQL 中空格、换行、注释稍有变化就导致重复缓存
- 建议统一用小写 + 下划线,不用点号或斜杠(部分存储后端对 key 字符有限制)
APCu / Redis 缓存如何支持“按前缀批量删除”?
APCu 本身不支持原生前缀删除,Redis 也没有 DEL user_* 原生命令(得靠 SCAN + DEL),所以“索引式管理”必须靠 key 设计配合工具逻辑:
- APCu:用
apcu_get()配合apcu_sma_info()查不到前缀,只能靠约定 key 结构 + 手动维护一个user_keys列表来跟踪 - Redis:推荐用
SCAN 0 MATCH "user_*" count 100分批获取再DEL,别用KEYS(阻塞主线程) - 更稳妥的做法是加一层“缓存命名空间”,比如把
$ns = "v2024q3"插入 key 前缀,失效时直接切新 Namespace
查完数据库立刻缓存,但数据更新了怎么办?
这是缓存和 DB 不一致的根源。不能只靠“缓存过期时间”被动解决,得主动干预:
立即学习“PHP免费学习笔记(深入)”;
- 写操作(INSERT/UPDATE/delete)后,同步删对应 key:
redis->del("user_profile_123"),而不是等 TTL 到期 - 对列表类缓存(如分页结果),更新某条记录时,建议连带清掉整个列表缓存(如
"user_list_status_1"),避免分页错乱 - 如果更新频繁又不想每次都穿透 DB,可用“延迟双删”:先删缓存 → 写 DB → sleep 100ms → 再删一次缓存(防主从延迟导致旧值重入)
- 别在缓存里存完整对象数组后还去 PHP 里 array_filter(),那等于没缓存;该拆分缓存粒度就拆分
真正难的不是拼 key,而是厘清哪些数据该缓存、缓存多久、谁负责失效。很多“缓存索引混乱”的问题,本质是业务状态流转没抽象清楚,key 设计只是表象。