缓存冷启动问题
如图所示,redis中没有数据,导致大量请求 打给了 mysql,mysql很难抗住大量的请求。
缓存预热
-
我们提前给redis中写入部分数据,再提供服务
我们肯定不可能将所有数据都写入redis
- 因为数据量过大,写入耗费时间太长了,**
- redis内存资源有限,无法容纳全部的数据
因此我们可以将部分热数据写入redis,而不是写入所有的数据
-
需要根据当天的具体访问情况,实时统计出访问频率较高的热数据(热数据统计)
-
然后将访问频率较高的热数据写入redis中
-
然后将写入了热数据的redis对外提供服务,这样就不至于冷启动,直接让数据库裸奔
缓存预热方案的目的就是要让redis中尽可能包含热数据,避免mysql裸奔(扛不住高并发)。
具体方案
因为方案使用到了storm完成实时计算,所以这里罗列了关于storm的基本原理,其实也可以采用flink等实时计算基数。
storm与流计算
Storm 是一个开源的分布式实时计算框架,可以以简单、可靠的方式进行大数据流的处理。通常用于实时分析,在线机器学习、持续计算、分布式 RPC、ETL 等场景。
Storm 与 Hadoop对比
Hadoop 采用 MapReduce 处理数据,而 MapReduce 主要是对数据进行批处理,这使得 Hadoop 更适合于海量数据离线处理的场景。而 Storm 的设计目标是对数据进行实时计算,这使得其更适合实时数据分析的场景。
Storm 与 Spark Streaming对比
Spark Streaming 并不是真正意义上的流处理框架。 Spark Streaming 接收实时输入的数据流,并将数据拆分为一系列批次,然后进行微批处理。只不过 Spark Streaming 能够将数据流进行极小粒度的拆分,使得其能够得到接近于流处理的效果,但其本质上还是批处理(或微批处理)。
流处理
什么是流处理
与流数据相对的是静态数据处理。
在流处理之前,数据通常存储在数据库或文件系统中,应用程序根据需要查询或计算数据,这就是传统的静态数据处理架构。Hadoop 采用 HDFS 进行数据存储,采用 MapReduce 进行数据查询或分析,这就是典型的静态数据处理架构。
而流处理则是直接对运动中数据的处理,在接收数据的同时直接计算数据。实际上,在真实世界中的大多数数据都是连续的流,如传感器数据,网站用户活动数据,金融交易数据等等 ,所有这些数据都是随着时间的推移而源源不断地产生。
接收和发送数据流并执行应用程序或分析逻辑的系统称为流处理器。流处理器的基本职责是确保数据有效流动,同时具备可扩展性和容错能力,Storm 和 Flink 就是其代表性的实现。
流处理的优点
- 可以立即对数据做出反应:降低了数据的滞后性,使得数据更具有时效性,更能反映对未来的预期;
- 可以处理更大的数据量:直接处理数据流,并且只保留数据中有意义的子集,然后将其传送到下一个处理单元,通过逐级过滤数据,从而降低实际需要处理的数据量;
- 更贴近现实的数据模型:在实际的环境中,一切数据都是持续变化的,想要通过历史数据推断未来的趋势,必须保证数据的不断输入和模型的持续修正,典型的就是金融市场、股票市场,流处理能更好地处理这些场景下对数据连续性和及时性的需求;
- 分散和分离基础设施:流式处理减少了对大型数据库的需求。每个流处理程序通过流处理框架维护了自己的数据和状态,这使其更适合于当下最流行的微服务架构。
方案讲解
本文的项目背景是一个电商详情页,通过nginx去请求后端服务,获取商品信息。
-
nginx+lua将访问流量上报到kafka中
-
storm从kafka中消费数据,实时统计出每个商品的访问次数,访问次数基于LRU内存数据结构的存储方案(
LRUMap) -
每个storm task启动的时候,基于zk分布式锁,将自己的id写入zk同一个节点中
-
每个storm task负责完成自己这里的热数据的统计(每隔一段时间,就遍历一下这个map,然后维护一个前3个商品的list,更新这个list)
-
写一个后台线程,每隔一段时间,比如1分钟,都将排名前3的热数据list,同步到zk中去,存储到当前storm task对应的一个znode中去
-
我们需要启动一个服务
- 服务可能部署了很多个实例
- 每次服务启动的时候,就会去拿到一个storm task的列表,然后根据taskid,一个一个的去尝试获取taskid对应的znode的zk分布式锁
- 如果能获取到分布式锁的话,那么就将那个storm task对应的热数据的list取出来
- 然后将数据从mysql中查询出来,写入缓存中,进行缓存的预热
编码实现思路
-
服务启动的时候,进行缓存预热(多个服务实例)
-
从zk中读取taskid列表
-
依次遍历每个taskid,尝试获取分布式锁,如果获取不到,快速报错,不要等待,因为说明已经有其他服务实例在预热了
-
直接尝试获取下一个taskid的分布式锁
-
即使获取到了分布式锁,也要检查一下这个taskid的预热状态,如果已经被预热过了,就不再预热了
-
执行预热操作,遍历productid列表,查询数据,然后写ehcache和redis
-
预热完成后,设置taskid对应的预热状态
图解
参考文章
-
中华石杉—亿级流量电商详情页系统(缓存架构、高可用架构、微服务架构)