前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >聊聊细节 - 你知道缓存的正确打开方式么?(1)

聊聊细节 - 你知道缓存的正确打开方式么?(1)

作者头像
桶哥
发布2019-09-04 15:51:25
5891
发布2019-09-04 15:51:25
举报
文章被收录于专栏:PHP饭米粒PHP饭米粒

0x00 前言


大的架构设计,各种高大上的概念层出不穷,相信很多人也或多或少的了解过,但其实,我们真的要把一个系统做强壮,做好,打磨细节才是根本,有句老话:细节是魔鬼,所以会来一系列的文章,来介绍各种细节上的问题,也欢迎大家多多投稿,本系列的开篇就是讲缓存

缓存,做为目前高并发系统的一个基石之一,已经是无处不在了,很多盆友认为如此成熟的技术,已经用的滚瓜烂熟了,没啥好说的吧

但是,真的是这样么?至少从我面试的一些情况来看,很多童鞋考虑的并不全面,下面以redis做为缓存为例,掰开来讲讲!

0x01 读的流程


基本流程如下:

代码语言:javascript
复制
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取到的值必为空,从而导致:

代码语言:javascript
复制
//没有缓存,则从DB获取数据
$data = getDataFromDb($key);

每次都会去查数据库

这种情况,称之为缓存穿透

优秀如你,应该知道怎么优化了吧

代码语言:javascript
复制
//获取缓存
$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数据为空,我们也设置一个空数组到缓存中,然后通过

代码语言:javascript
复制
is_null($data)

来判断,从而避免缓存穿透

0x03 缓存过期


问题2: 如果缓存时间正好过期,会发生什么情况?

这种情况下,如果系统的并发足够高,会导致很多的请求都穿透缓存,有点类似于缓存穿透, 理想状态,我们希望缓存失效时,只有一个请求走到DB,重建缓存

优秀如你,应该知道怎么优化了吧?

思路是,把缓存时间放到数据里进行判断,再通过一个锁机制保证只有一个请求能穿透到DB

代码语言:javascript
复制
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

本文由 桶哥 授权 饭米粒 发布,转载请注明本来源信息和以下的二维码(长按可识别二维码关注)

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-09-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 PHP饭米粒 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档