【企业方案】如何在海量数据中找到热点Key(附JdHotKey解决方案)

news2024/9/27 19:23:17

文章目录

  • 1. 概述
  • 2. 热点Key问题分析
  • 3. 解决方案
    • 3.2 滑动窗口
    • 3.2 京东毫秒级热Key探测框架
      • 3.2.1 JdHotKey使用教程
      • 3.2.2 JdHotKey常用API

1. 概述

如何在海量数据找到热点Key???这时候难免有人回答“这不简单,在同一秒内访问达到一定阈值的Key,这些就是热点Key,然后我们将这些Key对应的值缓存到像Redis这样的缓存中,下次访问的时候如果缓存命中直接从缓存获取不就可以了了么?”

话是这样说,但是这里有一个前提,就是身为程序员的你在编写代码的时候已经预知了哪些Key会是热点Key,所以才会放进缓存。这样的操作适合于双十一这样的高并发大场景,因为双十一的商品肯定会被大量用户访问,所以放进缓存可以减少大量的MySQL压力。而这种热点Key被称为静态热点Key,也就是可以提前预测到的热点数据。

但是如果有这样的一个场景,阁下该如何应对?

打个比如(不严谨),在疫情之前,口罩并不是必需品,所以在淘宝、京东这些网上购物平台口罩的销量并不算高。可是疫情的时候,由于口罩急缺,导致口罩突然称为了一个热门商品,但是程序员在编写代码的时候可能并没有将口罩作为热点商品放进缓存中,这时候因为口罩变成了热门商品,这样势必会给系统资源带来严峻的挑战。这种热点Key被称为动态热点Key,就是不能提前预测到的热点数据。

这时候可能会有人回答**“我手动把他放进缓存不就好了么”**,虽然确实可以,但是使用的是Redis,确实可以直接放进缓存就可以了,但是总不能24小时一直人工监测热点Key,如果某个商品在凌晨称为热点Key,难不成让人24小时值班?成本较大,不太合适。

如果用的缓存时JVM缓存,也就是本地缓存,那么就需要停机再重新上线,这期间会导致服务不可用,不仅仅用户体验感不好,还会导致销售额有所影响。

下面就来分析一下程序运行当中该如何发现并缓存这些动态热点Key


2. 热点Key问题分析

想要解决动态热点Key问题,最最最重要的一个关键点就是让程序运行的时候自己发现某个key在某个时间内是否超过阈值,如果有,则将其缓存,否则则不是热点Key。

当然这只是个简单的思路,热点Key的探测还需要有以下特性

  1. 实时性
    • 因为Key往往时突发性瞬间就热了,根本不会给人工去添加Key到缓存的机会,其次热的时间不确定,可能一热起来就把集群打爆了。
    • 所以热Key,需要在Key刚开始有苗头的时候,它就已经被缓存了。
  2. 准确性
    • 累加数量,做到不误探,精准探测,保证探测出的热key是完全符合用户自己设定的阈值。
  3. 集群一致性
    • 这个比较重要,尤其是某些带删除key的场景,要能做到删key时整个集群内的该key都会删掉,以避免数据的错误。
  4. 高性能
    • 这个是核心之一,高性能带来的就是低成本
    • 做热key探测目的就是为了降低数据层的负载,提升应用层的性能,节省服务器资源。

在不影响实时性的情况下,要完成实时热key探测,所消耗的机器资源越少,那么经济价值就越大。


3. 解决方案

下面给出两种解决方案:

  1. 滑动窗口
  2. 京东毫秒级热Key探测框架

其中滑动窗口属于一个比较简单的实现方案,可能不太符合上面所说的热点Key的探测特性,但是比较适合一些小系统、并发量不太、大不用太严谨的系统。


3.2 滑动窗口

滑动窗口,其实就是限流算法中的滑动窗口。

在限流算法中,滑动窗口算法是指将固定时间进行划分,并且随着时间的流逝,进行移动,固定数量的可以移动的格子,进行计数并判断阀值,若超过阈值则进行限流操作。

img

于是,基于滑动窗口算法改动,我们可以将超过阈值进行限流操作改为超过阈值则将该Key认为是热点Key,然后将其缓存。

