缓存键名必须带业务前缀,推荐格式为“模块名:资源类型:id”,如'user:info:123';需过滤变量、标准化id、避免tag依赖隔离、多应用时嵌入app_name标识,并确保清理策略与键名设计一致。

缓存键名必须带业务前缀,不能裸用 $id 或 $name
ThinkPHP 的缓存(比如 Cache::get())本身不校验键名唯一性,冲突时后写入的会直接覆盖前一个。常见翻车场景:用户模块用 'user_123',订单模块也用 'user_123'——结果查订单时拿到的是用户数据。
实操建议:
- 所有缓存键强制采用
模块名:资源类型:id格式,例如'user:info:123'、'order:detail:456' - 避免用下划线分隔,ThinkPHP 内部部分驱动(如 Redis)对
_无特殊处理,但冒号:在绝大多数场景下更安全、可读性更强 - 如果模块名含多级(如
admin.user),统一转为小写 + 短横线,例如'admin-user:token:789'
动态生成键名时,必须对变量做严格过滤和标准化
直接拼接用户输入(如 $_GET['sort'])进缓存键,可能产生非法字符、空格或重复键。ThinkPHP 不会帮你 trim 或 urlencode,而某些缓存驱动(如 FileCache)遇到斜杠或点号会报错或写入失败。
实操建议:
- 用
md5()或sha1()哈希原始参数再拼接,例如:'article:list:' . md5($category . '|' . $page) - 若需保留可读性,只允许字母、数字、短横线、下划线,其他一律替换为
-,推荐用preg_replace('/[^a-z0-9_-]/i', '-', $str) - 特别注意:
$id可能是字符串(如 UUID)、数组(如批量查询 ID 列表),不能直接(string)$id拼接,要先json_encode()并标准化空格
使用 Tag 缓存时,标签名不是命名空间,别指望它自动隔离键
ThinkPHP 支持给缓存打标签(Cache::tag('user')->set(...)),很多人误以为标签能防止键名冲突。实际上,tag 只是附加索引,底层键名仍是你传进去的那个字符串——Cache::tag('user')->get('info') 和 Cache::tag('order')->get('info') 如果底层都用了 'info',照样冲突。
实操建议:
- 标签只用于批量清理,比如
Cache::tag('user')->clear(),不要依赖它做命名隔离 - 即使用了 tag,键名仍要遵守前缀规范,例如
Cache::tag('user')->set('user:profile:123', $data) - Redis 驱动下,tag 实际通过
SET+SMEMBERS模拟,频繁打 tag 有额外开销,高并发场景慎用
多应用/多模块共用缓存时,必须在键名中嵌入应用标识
一个 ThinkPHP 项目跑多个子应用(如 app/admin 和 app/api),若都用 'config:site',缓存会互相污染。尤其上线灰度或 AB 测试时,admin 改了配置,api 可能意外读到旧值。
实操建议:
- 在全局加一层应用上下文标识,例如
config('app.app_name') . ':config:site',确保app_name在各入口中明确区分 - 如果用命令行任务(
php think xxx),注意 CLI 模式下app_name可能为空,需显式设置或 fallback 到'cli' - 不推荐靠目录路径推导应用名(如
basename(__DIR__)),因为部署时目录可能重命名,不可靠
最常被忽略的一点:缓存键设计不是“写一次就完事”,它得和你的清理策略对齐。比如你按 'user:* 批量删,但某个地方漏写了前缀,那个键就永远留在那里——既占空间,又难排查。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










