直接用 serialize() 作缓存键会导致语义等价数组生成不同 key,因它保留键顺序、类型细节且跨版本不一致;应改用 json_encode($arr, JSON_UNESCAPED_UNICODE | JSON_SORT_KEYS) 并预处理浮点精度、null/空字符串及非标值。

为什么 serialize() 不适合做缓存键
直接对二维数组用 serialize() 生成字符串当缓存 key,看似简单,但实际会踩坑:键长不可控、可读性差、不同 PHP 版本或扩展(如 OPcache)下序列化结果可能不一致,更严重的是 serialize() 保留数组键顺序和类型细节(比如 0 和 "0" 序列化结果不同),导致逻辑上等价的数组生成不同 key。
真正需要的是「语义等价即 key 等价」——只要数组内容相同(忽略键顺序、浮点精度微差、null/"" 差异等),就应生成同一 key。
- 用
json_encode($arr, JSON_UNESCAPED_UNICODE | JSON_SORT_KEYS)更稳妥:强制键排序、统一编码、丢弃非标类型(如 resource、closure) - 若数组含浮点数,先用
round($val, 6)统一精度,再 json 编码 - 若含 null 或空字符串且业务中视作等价,提前用
array_map(fn($v) => $v === null ? '' : $v, $arr)归一化
如何安全处理含非标值(对象、资源、闭包)的二维数组
遇到 json_encode() 报错 TypeError: json_encode(): Type is not supported,说明数组里混进了对象、资源或闭包。这类值无法直接参与缓存 key 构建,必须显式剥离或替换。
- 用
array_walk_recursive()遍历并检测类型:is_object($v) || is_resource($v) || is_callable($v) - 统一替换成占位符,如
'[object]'、'[resource]',避免 key 泄露敏感结构 - 若对象有稳定标识(如实现了
__toString()或含id属性),提取该字段代替整个对象 - 切勿用
var_export()或print_r(),它们输出含空格/换行,且格式不稳定
md5(json_encode(...)) 是不是最优解?
加 md5() 是为了缩短 key 长度,但要注意:MD5 碰撞概率虽低,但在海量缓存场景下不能完全忽略;更重要的是,它把调试成本变高了——你再也无法从 key 反推原始数据结构。
立即学习“PHP免费学习笔记(深入)”;
- 开发/测试环境建议直接用
json_encode(...)原始字符串,便于日志排查和人工验证 - 生产环境若 key 超过 Redis 64KB 限制(极少见)或需严格控制长度,才用
md5()或spl_hash() - 不要用
sha1()——它比 md5 长且无实际安全增益;也不要自己拼接字符串(如$a.'|'.$b),易被注入分隔符干扰 - 若用 md5,务必确保输入完全确定:开启
JSON_UNESCAPED_UNICODE,禁用JSON_UNESCAPED_SLASHES,否则斜杠转义差异会导致哈希不一致
PHP 8.1+ 中 array_key_first() 和降维无关,别误用
看到“降维”就想到 array_keys() 或 array_values() 是常见误解。二维数组转一维 key 的本质是「扁平化语义」,不是「提取某一层键」。像 array_key_first()、array_column() 这类函数只操作单层结构,对嵌套数组无效。
- 真要提取所有键路径(如
['user']['name'] → 'user.name'),得递归遍历 + 拼接,但这样生成的 key 无法还原原结构,仅适用于固定 schema 场景 - 更通用的做法是保持完整结构 json 化,而非强行“降维”成一维数组再拼接——后者丢失嵌套关系,key 冲突风险陡增
- 如果业务明确只需某几列(如缓存用户 ID 和状态),那就先用
array_map()提取子集,再 encode,而不是对整个二维数组硬降维