这就是滑动窗口的具体思路,代码可自行实现。


3.2 京东毫秒级热Key探测框架

京东根据多次被突发海量请求压垮数据层服务的场景,并时刻面临大量的爬虫刷子机器人用户的请求的经验,设计开发了一套通用轻量级热key探测框架——[JdHotkey](hotkey: 京东App后台中间件,毫秒级探测热点数据,毫秒级推送至服务器集群内存,大幅降低热key对数据层查询压力 (gitee.com))。这个框架历经多次高压压测和2020年京东618、双11大促考验。

JdHotKey可以对任意突发性的无法预先感知的热点数据,包括并不限于热点数据(如突发大量请求同一个商品)、热用户(如恶意爬虫刷子)、热接口(突发海量请求同一个接口)等,进行毫秒级精准探测到。然后对这些热数据、热用户等,推送到所有服务端JVM内存中,以大幅减轻对后端数据存储层的冲击,并可以由使用者决定如何分配、使用这些热key(譬如对热商品做本地缓存、对热用户进行拒绝访问、对热接口进行熔断或返回默认值)。这些热数据在整个服务端集群内保持一致性,并且业务隔离,worker端性能强悍。

img

它很轻量级,既不改redis源码也不改redis的客户端jar包,当然,它与redis没一点关系,完全不依赖redis。它是一个独立的系统,部署后,在server代码里引入jar,之后就像使用一个本地的HashMap一样来使用它即可。

框架自身会完成一切,包括对待测key的上报,对热key的推送,本地热key的缓存,过期、淘汰策略等等。框架会告诉你,它是不是个热key,其他的逻辑交给你自己去实现即可。

该框架由四部分组成:

  1. etcd集群
    • etcd作为一个高性能的配置中心,可以以极小的资源占用,提供高效的监听订阅服务。主要用于存放规则配置,各worker的ip地址,以及探测出的热key、手工添加的热key等。
  2. client端jar包
    • 就是在服务中添加的引用jar,引入后,就可以以便捷的方式去判断某key是否热key。
    • 该jar完成了key上报、监听etcd里的rule变化、worker信息变化、热key变化,对热key进行本地caffeine缓存等。
  3. worker端集群
    • worker端是一个独立部署的Java程序,启动后会连接etcd,并定期上报自己的ip信息,供client端获取地址并进行长连接。
    • 之后,主要就是对各个client发来的待测key进行累加计算,当达到etcd里设定的rule阈值后,将热key推送到各个client。
  4. dashboard控制台
    • 控制台是一个带可视化界面的Java程序,也是连接到etcd,之后在控制台设置各个APP的key规则,譬如2秒出现20次算热key。
    • 然后当worker探测出来热key后,会将key发往etcd,dashboard也会监听热key信息,进行入库保存记录。
    • 同时,dashboard也可以手工添加、删除热key,供各个client端监听。

网上对于JdHotKey的使用教程比较少,下面介绍以下如何使用该框架。


3.2.1 JdHotKey使用教程

第一步,安装etcd

在etcd下载页面下载对应操作系统的etcd,下载地址,使用3.4.x以上。

我这里下载的是Window版本。

image-20230803093716194


第二步,将其解压,并启动etcd.exe

image-20230803094745060


第三步,拷贝代码

前往hotkey: 京东App后台中间件,毫秒级探测热点数据,毫秒级推送至服务器集群内存,大幅降低热key对数据层查询压力 (gitee.com)将代码拉取下来。

image-20230803094919839


第四步,打开项目,运行SQL

在这个目录下会有一个SQL,需要创建一个hotkey_db的数据库

image-20230803095019727


第五步,启动worker

可以将将worker打包为jar,也可以直接在代码中启动(必须先启动etcd,否则报错)

image-20230803095321260


第六步,启动控制台dashboard

记得记得在yml中修改自己的MySQL地址和用户名和密码

image-20230803095522569


第七步,进入dashboard控制台

访问地址:IP:8081

因为我是本地启动,所以是127.0.0.1:8081

登录的账号密码默认admin 123456

image-20230803100008459

image-20230803100303615

