redis的keys命令和scan命令性能对比

news2024/9/27 15:22:19

项目场景

Redis的keys *命令在生产环境是慎用的,特别是一些并发量很大的项目,原因是Redis是单线程的,keys *会引发Redis锁,占用reids CPU,如果key数量很大而且并发是比较大的情况,效率是很慢的,很有可能导致服务雪崩,在Redis官方的文档是这样解释的,官方的推荐是使用scan命令或者集合
在这里插入图片描述

解决方案

搭建一个工程来实践一下,项目环境:

  • JDK 1.8

  • SpringBoot 2.2.1

  • Maven 3.2+

  • Mysql 8.0.26

  • spring-boot-starter-data-redis 2.2.1

  • jedis3.1.0

  • 开发工具

    • IntelliJ IDEA

    • smartGit

新建一个SpringBoot项目
在这里插入图片描述

选择需要的依赖
在这里插入图片描述
选择Maven项目和jdk对应的版本
在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>springboot-jedis</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-jedis</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>io.lettuce</groupId>
                    <artifactId>lettuce-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.11</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

package com.example.jedis.configuration;

import com.example.jedis.common.JedisTemplate;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnection;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

@Configuration
@ConditionalOnClass({GenericObjectPool.class, JedisConnection.class, Jedis.class})
@EnableRedisRepositories(basePackages = "com.example.jedis.repository")
@Slf4j
public class RedisConfiguration {


    @Bean
    public JedisPoolConfig jedisPoolConfig() {
        return new JedisPoolConfig();
    }

    @Bean
    public JedisPool jedisPool() {
      return new JedisPool(jedisPoolConfig());
    }


    @Bean
    public RedisConnectionFactory jedisConnectionFactory() {
        return new JedisConnectionFactory();
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(jedisConnectionFactory());
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        return template;
    }

    @Bean
    public JedisTemplate jedisTemplate(){
        return new JedisTemplate();
    }




}

写一个工具类,实现redis scankeys *的逻辑,当然也可以直接使用RedisTemplate

import cn.hutool.core.collection.ConcurrentHashSet;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.ScanParams;
import redis.clients.jedis.ScanResult;
import redis.clients.jedis.exceptions.JedisConnectionException;
import redis.clients.jedis.exceptions.JedisDataException;
import redis.clients.jedis.exceptions.JedisException;
import redis.clients.jedis.params.SetParams;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
@Component
@Slf4j
public class JedisUtil implements InitializingBean {


    @Resource
    private JedisPool jedisPool;

    private Jedis jedis;

    public JedisTemplate(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }

    public JedisTemplate() {

    }

    @Override
    public void afterPropertiesSet() {
        jedis = jedisPool.getResource();
    }


    public <T> T execute(Function<Jedis, T> action) {
        T apply = null;
        try {
            jedis = jedisPool.getResource();
            apply = action.apply(jedis);
        } catch (JedisException e) {
            handleException(e);
            throw e;
        } finally {
            jedis.close();
        }
        return apply;
    }

    public void execute(Consumer<Jedis> action) {
        try {
            jedis = jedisPool.getResource();
            action.accept(jedis);
        } catch (JedisException e) {
            handleException(e);
            throw e;
        } finally {
            jedis.close();
        }
    }

    public JedisPool getJedisPool() {
        return this.jedisPool;
    }
	
	 public Set<String> keys(final String pattern) {
        return execute(e->{
            return jedis.keys(pattern);
        });
    }

    public Set<String> scan(String pattern) {
        return execute(e->{
            return this.doScan(pattern);
        });
    }


    protected Set<String> doScan(String pattern) {
        Set<String> resultSet = new ConcurrentHashSet<>();
        String cursor = String.valueOf(0);
        try {
            do {
                ScanParams params = new ScanParams();
                params.count(300);
                params.match(pattern);
                ScanResult<String> scanResult = jedis.scan(cursor, params);
                cursor = scanResult.getCursor();
                resultSet.addAll(scanResult.getResult());
            } while (Integer.valueOf(cursor) > 0);
        } catch (NumberFormatException e) {
            log.error("doScan NumberFormatException:{}", e);
        } catch (Exception e) {
            log.error("doScan Exception :{}", e);
        }
        return resultSet;
    }


    protected void handleException(JedisException e) {
        if (e instanceof JedisConnectionException) {
            log.error("redis connection exception:{}", e);
        } else if (e instanceof JedisDataException) {
            log.error("jedis data exception:{}", e);
        } else {
            log.error("jedis exception:{}", e);
        }
    }

}

新增测试类


import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import cn.hutool.core.thread.ExecutorBuilder;
import cn.hutool.core.util.IdUtil;
import com.example.jedis.common.JedisTemplate;
import com.example.jedis.configuration.RedisConfiguration;
import com.example.jedis.model.UserDto;
import com.example.jedis.repository.UserRepository;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;

