• 秒杀系统中为什么需要预热功能?
  • 发布于 2个月前
  • 236 热度
    0 评论
一. 前言

预热是在整个秒杀环节中很容易忽视,但是非常重要的一环,千万不要小瞧这一步的作用。


二. 为什么要预热
2.1 预热包含哪些内容
预热主要是对数据和应用的预热,主要包括2个大的板块 :
数据预热 :主要是在工具层面进行预热,包括 MySQL 页缓存的预热, ES fileCache 的预热
应用预热 :主要是对业务里面的高频数据进行缓存初始化,把这些缓存更新到缓存系统中

2.2 为什么要去预热这些数据?
原因一: 基于不同存储介质的性能差距
内存的速度是远大于磁盘的,哪怕固态存储,在读写速度上,内存也是遥遥领先。
部分组件例如 MySQL 会将数据存储到磁盘中,使用时加载到内存中进行处理。
// 快多少 ? 
按照资料里面说的,内存大概会比 SSD 快 10~1000倍

// 小扩展 :为什么内存比磁盘快 ?
- 硬件的读写速度导致内存读写在基础层面就更快
- 内存不需要磁盘寻址,支持随机IO (主要针对于机械硬盘)
- CPU 在访问存储介质时,时可以直接访问到第一层存储介质 (内存,寄存器,高速缓存)
    - SSD所处的下一层是要通过内存中转的

原因二 :基于组件逻辑的回表方式
上面说原因一的时候提到了一点,一般存储到硬盘上的数据在处理的时候会放在内存里面 (当然Redis是直接内存中处理)。
如果在不预热的情况下,读取一个数据需要以下几个步骤 :
S1 : 查询内存缓存中是否有数据
S2 : 从磁盘中通过索引等手段查询到数据,将数据加载到内存中
S3 : 在内存中处理数据并且返回(内存中的数据暂时被作为缓存存在)
S4 : 查询当前数据后面的数据 ,如果在缓存中 (MySQL中一页一页获取数据)则直接返回,否则去磁盘继续执行 1-3步骤

这里面涉及到一个关键点 :以 MySQL 为主的组件通常是一页页的拉取数据,这是基于其数据存储方式的原因,所以连续读写的时候不需要再回存储进行查询了。


原因三 : 随机 IO 和 顺序 IO 带来的性能差距
有的人可能听过这,但是没有太过理解。顺序和随机带来的性能损耗是非常大的。
有很多数据结构为了使用顺序读写带来的优势,在底层结构上都会通过双向链表将连续数据串联起来。
如果是 SSD 则相对还好,如果是磁盘,则每一次随机读写都需要磁盘重新寻址,重新查找数据。

而数据预热在我看来就是把磁盘上的随机数据全部加载到内存中,形成一套虚拟的顺序数据,减少寻址和磁盘查询的时间。


三. 预热的方案
3.1 应用层面的预热

应用层面的预热就比较简单了 ,主要是把热点数据筛选出来放在缓存中,不过可以考虑是 :缓存是可以分本地和远端的。单独说这个是因为 : Redis 调用的耗时其实一点也不低,基本类型的调用都会花费 2-3ms , 而数据量越大,这个耗时就会越多。


简单算下, 哪怕平均耗时10秒 ,那么100万数据就得在这个上面花费1万秒,我们确实可以通过 Redis 的IO复用以及吞吐让这个1万秒均摊到不同的路上面,但是时间确实花出去了。


但是如果说用本地缓存呢 ?我们提前把数据预热到本地内存中,这些数据本身不会发生变动(当然也可以通过通知等手段来更新这类变化)。本地缓存的方案有很多,可以通过定时任务 + 时间轮来进行过期。可以通过软引用等方式来保证应用缓存不会影响整体堆大小。使用也很简单,没有更新的情况下用个基础Map就足够了。

软引用(Soft Reference): 软引用用于描述还有用但非必须的对象。当 Java 虚拟机内存不足时,才会对这些对象进行回收。

这也是我们经常说到的二级缓存。

3.2 数据预热
数据预热就多了,但是根源就是让对应的数据源对数据进行缓存。

MySQL
MySQL 的数据预热主要是对 InnerDB 缓冲池进行预热 ,让在秒杀来临之前,将常用的数据加载到缓冲池中,以减少磁盘 I/O 操作,提高查询性能。
MySQL 预热可以不像 Redis 一样通过业务预执行的方式进行预热,可以直接通过 SQL 对全表进行预热 (全表量会稍大,要视情况而定)。
select  count(*) from user;

// 补充知识点一 : innodb_buffer_pool 
- innodb_buffer_pool 是一个内存区域,用于缓存被频繁读取的数据和索引
- innodb_buffer_pool 中存储的是 InnerDB 的数据页,数据页本身也是为了减少磁盘读取
- innodb_buffer_pool_size 配置用于设置该空间的大小

// 问题和思考 :
- innodb_buffer_pool 是有大小限制的,一般也就几G的容量
- 如何才能更好的缓存需要的数据值得思考,一张 user 表肯定没办法全量缓存

// 数据没有得到预热的直接表现 : 
- 缓冲池占用低, 内存占用低
- 在日常使用中 , 内存占用可能30%都不到,不进行预热会导致使用率低

ElasticSearch
ES 其实预热的需求不大,因为本身就是基于内存操作。
而且ES主要针对事后查询,在我看来预热的场景不多。
但是效果是一样的,ElasticSearch 一样有查询缓存,相同的思路进行处理即可。

Redis 预热
Redis 预热在上面的业务处理中就已经提到了,一般情况下我们也是通过业务代码的形式去处理。

一般最简单的就是把相关流程预跑一下,其他的都视各自情况而定吧。


总结
预热这一步是整个秒杀或者高并发操作的前提,做完这一步后,秒杀的前置处理流程就基本上完成了。
用户评论