编辑
2024-06-27
学习记录
00
请注意,本文编写于 280 天前,最后修改于 202 天前,其中某些信息可能已经过时。

目录

介绍
事务命令
首先开启对事务的使用
redis事务使用
redis乐观锁使用
Java中lua脚本使用
原子性命令介绍
INCR
DECR
GETSET
SET

介绍

事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。 事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。 事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。

总结说:redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。

事务命令

MULTI :开启事务,redis会将后续的命令逐个放入队列中,然后使用EXEC命令来原子化执行这个命令系列。 EXEC:执行事务中的所有操作命令。 DISCARD:取消事务,放弃执行事务块中的所有命令。 WATCH:监视一个或多个key,如果事务在执行前,这个key(或多个key)被其他命令修改,则事务被中断,不会执行事务中的任何命令。 UNWATCH:取消WATCH对所有key的监视。

首先开启对事务的使用

redisTemplate.setEnableTransactionSupport(true);

不开启直接使用redis事务会报错提示 ERR EXEC without MULTI

org.springframework.data.redis.RedisSystemException: Error in execution; nested exception is io.lettuce.core.RedisCommandExecutionException: ERR EXEC without MULTI

redis事务使用

  • 使用multi开启事务
  • 添加多个命令
  • 使用EXEC执行命令

java代码示例

java
@Autowired private RedisTemplate<String, Object> redisTemplate; /** * 在一个"事务"中执行多个操作。 */ public void executeTransaction() { // 开始事务 redisTemplate.multi(); try { // 添加事务中的命令 redisTemplate.opsForValue().set("key1", "value1"); redisTemplate.opsForValue().set("key2", "value2"); // 执行事务(提交) redisTemplate.exec(); } catch (Exception e) { // 如果在事务执行过程中发生错误,可以回滚事务 redisTemplate.discard(); System.out.println("Transaction discarded due to error: " + e.getMessage()); } }

redis乐观锁使用

使用 watch可以监视一个key有没有被改变,从而实现乐观锁的效果

java
@Autowired private RedisTemplate<String, Object> redisTemplate; /** * 使用WATCH、MULTI、EXEC实现乐观锁更新。 * 尝试更新key对应的值,如果期间key被其他客户端改变,则更新失败。 * * @param key 锁定的键 * @param oldValue 期望的旧值 * @param newValue 新值 * @return true if the update was successful, false otherwise */ public boolean updateWithOptimisticLock(String key, String oldValue, String newValue) { Boolean result = redisTemplate.execute((RedisCallback<Boolean>) connection -> { connection.watch(key.getBytes()); // 监视key byte[] currentVal = connection.get(key.getBytes()); if (currentVal != null && new String(currentVal).equals(oldValue)) { // 检查值是否被改变 connection.multi(); // 开始事务 connection.set(key.getBytes(), newValue.getBytes()); // 更新操作 return connection.exec().size() > 0; // 执行事务,如果有结果表示成功 } else { connection.unwatch(); // 值已被改变,取消监视 return false; } }); return result != null && result; }

Java中lua脚本使用

java
private final RedisTemplate<String, Integer> redisTemplate; public AtomicOperationExample(RedisTemplate<String, Integer> redisTemplate) { this.redisTemplate = redisTemplate; // 设置键和值的序列化方式 redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new GenericToStringSerializer<>(Integer.class)); } /** * 原子性地对键对应的值加1。 * @param key 键 * @return 加1后的值 */ public Integer atomicIncrement(String key) { // Lua脚本,实现键值加1操作 String script = "if redis.call('exists', KEYS[1]) == 1 then " + "return redis.call('incr', KEYS[1]) " + "else " + "redis.call('set', KEYS[1], ARGV[1]) " + "return ARGV[1] " + "end"; // 创建DefaultRedisScript对象 DefaultRedisScript<Integer> redisScript = new DefaultRedisScript<>(script, Integer.class); // 执行Lua脚本,传入key和初始值1 return redisTemplate.execute(redisScript, Collections.singletonList(key), 1); }

原子性命令介绍

INCR

功能: 递增指定键的值。如果键不存在,它的值会被初始化为0,然后再执行递增操作。此命令仅适用于可以表示为整数的字符串值。 返回值: 递增后的值。

DECR

功能: 递减指定键的值。类似于INCR,如果键不存在,它的值会被初始化为0,然后再执行递减操作。同样,此命令也适用于可以表示为整数的字符串值。 返回值: 递减后的值。

GETSET

功能: 将键的值设为新值,并返回键的旧值。这个命令可以在原子性地更新键的同时获取其旧值,常用于实现某些形式的计数器或状态更新,同时还能获取更新前的状态。 返回值: 键的旧值。

SET

功能: 设置键的值。这是一个非常基础且多功能的命令,允许你设置键值对。从Redis 2.6.12版本开始,它还可以通过额外的参数来控制行为,比如设置键的过期时间、条件性地设置键值等。

EX seconds:设置键的过期时间为seconds秒。 PX milliseconds:设置键的过期时间为milliseconds毫秒。 NX:只有当键不存在时才设置值(相当于"set if not exists")。 XX:只有当键已经存在时才设置值(相当于"set if exists")。

返回值: 在普通模式下总是返回OK。如果使用了NX或XX选项,并且条件未满足,则返回nil。 这些命令在处理字符串数据时提供了基本的原子性操作,非常适合构建高性能的计数器、锁、简单的状态机等场景。

本文作者:Weee

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!