近期同事踩了个缓存写法的坑:
public function getOnByStrategyId(int $strategyId) { $vList = $this->cache->getByStrategyId($strategyId); if (empty($vList)) { $query = $this->db()->select()->from($this->table); $query = $query->where('strategy_id', $strategyId); $vList = $query->fetchAll(); $this->cache->setByStrategyId($strategyId, $vList); } }
问题1:可以看出问题在于if (empty($vList))这行,就是查询不到记录的时候,会缓存一个空数组
这种情况,如果用empty判断,就会穿透db,等于每个请求都要查db。
解决方案:需改成用=== false判断才不会穿透db
问题2:但可能会引发另一个问题,默认缓存时间是很长的一个时间
如果出现以下情况会获取到一个空数组,就会导致长期存入一个空缓存
1.主从同步延迟,从库过一会儿才写入完成
2.db查询失败
解决方案:这时可以判断是空缓存设置一个时间很短的缓存(比如3秒缓存)
相比已经比之前不缓存:3秒缓存可保证3秒才查询一次db,避免请求请求大量穿透db。
相比长期缓存:3秒缓存又可以让空数据及时过期,避免长期缓存一个空数据
优化后写法:
public function getOnByStrategyId(int $strategyId) { $vList = $this->cache->getByStrategyId($strategyId); if ($vList === false) { $query = $this->db()->select()->from($this->table); $query = $query->where('strategy_id', $strategyId); $vList = $query->fetchAll(); $this->cache->setByStrategyId($strategyId, $vList, $vList ? 0 : 3); } }