缓存键名必须包含域名前缀以避免多域名覆盖,需手动注入域名标识、动态加载配置与路由、redis按域名隔离数据库或加前缀,并重写clearcache()为按域名清理。

缓存键名必须包含域名前缀
ThinkPHP 默认的缓存键(如 config、route_rule)在多域名下会互相覆盖——比如 admin.example.com 和 api.example.com 都写入 config,后写入者必然覆盖前者。根本解法是让每个域名拥有独立缓存命名空间。
实操上不要依赖框架自动隔离,需手动注入域名标识:
- 在
app/common.php或应用初始化逻辑中,定义全局缓存前缀:$domain = $_SERVER['HTTP_HOST'] ?? 'default'; \think\Cache::setTag('domain', $domain); // 仅 Tag 方式可用时 // 或更通用:重写缓存键生成逻辑 - 对所有手动调用
cache()或Cache::get()的地方,显式拼接:cache('config_' . request()->host(), $data, 3600); - 若使用
File缓存驱动,注意path配置支持变量:'path' => runtime_path() . 'cache/' . str_replace('.', '_', request()->host()) . '/',否则多个域名共享同一文件目录,clearCache()会误删其他域名缓存
Route 和 Config 缓存无法自动按域名分离
ThinkPHP 的 route:build 和 config:cache 命令生成的缓存文件(如 route.php、config.php)默认不感知域名,执行一次就全站生效。这在多子域或泛域名部署中极易引发路由错乱或配置混用。
必须绕过命令行缓存,改用运行时动态加载:
- 禁用静态配置缓存:在
config/app.php中设'config_cache' => false,改由Config::load()按需加载对应域名的配置文件,例如:Config::load(config_path() . 'site/' . request()->host() . '.php');
- 路由注册不走
route.php缓存,而是在app/route.php中根据request()->host()动态导入不同路由文件:if (request()->host() === 'admin.example.com') { require config_path() . 'route/admin.php'; } - 若仍想用命令行缓存,需为每个域名单独执行并指定输出路径:
php think route:build --output runtime/cache/route_admin.php --domain=admin.example.com
,再在入口处根据域名加载对应文件
Redis 缓存需用 database 或 key 前缀做隔离
当多个域名共用同一个 Redis 实例(常见于 SaaS 多租户场景),仅靠缓存键名拼接不够稳妥——万一某域名缓存键生成逻辑出错,可能污染其他域名数据,且 flushdb 会清空全部。
推荐两种隔离方式,按实际运维能力选择:
- 用不同
database:在config/cache.php的 redis 配置中,根据域名动态切换:'redis' => [ 'select' => strpos(request()->host(), 'admin.') === 0 ? 1 : 0, ]。注意 Redis 默认只有 16 个 database,超量需改配置或换方案 - 强制统一加前缀:在
cache.php中配置'prefix' => request()->host() . ':',但必须确保该值在所有缓存操作前已初始化(建议在app/initialize.php中设置) - 避免用
TAG隔离:ThinkPHP 的 Redis Tag 实现依赖额外 key 存储关系,高并发下易产生竞争,且clearTag()在分布式 Redis 中不可靠
clearCache() 会跨域名误清,必须重写
原生 cache(false) 或 Cache::clear() 不识别当前域名上下文,调用即清空全部缓存,线上多域名环境等于自爆。
务必封装安全清除逻辑:
- 禁止直接调用全局清除方法,所有清除操作走自定义函数:
function clear_domain_cache($domain = null) { $domain = $domain ?: request()->host(); if ('File' === config('cache.type')) { $path = runtime_path() . 'cache/' . str_replace('.', '_', $domain) . '/'; is_dir($path) && rrmdir($path); } elseif ('Redis' === config('cache.type')) { $redis = Cache::store('redis')->handler(); $redis->setOption(\Redis::OPT_PREFIX, $domain . ':'); $redis->flushDB(); // 注意:这里 flushDB 是当前 prefix 绑定的 DB } } - 后台管理清缓存接口必须校验请求来源域名,不能允许任意域名触发全站清理
- CI/CD 自动部署脚本中的
php think clear命令必须加--domain=xxx参数,并在命令行中解析后只清理对应目录或 DB
域名和缓存的耦合点不在框架文档里,而在你每次写 cache()、删 runtime/cache、执行 php think 命令的那一刻——漏掉任意一处,就是缓存雪崩的起点。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











