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

Redis中的setIfAbsent方法和execute

Redis中的setIfAbsent方法

Redis中的setIfAbsent方法是一种原子操作,它的作用是只有在指定的键不存在时才会设置值。这个方法在并发环境下非常有用,因为它可以避免多个客户端同时尝试设置相同键而导致的冲突。

代码示例

在Java中使用setIfAbsent方法通常结合RedisTemplate来实现,语法如下:

redisTemplate.opsForValue().setIfAbsent(key, value, timeout, unit);

其中,key是要设置的键,value是要设置的值,timeoutunit是可选参数,分别表示键的过期时间和时间单位。

操作解释

setIfAbsent方法首先会检查给定的键是否存在。如果键不存在,它将创建一个新的键并设置给定的值。如果键已经存在,它将不执行任何操作。这个过程是原子的,意味着在检查和设置键的过程中不会有其他客户端或线程干扰。

应用场景

  • 分布式锁:可以用来实现分布式锁,确保某个操作在短时间内只能由一个节点执行。

  • 缓存:在缓存系统中,用于保证某个缓存项只有在不存在时才会被创建和存储。

  • 分布式事件处理:保证某个事件只被处理一次。

注意事项

使用setIfAbsent时需要注意并发问题,虽然是原子操作,但在高并发环境下仍可能存在竞态条件。合理设置键的过期时间可以提高系统的灵活性和性能。此外,在使用时需要注意异常处理,例如,当键已经存在时,该方法不会抛出异常,而是返回false0表示操作未成功。

在实际应用中,setIfAbsent方法可以有效地解决并发环境下的数据一致性问题,是实现分布式锁和缓存等功能的关键工具。

Redis RedisTemplate 核心方法 execute 详解

在 RedisTemplate 中,定义了几个 execute() 方法,这些方法是 RedisTemplate 的核心方法。RedisTemplate 中很多其他方法均是通过调用 execute 来执行具体的操作。例如:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

/*

 * (non-Javadoc)

 * @see org.springframework.data.redis.core.RedisOperations#delete(java.util.Collection)

 */

@Override

public Long delete(Collection<K> keys) {

   if (CollectionUtils.isEmpty(keys)) {

      return 0L;

   }

   byte[][] rawKeys = rawKeys(keys);

   return execute(connection -> connection.del(rawKeys), true);

}

上述方法是 RedisTemplate 中 delete 方法的源码,它就是使用 execute() 来执行具体的删除操作(即调用 connection.del(rawKeys) 方法)。

方法说明如下表:

方法定义方法说明
<T> T  execute(RedisCallback<T> action)在 Redis 连接中执行给定的操作
<T> T  execute(RedisCallback<T> action, boolean exposeConnection)在连接中执行给定的操作对象,可以公开也可以不公开。
<T> T  execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline)在可以公开或不公开的连接中执行给定的操作对象。
<T> T  execute(RedisScript<T> script, List<K> keys, Object... args)执行给定的 RedisScript
<T> T  execute(RedisScript<T> script, RedisSerializer<?> argsSerializer, RedisSerializer<T> resultSerializer, List<K> keys, Object... args)执行给定的 RedisScript,使用提供的 RedisSerializers 序列化脚本参数和结果。
<T> T  execute(SessionCallback<T> session)执行 Redis 会话

示例

execute(RedisCallback) 简单用法

使用 RedisTemplate 直接调用 opsFor** 来操作 Redis 数据库,每执行一条命令是要重新拿一个连接,因此很耗资源。如果让一个连接直接执行多条语句的方法就是使用 RedisCallback(它太复杂,不常用),推荐使用 SessionCallback。

本例将演示使用 RedisCallback 向 Redis 写入数据,然后再将写入的数据取出来,输出到控制台。如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

package com.hxstrive.redis.redistemplate.execute;

import org.junit.Test;

import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.test.context.SpringBootTest;

import org.springframework.dao.DataAccessException;

import org.springframework.data.redis.connection.RedisConnection;

import org.springframework.data.redis.connection.RedisStringCommands;

import org.springframework.data.redis.core.RedisCallback;

import org.springframework.data.redis.core.RedisTemplate;

import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)

@SpringBootTest

public class ExecuteSimple {

    /** 注入 RedisTemplate */

    @Autowired

    private RedisTemplate<String,String> redisTemplate;

    @Test