@SpringBootTest
@ContextConfiguration(classes = RedisConfiguration.class)
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
@Slf4j
class SpringbootJedisApplicationTests {

  
    @Autowired
    JedisUtil jedisUtil;

 
    @Test
    void testCrud() {
        IntStream.range(0,100000).forEach(e->{
            final UserDto userDto = UserDto.builder()
                    .id(IdUtil.getSnowflake().nextId())
                    .name("用户1")
                    .gender(UserDto.Gender.MALE)
                    .build();
            userRepository.save(userDto);
        });
    }

 


    @Test
    void testKeys() {
        TimeInterval timeInterval = DateUtil.timer();
        Set<String> setData = jedisUitil.keys("user:*");
        System.out.println("keys use:"+timeInterval.intervalRestart()+"ms");
        Set<String> setDataScan = jedisUitil.scan("user:*");
        System.out.println("scan use:"+timeInterval.intervalRestart()+"ms");
    }
}

使用了3千多额数据,测试keys *scan其实查询效率差别不大的,scan命令效率和分多少数量一批次也有关系
在这里插入图片描述

搞到一万的数据量

在这里插入图片描述
经过测试,scan查询效率并不一定是比keys * 快多少的,跟这个数据量和count批次有关系,需要自己调试,所以对于线上的业务场景,如果key数量很多的,可以使用集合来替换keys *

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

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

相关文章

LLM之Agent(三):HuggingGPT根据用户需求自动调用Huggingface合适的模型

​ 浙大和微软亚洲研究院开源的HuggingGPT&#xff0c;又名JARVIS&#xff0c;它可以根据用户的自然语言描述的需求就可以自动分析需要哪些AI模型&#xff0c;然后去Huggingface上直接调用对应的模型&#xff0c;最终给出用户的解决方案。 一、HuggingGPT的工作流程 它的…

Hadoop的介绍与安装

1 Hadoop的简介 Hadoop是一个开源的大数据框架&#xff0c;是一个分布式计算的解决方案。Hadoop是由java语言编写的&#xff0c;在分布式服务器集群上存储海量数据并运行分布式分析应用的开源框架&#xff0c;其核心部件是HDFS与MapReduce。 HDFS是一个分布式文件系统&#x…

专业课145+总分440+东南大学920考研专业基础综合信号与系统数字电路经验分享

个人情况简介 今年考研440&#xff0c;专业课145&#xff0c;数一140&#xff0c;期间一年努力辛苦付出&#xff0c;就不多表了&#xff0c;考研之路虽然艰难&#xff0c;付出很多&#xff0c;当收获的时候&#xff0c;都是值得&#xff0c;考研还是非常公平&#xff0c;希望大…

单片机第三季-第四课:STM32下载、MDK和调试器

目录 1&#xff0c;扩展板使用的STM32芯片类型 2&#xff0c;使用普中科技软件下载程序 3&#xff0c;keil介绍 4&#xff0c;JLINK调试器介绍 5&#xff0c;使用普中的调试器进行debug 6&#xff0c;使用Simulator仿真 1&#xff0c;扩展板使用的STM32芯片类型 扩展版…

【EtherCAT详解】基于Wireshark的EtherCAT帧结构解析

写在前面 EtherCAT的报文比较繁琐,且一些参考书籍错误较多,且晦涩难懂,对于初学者,很难快速的入门。本文适用于有一定基础的研究者,如对报文有一些研究、对canopen协议有一定了解、并且对TwinCAT有了解的研究者。当然,对于初学者来说,也是很好的引导,少走很多弯路。本…

图中点的层次(图的BFS)

给定一个 n 个点 m 条边的有向图&#xff0c;图中可能存在重边和自环。 所有边的长度都是 1&#xff0c;点的编号为 1∼n。 请你求出 1 号点到 n 号点的最短距离&#xff0c;如果从 1 号点无法走到 n 号点&#xff0c;输出 −1。 输入格式 第一行包含两个整数 n 和 m。 接…

Nat easy IP ACL

0表示匹配&#xff0c;1表示任意&#xff08;主机位0.0.0.255&#xff08;255主机位&#xff09;&#xff09; rule deny source 192.168.2.1 0 设置拒绝192.168.2.1的主机通过 记住将其应用到接口上 [AR2]acl 2000 //创建基本ACL [AR2-acl-basic-2000]rule deny source 192…

WordPress发送邮件设置

WordPress在修改登陆邮箱或找回登陆密码的时候&#xff0c;通常都需要发送邮件来进行操作验证&#xff0c;但服务商又禁止了服务器对外发送邮件的25端口&#xff0c;很多虚拟主机本身也禁用了mail函数&#xff0c;根本发不了邮件。 此时我们可以使用QQ邮箱、网易邮箱或者其他企…

