0x00 前言
大的架构设计,各种高大上的概念层出不穷,相信很多人也或多或少的了解过,但其实,我们真的要把一个系统做强壮,做好,打磨细节才是根本,有句老话:细节是魔鬼,所以会来一系列的文章,来介绍各种细节上的问题,也欢迎大家多多投稿,本系列的开篇就是讲缓存
缓存,做为目前高并发系统的一个基石之一,已经是无处不在了,很多盆友认为如此成熟的技术,已经用的滚瓜烂熟了,没啥好说的吧
但是,真的是这样么?至少从我面试的一些情况来看,很多童鞋考虑的并不全面,下面以redis做为缓存为例,掰开来讲讲!
0x01 读的流程
基本流程如下:
public function getData($key)
{
//获取缓存
$data = $redis->get($key);
//获取到缓存,直接返回
if (!empty($data)) {
return $data;
}
//没有缓存,则从DB获取数据
$data = getDataFromDb($key);
//数据写入缓存
$redis->set($key, $data, $expireTime);
//返回数据
return $data;
}
这个应该是大部分人的代码流程,看起来没什么问题,但这里面有两个细节需要考虑
0x02 缓存穿透
问题1、如果数据库里也没有这个key的数据呢?
这种情况下,从redis取到的值必为空,从而导致:
//没有缓存,则从DB获取数据
$data = getDataFromDb($key);
每次都会去查数据库
这种情况,称之为缓存穿透
优秀如你,应该知道怎么优化了吧
//获取缓存
$data = $redis->get($key);
//获取到缓存,直接返回
if (!is_null($data)) {
return $data;
}
//没有缓存,则从DB获取数据
$data = getDataFromDb($key);
//如果为空,则定义成一个空数组,可以根据自己//情况设置成什么样的数据if(empty($data)) {
$data = [];
}
//数据写入缓存
$redis->set($key, $data, $expireTime);
//返回数据
return $data;
即使db数据为空,我们也设置一个空数组到缓存中,然后通过
is_null($data)
来判断,从而避免缓存穿透
0x03 缓存过期
问题2: 如果缓存时间正好过期,会发生什么情况?
这种情况下,如果系统的并发足够高,会导致很多的请求都穿透缓存,有点类似于缓存穿透, 理想状态,我们希望缓存失效时,只有一个请求走到DB,重建缓存
优秀如你,应该知道怎么优化了吧?
思路是,把缓存时间放到数据里进行判断,再通过一个锁机制保证只有一个请求能穿透到DB
public function getData($key)
{
//获取缓存
$data = $redis->get($key);
//缓存未失效
if (!is_null($data)) {
//缓存未过期,直接返回
if($data['expireTime'] > time()) {
return $data['data'];
}
//缓存已过期,但未获得锁,返回数据,只让一个请求穿透
//利用redis的setnx,实现锁
if(!$redis->setNx()) {
return $data['data'];
}
}
//没有缓存,则从DB获取数据
$rData = getDataFromDb($key);
//如果为空,则定义成一个空数组,可以根据自己的情况设置
if(empty($rData)) {
$rData = [];
}
//过期时间提前60s
$data = [
'data' => $rData,
'expireTime' => $expireTime - 60
];
//数据写入缓存, 缓存
$redis->set($key, $data, $expireTime);
//返回数据
return $rData;
}
这个在高并发的系统中,可以很好的防止同时很多请求都穿透到DB
读缓存的流程,你现在清楚了么?
也欢迎这里面还有没考虑到的细节在留言中提出!
下一篇将介绍 如何正确的更新缓存
----------伟大的分割线-----------
PHP饭米粒(phpfamily) 由一群靠谱的人建立,愿为PHPer带来一些值得细细品味的精神食粮!
饭米粒只发原创或授权发表的文章,不转载网上的文章
所发的文章,均可找到原作者进行沟通。
也希望各位多多打赏(算作稿费给文章作者),更希望大家多多投稿。
投稿请联系:
shenzhe163@gmail.com
本文由 桶哥 授权 饭米粒 发布,转载请注明本来源信息和以下的二维码(长按可识别二维码关注)