    public void contextLoads() {

        redisTemplate.execute(new RedisCallback<String>() {

            @Override

            public String doInRedis(RedisConnection connection) throws DataAccessException {

                RedisStringCommands commands = connection.stringCommands();

                // 写入缓存

                commands.set("execute_key".getBytes(), "hello world".getBytes());

                // 从缓存获取值

                byte[] value = commands.get("execute_key".getBytes());

                System.out.println(new String(value));

                return null;

            }

        });

    }

}

运行示例,输出结果如下:

1

hello world

其实,在 RedisTemplate 中,其他很多方法均是通过调用 execute() 方法来实现,只是不同的方法实现不同的回调接口。部分源码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

// ...

@Override

public Long increment(K key) {

   byte[] rawKey = rawKey(key);

   return execute(connection -> connection.incr(rawKey), true);

}

/*

 * (non-Javadoc)

 * @see org.springframework.data.redis.core.ValueOperations#increment(java.lang.Object, long)

 */

@Override

public Long increment(K key, long delta) {

   byte[] rawKey = rawKey(key);

   return execute(connection -> connection.incrBy(rawKey, delta), true);

}

@Override

public void set(K key, V value, long timeout, TimeUnit unit) {

   byte[] rawKey = rawKey(key);

   byte[] rawValue = rawValue(value);

   execute(new RedisCallback<Object>() {

      @Override

      public Object doInRedis(RedisConnection connection) throws DataAccessException {

         potentiallyUsePsetEx(connection);

         return null;

      }

      public void potentiallyUsePsetEx(RedisConnection connection) {

         if (!TimeUnit.MILLISECONDS.equals(unit) || !failsafeInvokePsetEx(connection)) {

            connection.setEx(rawKey, TimeoutUtils.toSeconds(timeout, unit), rawValue);

         }

      }

      private boolean failsafeInvokePsetEx(RedisConnection connection) {

         boolean failed = false;

         try {

            connection.pSetEx(rawKey, timeout, rawValue);

         catch (UnsupportedOperationException e) {

            // in case the connection does not support pSetEx return false to allow fallback to other operation.

            failed = true;

         }

         return !failed;

      }

   }, true);

}

// ...

execute(SessionCallback) 简单用法

使用 RedisTemplate 直接调用 opsFor** 来操作 Redis 数据库,每执行一条命令是要重新拿一个连接,因此很耗资源。如果让一个连接直接执行多条语句的方法就是使用 SessionCallback,还可以使用 RedisCallback(它太复杂,不常用)。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

package com.hxstrive.redis.redistemplate.execute;

import org.junit.Test;

import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.test.context.SpringBootTest;

import org.springframework.dao.DataAccessException;

import org.springframework.data.redis.core.RedisOperations;

import org.springframework.data.redis.core.RedisTemplate;

import org.springframework.data.redis.core.SessionCallback;

import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)

@SpringBootTest

public class SessionCallbackSimple {

    /** 注入 RedisTemplate */

    @Autowired

    private RedisTemplate<String,String> redisTemplate;

    @Test

    public void contextLoads() {

        redisTemplate.execute(new SessionCallback() {

            @Override

            public String execute(RedisOperations operations) throws DataAccessException {

                operations.opsForValue().set("valueK1""value1");

                System.out.println("valueK1 = " + operations.opsForValue().get("valueK1"));

                operations.opsForList().leftPushAll("listK1""one""two");

                System.out.println("listK1 = " + operations.opsForList().size("listK1"));

                return null;

            }

        });

    }

}

运行示例,输出如下:

1

2

valueK1 = value1

listK1 = 2

execute(RedisScript) 简单用法

该示例使用 Lua 脚本实现获取锁的功能,代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

package com.hxstrive.redis.redistemplate.execute;

import org.junit.Test;

import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.test.context.SpringBootTest;

import org.springframework.data.redis.core.RedisTemplate;

import org.springframework.data.redis.core.script.DefaultRedisScript;

import org.springframework.test.context.junit4.SpringRunner;

import java.util.Collections;

@RunWith(SpringRunner.class)

@SpringBootTest

public class RedisScriptSimple {

    /** 注入 RedisTemplate */

    @Autowired

    private RedisTemplate<String,String> redisTemplate;

    @Test

