一 缓存穿透
缓存穿透指的是,当我们访问某个缓存KEY想取得对应的数据时,若此KEY不存在于缓存中,则会去查库。如何解决呢?将每次查询的结果都放入缓存不管是不是空。
public function getArticles($key) { $expire = 60 * 3; $data = Cache::get($key); //注意:此处使用is_null来判断而不是直接使用 (!$data)来判断。 //使用 (!$data)来判断的弊端是:如果$data的值为空字符串或者空数组,此处也是不成立的,会继续执行查询DB的语句,造成缓存穿透 if (!is_null($data)) { return $data; } $data = $this->searchDB(); Cache::put($key, $data, $expire); return $data; }
这样处理的原因是,即使当前查询的key为空字符串,或者空数组,结果也会被缓存起来。
当下一次访问时会直接返回,不会造成缓存穿透。
二 缓存加锁(Redis)
若系统的并发很高,当缓存过期时,则大量的请求会穿透缓存,同时到DB中查询,那我们可以设置缓存当缓存过期时,只去DB中请求一次并缓存吗?
可以,我们可以使用redis的setNx()setNx($key) 的作用类似于set($key) ,setNx的意思为 set Not Exists 如果$key不存在则设置,存在则不进行任何操作。
设置成功设置返回1,说明当前的请求获得了当前的操作权限,设置失败返回0,说明此资源已经被其他请求获得。
使用代码实现的话,思路如下:
①给存入缓存的数据增加一个过期时间字段暂时给这个字段起名字叫$data['expire'](这个过期时间要短于实际的缓存过期时间),方便在缓存过期前执行加锁和缓存更新。
②如果$data['expire']达到过期时间,则执行加锁以及缓存更新。
③此时如果有其他请求进入则返回更新之前的数据。
代码如下:
public function getArticlesLock($key) { $time = time(); $expire = 10 * 2; $lockKey = 'lock:k'; $data = Cache::get($key); if (!is_null($data)) { //缓存未过期 if ($data['expire'] > time()){ return $data['data']; } //加锁失败说明已经有请求执行加锁,返回之前的缓存数据 if (!Redis::setnx($lockKey,1)) { return $data['data']; } } sleep(3); $datat = $this->searchDB(); $data = [ 'data' => $datat, 'expire' => $time + $expire - 10 ]; $r = Cache::put($key, $data, $expire); //解锁 Redis::del($lockKey); return $data['data']; }
当然此处也可以使用set()来代替setnx()加锁,以及使用lua脚本解锁。
扫码二维码 获取免费视频学习资料
- 本文固定链接: http://www.phpxs.com/post/7399/
- 转载请注明:转载必须在正文中标注并保留原文链接
- 扫码: 扫上方二维码获取免费视频资料
查 看2022高级编程视频教程免费获取