在用户管理添加用户(重要的是所属APP)

image-20230803104006019


第八步,配置规则

image-20230803104146376

参数:

  1. key-(*代表任意以key为前缀),
  2. prefix-是否前缀,
  3. interval-间隔时间(秒),
  4. threshold-阈值,
  5. duration-缓存时间(秒),默认60

第九步,配置客户端

引入依赖

<dependency>
    <artifactId>hotkey-client</artifactId>
    <groupId>com.jd.platform.hotkey</groupId>
    <version>0.0.4-SNAPSHOT</version>
</dependency>

配置

@PostConstruct
public void initHotkey() {
    ClientStarter.Builder builder = new ClientStarter.Builder();
    // 注意,setAppName很重要,它和dashboard中相关规则是关联的。
    ClientStarter starter = builder.setAppName("sample")
            .setEtcdServer("http://127.0.0.1:2379")
            .setCaffeineSize(10)
            .build();
    starter.startPipeline();
}

编写测试接口

@RequestMapping("/get/{key}")
public Object get(@PathVariable String key) {
    String cacheKey = "skuId__" + key;
    if (JdHotKeyStore.isHotKey(cacheKey)) {
        log.info("hotkey:{}", cacheKey);
        //注意是get,不是getValue。getValue会获取并上报,get是纯粹的本地获取
        Object skuInfo = JdHotKeyStore.get(cacheKey);
        if (skuInfo == null) {
            Object theSkuInfo = "123" + "[" + key + "]" + key;
            JdHotKeyStore.smartSet(cacheKey, theSkuInfo);
            return theSkuInfo;
        } else {
            //使用缓存好的value即可
            return skuInfo;
        }

    } else {
        log.info("not hot:{}", cacheKey);
        return "123" + "[" + key + "]" + key;
    }
}

第十步,测试

在一秒钟内请求几次,因为我设置了2s内的阈值是3次,所以肯定会触发热Key的阈值。

image-20230803105957860

由打印的日志可以看见,一开始key还不是热key,当触发阈值之后就成为了热Key

image-20230803110118951

image-20230803110124441

3.2.2 JdHotKey常用API

  1. boolean JdHotKeyStore.isHotKey(String key):该方法会返回该key是否是热key,如果是返回true,如果不是返回false,并且会将key上报到探测集群进行数量计算。该方法通常用于判断只需要判断key是否热、不需要缓存value的场景,如刷子用户、接口访问频率等。
  2. Object JdHotKeyStore.get(String key):该方法返回该key本地缓存的value值,可用于判断是热key后,再去获取本地缓存的value值,通常用于redis热key缓存
  3. void JdHotKeyStore.smartSet(String key, Object value):方法给热key赋值value,如果是热key,该方法才会赋值,非热key,什么也不做。
  4. Object JdHotKeyStore.getValue(String key):该方法是一个整合方法,相当于isHotKey和get两个方法的整合,该方法直接返回本地缓存的value。 如果是热key,则存在两种情况,1是返回value,2是返回null。返回null是因为尚未给它set真正的value,返回非null说明已经调用过set方法了,本地缓存value有值了。 如果不是热key,则返回null,并且将key上报到探测集群进行数量探测。

最佳实践:

1 判断用户是否是刷子

if (JdHotKeyStore.isHotKey(“pin__” + thePin)) {
    //限流他,do your job
} 

2 判断商品id是否是热点

Object skuInfo = JdHotKeyStore.getValue("skuId__" + skuId);
if(skuInfo == null) {
    JdHotKeyStore.smartSet("skuId__" + skuId, theSkuInfo);
} else {
    //使用缓存好的value即可
}

或者这样:

if (JdHotKeyStore.isHotKey(key)) {
    //注意是get,不是getValue。getValue会获取并上报,get是纯粹的本地获取
    Object skuInfo = JdHotKeyStore.get("skuId__" + skuId);
    if(skuInfo == null) {
        JdHotKeyStore.smartSet("skuId__" + skuId, theSkuInfo);
    } else {
        //使用缓存好的value即可
    }

}

