当前位置: 首页 > news >正文

使用RedisCacheWriter#clean在线异步地批量扫描匹配删除缓存数据-spring-data-redis

1.背景

生产环境,某云的某个业务Redis实例,触发内存使用率,连续 3 次 平均值 >= 85 %告警。
运维同学告知,看看需要怎么优化或者升级配置?分享了其实例RDB的内存剖析链接。
通过内存剖析详情发现,存在某类未设置过期时间且无用的keys,其内存占用约3.8GB,内存占比25%

内存占比挺大,有确定的成本经济收益。
做事有动力啦!

Redis实例信息

某云Redis实例的基本信息

  • 实例规格:16G主从版
  • 版本:Redis 2.8(兼容3.0特性)

image.png

某云的Redis RDB内存剖析

  • 基本信息
    • 分析方法:使用已有备份集 (选择的备份文件:完成于)
  • 详情
  • Key内存占有情况
  • Key数量分布情况
  • Elements内存占用情况
  • Elements数量分布情况
  • Key过期时间分布 (内存)
  • Key过期时间分布 (数量)

image.png

2.目标

  • 在线异步地删除缓存数据
  • 不影响线上业务,不确定的风险可控(风险紧急预案)

3.结论先行

  • 在线清理了5GB+内存
  • 已使用内存总量,15.5GB -> 10.2GB

4.技术方案

变更三板斧:可灰度、可观测/可监控、可回滚

使用spring-data-redis提供的RedisCacheWriter#clean开源解决方案,在其基础上加入异步和并发控制。

  • 【批量策略】在线异步地批量扫描匹配删除,每批10/20个key
    • 先SCAN匹配,再批量DEL
    • SCAN(keyPattern) + DEL(allMatchKeys)
  • 【执行策略】预发环境,业务低峰时期执行
  • 【可观测】Redis实例性能监控,业务监控
  • 【风险紧急预案-兜底方案】删除容器实例,kill杀掉异步守护线程,停止执行(可回滚)

spring-boot版本

  • spring-data-redis-2.7.16
  • spring-boot-starter-data-redis-2.7.16

可观测-Redis实例性能监控

  • key模式: “message:queue:*_lock”
  • 清理时间: [2023-12-04 21:15:39.405, 2023-12-05 00:28:24.21]

清理途中,觉得每批10个key有些慢,调整到每批20个key。
【注意】应用重启后,会重新从头开始扫描,存在一段时间未删除keys,需要等一会才能看到删除效果。
不建议中途调整每批key数量!

CPU使用率 (%,平均值)

CPU使用率,增长1~3%

已使用内存总量 (Byte,求和)

已使用内存总量,15.5GB -> 10.22GB

image.png

image.png

image.png

平均时延 (us,平均值)

每批10个key,时延增长2~3微秒

每批20个key,时延增长7~13微秒

image.png

image.png

Keys 监控组 (Counts/s,求和)

del: 200

scan: 375

image.png

image.png

具体实现

scan批量策略,先批量扫描匹配,再批量删除,每批10/20个key,不断地迭代以上操作,直到数据被全部清理。