    public void contextLoads() {

        // 使用 lua 脚本实现获取锁

        // ARGV[1] = lock-key

        // ARGV[2] = 30

        String script = "local key = ARGV[1];" +

                "local expiration = ARGV[2];" +

                "local value = 1;" +

                "if redis.call('EXISTS', key) == 1 then " +

                "  return -1 " // 如果键存在,则返回-1

                "else " +

                "  redis.call('SET', key, value);" // 如果键不存在,则设置键和值

                "  redis.call('EXPIRE', key, expiration);" // 为键设置过期时间

                "  return 1;" // 返回1,表示锁获取成功

                "end";

        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);

        // 其中,lock-key 表示锁的键,30 表示过期时间为30秒

        Long val = redisTemplate.execute(redisScript, Collections.EMPTY_LIST, "lock-key""30");

        System.out.println("val = " + val);

        // 如果锁获取成功,则进入下一步操作

        if(null != val && val == 1) {

            System.out.println("获取锁成功");

        else {

            System.err.println("获取锁失败");

        }

    }

}

运行示例,输出如下:

1

2

val = 1

获取锁成功

相关文章:

Redis中的setIfAbsent方法和execute

Redis中的setIfAbsent方法 Redis中的setIfAbsent方法是一种原子操作&#xff0c;它的作用是只有在指定的键不存在时才会设置值。这个方法在并发环境下非常有用&#xff0c;因为它可以避免多个客户端同时尝试设置相同键而导致的冲突。 代码示例 在Java中使用setIfAbsent方法通…...

高考数学易错考点02 | 临阵磨枪

文章目录 前言解析几何立体几何排列组合概率导数及应用前言 本篇内容下载于网络,网络上的都是以 WORD 版本呈现,缺字缺图很不完整,没法使用,我只是做了补充和完善。有空准备进行第二次完善,添加问题解释的链接。 ##平面向量 40.向量 0 ⃗ \vec{0} 0 与数 0 0 0 有区别…...

国产高性能pSRAM选型指南:CSS6404LS-LI 64Mb QSPI伪静态存储器

一、芯片基础特性 核心参数 容量 &#xff1a;64Mb&#xff08;8M 8bit&#xff09;电压 &#xff1a;单电源供电 2.7-3.6V &#xff08;兼容3.3V系统&#xff09;接口 &#xff1a;Quad-SPI&#xff08;QPI/SPI&#xff09;同步模式封装 &#xff1a; SOP-8L (150mil) &#…...

Go 中 `json.NewEncoder/Decoder` 与 `json.Marshal/Unmarshal` 的区别与实践

Go 中 json.NewEncoder/Decoder 与 json.Marshal/Unmarshal 的区别与实践&#xff08;HTTP 示例&#xff09; 在 Go 中处理 JSON 有两种主要方式&#xff1a;使用 json.Marshal/Unmarshal 和使用 json.NewEncoder/Decoder。它们都能完成 JSON 的序列化与反序列化&#xff0c;但…...

UE5 2D角色PaperZD插件动画状态机学习笔记

UE5 2D角色PaperZD插件动画状态机学习笔记 0.安装PaperZD插件 这是插件下载安装地址 https://www.fab.com/zh-cn/listings/6664e3b5-e376-47aa-a0dd-f7bbbd5b93c0 1.右键创建PaperZD 动画序列 2.添加动画序列 3&#xff0c;右键创建PaperZD AnimBP &#xff08;动画蓝图&am…...

Ubuntu 16.04 密码找回

同事整理的供参考&#xff1a; 进入GRUB菜单 重启系统&#xff0c;在启动过程中长按Shift键&#xff08;或Esc键&#xff09;进入GRUB引导菜单。 若未显示GRUB菜单&#xff0c;可尝试在启动时连续按多次Shift/Esc键。 在GRUB菜单中选择默认的Ubuntu启动项&#xff08;第一…...

【论文阅读】DanceGRPO: Unleashing GRPO on Visual Generation

DanceGRPO: Unleashing GRPO on Visual Generation 原文摘要 研究背景与问题 生成模型的突破&#xff1a;扩散模型和整流流等生成模型在视觉内容生成领域取得了显著进展。核心挑战&#xff1a;如何让模型的输出更好地符合人类偏好仍是一个关键问题。现有方法的局限性&#xff1…...

CentOS在vmware局域网内搭建DHCP服务器【踩坑记录】

