问题描述

缓存失效:

  引起这个原因的主要因素是高并发下,我们一般设定一个缓存的过期时间时,可能有一些会设置5分钟啊,10分钟这些;并发很高时可能会出在某一个时间同时生成了很多的缓存,并且过期时间在同一时刻,这个时候就可能引发——当过期时间到后,这些缓存同时失效,请求全部转发到DB,DB可能会压力过重。 
  处理方法: 一个简单方案就是将缓存失效时间分散开,不要所以缓存时间长度都设置成5分钟或者10分钟;比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。 缓存失效时产生的雪崩效应,将所有请求全部放在数据库上,这样很容易就达到数据库的瓶颈,导致服务无法正常提供。尽量避免这种场景的发生。 
  

缓存穿透:

  出现场景:指查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。

  当在流量较大时,出现这样的情况,一直请求DB,很容易导致服务挂掉。 
处理方法: 
  方法1.在封装的缓存SET和GET部分增加个步骤,如果查询一个KEY不存在,就已这个KEY为前缀设定一个标识KEY;以后再查询该KEY的时候,先查询标识KEY,如果标识KEY存在,就返回一个协定好的非false或者NULL值,然后APP做相应的处理,这样缓存层就不会被穿透。当然这个验证KEY的失效时间不能太长。

  方法2.如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,一般只有几分钟。

  方法3.采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。

缓存并发: 
  出现场景:当网站并发访问高,一个缓存如果失效,可能出现多个进程同时查询DB,同时设置缓存的情况,如果并发确实很大,这也可能造成DB压力过大,还有缓存频繁更新的问题。 
  处理方法:对缓存查询加锁,如果KEY不存在,就加锁,然后查DB入缓存,然后解锁;其他进程如果发现有锁就等待,然后等解锁后返回数据或者进入DB查询。

解决方案

<?php
class StudentController extends BaseController{
//网站首页展示
public function index(){
//获取分数最高的10位学生信息
$top10ScoreStudents = StudentService::getTop10Students();
//todo something more detail...
}
}
<?php
namespace App\Service;
use App\Base\Service as BaseService;
//通常来说一个稍大型的 PHP 项目,都有有一个仓储层 Repository
use App\Repository\Studuent as StudentRepository;
class StudentService extends BaseService{
public static function getTop10Students(){
$config = App::getConfig();
$cacheKeyGenerator = CacheKeyGenerator::getInstance();
//使用通用的缓存键生成器去获取缓存新键,方便增加统一的前缀(可用于包含 app 名字和版本信息)
$top10StuduentsCacheKey = $cacheKeyGenerator->generate($config["cacheKeyAlias"]["top10Students"]["key"]);
//下面是常见的缓存获取代码
//从 redis client 连接池实例中获取一个可用的 client 连接,保持连接复用的连接池技术
$redisInstance = RedisInstancePool::getInstance();
//直接从缓存中获取,若为 null ,则更新缓存
$top10StuduentsCache = $redisInstance->get($top10StuduentsCacheKey );
//这里使用 === null 判断是为了避免空数据导致的缓存穿透
if ($top10StuduentsCache === null){
//从 db 中获取一份最新的缓存数据
//加一个访问锁,最多锁 20 秒,因为一个并发 1000 左右的单秒访问此接口时,若不加锁
//必然会导致直接多个请求直接命中数据库,也就是下面的 “StudentRepository::getTop10Students()”,明显可能会导致数据库瓶颈出现
if($lockForTop10Students = LockUtility::lock($config["cacheKeyAlias"]["top10Students"]["key"]),20){
$top10StudentsFromDb = StudentRepository::getTop10Students();
if(empty($top10StudentsFromDb) {
//防止空数据穿透到数据库
$top10StudentsFromDb = [];
}
$top10StuduentsCache = $top10StudentsFromDb;
//设置缓存
//ttl 是保存在全局 config 中的 redis key 生命周期
$redisInstance->set($config["cacheKeyAlias"]["top10Students"]["key"],$top10StudentsFromDb,$config["cacheKeyAlias"]["top10Students"]["ttl"]);
LockUtility::unlock($config["cacheKeyAlias"]["top10Students"]["key"]);
return $top10StuduentsCache;
} else {
//没有获取到访问锁,直接返回空结果
return [];
}
}
return $top10StuduentsCache;
}
}

高并发架构技术|缓存失效、缓存穿透问题 PHP 代码解决的更多相关文章

  1. 高并发架构系列:Redis缓存和MySQL数据一致性方案详解

    一.需求起因 在高并发的业务场景下,数据库大多数情况都是用户并发访问最薄弱的环节.所以,就需要使用redis做一个缓冲操作,让请求先访问到redis,而不是直接访问MySQL等数据库. 这个业务场景, ...

  2. 【高并发架构】Redis缓存高并发之-主从架构

    Redis主从架构 到目前为止,Redis Cluster 能实现很好的性能,但如果只是缓存几个G的数据,那么单机Redis就足够了,但缓存主要用来读的,单机的QPS有一定的极限,一两万QPS一台应该 ...

