应使用 redis hash 存储数据字典,而非 cache::set();因后者序列化整个数组为 string 类型,导致读写低效、并发风险高、无法字段级操作,而 hash 支持 hget/hgetall 等命令,省内存且可原生操作。

ThinkPHP 做数据字典缓存,别直接用 Cache::store('redis') 存整个数组——Redis Hash 才是更合理、更省内存、支持字段级读写的方案;但必须绕过 Cache 抽象层,用原生 Redis 实例操作,否则无法利用 HGET/HGETALL 等命令。
为什么不能用 Cache::set() 存字典?
数据字典(如状态码映射、地区列表、配置项)通常是关联数组,比如 ['1' => '启用', '0' => '禁用']。用 Cache::set('status_map', $array, 3600) 会把整个数组序列化成一个字符串存为 string 类型,导致:
- 每次读取都得反序列化全部内容,哪怕只查一个状态值
- 更新单个条目(如修改“禁用”文案)必须先 get 全量、改完再 set 全量,有并发覆盖风险
- 无法用 Redis 的
HEXISTS快速判断某个 key 是否存在,只能靠 PHP 层遍历 - 缓存前缀(如
think:cache:status_map)和序列化格式被框架锁定,调试时redis-cli直接get看不懂
如何正确使用 Redis Hash 存字典?
ThinkPHP 5/6 都可通过 think-redis 扩展获取底层 \Redis 实例,再调用原生命令操作 hash。关键不是“能不能”,而是“怎么绕过 Cache 封装还保持连接复用”:
- TP6 推荐方式:
use think\facade\Cache; $redis = Cache::store('redis')->getHandler();—— 这个$redis是原生\Redis实例,可直接调hSet()、hGetAll()等 - TP5 若已配置 redis store,可用
Cache::init(['type'=>'redis']); $redis = Cache::getHandler(); - 不推荐
new \Redis()手动 connect:容易漏掉密码、database、超时等配置,且无法共享框架的连接池 - 写入示例:
$redis->hMset('dict:status', ['1' => '启用', '0' => '禁用', '2' => '审核中']); $redis->expire('dict:status', 86400); - 读取单个字段:
$redis->hGet('dict:status', '1');读全部:$redis->hGetAll('dict:status')
本地化读取:避免每次请求都连 Redis
字典类数据变更频率极低(上线后可能几个月不动),但访问频次高。全走 Redis 有网络开销和连接压力。可行的本地化策略:
- 首次请求时从 Redis
hGetAll('dict:status')读出,用static $cache = []或apcu_store()缓存在 PHP 进程内(注意 APCu 需开启且非 CLI 环境) - 后续请求优先读本地变量,仅当本地未命中或明确刷新时才查 Redis
- 不要用
file_put_contents()写 PHP 文件模拟本地缓存——无原子写、无并发锁、更新后需 reload opcode cache - 若用
apcu,记得设好过期时间(如apcu_store('dict_status', $data, 300)),并监听配置变更事件主动apcu_delete() - CLI 任务(如定时同步字典)里更新 Redis 后,应同时
apcu_clear_cache()或发信号清空所有 worker 的本地副本
容易被忽略的兼容性细节
Hash 操作看似简单,但在 TP 生态里几个点极易踩坑:
-
hSet()在 Redis 4.0+ 返回1(新增)或0(更新),但旧版返回bool,TP5 的部分封装可能误判失败 - TP6 的
think-redis默认用phpredis扩展,如果服务器只装了predis,getHandler()会报错,需确认extension_loaded('redis') - 字典 key 名含冒号(如
dict:user:role)没问题,但千万别在 field 里用冒号——虽然 Redis 允许,但某些监控工具或导出脚本会按冒号切分,造成解析错乱 - 用
hGetAll()返回的是关联数组,但字段名全是字符串(即使你存的是数字 key),PHP 里isset($arr[1])会失败,得用isset($arr['1']) - Redis 本身不区分大小写,但 PHP 数组 key 区分,
hGet('dict:status', '1')和hGet('dict:status', '1')结果一样,但你自己代码里别混用'1'和1
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