参考:

  1. 京东毫秒级热key探测框架设计与实践,已实战于618大促 (qq.com)
  2. hotkey: 京东App后台中间件,毫秒级探测热点数据,毫秒级推送至服务器集群内存,大幅降低热key对数据层查询压力 (gitee.com)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/832476.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用IDEA操作Mysql数据库

idea中自带了关于数据库的连接 首先要确保你的MySQL正在运行中 打开idea找到database&#xff08; view —> Tool Windows —> database&#xff09;&#xff0c;大家也可以定个快捷键&#xff0c;方便以后日常操作 就是这个样子&#xff0c;然后点加号 然后就可以编写执…

Mac提示文件:已损坏,无法打开。你应该把它移到废纸篓

文章目录 一、电脑信息二、打开任何来源设置三、更改应用程序拓展属性 一、电脑信息 我的是新版的Venture 13的系统。UI改的比较多。与之前的配置还是有很大的区别的。 打开下载的软件&#xff0c;显示已经损坏&#xff0c;打不开。抛开软件本身的问题外&#xff0c;一般是Ma…

【C语言学习】C语言的基础数据类型

一、数据类型 1.整型 short(短整型) int&#xff08;整型 long&#xff08;长整型&#xff09; long long&#xff08;长整型&#xff09;2.浮点型 float&#xff08;单精度型&#xff09; double&#xff08;双精度型&#xff09; long double3.字符类型 char…

【MMCV】mmpretrain/mmclassification概览、环境安装与验证

概览 MMPretrain 是一个全新升级的预训练开源算法框架,旨在提供各种强大的预训练主干网络, 并支持了不同的预训练策略。MMPretrain 源自著名的开源项目 MMClassification 和 MMSelfSup,并开发了许多令人兴奋的新功能。 目前,预训练阶段对于视觉识别至关重要,凭借丰富而强…

Android 性能调优之bitmap的优化

背景 Android开发中&#xff0c;加载图片过多、过大很容易引起OutOfMemoryError异常&#xff0c;即我们常见的内存溢出。因为Android对单个应用施加内存限制&#xff0c;默认分配的内存只有几M&#xff08;具体视不同系统而定&#xff09;。而载入的图片如果是JPG之类的压缩格…

Java POI 百万规模数据的导入和导出

目录 1、百万数据导入1.1 需求分析1.2 思路分析1.3 代码实现1.3.1 步骤分析1.3.2 自定义处理器1.3.3 自定义解析1.3.4 测试 2、百万数据导出2.1、概述2.2、解决方案分析2.3、原理分析2.4、百万数据的导出2.4.1、模拟数据2.4.2、思路分析2.4.3、代码实现2.4.4、测试结果 1、百万…

面试题:请说下什么是重绘和重排(回流)?他们的区别是什么?

答&#xff1a; ● 第一次渲染 ○ html结构 解析为 dom树 ○ css样式 解析为 样式规则 ○ dom树 和 样式规则 匹配下&#xff0c;生成渲染树&#xff01; ○ 接下来就是重排&#xff1a;根据渲染树&#xff0c;得到每个盒子的几何信息&#xff08;大小位置&#xff09; ○ 最后…

html纯前端压缩图片神器,js压缩图片保持原有像素,html图片压缩自定义大小,html/js实现图片压缩

前言 纯前端实现&#xff0c;关键使用image和canvas容器 使用jquery和layui方便简写 可自定义压缩质量、缩放大小、图片类型 打开index.html即可使用 效果 实现&#xff1a;https://download.csdn.net/download/weixin_43992507/88158384

springboot访问请求404的原因

是记录&#xff0c;可能出现错误 可能出现的原因 1.你请求的URL路径不对,比如说你请求的路径是/usr/list,GET方法,但是你UserController上面的RequestMapping是这个样子:RequestMapping(“user”)&#xff0c;有可能哈 2.前端的请求时GET方法&#xff0c;后端对应的处理函数的方…

中国篆刻艺术孙溟㠭作品《活着》

人人为生活挣扎着&#xff0c;做着不想做的事&#xff0c;说着不想说的话&#xff0c;为生活低头弯腰委屈求全人生苦多甜少&#xff0c;何时了&#xff01;何时了&#xff01;甜来人生到头了…… 孙溟㠭篆刻作品《活着》 孙溟㠭篆刻作品《活着》 孙溟㠭篆刻作品《活着》 文/…

