秒杀 :千万别忘了预热这一步

👈👈👈 欢迎点赞收藏关注哟

首先分享之前的所有文章 >>>> 😜😜😜
文章合集 : 🎁 juejin.cn/post/694164...
Github : 👉 github.com/black-ant
CASE 备份 : 👉 gitee.com/antblack/ca...

一. 前言

这周一直在玩AI绘画,蛮好玩的,所以其他的就不深入了,主要走一篇秒杀的流程点。

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

二. 为什么要预热

2.1 预热包含哪些内容

预热主要是对数据和应用的预热,主要包括2个大的板块 :

  • 数据预热 :主要是在工具层面进行预热,包括 MySQL 页缓存的预热, ES fileCache 的预热
  • 应用预热 :主要是对业务里面的高频数据进行缓存初始化,把这些缓存更新到缓存系统中

2.2 为什么要去预热这些数据?

原因一: 基于不同存储介质的性能差距

内存的速度是远大于磁盘的,哪怕固态存储,在读写速度上,内存也是遥遥领先。

部分组件例如 MySQL 会将数据存储到磁盘中,使用时加载到内存中进行处理。

java 复制代码
// 快多少 ? 
按照资料里面说的,内存大概会比 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 对全表进行预热 (全表量会稍大,要视情况而定)。

java 复制代码
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 预热在上面的业务处理中就已经提到了,一般情况下我们也是通过业务代码的形式去处理。

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

总结

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

关联文档 :

以下是整个秒杀系列涉及到的文章 :

秒杀 :用CDN把请求处理在服务器之外

秒杀 : 做一个完善的全链路日志实现方案有多简单

秒杀 :负载均衡--为什么他们开口就说百万并发

花20分钟白嫖一整套 DevOps 发布方案

再次白嫖 ,30分钟搭建自己的 DevOps + K8S 集群

随笔 : 不怎么优雅的 K8S 拉取 Docker 私服镜像流程

相关推荐
雷神乐乐10 分钟前
File.separator与File.separatorChar的区别
java·路径分隔符
小刘|14 分钟前
《Java 实现希尔排序:原理剖析与代码详解》
java·算法·排序算法
逊嘘33 分钟前
【Java语言】抽象类与接口
java·开发语言·jvm
morris13141 分钟前
【SpringBoot】Xss的常见攻击方式与防御手段
java·spring boot·xss·csp
monkey_meng1 小时前
【Rust中的迭代器】
开发语言·后端·rust
余衫马1 小时前
Rust-Trait 特征编程
开发语言·后端·rust
monkey_meng1 小时前
【Rust中多线程同步机制】
开发语言·redis·后端·rust
七星静香1 小时前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
Jacob程序员1 小时前
java导出word文件(手绘)
java·开发语言·word
ZHOUPUYU1 小时前
IntelliJ IDEA超详细下载安装教程(附安装包)
java·ide·intellij-idea