import java.nio.charset.StandardCharsets;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;import cn.hutool.core.thread.ThreadFactoryBuilder;
import com.spring.boot.redis.example.model.CacheKey;
import com.spring.boot.redis.example.service.CacheService;
import lombok.extern.slf4j.Slf4j;import org.springframework.data.redis.cache.BatchStrategies;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.StopWatch;/*** 缓存服务实现** @author guang.yi* @since 2023/7/30*/
@Slf4j
@Service("cacheService")
public class CacheServiceImpl implements CacheService {/*** 并发开关*/private final ConcurrentMap<String, Boolean> concurrentSwitch = new ConcurrentHashMap<>(16);private final ExecutorService executorService = new ThreadPoolExecutor(1, 1, 5L, TimeUnit.MINUTES,new ArrayBlockingQueue<>(1),new ThreadFactoryBuilder().setNamePrefix("cache-clean-").setDaemon(true).build());private final RedisConnectionFactory redisConnectionFactory;public CacheServiceImpl(RedisConnectionFactory redisConnectionFactory) {this.redisConnectionFactory = redisConnectionFactory;log.info("create CacheServiceImpl");}@Overridepublic boolean cleanCache(CacheKey cacheKey) {String keyPattern = cacheKey.getKeyPattern();// 避免多次重复地操作if (concurrentSwitch.putIfAbsent(keyPattern, Boolean.TRUE) == null) {// 异步地执行executorService.execute(() -> this.clean(cacheKey));return true;}return false;}private void clean(CacheKey cacheKey) {log.info("cleanCache start, cacheKey={}", cacheKey);StopWatch stopWatch = new StopWatch("cleanCache");stopWatch.start();this.clean(cacheKey.getCacheName(), cacheKey.getKeyPattern());stopWatch.stop();log.info("cleanCache end, cacheKey={}, stopWatch={}", cacheKey, stopWatch);}/*** 缓存Redis的历史数据清理* <pre>* 【批量策略】在线异步地批量扫描匹配删除,每批10个key* 先SCAN,再批量DEL* 【执行策略】预发环境,业务低峰时期* </pre>** @see org.springframework.data.redis.cache.RedisCacheWriter#clean* @see org.springframework.data.redis.cache.DefaultRedisCacheWriter#clean*/private void clean(String cacheName, String keyPattern) {// 【批量策略】SCAN,每批10个keyRedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory, BatchStrategies.scan(10));// 先SCAN,再批量DELredisCacheWriter.clean(cacheName, keyPattern.getBytes(StandardCharsets.UTF_8));}}

# .A.1. Core Properties
spring:# RedisPropertiesredis:database: 0host: "localhost"port: 6379timeout: 1sconnect-timeout: 300ms
#    client-name: "user-cache"
#    client-type: lettuce
#    sentinel:
#      master: ""
#      nodes: "host:port"
#    cluster:
#      nodes: "host:port"
#      max-redirects: 3
#    jedis:
#      pool:
#        enabled: true
#        max-idle: 8
#        min-idle: 0
#        max-active: 8
#        max-wait: 300ms
#        time-between-eviction-runs: 5mlettuce:shutdown-timeout: 100mspool:enabled: truemax-idle: 8min-idle: 0max-active: 8max-wait: -1time-between-eviction-runs: 5m

开源解决方案有哪些坑?

深入源代码,深究实现细节,趴开裤子看看底细。

源代码做了简化

开源解决方案结论

深入源代码看,scan批量策略的实现方案靠谱keys批量策略存在大坑,不靠谱。

scan批量策略,先批量扫描匹配,再批量删除,每批10/20个key,不断地迭代以上操作,直到数据被全部清理。

RedisCacheWriter#clean

org.springframework.data.redis.cache.RedisCacheWriter#clean

BatchStrategy批量策略,有keysscan两种,分别对应Redis的KEYSSCAN命令。

批量策略默认使用keys,对于真实业务使用场景,一点都不实用。
因为KEYS命令会先收集所有满足匹配条件的keys,等所有都收集好了,再一次性全量DEL删除命令。
对于大量的keys需要删除时,其操作可能夯住线上Redis实例,存在严重影响Redis实例干活的风险。


package org.springframework.data.redis.cache;import java.time.Duration;import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;/*** {@link RedisCacheWriter} provides low level access to Redis commands ({@code SET, SETNX, GET, EXPIRE,...}) used for* caching. <br />* The {@link RedisCacheWriter} may be shared by multiple cache implementations and is responsible for writing / reading* binary data to / from Redis. The implementation honors potential cache lock flags that might be set.* <p>* The default {@link RedisCacheWriter} implementation can be customized with {@link BatchStrategy} to tune performance* behavior.** @author Christoph Strobl* @author Mark Paluch* @since 2.0*/
public interface RedisCacheWriter extends CacheStatisticsProvider {/*** Create new {@link RedisCacheWriter} without locking behavior.** @param connectionFactory must not be {@literal null}.* @return new instance of {@link DefaultRedisCacheWriter}.*/static RedisCacheWriter nonLockingRedisCacheWriter(RedisConnectionFactory connectionFactory) {return nonLockingRedisCacheWriter(connectionFactory, BatchStrategies.keys());}/*** Create new {@link RedisCacheWriter} without locking behavior.** @param connectionFactory must not be {@literal null}.* @param batchStrategy must not be {@literal null}.* @return new instance of {@link DefaultRedisCacheWriter}.* @since 2.6*/static RedisCacheWriter nonLockingRedisCacheWriter(RedisConnectionFactory connectionFactory,BatchStrategy batchStrategy) {Assert.notNull(connectionFactory, "ConnectionFactory must not be null!");Assert.notNull(batchStrategy, "BatchStrategy must not be null!");return new DefaultRedisCacheWriter(connectionFactory, batchStrategy);}/*** Remove all keys following the given pattern.* 按照给定模式删除所有键。** @param name The cache name must not be {@literal null}.* @param pattern The pattern for the keys to remove. Must not be {@literal null}.*/void clean(String name, byte[] pattern);}

DefaultRedisCacheWriter#clean

源代码做了简化

RedisCacheWriter#clean默认实现是org.springframework.data.redis.cache.DefaultRedisCacheWriter#clean

通过批量策略清理缓存数据batchStrategy.cleanCache(connection, name, pattern)


package org.springframework.data.redis.cache;import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;import org.springframework.dao.PessimisticLockingFailureException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStringCommands.SetOption;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;/*** {@link RedisCacheWriter} implementation capable of reading/writing binary data from/to Redis in {@literal standalone}* and {@literal cluster} environments. Works upon a given {@link RedisConnectionFactory} to obtain the actual* {@link RedisConnection}. <br />* {@link DefaultRedisCacheWriter} can be used in* {@link RedisCacheWriter#lockingRedisCacheWriter(RedisConnectionFactory) locking} or* {@link RedisCacheWriter#nonLockingRedisCacheWriter(RedisConnectionFactory) non-locking} mode. While* {@literal non-locking} aims for maximum performance it may result in overlapping, non atomic, command execution for* operations spanning multiple Redis interactions like {@code putIfAbsent}. The {@literal locking} counterpart prevents* command overlap by setting an explicit lock key and checking against presence of this key which leads to additional* requests and potential command wait times.** @author Christoph Strobl* @author Mark Paluch* @author André Prata* @since 2.0*/
class DefaultRedisCacheWriter implements RedisCacheWriter {private final RedisConnectionFactory connectionFactory;private final Duration sleepTime;private final CacheStatisticsCollector statistics;private final BatchStrategy batchStrategy;/** (non-Javadoc)* @see org.springframework.data.redis.cache.RedisCacheWriter#clean(java.lang.String, byte[])*/@Overridepublic void clean(String name, byte[] pattern) {Assert.notNull(name, "Name must not be null!");Assert.notNull(pattern, "Pattern must not be null!");execute(name, connection -> {boolean wasLocked = false;try {if (isLockingCacheWriter()) {doLock(name, connection);wasLocked = true;}// 通过批量策略清理缓存数据long deleteCount = batchStrategy.cleanCache(connection, name, pattern);while (deleteCount > Integer.MAX_VALUE) {statistics.incDeletesBy(name, Integer.MAX_VALUE);deleteCount -= Integer.MAX_VALUE;}statistics.incDeletesBy(name, (int) deleteCount);} finally {if (wasLocked && isLockingCacheWriter()) {doUnlock(name, connection);}}return "OK";});}}

BatchStrategy批量策略

org.springframework.data.redis.cache.BatchStrategy


package org.springframework.data.redis.cache;import org.springframework.data.redis.connection.RedisConnection;/*** A {@link BatchStrategy} to be used with {@link RedisCacheWriter}.* <p>* Mainly used to clear the cache.* <p>* Predefined strategies using the {@link BatchStrategies#keys() KEYS} or {@link BatchStrategies#scan(int) SCAN}* commands can be found in {@link BatchStrategies}.** @author Mark Paluch* @author Christoph Strobl* @since 2.6*/
public interface BatchStrategy {/*** Remove all keys following the given pattern.** @param connection the connection to use. Must not be {@literal null}.* @param name The cache name. Must not be {@literal null}.* @param pattern The pattern for the keys to remove. Must not be {@literal null}.* @return number of removed keys.*/long cleanCache(RedisConnection connection, String name, byte[] pattern);}

BatchStrategies批量策略实现

org.springframework.data.redis.cache.BatchStrategies

BatchStrategy批量策略,有keysscan两种,分别对应Redis的KEYSSCAN命令。

scan批量策略,先批量扫描匹配,再批量删除,每批10/20个key,不断地迭代以上操作,直到数据被全部清理。

keys批量策略,对于真实业务使用场景,一点都不实用。
因为KEYS命令会先收集所有满足匹配条件的keys,等所有都收集好了,再一次性全量DEL删除命令。
对于大量的keys需要删除时,其操作可能夯住线上Redis实例,存在严重影响Redis实例干活的风险。


package org.springframework.data.redis.cache;import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Optional;import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.util.Assert;/*** A collection of predefined {@link BatchStrategy} implementations using {@code KEYS} or {@code SCAN} command.** @author Mark Paluch* @author Christoph Strobl* @since 2.6*/
public abstract class BatchStrategies {private BatchStrategies() {// can't touch this - oh-oh oh oh oh-oh-oh}/*** A {@link BatchStrategy} using a single {@code KEYS} and {@code DEL} command to remove all matching keys.* {@code KEYS} scans the entire keyspace of the Redis database and can block the Redis worker thread for a long time* on large keyspaces.* <p>* {@code KEYS} is supported for standalone and clustered (sharded) Redis operation modes.** @return batching strategy using {@code KEYS}.*/public static BatchStrategy keys() {return Keys.INSTANCE;}/*** A {@link BatchStrategy} using a {@code SCAN} cursors and potentially multiple {@code DEL} commands to remove all* matching keys. This strategy allows a configurable batch size to optimize for scan batching.* <p>* Note that using the {@code SCAN} strategy might be not supported on all drivers and Redis operation modes.** @return batching strategy using {@code SCAN}.*/public static BatchStrategy scan(int batchSize) {Assert.isTrue(batchSize > 0, "Batch size must be greater than zero!");return new Scan(batchSize);}/*** {@link BatchStrategy} using {@code KEYS}.*/static class Keys implements BatchStrategy {static Keys INSTANCE = new Keys();@Overridepublic long cleanCache(RedisConnection connection, String name, byte[] pattern) {// `KEYS`命令会先收集所有满足匹配条件的keys,等所有都收集好了,再一次性全量`DEL`删除命令byte[][] keys = Optional.ofNullable(connection.keys(pattern)).orElse(Collections.emptySet()).toArray(new byte[0][]);if (keys.length > 0) {connection.del(keys);}return keys.length;}}/*** {@link BatchStrategy} using {@code SCAN}.*/static class Scan implements BatchStrategy {private final int batchSize;Scan(int batchSize) {this.batchSize = batchSize;}@Overridepublic long cleanCache(RedisConnection connection, String name, byte[] pattern) {// 批量扫描匹配删除,每批10/20个key// 先SCAN匹配,再批量DEL// SCAN(keyPattern, match, batchSize) + DEL(allMatchKeys, batchSize)Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions().count(batchSize).match(pattern).build());long count = 0;PartitionIterator<byte[]> partitions = new PartitionIterator<>(cursor, batchSize);while (partitions.hasNext()) {List<byte[]> keys = partitions.next();count += keys.size();if (keys.size() > 0) {connection.del(keys.toArray(new byte[0][]));}}return count;}}/*** Utility to split and buffer outcome from a {@link Iterator} into {@link List lists} of {@code T} with a maximum* chunks {@code size}.** @param <T>*/static class PartitionIterator<T> implements Iterator<List<T>> {private final Iterator<T> iterator;private final int size;PartitionIterator(Iterator<T> iterator, int size) {this.iterator = iterator;this.size = size;}@Overridepublic boolean hasNext() {return iterator.hasNext();}@Overridepublic List<T> next() {if (!hasNext()) {throw new NoSuchElementException();}List<T> list = new ArrayList<>(size);while (list.size() < size && iterator.hasNext()) {list.add(iterator.next());}return list;}}
}

5.参考引用

  • Spring Data Redis / Redis / Redis Cache
  • redis-spring-boot-starter-example

献给杭州2023年的第一场雪❄️

2023.12.18

相关文章:

使用RedisCacheWriter#clean在线异步地批量扫描匹配删除缓存数据-spring-data-redis

1.背景 生产环境&#xff0c;某云的某个业务Redis实例&#xff0c;触发内存使用率&#xff0c;连续 3 次 平均值 > 85 %告警。 运维同学告知&#xff0c;看看需要怎么优化或者升级配置&#xff1f;分享了其实例RDB的内存剖析链接。 通过内存剖析详情发现&#xff0c;存在某…...

机器视觉:AI赋能缺陷检测,铸就芯片产品的大算力与高能效

导言&#xff1a;近年来&#xff0c;国内芯片行业快速发展&#xff0c;市场对芯片需求的不断增大&#xff0c;芯片的缺陷检测压力也越来越大。芯片产品在生产制造过程中&#xff0c;需要经历数道工序&#xff0c;每个生产环节的材料、环境、工艺参数等都有可能造成产品缺陷。不…...

(9)Linux Git的介绍以及缓冲区

&#x1f4ad; 前言 本章我们先对缓冲区的概念进行一个详细的探究&#xff0c;之后会带着大家一步步去编写一个简陋的 "进度条" 小程序。最后我们来介绍一下 Git&#xff0c;着重讲解一下 Git 三板斧&#xff0c;一般只要掌握三板斧就基本够用了。 缓冲区&#xff…...

华为云之ECS云产品快速入门

华为云之ECS云产品快速入门 一、ECS云服务器介绍二、本次实践目标三、创建虚拟私有云VPC1.虚拟私有云VPC介绍2.进入虚拟私有云VPC管理页面3.创建虚拟私有云4.查看创建的VPC 四、创建弹性云服务器ECS——Linux1.进入ECS购买界面2.创建弹性云服务器(Linux)——基础配置步骤3.创建…...

tcp 的限制 (TCP_WRAPPERS)

#江南的江 #每日鸡汤&#xff1a;青春是打开了就合不上的书&#xff0c;人生是踏上了就回不了头的路&#xff0c;爱情是扔出了就收不回的赌注。 #初心和目标&#xff1a;拿到高级网络工程师 TCP_WRAPPERs Tcp_wrappers 对于七层模型中是位于第四层的安全工具&#xff0c;他…...

如何保证架构的质量

1. 如何保证架构的质量: ①. 稳定性、健壮性(1). 系统稳定性: ①. 定义:a. 当一个实际的系统处于一个平衡的状态时,如果受到外来作用的影响时,系统经过一个过渡过程仍然能够回到原来的平衡状态.b. 可以说这个系统是稳定的,否则系统不稳定c. 如一根绳子绑着小球,处于垂直状态,…...

JavaWeb笔记之前端开发JavaScript

一、引言 1.1 简介 JavaScript一种解释性脚本语言&#xff0c;是一种动态类型、弱类型、基于原型继承的语言&#xff0c;内置支持类型。 它的解释器被称为JavaScript引擎&#xff0c;作为浏览器的一部分&#xff0c;广泛用于客户端的脚本语言&#xff0c;用来给HTML网页增加…...

SCAU:18063 圈中的游戏

18063 圈中的游戏 时间限制:1000MS 代码长度限制:10KB 提交次数:0 通过次数:0 题型: 编程题 语言: G;GCC;VC Description 有n个人围成一圈&#xff0c;从第1个人开始报数1、2、3&#xff0c;每报到3的人退出圈子。编程使用链表找出最后留下的人。输入格式 输入一个数n&a…...

.NET Core中鉴权 Authentication Authorization

Authentication: 鉴定身份信息&#xff0c;例如用户有没有登录&#xff0c;用户基本信息 Authorization: 判定用户有没有权限 使用框架提供的Cookie鉴权方式 1.首先在服务容器注入鉴权服务和Cookie服务支持 services.AddAuthentication(options > {options.DefaultAuthe…...

PyTorch深度学习实战(26)——卷积自编码器(Convolutional Autoencoder)

PyTorch深度学习实战&#xff08;26&#xff09;——卷积自编码器 0. 前言1. 卷积自编码器2. 使用 t-SNE 对相似图像进行分组小结系列链接 0. 前言 我们已经学习了自编码器 (AutoEncoder) 的原理&#xff0c;并使用 PyTorch 搭建了全连接自编码器&#xff0c;但我们使用的数据…...

Milvus实战:构建QA系统及推荐系统

Milvus简介 全民AI的时代已经在趋势之中&#xff0c;各类应用层出不穷&#xff0c;而想要构建一个完善的AI应用/系统&#xff0c;底层存储是不可缺少的一个组件。 与传统数据库或大数据存储不同的是&#xff0c;这种场景下则需要选择向量数据库&#xff0c;是专门用来存储和查…...

使用Docker部署Nexus Maven私有仓库并结合Cpolar实现远程访问

文章目录 1. Docker安装Nexus2. 本地访问Nexus3. Linux安装Cpolar4. 配置Nexus界面公网地址5. 远程访问 Nexus界面6. 固定Nexus公网地址7. 固定地址访问Nexus Nexus是一个仓库管理工具&#xff0c;用于管理和组织软件构建过程中的依赖项和构件。它与Maven密切相关&#xff0c;可…...

GEE-Sentinel-2月度时间序列数据合成并导出

系列文章目录 第一章&#xff1a;时间序列数据合成 文章目录 系列文章目录前言时间序列数据合成总结 前言 利用每个月可获取植被指数数据取均值&#xff0c;合成月度平均植被指数&#xff0c;然后将12个月中的数据合成一个12波段的时间数据合成数据。 时间序列数据合成 代码…...

【深度学习】语言模型与注意力机制以及Bert实战指引之二

文章目录 前言 前言 这一篇是bert实战的完结篇&#xff0c;准备中。...

计算机网络 网络层下 | IPv6 路由选择协议,P多播,虚拟专用网络VPN,MPLS多协议标签

文章目录 5 IPv65.1 组成5.2 IPv6地址5.3 从IPv4向IPv6过渡5.3.1 双协议栈5.3.2 隧道技术 6 因特网的路由选择协议6.1 内部网关协议RIP6.2 内部网关协议 OSPF基本特点 6.3 外部网关协议 BGP6.3.1 路由选择 6.4 路由器组成6.4.1 基本了解6.4.2 结构 7 IP多播7.1 硬件多播7.2 IP多…...

【MATLAB第83期】基于MATLAB的LSTM代理模型的SOBOL全局敏感性运用

【MATLAB第83期】基于MATLAB的LSTM代理模型的SOBOL全局敏感性运用 引言 在前面几期&#xff0c;介绍了敏感性分析法&#xff0c;本期来介绍lstm作为代理模型的sobol全局敏感性分析模型。 【MATLAB第31期】基于MATLAB的降维/全局敏感性分析/特征排序/数据处理回归问题MATLAB代…...

求奇数的和 C语言xdoj147

题目描述&#xff1a;计算给定一组整数中奇数的和&#xff0c;直到遇到0时结束。 输入格式&#xff1a;共一行&#xff0c;输入一组整数&#xff0c;以空格分隔 输出格式&#xff1a;输出一个整数 示例&#xff1a; 输入&#xff1a;1 2 3 4 5 0 6 7 输出&#xff1a;9 #inclu…...

全链路压力测试:解析其主要特点

随着信息技术的飞速发展和云计算的普及&#xff0c;全链路压力测试作为一种关键的质量保障手段&#xff0c;在软件开发和系统部署中扮演着至关重要的角色。全链路压力测试以模拟真实生产环境的压力和负载&#xff0c;对整个业务流程进行全面测试&#xff0c;具有以下主要特点&a…...

算法基础之约数个数

约数个数 核心思想&#xff1a; 用哈希表存每个质因数的指数 然后套公式 #include <iostream>#include <algorithm>#include <unordered_map>#include <vector>using namespace std;const int N 110 , mod 1e9 7;typedef long long LL; //long l…...

【ECharts】折线图

文章目录 折线图1折线图2折线图3示例 参考&#xff1a; Echarts官网 Echarts 配置项 折线图1 带X轴、Y轴标记线&#xff0c;其中X轴是’category’ 类目轴&#xff0c;适用于离散的类目数据。 let myChart echarts.init(this.$refs.line_chart2); let yList [400, 500, 6…...

web vue 项目 Docker化部署

Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段&#xff1a; 构建阶段&#xff08;Build Stage&#xff09;&#xff1a…...

XCTF-web-easyupload

试了试php&#xff0c;php7&#xff0c;pht&#xff0c;phtml等&#xff0c;都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接&#xff0c;得到flag...

java 实现excel文件转pdf | 无水印 | 无限制

文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

NLP学习路线图(二十三):长短期记忆网络(LSTM)

在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...

今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存

文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...

Mysql中select查询语句的执行过程

目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析&#xff08;Parser&#xff09; 2.4、执行sql 1. 预处理&#xff08;Preprocessor&#xff09; 2. 查询优化器&#xff08;Optimizer&#xff09; 3. 执行器…...

【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题

【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要&#xff1a; 近期&#xff0c;在使用较新版本的OpenSSH客户端连接老旧SSH服务器时&#xff0c;会遇到 "no matching key exchange method found"​, "n…...

虚拟电厂发展三大趋势:市场化、技术主导、车网互联

市场化&#xff1a;从政策驱动到多元盈利 政策全面赋能 2025年4月&#xff0c;国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》&#xff0c;首次明确虚拟电厂为“独立市场主体”&#xff0c;提出硬性目标&#xff1a;2027年全国调节能力≥2000万千瓦&#xff0…...

CVPR2025重磅突破:AnomalyAny框架实现单样本生成逼真异常数据,破解视觉检测瓶颈!

本文介绍了一种名为AnomalyAny的创新框架&#xff0c;该方法利用Stable Diffusion的强大生成能力&#xff0c;仅需单个正常样本和文本描述&#xff0c;即可生成逼真且多样化的异常样本&#xff0c;有效解决了视觉异常检测中异常样本稀缺的难题&#xff0c;为工业质检、医疗影像…...

go 里面的指针

指针 在 Go 中&#xff0c;指针&#xff08;pointer&#xff09;是一个变量的内存地址&#xff0c;就像 C 语言那样&#xff1a; a : 10 p : &a // p 是一个指向 a 的指针 fmt.Println(*p) // 输出 10&#xff0c;通过指针解引用• &a 表示获取变量 a 的地址 p 表示…...