ThinkPHP默认缓存慢因serialize()冗余且反序列化开销大,建议改用json_encode/decode;自定义驱动需继承think\Cache并实现doRead/doWrite/doDelete;Redis键名须md5处理防非法字符。

为什么 ThinkPHP 默认缓存序列化慢得明显
因为默认用 serialize(),它对数组和对象生成冗长字符串,且 PHP 反序列化时要重建符号表、处理引用关系,CPU 和内存开销都高。尤其在 Redis 或 Memcached 里存大数组(比如商品列表、配置树),unserialize() 成为瓶颈。
实操建议:
- 优先改用
json_encode()+json_decode(),前提是缓存数据不含资源句柄、闭包、私有/受保护属性(否则会丢数据或报错) - 若必须保留对象结构(如带魔术方法的模型实例),可封装一层轻量序列化:先
get_object_vars()提纯,再 json 处理 - 确认
opcache.enable开启,避免反复加载反序列化逻辑本身
怎么写一个兼容原生接口的自定义缓存驱动
ThinkPHP 的缓存驱动需实现 think\CacheInterface,但直接继承 think\Cache 更稳妥——它已帮你处理了前缀、过期时间、标签等通用逻辑。
常见错误现象:Cache::store('myredis')->get('key') 报 Call to undefined method,往往是没重写 doRead() / doWrite() / doDelete() 这三个核心方法。
立即学习“PHP免费学习笔记(深入)”;
实操建议:
- 在
app/common/cache/MyRedis.php中定义类,继承think\Cache -
doRead()必须返回false表示未命中(不能返回null或空字符串) - 写入前统一用
json_encode($data, JSON_UNESCAPED_UNICODE),读取后加json_decode($content, true) - 别在驱动里做连接池管理,交给底层客户端(如
predis/predis自带连接复用)
Redis 缓存驱动里 setOption() 和 setHandler() 到底该用哪个
setHandler() 是 ThinkPHP 6.0+ 推荐方式,用于注入已初始化的客户端实例;setOption() 是旧版兼容写法,只传配置数组,框架内部 new 实例——容易导致连接重复创建。
使用场景:你已在 config/cache.php 中配好 Redis 连接,又想在自定义驱动中复用同一连接,就必须用 setHandler()。
实操建议:
- 在
app/common/cache/MyRedis.php构造函数里接收 handler:public function __construct($handler = null) - 若
$handler为空,才 fallback 到new \Predis\Client(...) - 别在
doWrite()里每次调$this->handler->setex(),先检查$this->handler是否有效,避免静默失败
缓存键名含中文或特殊字符时为什么总查不到
ThinkPHP 默认会对键名做 md5() 处理,但部分自定义驱动(尤其是直连 Redis 的)可能跳过了这步,导致键名里出现空格、斜杠、花括号等非法字符,Redis 直接拒绝存储或返回空。
性能影响:不规范的键名还会影响 Redis 的 key 空间统计、SCAN 扫描效率,甚至触发集群 slot 分配异常。
实操建议:
- 强制统一键名格式:
$key = md5($prefix . ':' . $originKey),不要依赖客户端自动转义 - 避免在键名里拼接用户输入,尤其
$_GET['id']这类,先preg_replace('/[^a-zA-Z0-9_\-]/', '', $id) - 调试时用
redis-cli --scan --pattern "tp_*"查看真实键名,比日志更可靠
缓存优化最易被忽略的一点:不是换序列化方式或换驱动,而是确认业务层是否真的需要缓存——有些接口每秒调用几百次,但数据 10 分钟才变一次,这时候加个 ttl=600 比折腾 igbinary 实际得多。



