1. 重新设置环境 配置dhcp服务踩了不少坑&#xff0c;这里重头搭建记录一下&#xff1a; 1.1 centos 网卡还原 如果之前搭了乱七八糟的环境&#xff0c;导致NAT模式也没法上网&#xff0c;这里重新还原 我们需要在NAT模式下联网&#xff0c;下载DHCP服务 先把centos的网卡还…...

AI炼丹日志-28 - Audiblez 将你的电子书epub转换为音频mp3 做有声书

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; 大模型与Java双线更新中&#xff01; 目前《大语言模型实战》已连载至第22篇&#xff0c;探索 MCP 自动操作 FigmaCursor 实…...

图像处理篇---face_recognition库实现人脸检测

以下是使用face_recognition库实现人脸检测的详细步骤、实例代码及解释&#xff1a; 一、环境准备 1. 安装依赖库 pip install face_recognition opencv-python # 核心库 pip install matplotlib # 用于显示图像&#xff08;可选&#xff09;2. 依赖说明 face_recognitio…...

74. 搜索二维矩阵 (力扣)

给你一个满足下述两条属性的 m x n 整数矩阵&#xff1a; 每行中的整数从左到右按非严格递增顺序排列。每行的第一个整数大于前一行的最后一个整数。 给你一个整数 target &#xff0c;如果 target 在矩阵中&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。…...

8088单板机C语言sprintf()格式化串口输出---Prj04

#include "tiny_stdarg.h" // 使用自定义可变参数实现#define ADR_273 0x0200 #define ADR_244 0x0400 #define LED_PORT 0x800 #define PC16550_THR 0x1f0 #define PC16550_LSR 0x1f5 / //基本的IO操作函数 / char str[]"Hello World! 20250531 Ve…...

板凳-------Mysql cookbook学习 (九)

第4章&#xff1a;表管理 4.0 引言 MySQL &#xff1a;&#xff1a; 员工样例数据库 &#xff1a;&#xff1a; 3 安装 https://dev.mysql.com/doc/employee/en/employees-installation.html Employees 数据库与几种不同的 存储引擎&#xff0c;默认情况下启用 InnoDB 引擎。编…...

深入解析 Flask 命令行工具与 flask run命令的使用

Flask 是一个轻量级的 Python Web 应用框架&#xff0c;其内置的命令行工具&#xff08;CLI&#xff09;基于 Click 库&#xff0c;提供了方便的命令行接口&#xff0c;用于管理和运行 Flask 应用程序。本文将详细介绍 Flask 命令行工具的功能&#xff0c;以及如何使用 flask r…...

第6篇:中间件 SQL 重写与语义分析引擎实现原理

6.1 章节导读 SQL 是数据库中间件的“输入语言”。 在一个真正强大的中间件系统中&#xff0c;SQL 语句的执行通常不再是“原封不动”地传递给数据库&#xff0c;而是需要先经过&#xff1a; 语义分析&#xff1a;解析 SQL 的结构和含义。 SQL 重写&#xff1a;根据中间件逻辑…...

基于SpringBoot的“嗨玩旅游”网站设计与实现(源码+定制+开发)嗨玩旅游平台开发:景点展示与个性化推荐系统(SpringBoot)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…...

python版若依框架开发:python版若依部署

python版若依框架开发 从0起步,扬帆起航。 python版若依部署文章目录 python版若依框架开发1.源码2.概述3.部署1.源码 https://gitee.com/insistence2022/RuoYi-Vue-FastAPI 请诸君移步上述链接,即可对python版若依项目进行初步了解。 2.概述 若依框架本身基于java,可以快…...

React进阶:状态管理选择题

React进阶&#xff1a;状态管理选择题 引言 随着React应用复杂度增加&#xff0c;选择合适的状态管理方案成为我们面临的关键决策。 状态管理本质上是解决"谁来存储数据"以及"如何更新和分发这些数据"的问题。在React生态中&#xff0c;随着应用规模扩大…...

h5的aliplayer-min.js 加密视频会走到debugger

h5的aliplayer-min.js 如果 https://g.alicdn.com/apsara-media-box/imp-web-player/2.19.0/aliplayer-min.js走加密视频的话会有debugger 更换aliplayer-min.js版本解决了 https://g.alicdn.com/apsara-media-box/imp-web-player/2.25.1/aliplayer-min.js 对应css&#xff1a…...

第5篇《中间件负载均衡与连接池管理机制设计》

