高并发场景下的热点key问题探析与应对策略

目录

一、问题描述

二、发现机制

三、解决策略分析

 (一)解决策略一:多级缓存策略

客户端本地缓存

代理节点本地缓存

 (二)解决策略二:多副本策略

 (三)解决策略三:热点 Key 拆分与动态分散

四、总结


干货分享,感谢您的阅读!

在高并发场景下,缓存作为前置查询机制,显著减轻了数据库的压力,提高了系统性能。然而,这也带来了缓存失效、增加回溯率等风险。常见的问题包括缓存穿透、缓存雪崩、热Key和大Key等。这些问题如果不加以处理,会影响系统的稳定性和性能。因此,采用有效的缓存策略,如缓存空结果、布隆过滤器、缓存过期时间随机化、多级缓存等,对于保障系统在高并发情况下的可靠性至关重要。本次我们将详细探讨热点key及其应对策略。

历史缓存热门问题回顾:

热门问题具体分析和解决方案
缓存穿透高并发场景下的缓存穿透问题探析与应对策略-CSDN博客
缓存雪崩高并发场景下的缓存雪崩探析与应对策略-CSDN博客
缓存击穿高并发场景下的缓存击穿问题探析与应对策略-CSDN博客
大 Key问题高并发场景下的大 Key 问题及应对策略-CSDN博客
热点Key发现机制优化分布式系统性能:热key识别与实战解决方案-CSDN博客

一、问题描述

热点 key 问题是指某些数据的访问量非常高,超过了缓存服务器的处理能力。这种现象在电商促销、社交媒体热点等场景中特别常见。热点 key 问题主要有以下几个方面:

  1. 流量集中,达到物理网卡上限:当大量请求集中到某个热点 key 时,这些请求会被路由到相同的缓存服务器。随着流量增加,服务器的物理网卡可能达到带宽上限,无法再处理更多请求。
  2. 请求过多,缓存分片服务被打垮:缓存系统通常使用分片机制来分担负载。然而,热点 key 的访问量可能过高,单个分片无法处理,导致该分片服务被打垮。
  3. 缓存分片打垮,重建再次被打垮,引起业务雪崩:当某个缓存分片被打垮后,系统可能会尝试重建该分片。然而,重建过程中的负载再次集中到该分片上,导致分片再次被打垮,形成恶性循环,引起业务系统的雪崩。

二、发现机制

本部分可直接见:优化分布式系统性能:热key识别与实战解决方案-CSDN博客

在现代分布式系统中,热key问题已经成为影响系统性能和稳定性的重要因素之一。热key,指的是在分布式缓存系统中某些特定的key被频繁访问,导致这些key所在节点的负载过高,甚至可能导致系统瓶颈或崩溃。尽管我们可以通过本地缓存、热key备份和迁移等方式来解决热key问题,但如果热key已经出现而没有及时发现和处理,问题将变得更加棘手。因此,如何提前发现并及时处理热key,是保障系统稳定性和性能的关键。

通过人为预测,客户端监控,机器层面监控,Redis服务端Monitor以及热点发现系统等多种手段,可以及时识别并处理潜在的热点key。每种解决方案都有其独特的优势和局限性,应根据具体业务场景选择合适的策略进行实施。

在实施过程中,需要关注解决方案的实时性、成本效益以及对现有系统的影响。同时,建议采用综合的监控和预测机制,持续优化和调整策略,以确保系统在面对高并发和复杂业务场景时能够稳定可靠地运行。热key问题的解决不仅是技术层面的挑战,更是对系统架构设计和运维管理能力的综合考验。通过有效的热key管理,可以提升系统的响应速度和整体性能,为用户提供更加稳定和高效的服务体验。

三、解决策略分析

 (一)解决策略一:多级缓存策略

多级缓存策略通过在客户端和服务端都设置缓存层,以便将缓存离用户更近,从而减少对远程缓存服务器的访问。

客户端本地缓存

在客户端加入本地缓存,如使用 Guava Cache 或 Ehcache,热点数据可以直接命中本地缓存,从根本上减少热点请求到缓存服务的次数。

  • 优点:减少网络延迟,提高缓存命中率,降低远程缓存服务器压力。
  • 缺点:容量有限,容易受到业务数据的入侵。

可以通过改造 Redis SDK 集成本地缓存功能,从而对业务代码无感知:

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * @program: zyfboot-javabasic
 * @author: zhangyanfeng
 * @create: 2013-03-23 22:33
 **/
public class LocalCache {

    private static final LoadingCache<String, String> localCache = CacheBuilder.newBuilder()
            .maximumSize(1000)
            .expireAfterWrite(10, TimeUnit.MINUTES)
            .build(new CacheLoader<String, String>() {
                @Override
                public String load(String key) throws Exception {
                    // 默认返回空值,可以改为从远程缓存或数据库加载数据
                    return null;
                }
            });

