听说Redis都会遇到并发、雪崩等难题?我用10分钟就解决了

珠江路在线   2019年12月19日  编辑:

一、Redis雪崩、穿透、并发等5大难题解决 方案

缓存雪崩

数据未加载到缓存中,或者缓存同一 工夫大面积的失效,从而招致全部 申请都去查数据库,招致数据库CPU和内存负载过高,甚至宕机 。

比方一个雪崩的 容易过程:

1、redis集群大面积故障

2、缓存失效,但依旧大量 申请 拜访缓存服务redis

3、redis大量失效后,大量 申请转向到mysql数据库

4、mysql的调用量暴增,很快就扛不住了,甚至直接宕机

5、因为大量的 利用服务依赖mysql和redis的服务,这个时候很快 调演变成各服务器集群的雪崩,最后网站彻底 瓦解 。

据说Redis都会遇到并发、雪崩等难题?我用10分钟就解决了

如何预防缓存雪崩:

据说Redis都会遇到并发、雪崩等难题?我用10分钟就解决了

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操作放在队列中使其串行化,必须的一个一个执行 。

缓存预热

缓存预热便是系统上线后,将 有关的缓存数据直接加载到缓存系统 。

这样就 能够幸免在消费者 申请的时候,先 查问数据库, 而后再将数据缓存的问题!消费者直接 查问事先被预热的缓存数据!

解决思路:

1、直接写个缓存刷新页面,上线时手工操作下;

2、数据量不大, 能够在 名目启动的时候自动进行加载;

目标便是在系统上线前,将数据加载到缓存中 。

二、Redis为何是单线程,高并发快的3大缘由详解

Redis的高并发和 快捷缘由

1.redis是基于内存的,内存的读写速度十分快;

2.redis是单线程的,省去了众多上下文切换线程的 工夫;

3.redis 使用多路复用技术, 能够 解决并发的衔接 。非堵塞IO 内部实现采纳epoll,采纳了epoll+自己实现的 容易的事件框架 。epoll中的读、写、关闭、衔接都转化成了事件, 而后利用epoll的多路复用 特点,绝不在io上 浪费丝毫 工夫 。

下面重点介绍单线程设计和IO多路复用核心设计快的缘由 。

据说Redis都会遇到并发、雪崩等难题?我用10分钟就解决了

为何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单线程的优劣势

单 历程单线程优势

  1. 代码更清楚, 解决逻辑更 容易不用去考量各种锁的问题,不存在加锁 开释锁操作,没有因为可能浮现死锁而招致的性能 消费不存在多 历程或者多线程招致的切换而 消费CPU

单 历程单线程 弊病

  1. 无奈 施展多核CPU性能,不过 能够通过在单机开多个Redis实例来完善;

IO多路复用技术

redis 采纳网络IO多路复用技术来 保障在多衔接的时候, 系统的高吞吐量 。

多路-指的是多个socket衔接,复用-指的是复用一个线程 。多路复用重要有三种技术:select,poll,epoll 。epoll是最新的也是当前最好的多路复用技术 。

这里“多路”指的是多个网络衔接,“复用”指的是复用同一个线程 。采纳多路 I/O 复用技术 能够让单个线程高效的 解决多个衔接 申请(尽量削减网络IO的 工夫 消费),且Redis在内存中操作数据的速度十分快(内存内的操作不会成为这里的性能瓶颈),重要以上两点造就了Redis 存在很高的吞吐量 。

据说Redis都会遇到并发、雪崩等难题?我用10分钟就解决了

Redis高并发快总结

据说Redis都会遇到并发、雪崩等难题?我用10分钟就解决了

1. Redis是纯内存数据库,一般都是 容易的存取操作,线程占用的 工夫众多, 工夫的 花费重要集中在IO上,所以读取速度快 。

2. 再说一下IO,Redis 使用的是非堵塞IO,IO多路复用, 使用了单线程来轮询 形容符,将数据库的开、关、读、写都转换成了事件,削减了线程切换时上下文的切换和竞争 。

3. Redis采纳了单线程的模型, 保障了每个操作的原子性,也削减了线程的上下文切换和竞争 。

4. 另外,数据 构造也帮了不少忙,Redis全程 使用hash 构造,读取速度快,还有一些特别的数据 构造,对数据存储进行了优化,如压缩表,对短数据进行压缩存储,再如,跳表, 使用有序的数据 构造加速读取的速度 。

5. 还有丝毫,Redis采纳自己实现的事件 拆散器,效率 比较高,内部采纳非堵塞的执行 模式,吞吐 威力 比较大 。

三、Redis缓存和MySQL数据 统一性 方案详解

需求起因

在高并发的业务场景下,数据库大多数状况都是消费者并发 拜访最 薄弱的环节 。所以,就需求 使用redis做一个缓冲操作,让 申请先 拜访到redis,而不是直接 拜访MySQL等数据库 。

据说Redis都会遇到并发、雪崩等难题?我用10分钟就解决了

这个业务场景,重要是解决读数据从Redis缓存,一般都是依照下图的流程来进行业务操作 。

据说Redis都会遇到并发、雪崩等难题?我用10分钟就解决了

读取缓存步骤一般没有什么问题,然而一旦 波及到数据更新:数据库和缓存更新,就方便浮现缓存(Redis)和数据库(MySQL)间的数据 统一性问题 。

无论是先写MySQL数据库,再删除Redis缓存;还是先删除缓存,再写库,都有可能浮现数据不 统一的状况 。举一个例子:

1.假如删除了缓存Redis,还没有来得及写库MySQL,另一个线程就来读取,发现缓存为空,则去数据库中读取数据写入缓存,此时缓存中为脏数据 。

2.假如先写了库,在删除缓存前,写库的线程宕机了,没有删除掉缓存,则也会浮现数据不 统一状况 。

因为写和读是并发的,没法 保障顺序,就会浮现缓存和数据库的数据不 统一的问题 。

如来解决?这里给出两个解决 方案,先易后难, 联合业务和技术代价 取舍 使用 。

缓存和数据库 统一性解决 方案

据说Redis都会遇到并发、雪崩等难题?我用10分钟就解决了

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秒 。

设置缓存过期 工夫

从 实际上来说,给缓存设置过期 工夫,是 保障最后 统一性的解决 方案 。全部的写操作以数据库为准, 只有 到达缓存过期 工夫,则后面的读 申请自然会从数据库中读取新值 而后回填缓存 。

该 方案的 弊病

联合双删策略+缓存超时设置,这样最差的状况便是在超时 工夫内数据存在不 统一,并且又添加了写 申请的耗时 。

2、第二种 方案:异步更新缓存(基于订阅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 。