  3. 朱晔的互联网架构实践心得S2E6:浅谈高并发架构设计的16招

    朱晔的互联网架构实践心得S2E6:浅谈高并发架构设计的16招 概览 标题中的高并发架构设计是指设计一套比较合适的架构来应对请求.并发量很大的系统,使系统的稳定性.响应时间符合预期并且能在极端的情况下自 ...

  4. 高并发架构系列:Redis并发竞争key的解决方案详解

    https://blog.csdn.net/ChenRui_yz/article/details/85096418 https://blog.csdn.net/ChenRui_yz/article/l ...

  5. JAVA架构师眼中的高并发架构,分布式架构 应用服务器集群

    前言 高并发经常会发生在有大活跃用户量,用户高聚集的业务场景中,如:秒杀活动,定时领取红包等. 为了让业务可以流畅的运行并且给用户一个好的交互体验,我们需要根据业务场景预估达到的并发量等因素,来设计适 ...

  6. 高并发架构系列:如何从0到1设计一个类Dubbo的RPC框架

    在过去持续分享的几十期阿里Java面试题中,几乎每次都会问到Dubbo相关问题,比如:“如何从0到1设计一个Dubbo的RPC框架”,这个问题主要考察以下几个方面: 你对RPC框架的底层原理掌握程度. ...

  7. 高并发架构系列:MQ消息队列的12点核心原理总结

    消息队列已经逐渐成为分布式应用场景.内部通信.以及秒杀等高并发业务场景的核心手段,它具有低耦合.可靠投递.广播.流量控制.最终一致性 等一系列功能. 无论是 RabbitMQ.RocketMQ.Act ...

  8. 高并发架构系列:Redis为什么是单线程、及高并发快的3大原因详解

    Redis的高并发和快速原因 1.redis是基于内存的,内存的读写速度非常快: 2.redis是单线程的,省去了很多上下文切换线程的时间: 3.redis使用多路复用技术,可以处理并发的连接.非阻塞 ...

  9. 高并发场景系列(一) 利用redis实现分布式事务锁,解决高并发环境下减库存

    原文:http://blog.csdn.net/heyewu4107/article/details/71009712 高并发场景系列(一) 利用redis实现分布式事务锁,解决高并发环境下减库存 问 ...

随机推荐

  1. Java IO网络编程经典模板

    本文会从传统的BIO到NIO再到AIO自浅至深介绍,并附上完整的代码讲解. 下面代码中会使用这样一个例子:客户端发送一段算式的字符串到服务器,服务器计算后返回结果到客户端. 代码的所有说明,都直接作为 ...

  2. 对象生命周期中至少被GC一次后存活

    Finalize调用流程:GC时,当对象变成(GC Roots)不可达时,若该对象覆盖(重写)了finalize方法并且未执行过finalze方法,则将其放入F-Queue队列,由一低优先级线程执行该 ...

  3. 【 MAKEFILE 编程基础之四】详解MAKEFILE 函数的语法与使用!

    本站文章均为 李华明Himi 原创,转载务必在明显处注明: 转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/gcc-makefile/771.html   ...

  4. 邮件过滤-LSTM-Spam Filtering

    Github: https://github.com/cjyanyi/Spam_Filtering_LSTM_Enron 模型结构: CNN-LSTM 开发库: Keras word2vec Enro ...

  5. Java后端开发奋斗之路

    本人方向:Java后端开发方向,本文中内容持续更新中 Java技术栈:https://www.cnblogs.com/wyb666/p/10222070.html 推荐书籍:<程序员代码面试指南 ...

  6. Java Swing类 例子代码:将子类继承JFrame 并且接口按键监听ActionLisetener (将内容直接添加到JFrame不创建Contaniner)

    package rom; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Container; import ...

  7. gentoo kvm qemu

    首先使用 grep --color -E "vmx|svm" /proc/cpuinfo 检查 cpu 能不能支持虚拟化,注意 biso 里面要开启虚拟化. 内核开启相应选项 [* ...

  8. nginx面试中最常见的18道题

    1.请解释一下什么是Nginx? Nginx是一个web服务器和反向代理服务器,用于HTTP.HTTPS.SMTP.POP3和IMAP协议. 2.请列举Nginx的一些特性. Nginx服务器的特性包 ...

  9. web:频繁刷新浏览器的页面【小工具】

    [目的] 频繁刷新某一浏览器页面,小测试一下加载性能,或者打开的文件是否及时关闭,会不会导致服务器奔溃 [小工具] 新建txt,输入以下内容,并保存为html的格式,然后在浏览器中打开,则会定时刷新指 ...

  10. 《算法》第三章部分程序 part 6

    ▶ 书中第三章部分程序,加上自己补充的代码,包含双向索引表.文建索引.稀疏向量类型 ● 双向索引表 package package01; import edu.princeton.cs.algs4.S ...