Win10安装ROS2遇到的小问题

按照网上教程安装ROS2&#xff0c;卡在了第一步。在cmd或powershell安装Chocolatey时&#xff0c;出现以下两种错误&#xff1a; “%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe” -NoPro …~here-string 标题后面和行尾之前不允许包含任何字符。 …… 或者 使…

字符串函数strlen的用法详解及其相关题目

strlne函数的使用 一.strlen函数的声明二.strlen函数的头文件三.相关题目代码1代码2题目1题目2题目3题目4题目5题目6 一.strlen函数的声明 size_t strlen ( const char * str );二.strlen函数的头文件 使用strlen函数我们需要使用以下头文件 #include <string.h>三.相…

LaTex入门简明教程

文章目录 写在前面安装Texlive的安装TeXstudio 的安装 LaTex 的使用节指令图指令表指令公式指令参考文献指令引用指令TeXstudio 编译 LaTex 的 \label{} 写法建议最后 写在前面 这篇文章面向没有任何 LaTex 基础的小白&#xff0c;主要讲解了 LaTex 的安装和使用。读完文章之后…

SVPWM原理及simulink

关注微♥“电击小子程高兴的MATLAB小屋”获得专属优惠 一.SVPWM原理 SPWM常用于变频调速控制系统&#xff0c;经典的SPWM控制主要目的是使变频器的输出电压尽量接近正弦波&#xff0c;并未关注输出的电流波形。而矢量控制的最终目的是得到圆形的旋转磁场&#xff0c;这样就要求…

大数据项目——基于Django协同过滤算法的房源可视化分析推荐系统的设计与实现

大数据项目——基于Django协同过滤算法的房源可视化分析推荐系统的设计与实现 技术栈&#xff1a;大数据爬虫/机器学习学习算法/数据分析与挖掘/大数据可视化/Django框架/Mysql数据库 本项目基于 Django框架开发的房屋可视化分析推荐系统。这个系统结合了大数据爬虫、机器学习…

SaToken利用Redis做持久化

官网解释 官网解释 教程 引入依赖 <!-- 提供Redis连接池 --> <dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId> </dependency><!-- Sa-Token 整合 Redis &#xff08;使用 jdk 默认序…

Linux--网络编程-ftp(TCP)网络通信-文件交互

项目要求&#xff1a;实现以下内容 远程控制&#xff1a; 1、查看服务器当前路径文件 ls 3、进入、退出服务器文件夹 cd 4、上传文件到服务器 put xxx 本地控制&#xff1a; 1、查看本地&#xff08;客户端&#xff09;文件 lls 2、进入客户端文件夹 lcd 3、获取服务器的文件…

【开发PaaS】基于Postgresql的开发平台Supabase

Supadase是开源的。我们选择可扩展的开源工具&#xff0c;使其易于使用。 Supadase不是Firebase的1对1映射。虽然我们正在构建Firebase提供的许多功能&#xff0c;但我们不会以同样的方式进行&#xff1a; 我们的技术选择大不相同&#xff1b;我们使用的一切都是开源的&#…

网络安全(二)-- Linux 基本安全防护技术

4.1. 概述 安全防护基础主要是会用Linux系统&#xff0c; 熟悉Linux基本操作命令。 在这个章节中&#xff0c;我们主要探讨自主访问控制&#xff08;许可位、ACL&#xff09;、文件属性、 PAM技术、能力机制等。 4.1.1. 补充命令 本章节中&#xff0c;涉及一些新的命令&#…

leetcode做题笔记1038. 从二叉搜索树到更大和树

给定一个二叉搜索树 root (BST)&#xff0c;请将它的每个节点的值替换成树中大于或者等于该节点值的所有节点值之和。 提醒一下&#xff0c; 二叉搜索树 满足下列约束条件&#xff1a; 节点的左子树仅包含键 小于 节点键的节点。节点的右子树仅包含键 大于 节点键的节点。左右…

@PostConstruct使用详解

一、简介 在Spring应用程序中启动时&#xff0c;有时需要在所有Bean都已加载&#xff0c;初始化并准备好之后执行某些自定义代码。这时&#xff0c;Spring提供了一个可用的方式&#xff0c;即使用PostConstruct注解。这个注解用于标记一个方法&#xff0c;这个方法将在Bean初始…

【Cesium】模型平面裁切

const scene viewer.scene;let tileset; let targetY 400.0; let planeEntities []; let selectedPlane; // 选择的切面 let clippingPlanes; // 切面属性// 当鼠标点击切面时&#xff0c;修改相关属性 const downHandler new Cesium.ScreenSpaceEventHandler(viewer.sce…