Think\Cache 默认不支持分片缓存,因其底层驱动(如File、Redis)未内置哈希分片逻辑,所有key直传,需开发者在key层面手动实现路由映射,如md5取模生成子目录或带哈希前缀的key。

为什么 think\Cache 默认不支持分片缓存
ThinkPHP 的缓存驱动(比如 File、Redis)本身不内置哈希分片逻辑,所有 key 都直传到底层,靠驱动自己处理。这意味着:当缓存 key 量级上万、且集中在同一目录或 Redis key 前缀下时,File 驱动会遭遇单目录文件数瓶颈(Linux ext4 下 >32K 文件性能明显下滑),Redis 则可能因单个 key 过大或热点 key 导致响应延迟抖动。
分片不是 ThinkPHP 的“功能开关”,而是你得在 key 层面做路由控制 —— 把原始 key 映射到多个物理存储单元(如不同子目录、不同 Redis db 或带 hash 后缀的 key)。
- 别指望配置里加个
'shard' => true就生效,没有这个配置项 - 不能依赖
Cache::tag()实现分片,tag 是逻辑分组,不是物理隔离 - 自定义缓存类时,
get/set方法必须重写 key 构造逻辑,否则分片形同虚设
如何用 md5 + 取模实现 key 哈希分片(File 驱动适用)
File 缓存分片的核心是让 key 落入不同子目录,避免单目录膨胀。典型做法是对原始 key 做哈希后取模,生成二级路径前缀,例如 cache/0a/1b/my_data。
实操上需继承 think\cache\driver\File 并重写 doRead、doWrite、doHas 等方法,在其中插入分片路径计算:
立即学习“PHP免费学习笔记(深入)”;
- 用
md5($key)得到 32 位字符串,取前两位(如substr(md5($key), 0, 2))作为子目录名,可支撑 256 个分片 - 构造真实路径时拼接为
$this->options['path'] . $subdir . DS . md5($key),而非直接用$key - 注意
clear()必须遍历所有分片子目录,不能只清根目录 - 原生
File驱动的options['expire']和options['prefix']仍生效,但prefix应该放在分片路径之后,避免干扰哈希一致性
Redis 分片:用 hash_tag 或 SELECT 不如改 key 结构
Redis 本身不支持自动分片(Cluster 模式除外),ThinkPHP 的 Redis 驱动也不解析哈希标签({user:123})。强行用 SELECT 切库会导致连接复用失效、事务不可用,还增加运维复杂度。
更轻量可控的做法是:在 key 命名时嵌入哈希段,例如把 user:10086 改成 user:0a:10086(其中 0a 来自 md5('10086') % 256 的十六进制表示)。
- 这样所有操作仍在默认 db,无需
SELECT,兼容 pipeline 和 lua 脚本 - 确保 key 拆分逻辑在
set/get入口统一处理,不要在业务层散落多处 - 如果用了 Redis Cluster,key 中的
{...}hash tag 必须包裹整个分片标识部分,例如{user:0a}:10086,否则集群会按完整 key 哈希,失去分片意义 - 注意
flushdb仍只清当前 db,无法按分片清理,批量删除需用SCAN+ 模式匹配,代价较高
分片后 Cache::tag() 失效怎么办
Cache::tag() 在 ThinkPHP 中本质是维护一个 tag → key 映射表,存在单独的 tag key 里。一旦你对原始 key 做了哈希分片,tag 表里的 key 就和实际存储的 key 对不上了 —— 比如 tag 记的是 user_list,但真实 key 是 user_list:1f,clearTag 自然找不到。
- 最简方案:放弃 tag,改用带业务前缀的 key 命名(如
user:list:10086),再用Redis SCAN user:list:*批量删(File 驱动同理,用glob匹配) - 若必须保留 tag,需重写
Tag类,使其在记录和查询时也对 tag 名做同样哈希(如md5($tag) % 256),并把 tag 映射表也分片存储 - 别试图在 tag 名里硬编码分片号(如
user_list_0a),这会让 tag 语义混乱,且无法跨分片聚合
分片从来不是加一层封装就完事的事——它把 key 的一致性、清理的边界、监控的粒度全都推到了你手上。最容易被忽略的,是那个看似无关的 clear() 和 clearTag() 行为,它们在分片后不再“透明”,必须重新定义语义。


















