大型高并发与高可用的三层缓存架构总结
摘要: 对于高并发架构,毫无疑问缓存是最重要的一环,对于大量的高并发,可以采用三层缓存架构来实现,nginx+redis+ehcache nginx
对于中间件nginx常用来做流量的分发,同时nginx本身也有自己的缓存(容量有限),我们可以用来缓存热点数据,让用户的请求直接走缓存并返回,减少流向服务器的流量 一.模板引擎 通常我们可以配合使用freemaker/velocity等模板引擎来抗住大量的请求 小型系统可能直接在服务器端渲染出所有的页面并放入缓存,之后的相同页面请求就可以直接返回,不用去查询数据源或者做数据逻辑处理 对于页面非常之多的系统,当模板有改变,上述方法就需要重新渲染所有的页面模板,毫无疑问是不可取的。因此配合nginx+lua(OpenResty),将模板单独保存在nginx缓存中,同时对于用来渲染的数据也存在nginx缓存中,但是需要设置一个缓存过期的时间,以尽可能保证模板的实时性 二.双层nginx来提升缓存命中率 对于部署多个nginx而言,如果不加入一些数据的路由策略,那么可能导致每个nginx的缓存命中率很低。因此可以部署双层nginx 分发层nginx负责流量分发的逻辑和策略,根据自己定义的一些规则,比如根据productId进行hash,然后对后端nginx数量取模将某一个商品的访问请求固定路由到一个nginx后端服务器上去 后端nginx用来缓存一些热点数据到自己的缓存区(分发层只能配置1个吗) redis
用户的请求,在nginx没有缓存相应的数据,那么会进入到redis缓存中,redis可以做到全量数据的缓存,通过水平扩展能够提升并发、高可用的能力 一.持久化机制:将redis内存中的数据持久化到磁盘中,然后可以定期将磁盘文件上传至S3(AWS)或者ODPS(阿里云)等一些云存储服务上去。 如果同时使用RDB和AOF两种持久化机制,那么在redis重启的时候,会使用AOF来重新构建数据,因为AOF中的数据更加完整,建议将两种持久化机制都开启,用AO F来保证数据不丢失,作为数据恢复的第一选择;用RDB来作不同程度的冷备,在AOF文件都丢失或损坏不可用的时候来快速进行数据的恢复。 实战踩坑:对于想从RDB恢复数据,同时AOF开关也是打开的,一直无法正常恢复,因为每次都会优先从AOF获取数据(如果临时关闭AOF,就可以正常恢复)。此时首先停止redis,然后关闭AOF,拷贝RDB到相应目录,启动redis之后热修改配置参数redis config set appendonly yes,此时会自动生成一个当前内存数据的AOF文件,然后再次停止redis,打开AOF配置,再次启动数据就正常启动 RDB 对redis中的数据执行周期性的持久化,每一刻持久化的都是全量数据的一个快照。对redis性能影响较小,基于RDB能够快速异常恢复 AOF 以append-only的模式写入一个日志文件中,在redis重启的时候可以通过回放AOF日志中的写入指令来重新构建整个数据集。(实际上每次写的日志数据会先到linux os cache,然后redis每隔一秒调用操作系统fsync将os cache中的数据写入磁盘)。对redis有一定的性能影响,能够尽量保证数据的完整性。redis通过rewrite机制来保障AOF文件不会太庞大,基于当前内存数据并可以做适当的指令重构。 二.redis集群 replication 一主多从架构,主节点负责写,并且将数据同步到其他salve节点(异步执行),从节点负责读,主要就是用来做读写分离的横向扩容架构。这种架构的master节点数据一定要做持久化,否则,当master宕机重启之后内存数据清空,那么就会将空数据复制到slave,导致所有数据消失 sentinal哨兵 哨兵是redis集群架构中很重要的一个组件,负责监控redis master和slave进程是否正常工作,当某个redis实例故障时,能够发送消息报警通知给管理员,当master node宕机能够自动转移到slave node上,如果故障转移发生来,会通知client客户端新的master地址。sentinal至少需要3个实例来保证自己的健壮性,并且能够更好地进行quorum投票以达到majority来执行故障转移。 前两种架构方式最大的特点是,每个节点的数据是相同的,无法存取海量的数据。因此哨兵集群的方式使用与数据量不大的情况 redis cluster redis cluster支撑多master node,每个master node可以挂载多个slave node,如果mastre挂掉会自动将对应的某个slave切换成master。需要注意的是redis cluster架构下slave节点主要是用来做高可用、故障主备切换的,如果一定需要slave能够提供读的能力,修改配置也可以实现(同时也需要修改jedis源码来支持该情况下的读写分离操作)。redis cluster架构下,master就是可以任意扩展的,直接横向扩展master即可提高读写吞吐量。slave节点能够自动迁移(让master节点尽量平均拥有slave节点),对整个架构过载冗余的slave就可以保障系统更高的可用性。 ehcache
tomcat jvm堆内存缓存,主要是抗redis出现大规模灾难。如果redis出现了大规模的宕机,导致nginx大量流量直接涌入数据生产服务,那么最后的tomcat堆内存缓存也可以处理部分请求,避免所有请求都直接流向DB 针对上面的技术我特意整理了一下,有很多技术不是靠几句话能讲清楚,所以干脆找朋友录制了一些视频,很多问题其实答案很简单,但是背后的思考和逻辑不简单,要做到知其然还要知其所以然。如果想学习Java工程化、高性能及分布式、深入浅出。微服务、Spring,MyBatis,Netty源码分析的朋友可以加我的Java进阶群:694549689,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给大家。 缓存数据更新策略
对时效性要求高的缓存数据,当发生变更的时候,直接采取数据库和redis缓存双写的方案,让缓存时效性最高。 对时效性不高的数据,当发生变更之后,采取MQ异步通知的方式,通过数据生产服务来监听MQ消息,然后异步去拉取服务的数据更新tomcat jvm缓存和redis缓存,对于nginx本地缓存过期之后就可以从redis中拉取新的数据并更新到nginx本地。 经典的缓存+数据库读写的模式,cache aside pattern
读的时候,先读缓存,缓存没有的话,那么就读数据库,然后取出数据后放入缓存,同时返回响应 更新的时候,先删除缓存,然后再更新数据库 之所以更新的时候只是删除缓存,因为对于一些复杂有逻辑的缓存数据,每次数据变更都更新一次缓存会造成额外的负担,只是删除缓存,让该数据下一次被使用的时候再去执行读的操作来重新缓存,这里采用的是懒加载的策略。举个例子,一个缓存涉及的表的字段,在1分钟内就修改了20次,或者是100次,那么缓存跟新20次,100次;但是这个缓存在1分钟内就被读取了1次,因此每次更新缓存就会有大量的冷数据,对于缓存符合28黄金法则,20%的数据,占用了80%的访问量 数据库和redis缓存双写不一致的问题
最初级的缓存不一致问题以及解决方案 问题:如果先修改数据库再删除缓存,那么当缓存删除失败来,那么会导致数据库中是最新数据,缓存中依旧是旧数据,造成数据不一致。 解决方案:可以先删除缓存,再修改数据库,如果删除缓存成功但是数据库修改失败,那么数据库中是旧数据,缓存是空不会出现不一致 比较复杂的数据不一致问题分析 问题:对于数据发生来变更,先删除缓存,然后去修改数据库,此时数据库中的数据还没有修改成功,并发的读请求到来去读缓存发现是空,进而去数据库查询到此时的旧数据放到缓存中,然后之前对数据库数据的修改成功来,就会造成数据不一致 解决方案:将数据库与缓存更新与读取操作进行异步串行化。当更新数据的时候,根据数据的唯一标识,将更新数据操作路由到一个jvm内部的队列中,一个队列对应一个工作线程,线程串行拿到队列中的操作一条一条地执行。当执行队列中的更新数据操作,删除缓存,然后去更新数据库,此时还没有完成更新的时候过来一个读请求,读到了空的缓存那么可以先将缓存更新的请求发送至路由之后的队列中,此时会在队列积压,然后同步等待缓存更新完成,一个队列中多个相同数据缓存更新请求串在一起是没有意义的,因此可以做过滤处理。等待前面的更新数据操作完成数据库操作之后,才会去执行下一个缓存更新的操作,此时会从数据库中读取最新的数据,然后写入缓存中,如果请求还在等待时间范围内,不断轮询发现可以取到缓存中值就可以直接返回(此时可能会有对这个缓存数据的多个请求正在这样处理);如果请求等待事件超过一定时长,那么这一次的请求直接读取数据库中的旧值 对于这种处理方式需要注意一些问题: 读请求长时阻塞:由于读请求进行来非常轻度的异步化,所以对超时的问题需要格外注意,超过超时时间会直接查询DB,处理不好会对DB造成压力,因此需要测试系统高峰期QPS来调整机器数以及对应机器上的队列数最终决定合理的请求等待超时时间 多实例部署的请求路由:可能这个服务会部署多个实例,那么必须保证对应的请求都通过nginx服务器路由到相同的服务实例上 热点数据的路由导师请求的倾斜:因为只有在商品数据更新的时候才会清空缓存,然后才会导致读写并发,所以更新频率不是太高的话,这个问题的影响并不是特别大,但是的确可能某些机器的负载会高一些 分布式缓存重建并发冲突解决方案
对于缓存生产服务,可能部署在多台机器,当redis和ehcache对应的缓存数据都过期不存在时,此时可能nginx过来的请求和kafka监听的请求同时到达,导致两者最终都去拉取数据并且存入redis中,因此可能产生并发冲突的问题,可以采用redis或者zookeeper类似的分布式锁来解决,让请求的被动缓存重建与监听主动的缓存重建操作避免并发的冲突,当存入缓存的时候通过对比时间字段废弃掉旧的数据,保存最新的数据到缓存 缓存冷启动以及缓存预热解决方案
当系统第一次启动,大量请求涌入,此时的缓存为空,可能会导致DB崩溃,进而让系统不可用,同样当redis所有缓存数据异常丢失,也会导致该问题。因此,可以提前放入数据到redis避免上述冷启动的问题,当然也不可能是全量数据,可以根据类似于当天的具体访问情况,实时统计出访问频率较高的热数据,这里热数据也比较多,需要多个服务并行的分布式去读写到redis中(所以要基于zk分布式锁) 通过nginx+lua将访问流量上报至kafka中,storm从kafka中消费数据,实时统计处每个商品的访问次数,访问次数基于LRU(apache commons collections LRUMap)内存数据结构的存储方案,使用LRUMap去存放是因为内存中的性能高,没有外部依赖,每个storm task启动的时候基于zk分布式锁将自己的id写入zk同一个节点中,每个storm task负责完成自己这里的热数据的统计,每隔一段时间就遍历一下这个map,然后维护一个前1000的数据list,然后去更新这个list,最后开启一个后台线程,每隔一段时间比如一分钟都将排名的前1000的热数据list同步到zk中去,存储到这个storm task对应的一个znode中去 部署多个实例的服务,每次启动的时候就会去拿到上述维护的storm task id列表的节点数据,然后根据taskid,一个一个去尝试获取taskid对应的znode的zk分布式锁,如果能够获取到分布式锁,再去获取taskid status的锁进而查询预热状态,如果没有被预热过,那么就将这个taskid对应的热数据list取出来,从而从DB中查询出来写入缓存中,如果taskid分布式锁获取失败,快速抛错进行下一次循环获取下一个taskid的分布式锁即可,此时就是多个服务实例基于zk分布式锁做协调并行的进行缓存的预热 缓存热点导致系统不可用解决方案
对于瞬间大量的相同数据的请求涌入,可能导致该数据经过hash策略之后对应的应用层nginx被压垮,如果请求继续就会影响至其他的nginx,最终导致所有nginx出现异常整个系统变得不可用。 基于nginx+lua+storm的热点缓存的流量分发策略自动降级来解决上述问题的出现,可以设定访问次数大于后95%平均值n倍的数据为热点,在storm中直接发送http请求到流量分发的nginx上去,使其存入本地缓存,然后storm还会将热点对应的完整缓存数据没发送到所有的应用nginx服务器上去,并直接存放到本地缓存。对于流量分发nginx,访问对应的数据,如果发现是热点标识就立即做流量分发策略的降级,对同一个数据的访问从hash到一台应用层nginx降级成为分发至所有的应用层nginx。storm需要保存上一次识别出来的热点List,并同当前计算出来的热点list做对比,如果已经不是热点数据,则发送对应的http请求至流量分发nginx中来取消对应数据的热点标识 缓存雪崩解决方案
redis集群彻底崩溃,缓存服务大量对redis的请求等待,占用资源,随后缓存服务大量的请求进入源头服务去查询DB,使DB压力过大崩溃,此时对源头服务的请求也大量等待占用资源,缓存服务大量的资源全部耗费在访问redis和源服务无果,最后使自身无法提供服务,最终会导致整个网站崩溃。 事前的解决方案,搭建一套高可用架构的redis cluster集群,主从架构、一主多从,一旦主节点宕机,从节点自动跟上,并且最好使用双机房部署集群。 事中的解决方案,部署一层ehcache缓存,在redis全部实现情况下能够抗住部分压力;对redis cluster的访问做资源隔离,避免所有资源都等待,对redis cluster的访问失败时的情况去部署对应的熔断策略,部署redis cluster的降级策略;对源服务访问的限流以及资源隔离 事后的解决方案:redis数据做了备份可以直接恢复,重启redis即可;redis数据彻底失败来或者数据过旧,可以快速缓存预热,然后让redis重新启动。然后由于资源隔离的half-open策略发现redis已经能够正常访问,那么所有的请求将自动恢复 缓存穿透解决方案
对于在多级缓存中都没有对应的数据,并且DB也没有查询到数据,此时大量的请求都会直接到达DB,导致DB承载高并发的问题。解决缓存穿透的问题可以对DB也没有的数据返回一个空标识的数据,进而保存到各级缓存中,因为有对数据修改的异步监听,所以当数据有更新,新的数据会被更新到缓存汇中。 nginx缓存失效导致redis压力倍增
可以在nginx本地,设置缓存数据的时候随机缓存的有效期,避免同一时刻缓存都失效而大量请求直接进入redis 这个过程值得我们去深入学习和思考。如果对你有帮助请动动小手关注下吧! 如果你也想在IT行业拿高薪,可以参加我们的训练营课程,选择最适合自己的课程学习,技术大牛亲授,7个月后,进入名企拿高薪。我们的课程内容有:Java工程化、高性能及分布式、高性能、深入浅出。高架构。性能调优、Spring,MyBatis,Netty源码分析和大数据等多个知识点。如果你想拿高薪的,想学习的,想就业前景好的,想跟别人竞争能取得优势的,想进阿里面试但担心面试不过的,你都可以来,群号为: 454377428 注:加群要求 1、具有1-5工作经验的,面对目前流行的技术不知从何下手,需要突破技术瓶颈的可以加。 2、在公司待久了,过得很安逸,但跳槽时面试碰壁。需要在短时间内进修、跳槽拿高薪的可以加。 3、如果没有工作经验,但基础非常扎实,对java工作机制,常用设计思想,常用java开发框架掌握熟练的,可以加。 4、觉得自己很牛B,一般需求都能搞定。但是所学的知识点没有系统化,很难在技术领域继续突破的可以加。 5.阿里Java高级大牛直播讲解知识点,分享知识,多年工作经验的梳理和总结,带着大家全面、科学地建立自己的技术体系和技术认知! 6.小号或者小白之类加群一律不给过,谢谢。 目标已经有了,下面就看行动了!记住:学习永远是自己的事情,你不学时间也不会多,你学了有时候却能够使用自己学到的知识换得更多自由自在的美好时光!时间是生命的基本组成部分,也是万物存在的根本尺度,我们的时间在那里我们的生活就在那里!我们价值也将在那里提升或消弭!Java程序员,加油吧
大型高并发与高可用的三层缓存架构总结相关推荐
- Nginx+Redis+Ehcache:大型高并发与高可用的三层缓存架构总结
对于高并发架构,毫无疑问缓存是最重要的一环,对于大量的高并发,可以采用三层缓存架构来实现nginx+redis+ehcache. Nginx 对于中间件nginx常用来做流量的分发,同时nginx本身 ...
- 最新亿级流量电商详情页系统的大型高并发与高可用缓存架构实战第一版附全套资料
课程介绍(非升级版) 对于高并发的场景来说,比如电商类,o2o,门户,等等互联网类的项目,缓存技术是Java项目中最常见的一种应用技术.然而,行业里很多朋友对缓存技术的了解与掌握,仅仅停留在掌握red ...
- Nginx+Redis+Ehcache:大型高并发与高可用的3层缓存架构总结
摘要: 对于高并发架构,毫无疑问缓存是最重要的一环,对于大量的高并发,可以采用三层缓存架构来实现,nginx+redis+ehcache Nginx 对于中间件nginx常用来做流量的分发,同时ngi ...
- 亿级流量电商详情页系统的大型高并发与高可用缓存架构实战
2019独角兽企业重金招聘Python工程师标准>>> 对于高并发的场景来说,比如电商类,o2o,门户,等等互联网类的项目,缓存技术是Java项目中最常见的一种应用技术.然而,行业里 ...
- 亿级流量电商详情页系统的大型高并发与高可用缓存架构实战 目录
对于高并发的场景来说,比如电商类,o2o,门户,等等互联网类的项目,缓存技术是Java项目中最常见的一种应用技术.然而,行业里很多朋友对缓存技术的了解与掌握,仅仅停留在掌握redis/memcache ...
- TRTC助力高并发、高可用实时音视频互动场景落地(内含开发福利)
疫情之下,大家在工作生活中更多开始使用直播,视频会议.网络教学等场景需求被点燃,但与此同时不可避免会带来突发的大规模在线视频与协作需求与流量冲击,面临高并发.高可用.高性能的挑战. 面对疫情压力,腾讯 ...
- 如果淘宝双十一架构用. Net Core,如何“擒住”高并发、高可用、低延迟?
电商的秒杀和抢购,对我们来说,都不是一个陌生的东西.然而,从技术的角度来说,这对于Web系统是一个巨大的考验.当一个Web系统,在一秒钟内收到数以万计甚至更多请求时,系统的优化和稳定至关重要. 缓存技 ...
- Redis面试 - 如何保证 redis 的高并发和高可用?
面试题 如何保证 redis 的高并发和高可用?redis 的主从复制原理能介绍一下么?redis 的哨兵原理能介绍一下么? 面试官心理分析 其实问这个问题,主要是考考你,redis 单机能承载多高并 ...
- 高并发和高可用的常规理解
高并发与高可用 究竟啥才是互联网架构"高并发" 一.什么是高并发 高并发(High Concurrency)是互联网分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计保证 ...
最新文章
- 高职扩招计算机应用课程,高职扩招带来的“新生”
- 洛谷P1613 跑路
- 园艺专业相关计算机知识,2017年秋园艺概论(专业选修)
- NavigationDrawer和NavigationView-Android M新控件
- 在树莓派上借助Mono + Jexus 布署 .Net 4.0 WebForm应用
- spring教程--AOP详解
- 开发经验分享_03_解决问题3步走(思路)
- vue 代理设置 访问图片_详解Vue源码之数据的代理访问
- ai音响怎么连接网络_KTV音响设备怎么连接?点歌机怎么连接?学习下
- wow修改人物模型_一张照片生成人物动画!三星最新AI研究成果出炉
- 卡巴斯基宣布高端静谧岑寂僻静产物PURE
- python创意网络爬虫_基于Python专用型网络爬虫的设计及实现
- 【校园快递信息系统——开题报告 分享(仅供参考呀)】
- [NowCoder5673E]Enigmatic Partition
- SCI论文写作中常用的连接词和短语
- Linux 30岁啦,这些历史你知道多少呢?
- linux 小括号 中括号 双小括号 双中括号
- 光纤中的多种光学模式芯径_光纤的结构是什么?种类有哪些?该怎么选择?
- python 与 selenium
- ERC20接口下USDT代币的深入解析