SAP从放弃到入门系列之创建特殊库存转储预留

文章概览 一、思路二、过程2.1前台的主要过程&#xff1a;2.2 BAPI的实现过程&#xff1a; 之前写过几篇生产领料的思路包括代码&#xff0c;有兴趣的可以翻翻之前我发的文章。最近遇到既有项目专用物资、按单专用物资、通用物资合并领料的业务模式&#xff0c;所以领料的库存的…

《Java-SE-第二十七章》之常见的锁策略

前言 在你立足处深挖下去,就会有泉水涌出!别管蒙昧者们叫嚷:“下边永远是地狱!” 博客主页&#xff1a;KC老衲爱尼姑的博客主页 博主的github&#xff0c;平常所写代码皆在于此 共勉&#xff1a;talk is cheap, show me the code 作者是爪哇岛的新手&#xff0c;水平很有限&…

【腾讯云 Cloud studio 实战训练营】云端 IDE 构建移动端H5

&#x1f431; 个人主页&#xff1a;不叫猫先生&#xff0c;公众号&#xff1a;前端舵手 &#x1f64b;‍♂️ 作者简介&#xff1a;2022年度博客之星前端领域TOP 2&#xff0c;前端领域优质作者、阿里云专家博主&#xff0c;专注于前端各领域技术&#xff0c;共同学习共同进步…

【SpringBoot】有哪些优点+配置文件如何配置?

博主简介&#xff1a;想进大厂的打工人博主主页&#xff1a;xyk:所属专栏: JavaEE进阶 Spring 的诞⽣是为了简化 Java 程序的开发的&#xff0c;⽽ Spring Boot 的诞⽣是为了简化 Spring 程序开发 的。Spring Boot是一个开源的Java框架&#xff0c;用于快速构建应用程序和微服…

如何将文件写入数据库呢???(走过路过不要错过)

1.首先建立数据库。。。建立一个名为books的数据库&#xff0c;建立一个fs表。 create database if not exists books; use books; create table fs(id int unsigned auto_increment primary key ,name varchar(50) not null ,files longblob ); 假如你不喜欢代码建立&#x…

redux-promise-middleware和applyMiddleware的理解与使用

一、作用&#xff1a; applyMiddleware是一个中间件&#xff0c;通常和applyMiddleware结合使用&#xff0c;是dispatch与reducers之间的应用&#xff0c;用于处理dispatch发送的异步action操作 二、使用 1、安装redux-promise-middleware cnpm i redux-promise-middleware…

GB28181智能安全帽方案探究及技术实现

什么是智能安全帽&#xff1f;​ 智能安全帽是一种集成先进科技的安全帽&#xff0c;可基于GB28181规范&#xff0c;适用于铁路巡检、电力、石油化工等高风险行业的作业人员&#xff0c;以及消防、救援等紧急情况下的安全防护。 智能安全帽通常具有以下功能&#xff1a; 实时…

WEB开发的基础知识

WEB开发的基础知识 1、java SE 和 java EE 2、web开发的基本理念 3、URI和URL 4、http请求方式 5、get和post的区别 6、web开发行业术语

hdu Perfect square number

题意&#xff1a; 有n个数&#xff08;n<300&#xff09;&#xff0c;将其中的任意的一个数改为x&#xff08;x在[1,300]&#xff09;&#xff0c;求改之后&#xff0c;区间和为完全平方数的最大区间个数是多少 思路&#xff1a; 将a[x]改之后的区间个数等于&#xff1a;改…

ThinkPHP v6.0.8 CacheStore 反序列化漏洞

漏洞说明 1. 漏洞原理&#xff1a;ThinkPHP 6.0.8 CacheStore 会触发POP利用链子&#xff0c;造成任意命令执行 2. 组件描述&#xff1a; ThinkPHP是一个免费开源的&#xff0c;快速、简单的面向对象的轻量级PHP开发框架 3. 影响版本&#xff1a;V6.0.8 漏洞复现 1. 环境安…