    public static String get(String key) {
        try {
            return localCache.get(key);
        } catch (ExecutionException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static void put(String key, String value) {
        localCache.put(key, value);
    }

    public static void main(String[] args) {
        // 示例:设置和获取本地缓存
        LocalCache.put("hot_key", "hot_value");
        System.out.println("Local cache value: " + LocalCache.get("hot_key"));
    }
}

代理节点本地缓存

如果缓存集群为代理模式,可以在代理节点上添加本地缓存。代理节点可以水平扩展,通过分散压力解决容量有限的问题。

  • 优点:缓存容量可以扩展,通过代理节点减少远程缓存服务器的压力。
  • 缺点:性能稍逊于客户端本地缓存,因为代理节点距离用户较远。

 (二)解决策略二:多副本策略

多副本策略的基本思路是为热点 key 创建多个副本,并将这些副本分布在不同的缓存节点上。客户端在读取数据时,可以随机选择一个副本节点进行读取,从而分散读取请求,减轻单个节点的压力。多副本策略的实现需要解决以下几个问题:

  1. 副本创建和同步:需要确保热点 key 的多个副本在创建后能够及时同步更新,以保证数据一致性。
  2. 读取请求分发:客户端在读取数据时,需要能够随机选择一个副本节点进行读取。
  3. 一致性保证:需要处理多副本之间的数据一致性问题,尤其是在写操作较多的场景下。

以下是一个简单的多副本策略实现示例,基于 Redis 的主从复制机制:

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * @program: zyfboot-javabasic
 * @author: zhangyanfeng
 * @create: 2013-03-24 21:12
 **/
public class MultiReplicaCache {

    private static final int NUM_REPLICAS = 3;
    private static final List<JedisPool> replicaPools = new ArrayList<>();
    private static final Random random = new Random();

    static {
        for (int i = 0; i < NUM_REPLICAS; i++) {
            JedisPool pool = new JedisPool(new JedisPoolConfig(), "localhost", 6379 + i);
            replicaPools.add(pool);
        }
    }

    public static void set(String key, String value) {
        try (Jedis jedis = replicaPools.get(0).getResource()) {
            jedis.set(key, value);
        }
        for (int i = 1; i < NUM_REPLICAS; i++) {
            try (Jedis jedis = replicaPools.get(i).getResource()) {
                jedis.slaveof("localhost", 6379);
            }
        }
    }

    public static String get(String key) {
        int replicaIndex = random.nextInt(NUM_REPLICAS);
        try (Jedis jedis = replicaPools.get(replicaIndex).getResource()) {
            return jedis.get(key);
        }
    }

    public static void main(String[] args) {
        String key = "hot_key";
        String value = "hot_value";
        MultiReplicaCache.set(key, value);
        System.out.println("Cache value: " + MultiReplicaCache.get(key));
    }
}

可以看到直接的优点是:1.分散读取压力:多个副本可以显著分散读取请求,减少单个节点的压力;2.提高读取性能:通过多副本并行读取,提高系统的整体读取性能。

但重点需要关注其存在的两大基本问题

  • 一致性问题:多副本之间的数据同步可能会导致一致性问题,特别是在写操作频繁的情况下。
  • 资源消耗增加:创建多个副本会增加存储和网络资源的消耗。

 (三)解决策略三:热点 Key 拆分与动态分散

动态分散热点 key 的基本思路是在存储热点 key 时,将其拆分成多个子 key,并将这些子 key 分布到不同的分片上进行存储。在读取数据时,通过组合子 key 的结果来还原原始数据。这种方法可以显著分散对单个热点 key 的访问压力。

实现热点 key 动态分散思路:

  1. 拆分热点 Key:将一个热点 key 拆分成多个子 key。
  2. 分布式存储子 Key:将子 key 分布到不同的分片上进行存储。
  3. 组合读取子 Key:在读取数据时,通过组合子 key 的结果来还原原始数据。

简单实现如下:

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * @program: zyfboot-javabasic
 * @author: zhangyanfeng
 * @create: 2013-03-24 21:35
 **/
public class HotKeyDistribution {

    private static final int NUM_PARTS = 5;
    private static final List<JedisPool> shardPools = new ArrayList<>();
    private static final Random random = new Random();

    static {
        for (int i = 0; i < NUM_PARTS; i++) {
            JedisPool pool = new JedisPool(new JedisPoolConfig(), 
                                           "localhost", 6379 + i);
            shardPools.add(pool);
        }
    }

    public static void set(String key, String value) {
        int partLength = value.length() / NUM_PARTS;
        for (int i = 0; i < NUM_PARTS; i++) {
            String partKey = key + "_" + i;
            String partValue = value.substring(i * partLength, 
                                (i + 1) * partLength);
            try (Jedis jedis = shardPools.get(i % shardPools.size()).getResource()) {
                jedis.set(partKey, partValue);
            }
        }
    }

    public static String get(String key) {
        StringBuilder value = new StringBuilder();
        for (int i = 0; i < NUM_PARTS; i++) {
            String partKey = key + "_" + i;
            try (Jedis jedis = shardPools.get(i % shardPools.size()).getResource()) {
                value.append(jedis.get(partKey));
            }
        }
        return value.toString();
    }

    public static void main(String[] args) {
        String key = "hot_key";
        String value = "this_is_a_very_hot_key_value_with_large_size";
        HotKeyDistribution.set(key, value);
        System.out.println("Cache value: " + HotKeyDistribution.get(key));
    }
}

四、总结

高并发场景下的热点 key 问题是分布式系统中常见的挑战之一,直接影响系统的性能和稳定性。为了有效应对这一问题,可以采用多级缓存策略、多副本策略以及热点 Key 的拆分与动态分散等多种策略。在实施过程中,需要综合考虑系统的实时性需求、成本效益和对现有架构的影响,持续优化和调整策略,以确保系统在面对复杂的业务场景时能够稳定可靠地运行,为用户提供高效的服务体验。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

计算机组成原理——锁存器和触发器

文章目录 1. SR锁存器 1.1 电路结构 1.2 电路解析 2. 带en输入的SR锁存器 2.1 ​​​​​​​电路结构 2.2 工作原理 3. ​​​​​​​带En输入的D锁存器 3.1 电路结构 3.2 工作原理 4. ​​​​​​​边沿触发的D触发器 4.1 电路结构 4.2 工作原理 1. SR锁存器 …

Python | Leetcode Python题解之第207题课程表

题目&#xff1a; 题解&#xff1a; class Solution:def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:edges collections.defaultdict(list)indeg [0] * numCoursesfor info in prerequisites:edges[info[1]].append(info[0])indeg[info[…

C语言 | Leetcode C语言题解之第207题课程表

题目&#xff1a; 题解&#xff1a; bool canFinish(int numCourses, int** prerequisites, int prerequisitesSize, int* prerequisitesColSize) {int** edges (int**)malloc(sizeof(int*) * numCourses);for (int i 0; i < numCourses; i) {edges[i] (int*)malloc(0);…

AD PCB板子裁剪与泪滴设置

在剪裁板子时。首先&#xff0c;选择选择板子的机械层&#xff0c;之后选择画线。在原来的板子上画上自己想要裁剪的图形。如下下图 之后&#xff0c;选择按照所画的线裁剪板子即可&#xff0c;如下 在焊接PCB时&#xff0c;为了防止多次焊接导至焊盘脱落可以加大焊点的接触面积…

leetcode-19-回溯-组合问题(剪枝、去重)

引自代码随想录 一、[77]组合 给定两个整数 n 和 k&#xff0c;返回 1 ... n 中所有可能的 k 个数的组合。 示例: 输入: n 4, k 2 输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4]] 1、大致逻辑 k为树的深度&#xff0c;到叶子节点的路径即为一个结果 开始索引保证不…

[C++][设计模式][备忘录模式]详细讲解

目录 1.动机2.模式定义3.要点总结4.代码感受 1.动机 在软件构建过程中&#xff0c;某些对象的状态转换过程中&#xff0c;可能由于某中需要&#xff0c;要求程序能够回溯到对象之前处于某个点的状态。 如果使用一些公开接口来让其他对象得到对象的状态&#xff0c;便会暴露对象…

3D在线展览馆的独特魅力,技术如何重塑展览业的未来?

在数字化和虚拟现实技术迅猛发展的今天&#xff0c;3D在线展览馆已经成为一种颇具前景的创新形式。搭建3D在线展览馆不仅能够突破传统展览的时空限制&#xff0c;还能为参观者提供身临其境的体验&#xff0c;极大地提升展示效果和用户互动。 一、3D在线展览馆的意义 1、突破时空…

《Windows API每日一练》7.3 计时器时钟

知道如何使用Windows计时器之后&#xff0c;可以看看一些有用的计时器应用程序了。时钟是计时器最明显的应用&#xff0c;我们来看两个例子&#xff1a;一个是数字时钟&#xff0c;另一个是模拟时钟。 本节必须掌握的知识点&#xff1a; 第45练&#xff1a;7段数码管数字时钟 …

KV260视觉AI套件--PYNQ-DPU

目录 1. 简介 2. DPU 原理介绍 2.1 基本原理 2.2 增强型用法 3. DPU 开发流程 3.1 添加 DPU IP 3.2 在 BD 中调用 3.3 配置 DPU 参数 3.4 DPU 与 Zynq MPSoC互联 3.5 分配地址 3.6 生成 Bitstream 3.7 生成 BOOT.BIN 4. 总结 1. 简介 在《Vitis AI 环境搭建 &…

three.js - MeshPhongMaterial材质(实现玻璃水晶球效果)

1、概念 phong网格材质&#xff1a;Mesh - Phong - Material 一种用于具有镜面高光的光泽表面的材质。 它可以模拟&#xff0c;具有镜面高光的光泽表面&#xff0c;提供镜面反射效果。 MeshPhongMaterial&#xff1a; MeshPhongMaterial是一种基于Phong光照模型的材质&#…

Geeker-Admin:现代化的开源后台管理框架

Geeker-Admin&#xff1a;优雅管理&#xff0c;高效开发&#xff0c;尽在Geeker-Admin- 精选真开源&#xff0c;释放新价值。 概览 Geeker-Admin是一个基于Vue 3.4、TypeScript、Vite 5、Pinia和Element-Plus构建的开源后台管理框架。它为开发者提供了一套现代化、响应式的管理…

如何在Ubuntu20上离线安装joern(包括sbt和scala)

在Ubuntu 20上离线安装Joern&#xff0c;由于Joern通常需要通过互联网从其官方源或GitHub等地方下载&#xff0c;但在离线环境中&#xff0c;我们需要通过一些额外的步骤来准备和安装。&#xff08;本人水平有限&#xff0c;希望得到大家的指正&#xff09; 我们首先要做的就是…

【机器学习】Python sorted 函数

目录&#xff1a; 什么是sorted()函数列表降序排序应用到字符串自定义排序规则实际应用 Python中的内置函数——sorted()。 1. 什么是sorted()函数 在Python中&#xff0c;sorted()是一个内置函数&#xff0c;用于对任何可迭代对象&#xff08;如列表、元组、字符串等&…

jenkins 发布服务到linux服务器

1.环境准备 1.1 需要一台已经部署了jenkins的服务器&#xff0c;上面已经集成好了&#xff0c;jdk、maven、nodejs、git等基础的服务。 1.2 需要安装插件 pusblish over ssh 1.3 准备一台额外的linux服务器&#xff0c;安装好jdk 2.流程描述 2.1 配置jenkins&#xff0c;包括p…

每日一题——Python实现PAT乙级1090 危险品装箱(举一反三+思想解读+逐步优化)4千字好文

一个认为一切根源都是“自己不够强”的INTJ 个人主页&#xff1a;用哲学编程-CSDN博客专栏&#xff1a;每日一题——举一反三Python编程学习Python内置函数 Python-3.12.0文档解读 题目链接&#xff1a;https://pintia.cn/problem-sets/994805260223102976/exam/problems/typ…

LoadBalance 负载均衡

什么是负载均衡 负载均衡(Load Balance&#xff0c;简称 LB),是⾼并发,⾼可⽤系统必不可少的关键组件. 当服务流量增⼤时,通常会采⽤增加机器的⽅式进⾏扩容,负载均衡就是⽤来在多个机器或者其他资源中,按照⼀定的规则合理分配负载. 负载均衡的⼀些实现 服务多机部署时,开发⼈…

微积分-导数3(微分法则)

常见函数的导数 常量函数的导数 d d x ( c ) 0 \frac{d}{dx}(c) 0 dxd​(c)0 常量函数的图像是一条水平线 y c y c yc&#xff0c;它的斜率为0&#xff0c;所以我们必须有 f ′ ( x ) 0 f(x) 0 f′(x)0。从导数的定义来看&#xff0c;证明也很简单&#xff1a; f ′ …

44 - 50题高级字符串函数 / 正则表达式 / 子句 - 高频 SQL 50 题基础版

目录 1. 相关知识点2.例子2.44 - 修复表中的名字2.45 - 患某种疾病的患者2.46 - 删除重复的电子邮箱2.47 - 第二高的薪水2.48 - 按日期分组销售产品2.49 - 列出指定时间段内所有的下单产品2.50 - 查找拥有有效邮箱的用户 1. 相关知识点 相关函数 函数含义concat()字符串拼接upp…

MT6989(天玑9300)芯片性能参数_MTK联发科5G处理器

MT6989是联发科Dimensity旗舰系列的成员&#xff0c;旨在为旗舰5G智能手机供应商提供最先进的技术和性能。MT6989也是联发科目前最具创新和强大的5G智能手机芯片&#xff0c;具有领先的功耗效率&#xff0c;无与伦比的计算架构&#xff0c;有史以来最快和最稳定的5G调制解调器&…

MySQL之主从同步、分库分表

1、主从同步的原理 MySQL主从复制的核心是二进制日志 二进制日志&#xff08;binlog&#xff09;记录了所有DDL语句和DML语句&#xff0c;但不包括数据查询&#xff08;select、show&#xff09;语句。 1.1、复制分三步 master主库在事务提交时&#xff0c;会把数据变更记录…