知乎问题:如何说服技术老大用 Redis

科技资讯 投稿 6500 0 评论

知乎问题:如何说服技术老大用 Redis

缓存的世界很广阔,对于应用系统来讲,我们经常将缓存划分为本地缓存分布式缓存

本地缓存 :应用中的缓存组件,缓存组件和应用在同一进程中,缓存的读写非常快,没有网络开销。但各应用或集群的各节点都需要维护自己的单独缓存,无法共享缓存。

分布式缓存:和应用分离的缓存组件或服务,与本地应用隔离,多个应用可直接共享缓存。

1 缓存的本质

所谓的“更快”,本质上做到了如下两点:

    将原来需要实时计算的内容提前算好、把一些公用的数据进行复用,这可以减少 CPU 消耗,从而提升响应性能。

  • 将原来对网络、磁盘等较慢介质的读写访问变为对内存等较快介质的访问,从而提升响应性能。

升级硬件往往是更好的解决方案,即使需要一些额外的投入成本,也通常要优于引入缓存后可能带来的风险。

从运维角度来说,缓存会掩盖掉一些缺陷,让问题在更久的时间以后,出现在距离发生现场更远的位置上。

因此,缓存是把双刃剑

2 本地缓存 JDK Map

    HashMap

  • ConcurrentHashMap

  • LinkedHashMap

  • TreeMap

笔者曾经负责艺龙红包系统,红包活动就是存储在 ConcurrentHashMap 中,通过定时任务刷新缓存

1、红包系统启动后,初始化一个 ConcurrentHashMap 作为红包活动缓存 ;

3、定时任务每隔 30 秒,执行缓存加载方法,刷新缓存。

    红包系统是高并发应用,快速将请求结果响应给前端,大大提升用户体验;

  • 定时任务刷新缓存并不会影响红包系统的业务。

单体应用都使用这种方案,该方案的特点是简洁易用,工程实现也容易 。

3 本地缓存框架

因为现实场景里,我们可能需要给缓存添加缓存统计过期失效淘汰策略等功能。

本地缓存框架应运而生。

1、高并发的场景,应用重启之后,本地缓存就失效了,系统的负载就比较大,需要花较长的时间才能恢复;

缓存同步比较头疼。

4 分布式缓存

Redis 是分布式缓存的首选,甚至我们一提到缓存,很多后端工程师首先想到的就它。

1、容量和性能可扩展

2、高可用性

但是分布式缓存的缺点同样不容忽视。

1、网络延迟

2、复杂性

笔者曾经也认为无脑上缓存,系统就一定更快,但直到一次事故,对于分布式缓存的观念才彻底改变。

通过 jstat 命令发现 GC 频率极高,几次请求就将新生代占满了,而且 CPU 的消耗都在 GC 线程上。初步判断是缓存值过大导致的,果不其然,缓存大小在 300k 到 500k 左右。

    修改新生代大小,从原来的 2G 修改成 4G,并精简缓存数据大小 (从平均 300k 左右降为 80k 左右;
  1. 缓存拆成两个部分,第一部分是全量数据,第二部分是增量数据(数据量很小)。页面第一次请求拉取全量数据,当比分有变化的时候,通过 websocket 推送增量数据。

经过这次优化,笔者理解到:缓存虽然可以提升整体速度,但是在高并发场景下,缓存对象大小依然是需要关注的点,稍不留神就会产生事故。另外我们也需要合理地控制读取策略,最大程度减少 GC 的频率 , 从而提升整体性能。

5 多级缓存

后来随着访问量的激增,出现了一个可怕的问题:“因为 Java 程序更新很频繁,每次更新的时候都要重启。一旦重启后,整个 Ehcache 缓存里的数据都被清掉。重启后若大量访问进来的话,开源中国的数据库基本上很快就会崩掉”。

J2Cache,使用了多级缓存 Ehcache + Redis

    离用户越近,速度越快;
  1. 减少分布式缓存查询频率,降低序列化和反序列化的 CPU 消耗;
  2. 大幅度减少网络 IO 以及带宽消耗。

本地缓存做为一级缓存,分布式缓存做为二级缓存,首先从一级缓存中查询,若能查询到数据则直接返回,否则从二级缓存中查询,若二级缓存中可以查询到数据,则回填到一级缓存中,并返回数据。若二级缓存也查询不到,则从数据源中查询,将结果分别回填到一级缓存,二级缓存中。

1、业务网关刚启动时,本地缓存没有数据,读取 Redis 缓存,如果 Redis 缓存也没数据,则通过 RPC 调用导购服务读取数据,然后再将数据写入本地缓存和 Redis 中;若 Redis 缓存不为空,则将缓存数据写入本地缓存中。

3、Guava 配置了 refresh 机制,每隔一段时间会调用自定义 LoadingCache 线程池(5个最大线程,5个核心线程)去导购服务同步数据到本地缓存和 Redis 中。

也就是说: 虽然 LoadingCache 线程一直在调用接口更新缓存信息,但是各个 服务器本地缓存中的数据并非完成一致。 说明了两个很重要的点:

2、LoadingCache 线程池数量配置的不太合理, 导致了线程堆积

1、惰性加载结合消息机制来更新缓存数据,也就是:当导购服务的配置发生变化时,通知业务网关重新拉取数据,更新缓存。

动态修改线程池参数。

6 没有银弹

没有银弹是 Fred Brooks 在 1987 年所发表的一篇关于软件工程的经典论文。

通俗来讲:在技术领域中没有一种通用的解决方案可以解决所有问题

虽然技术不断发展和进步,但是对于复杂的问题,仍需要结合多种技术和方法,进行系统性的思考和综合性的解决方案设计,才能得到最优解决方案。

假如应用就是一个单体应用,缓存可以不共享,通过定时任务刷新缓存对业务没有影响,而且本地内存可以 Hold 住缓存的对象大小,那么你的技术老大的方案没有问题。

总而言之,在技术领域中,没有银弹。我们需要不断探索和研究新的技术,但同时也需要认识到技术的局限性,不盲目追求所谓的“银弹”,而是结合具体问题和需求,选择最适合的解决方案。


点赞、在看、转发一下,你的支持会激励我输出更高质量的文章,非常感谢!

编程笔记 » 知乎问题:如何说服技术老大用 Redis

赞同 (28) or 分享 (0)
游客 发表我的评论   换个身份
取消评论

表情
(0)个小伙伴在吐槽