通知:即日起,禁止携带Note 7登机,且不得作为航空货物托运或承运
06-17
这个问题很微妙。可能在内心深处,这位同学觉得Redis是所有应用缓存的标配。
缓存的世界非常广阔。对于应用系统来说,我们经常将缓存分为本地缓存和分布式缓存。
本地缓存:应用程序中的缓存组件。缓存组件和应用程序位于同一进程中。
缓存读写速度非常快,并且没有网络开销。然而,集群中的每个应用程序或节点都需要维护自己独立的缓存,并且缓存不能共享。
分布式缓存:与应用程序分离、与本地应用程序隔离的缓存组件或服务。多个应用程序可以直接共享缓存。
1 缓存的本质 我们常说:“有了缓存的加入,我们的系统会更快”。所谓“更快”本质上实现了以下两点:减少CPU消耗,提前计算出原本需要实时计算的内容,并复用一些公共数据,这样可以减少CPU消耗,从而提高响应性能。
通过将原来对网络、磁盘等较慢介质的读写访问改为对内存等较快介质的访问,减少I/O消耗,从而提高响应性能。如果可以通过增强CPU和I/O本身的性能来满足需求,那么升级硬件往往是更好的解决方案。
即使需要一些额外的投资成本,通常也比引入缓存可能带来的风险要好。从开发角度来说,引入缓存会增加系统复杂度,因为你要考虑缓存失效、更新、一致性等问题。
从运维的角度来看,缓存会掩盖一些缺陷,让问题在距离发生地点更远的地方、在更长的时间内出现。从安全角度来看,缓存可能会泄露某些机密数据,也容易受到攻击。
因此,缓存是一把双刃剑。 2 本地缓存 JDK MapJDK Map常用于缓存实现:HashMap HashMap是一个基于哈希表的集合类,提供快速的插入、查找和删除操作。
您可以使用键值对作为缓存项的存储方式,其中键作为缓存项的唯一标识,值作为缓存项的内容。 ConcurrentHashMap ConcurrentHashMap是一个线程安全的HashMap,可以保证多线程环境下高效的并发读写操作。
LinkedHashMap LinkedHashMap是一个有序的HashMap,它保留了元素插入的顺序,可以按插入顺序或访问顺序进行遍历。 TreeMap TreeMap是一个基于红黑树的有序Map,可以按照key的顺序进行遍历。
作者曾负责艺龙红包系统。红包活动存储在ConcurrentHashMap中,通过定时任务刷新缓存。
核心流程: 1、红包系统启动后,初始化一个ConcurrentHashMap作为红包活动缓存; 2、数据库查询所有红包活动,并将活动信息存储在Map中; 3、定时任务每30秒执行一次缓存加载方法。刷新缓存。
红包系统为什么要将红包活动信息存储在本地内存ConcurrentHashMap中?红包系统是一个高并发应用,快速响应请求结果到前端,大大提高了用户体验;红包活动数量不大,即使全部放入Map中,也不会出现内存溢出问题;计划任务不会刷新缓存。影响红包制度的业务。
作者已经看到许多单一应用程序使用此解决方案。该方案具有简单、易用、易于工程实现的特点。
3、本地缓存框架虽然可以利用JDK Map快速构建缓存,但是缓存功能还是比较弱。因为在实际场景中,我们可能需要给缓存添加缓存统计、过期、淘汰策略等功能。
于是,本地缓存框架应运而生。流行的Java缓存框架包括:Ehcache、Google Guava、Caffine Cache。
下图展示了使用Caffine框架的示例。虽然本地缓存框架很强大,但是本地缓存的缺点仍然很明显。
1、高并发场景下,应用重启后,本地缓存会失效,系统负载比较大,需要很长时间才能恢复; 2、每个应用节点都会维护自己独立的缓存,缓存同步变得很头疼。 。
4 分布式缓存 分布式缓存是指将缓存数据分布在多台机器上,以提高缓存容量和并发读写能力的缓存系统。分布式缓存通常由多台机器组成一个集群。
每台机器都运行相同的缓存服务进程,缓存数据均匀分布在集群中的节点之间。 Redis是分布式缓存的首选。
即使提到缓存,也是很多后端工程师首先想到的。下图是中私车订单的Redis集群架构。
将Redis集群拆分为4个分片,每个分片包含一个master和一个slave,并且主从可以切换。应用程序A根据不同的缓存键访问不同的分片。
与本地缓存相比,分布式缓存具有以下优势: 1、容量和性能可扩展。通过增加集群中的机器数量,可以扩展缓存容量和并发读写能力。
同时,缓存的数据在应用程序之间共享。2、高可用性由于数据分布在多台机器上,即使其中一台机器出现故障,缓存服务也能继续提供服务。
但分布式缓存的缺点也不容忽视。 1、网络延迟分布式缓存通常需要网络通信来读写数据,这可能会造成网络延迟等问题。
与本地缓存相比,响应时间更长。 2.复杂性分布式缓存需要考虑序列化、数据碎片、缓存大小等问题,比本地缓存复杂。
作者曾经认为,如果没有脑基缓存,系统会更快,但直到一次意外,分布式缓存的概念才彻底改变。 2017年,同事们开发了一个实时评分系统。
所有请求均从分布式缓存Memcached中获取并直接响应。一般情况下,从缓存中查询数据是很快的,但是如果在线用户稍微多一点,整个系统就会变得特别卡。
通过jstat命令,我们发现GC频率极高。只需几个请求,新生代就满了,CPU消耗全部在GC线程上。
初步判断是缓存值太大。正如预期的那样,缓存大小约为 k 到 k。
解决过程相当曲折,分为两步:将新生代大小从原来的2G修改为4G,精简缓存数据大小(从平均k左右调整为80k左右);将缓存分为两部分。一部分是全量数据,第二部分是增量数据(数据量很小)。
页面第一次请求拉取全量数据,当分数发生变化时,通过websocket推送增量数据。经过这次优化,笔者了解到,虽然缓存可以提高整体速度,但是在高并发场景下,缓存对象的大小仍然是一个需要关注的点,一不小心就可能会发生意外。
另外,我们还需要合理控制读取策略,尽量减少GC的频率,从而提高整体性能。 5 多级缓存开源中文网站最初使用的是本地缓存框架Ehcache。
后来,随着访问量的增加,出现了一个可怕的问题:“由于Java程序更新非常频繁,所以每次更新都必须重新启动,一旦重新启动,Ehcache缓存中的数据就会全部被清空。重新启动后,如果大量访问进来的话,开源中文数据库基本上很快就会崩溃。
”因此,开源中国开发了多级缓存框架J2Cache,采用多级缓存Ehcache + Redis。多级缓存有以下优点:离用户越近,速度越快;降低分布式缓存查询频率,减少序列化和反序列化的CPU消耗;大大减少了网络IO和带宽消耗。
本地缓存作为一级缓存,分布式缓存作为二级缓存。首先从一级缓存中查询。
如果可以查询到数据,则直接返回。否则从二级缓存中查询。
如果二级缓存中可以查询到数据,则将数据回填到一级缓存中并返回数据。如果在二级缓存中找不到该查询,则从数据源中查询,并将结果分别回填到一级缓存和二级缓存中。
2018年,笔者所在的一家电商公司需要优化App首页界面的性能。作者花了两天左右的时间完成了整个方案,采用了两级缓存模式,并利用了Guava的延迟加载机制。
整体架构如下图所示: 缓存读取流程如下: 1. 业务网关刚启动时,本地缓存没有数据,读取Redis缓存,如果Redis缓存中没有数据,通过RPC调用导购服务读取数据,然后将数据写入本地缓存和Redis;如果Redis缓存不为空,则将缓存数据写入本地缓存。 2、由于步骤1已经预热了本地缓存,后续请求直接读取本地缓存返回给用户。
3、Guava配置了刷新机制,每隔一段时间就会调用自定义的LoadingCache线程池(最大线程5个,核心线程5个),将导购服务的数据同步到本地缓存和Redis。优化后性能非常好,平均时间在5ms左右。
一开始我以为出问题的几率很小,但是有一天晚上,我突然发现应用程序首页显示的数据有时相同,有时不同。 也就是说:虽然LoadingCache线程一直在调用接口更新缓存信息,但是各个服务器本地缓存中的数据并不完全一致。
它解释了两个非常重要的点: 1. 延迟加载仍然可能会导致多台机器上的数据不一致。 2、LoadingCache线程池数量配置不合理,导致线程堆积。
最终,我们的解决方案是: 1、延迟加载 结合消息机制更新缓存数据,即:当导购服务的配置发生变化时,通知业务网关重新拉取数据并更新缓存。 2、适当增大LoadigCache的线程池参数,在线程池中埋点,监控线程池的使用情况。
当线程繁忙时,可以发出警报,然后动态修改线程池参数。 6 No Silver Bullet No Silver Bullet 是 Fred Brooks 在 2017 年发表的一篇关于软件工程的经典论文。
论文强调,不存在真正的银弹,所谓银弹是指没有任何技术或方法可以在十年内将软件工程的生产力提高十倍。通俗地说:技术中的每一个问题都没有一刀切的解决方案。
技术本质上是为了解决问题而存在的。每个问题都有其独特的环境和约束。
没有一种通用的技术或工具能够完美解决所有问题。尽管技术不断发展和进步,但对于复杂的问题,仍然需要结合多种技术和方法进行系统思考和综合方案设计,才能得到最优解决方案。
回到文章开头的问题,如何说服技术老大使用Redis?如果应用是单体应用,缓存不需要共享,通过定时任务刷新缓存对业务没有影响,并且本地内存能容纳缓存对象的大小,那么你技术老大的解决方案就是没有问题。如果应用业务复杂,需要利用缓存来提升系统性能,而分布式缓存共享特性使得研发速度更快,那么从研发成本、代码维护、人力模型等多个角度来看,Redis确实是一个不错的选择。
向技术老大提出自己的意见。总而言之,在技术上,没有灵丹妙药。
我们需要不断探索和研究新技术,但同时也需要认识到技术的局限性,不盲目追求所谓的“银弹”,而是根据具体问题和需求选择最合适的解决方案。
版权声明:本文内容由互联网用户自发贡献,本站不拥有所有权,不承担相关法律责任。如果发现本站有涉嫌抄袭的内容,欢迎发送邮件 举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。
标签:
相关文章
06-21
06-18
06-18
最新文章
【玩转GPU】ControlNet初学者生存指南
【实战】获取小程序中用户的城市信息(附源码)
包雪雪简单介绍Vue.js:开学
Go进阶:使用Gin框架简单实现服务端渲染
线程池介绍及实际案例分享
JMeter 注释 18 - JMeter 常用配置组件介绍
基于Sentry的大数据权限解决方案
【云+社区年度征文集】GPE监控介绍及使用