1、Redis雪崩、穿透、并发等5大难题解决方案前端
缓存雪崩mysql
数据未加载到缓存中,或者缓存同一时间大面积的失效,从而致使全部请求都去查数据库,致使数据库CPU和内存负载太高,甚至宕机。redis
好比一个雪崩的简单过程:sql
一、redis集群大面积故障数据库
二、缓存失效,但依然大量请求访问缓存服务redis后端
三、redis大量失效后,大量请求转向到mysql数据库缓存
四、mysql的调用量暴增,很快就扛不住了,甚至直接宕机服务器
五、因为大量的应用服务依赖mysql和redis的服务,这个时候很快会演变成各服务器集群的雪崩,最后网站完全崩溃。网络
如何预防缓存雪崩:数据结构
1.缓存的高可用性
缓存层设计成高可用,防止缓存大面积故障。即便个别节点、个别机器、甚至是机房宕掉,依然能够提供服务,例如 Redis Sentinel 和 Redis Cluster 都实现了高可用。
2.缓存降级
能够利用ehcache等本地缓存(暂时支持),但主要仍是对源服务访问进行限流、资源隔离(熔断)、降级等。
当访问量剧增、服务出现问题仍然须要保证服务仍是可用的。系统能够根据一些关键数据进行自动降级,也能够配置开关实现人工降级,这里会涉及到运维的配合。
降级的最终目的是保证核心服务可用,即便是有损的。
好比推荐服务中,不少都是个性化的需求,假如个性化需求不能提供服务了,能够降级补充热点数据,不至于形成前端页面是个大空白。
在进行降级以前要对系统进行梳理,好比:哪些业务是核心(必须保证),哪些业务能够允许暂时不提供服务(利用静态页面替换)等,以及配合服务器核心指标,来后设置总体预案,好比:
(1)通常:好比有些服务偶尔由于网络抖动或者服务正在上线而超时,能够自动降级;
(2)警告:有些服务在一段时间内成功率有波动(如在95~100%之间),能够自动降级或人工降级,并发送告警;
(3)错误:好比可用率低于90%,或者数据库链接池被打爆了,或者访问量忽然猛增到系统能承受的最大阀值,此时能够根据状况自动降级或者人工降级;
(4)严重错误:好比由于特殊缘由数据错误了,此时须要紧急人工降级。
3.Redis备份和快速预热
1)Redis数据备份和恢复
2)快速缓存预热
4.提早演练
最后,建议仍是在项目上线前,演练缓存层宕掉后,应用以及后端的负载状况以及可能出现的问题,对高可用提早预演,提早发现问题。
缓存穿透
缓存穿透是指查询一个一不存在的数据。例如:从缓存redis没有命中,须要从mysql数据库查询,查不到数据则不写入缓存,这将致使这个不存在的数据每次请求都要到数据库去查询,形成缓存穿透。
解决思路:
若是查询数据库也为空,直接设置一个默认值存放到缓存,这样第二次到缓冲中获取就有值了,而不会继续访问数据库。设置一个过时时间或者当有值的时候将缓存中的值替换掉便可。
能够给key设置一些格式规则,而后查询以前先过滤掉不符合规则的Key。
缓存并发
这里的并发指的是多个redis的client同时set key引发的并发问题。其实redis自身就是单线程操做,多个client并发操做,按照先到先执行的原则,先到的先执行,其他的阻塞。固然,另外的解决方案是把redis.set操做放在队列中使其串行化,必须的一个一个执行。
缓存预热
缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。
这样就能够避免在用户请求的时候,先查询数据库,而后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
解决思路:
一、直接写个缓存刷新页面,上线时手工操做下;
二、数据量不大,能够在项目启动的时候自动进行加载;
目的就是在系统上线前,将数据加载到缓存中。
2、Redis为何是单线程,高并发快的3大缘由详解
Redis的高并发和快速缘由
1.redis是基于内存的,内存的读写速度很是快;
2.redis是单线程的,省去了不少上下文切换线程的时间;
3.redis使用多路复用技术,能够处理并发的链接。非阻塞IO 内部实现采用epoll,采用了epoll+本身实现的简单的事件框架。epoll中的读、写、关闭、链接都转化成了事件,而后利用epoll的多路复用特性,毫不在io上浪费一点时间。
下面重点介绍单线程设计和IO多路复用核心设计快的缘由。
为何Redis是单线程的?
1.官方答案
由于Redis是基于内存的操做,CPU不是Redis的瓶颈,Redis的瓶颈最有多是机器内存的大小或者网络带宽。既然单线程容易实现,并且CPU不会成为瓶颈,那就瓜熟蒂落地采用单线程的方案了。
2.性能指标
关于redis的性能,官方网站也有,普通笔记本轻松处理每秒几十万的请求。
3.详细缘由
1)不须要各类锁的性能消耗
Redis的数据结构并不全是简单的Key-Value,还有list,hash等复杂的结构,这些结构有可能会进行很细粒度的操做,好比在很长的列表后面添加一个元素,在hash当中添加或者删除
一个对象。这些操做可能就须要加很是多的锁,致使的结果是同步开销大大增长。
总之,在单线程的状况下,就不用去考虑各类锁的问题,不存在加锁释放锁操做,没有由于可能出现死锁而致使的性能消耗。
2)单线程多进程集群方案
单线程的威力实际上很是强大,每核心效率也很是高,多线程天然是能够比单线程有更高的性能上限,可是在今天的计算环境中,即便是单机多线程的上限也每每不能知足须要了,须要进一步摸索的是多服务器集群化的方案,这些方案中多线程的技术照样是用不上的。
因此单线程、多进程的集群不失为一个时髦的解决方案。
3)CPU消耗
采用单线程,避免了没必要要的上下文切换和竞争条件,也不存在多进程或者多线程致使的切换而消耗 CPU。
可是若是CPU成为Redis瓶颈,或者不想让服务器其余CUP核闲置,那怎么办?
能够考虑多起几个Redis进程,Redis是key-value数据库,不是关系数据库,数据之间没有约束。只要客户端分清哪些key放在哪一个Redis进程上就能够了。
Redis单线程的优劣势
单进程单线程优点
代码更清晰,处理逻辑更简单不用去考虑各类锁的问题,不存在加锁释放锁操做,没有由于可能出现死锁而致使的性能消耗不存在多进程或者多线程致使的切换而消耗CPU
单进程单线程弊端
没法发挥多核CPU性能,不过能够经过在单机开多个Redis实例来完善;
IO多路复用技术
redis 采用网络IO多路复用技术来保证在多链接的时候, 系统的高吞吐量。
多路-指的是多个socket链接,复用-指的是复用一个线程。多路复用主要有三种技术:select,poll,epoll。epoll是最新的也是目前最好的多路复用技术。
这里“多路”指的是多个网络链接,“复用”指的是复用同一个线程。采用多路 I/O 复用技术可让单个线程高效的处理多个链接请求(尽可能减小网络IO的时间消耗),且Redis在内存中操做数据的速度很是快(内存内的操做不会成为这里的性能瓶颈),主要以上两点造就了Redis具备很高的吞吐量。
Redis高并发快总结
1. Redis是纯内存数据库,通常都是简单的存取操做,线程占用的时间不少,时间的花费主要集中在IO上,因此读取速度快。
2. 再说一下IO,Redis使用的是非阻塞IO,IO多路复用,使用了单线程来轮询描述符,将数据库的开、关、读、写都转换成了事件,减小了线程切换时上下文的切换和竞争。
3. Redis采用了单线程的模型,保证了每一个操做的原子性,也减小了线程的上下文切换和竞争。
4. 另外,数据结构也帮了很多忙,Redis全程使用hash结构,读取速度快,还有一些特殊的数据结构,对数据存储进行了优化,如压缩表,对短数据进行压缩存储,再如,跳表,使用有序的数据结构加快读取的速度。
5. 还有一点,Redis采用本身实现的事件分离器,效率比较高,内部采用非阻塞的执行方式,吞吐能力比较大。
3、Redis缓存和MySQL数据一致性方案详解
需求原由
在高并发的业务场景下,数据库大多数状况都是用户并发访问最薄弱的环节。因此,就须要使用redis作一个缓冲操做,让请求先访问到redis,而不是直接访问MySQL等数据库。
这个业务场景,主要是解决读数据从Redis缓存,通常都是按照下图的流程来进行业务操做。
读取缓存步骤通常没有什么问题,可是一旦涉及到数据更新:数据库和缓存更新,就容易出现缓存(Redis)和数据库(MySQL)间的数据一致性问题。
无论是先写MySQL数据库,再删除Redis缓存;仍是先删除缓存,再写库,都有可能出现数据不一致的状况。举一个例子:
1.若是删除了缓存Redis,尚未来得及写库MySQL,另外一个线程就来读取,发现缓存为空,则去数据库中读取数据写入缓存,此时缓存中为脏数据。
2.若是先写了库,在删除缓存前,写库的线程宕机了,没有删除掉缓存,则也会出现数据不一致状况。
由于写和读是并发的,无法保证顺序,就会出现缓存和数据库的数据不一致的问题。
如来解决?这里给出两个解决方案,先易后难,结合业务和技术代价选择使用。
缓存和数据库一致性解决方案
1.第一种方案:采用延时双删策略
在写库先后都进行redis.del(key)操做,而且设定合理的超时时间。
伪代码以下:
public void write(String key,Object data){
redis.delKey(key);
db.updateData(data);
Thread.sleep(500);
redis.delKey(key);
}
具体的步骤就是:
先删除缓存;再写数据库;休眠500毫秒;再次删除缓存。
那么,这个500毫秒怎么肯定的,具体该休眠多久呢?
须要评估本身的项目的读数据业务逻辑的耗时。这么作的目的,就是确保读请求结束,写请求能够删除读请求形成的缓存脏数据。
固然这种策略还要考虑redis和数据库主从同步的耗时。最后的的写数据的休眠时间:则在读数据业务逻辑的耗时基础上,加几百ms便可。好比:休眠1秒。
设置缓存过时时间
从理论上来讲,给缓存设置过时时间,是保证最终一致性的解决方案。全部的写操做以数据库为准,只要到达缓存过时时间,则后面的读请求天然会从数据库中读取新值而后回填缓存。
该方案的弊端
结合双删策略+缓存超时设置,这样最差的状况就是在超时时间内数据存在不一致,并且又增长了写请求的耗时。
二、第二种方案:异步更新缓存(基于订阅binlog的同步机制)
技术总体思路:
MySQL binlog增量订阅消费+消息队列+增量数据更新到redis
读Redis:热数据基本都在Redis写MySQL:增删改都是操做MySQL更新Redis数据:MySQ的数据操做binlog,来更新到Redis
Redis更新
1)数据操做主要分为两大块:
一个是全量(将所有数据一次写入到redis)一个是增量(实时更新)
这里说的是增量,指的是mysql的update、insert、delate变动数据。
2)读取binlog后分析 ,利用消息队列,推送更新各台的redis缓存数据。
这样一旦MySQL中产生了新的写入、更新、删除等操做,就能够把binlog相关的消息推送至Redis,Redis再根据binlog中的记录,对Redis进行更新。
其实这种机制,很相似MySQL的主从备份机制,由于MySQL的主备也是经过binlog来实现的数据一致性。
这里能够结合使用canal(阿里的一款开源框架),经过该框架能够对MySQL的binlog进行订阅,而canal正是模仿了mysql的slave数据库的备份请求,使得Redis的数据更新达到了相同的效果。
固然,这里的消息推送工具你也能够采用别的第三方:kafka、rabbitMQ等来实现推送更新Redis。
以上是Redis详解,以为不错请点赞支持下。