5.1 章节导读 在数据库中间件中&#xff0c;如何高效地管理数据库连接与请求调度至关重要。 本节围绕两个核心模块展开&#xff1a; 连接池管理&#xff1a;提升连接复用能力&#xff0c;避免频繁建立/断开连接。 负载均衡策略&#xff1a;合理调度 SQL 请求&#xff0c;提升…...

DashBoard安装使用

DashBoard安装使用 一、实验目的 1、掌握dashboard 的安装部署 2、熟悉图像化部署任务&#xff1a;产生pod---定义服务--验证访问 二、实验内容&#xff1a; 1、配置步骤 1.1、Helm安装 离线安装&#xff08;适用于内网/离线环境&#xff09; # 根据系统架构选择版本&am…...

极客大挑战 2019 EasySQL 1(万能账号密码,SQL注入,HackBar)

题目 做法 启动靶机&#xff0c;打开给出的网址 随便输点东西进去&#xff0c;测试一下 输入1、1’、1"判断SQL语句闭合方式 输入以上两个都是以下结果 但是&#xff0c;输入1’时&#xff0c;出现的是另外结果 输入1&#xff0c;1"时&#xff0c;SQL语句没有…...

C# CallerMemberName特性

当你在一个方法运用了CallerMemberName特性&#xff0c;编译器会自动将调用该方法的方法或属性的名称作为该参数的默认值&#xff0c;可应用于MVVM框架。 代码&#xff1a; using System.ComponentModel; using System.Runtime.CompilerServices;public class Person : INoti…...

采用 Docker GPU 部署的 Ubuntu 或者 windows 桌面环境

# 国内下载不了 docker pull gezp/ubuntu-desktop:24.04-cu12.6.2# 阿里云镜像 docker pull registry.cn-hongkong.aliyuncs.com/gezp/ubuntu-desktop:24.04-cu12.6.2# create container with nomachine docker run -d --restarton-failure --name myubuntu --shm-size1024m -e…...

关于面试找工作的总结(四)

不同情况下收到offer后的处理方法 1.不会去的,只是面试练手2.还有疑问,考虑中3.offer/职位不满足期望的4.已确认,但又收到更好的5.还想挽回之前的offer6.确认,准备入职7.还想拖一下的1.不会去的,只是面试练手 HR您好,非常荣幸收到贵司的offer,非常感谢一直以来您的帮助,…...

分布式拜占庭容错算法——实现工作量证明(PoW)算法详解

Java 实现工作量证明&#xff08;PoW&#xff09;算法详解 一、PoW 核心原理 #mermaid-svg-AAj0Pvst1PVcVy5v {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-AAj0Pvst1PVcVy5v .error-icon{fill:#552222;}#mermaid…...

深度解析Mysql中MVCC的工作机制

MVCC,多版本并发控制 定义&#xff1a;维护一个数据的多个版本&#xff0c;使读写操作没有冲突&#xff0c;依赖于&#xff1a;隐藏字段&#xff0c;undo log日志&#xff0c;readView MVCC会为每条版本记录保存三个隐藏字段 DB_TRX_ID: 记录最近插入或修改该记录的事务IDDB_R…...

MP4文件声音与视频分离

最近学习PR剪辑 要添加视频文件和音频文件 但是直接给MP4文件 得到的是一个整体 不管怎么切分 都是无法得到单独的整体 这就需要将视频文件和音频文件分离 我推荐使用ffmpeg工具进行分离 迅雷链接&#xff1a;https://pan.xunlei.com/s/VORu5x64jjL-gXFd_VTpYjRPA1?pwd8wec#…...

接口自动化测试之pytest 运行方式及前置后置封装

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一、Pytest 优点认知 1.可以结合所有的自动化测试工具 2.跳过失败用例以及失败重跑 3.结合allure生产美观报告 4.和Jenkins持续集成 5.很多强大的插件 pytest-htm…...

服务器被攻击了怎么办

可以上一个高防IP或者AI云防护都是可以的。&#xff08;有效防御CC、APl接口、http、tcp、WEB应用扫描/爬虫、SYN、WAF、DDOS、UDP、入侵、渗透、SQL注入、XSS跨站脚本攻击、远程恶意代码执行、session fixation、Webshell攻击、恶意请求&#xff0c;恶意扫描、暴力破解、CSRF等…...