文章目录
- 1.手写Redis分布式锁
- 1.RedisShareLockUtil
- 2.使用方式
- 2.RedisUtil二次封装
- 1.RedisUtil
- 2.使用案例
1.手写Redis分布式锁
1.RedisShareLockUtil
package com.sunxiansheng.redis.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@Component
public class RedisShareLockUtil {
private static final Logger logger = LoggerFactory.getLogger(RedisShareLockUtil.class);
private static final long DEFAULT_TIMEOUT = 10; // 默认超时时间(秒)
private static final String DEFAULT_LOCK_NAME_PREFIX = "lock:"; // 默认锁名前缀
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 尝试获取锁
* @param lockKey 锁的key
* @param timeout 超时时间(秒)
* @return 锁的value,用于释放锁
*/
public String tryLock(String lockKey, long timeout) {
String value = UUID.randomUUID().toString();
Boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, value, timeout, TimeUnit.SECONDS);
while (locked == null || !locked) {
if (locked == null) {
logger.error("尝试获取锁时出现错误,可能是 Redis 连接问题,key: {}", lockKey);
} else {
logger.warn("获取锁失败,重试中,key: {}", lockKey);
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
logger.error("尝试获取锁时线程被中断,key: {}", lockKey, e);
return null;
}
locked = redisTemplate.opsForValue().setIfAbsent(lockKey, value, timeout, TimeUnit.SECONDS);
}
logger.info("成功获取锁,key: {}", lockKey);
return value;
}
/**
* 释放锁
* @param lockKey 锁的key
* @param value 锁的value
*/
public boolean releaseLock(String lockKey, String value) {
String scriptText = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
DefaultRedisScript<Long> script = new DefaultRedisScript<>();
script.setScriptText(scriptText);
script.setResultType(Long.class);
Long result = redisTemplate.execute(script, Collections.singletonList(lockKey), value);
if (result != null && result > 0) {
logger.info("成功释放锁,key: {}", lockKey);
return true;
} else {
if (result == null) {
logger.error("释放锁失败,Redis 执行错误,key: {}", lockKey);
} else {
logger.warn("释放锁失败,锁不存在或锁的值不匹配,key: {}", lockKey);
}
return false;
}
}
/**
* 使用锁执行操作,使用默认超时时间
* @param lockKey 锁的key
* @param action 要执行的操作
*/
public void executeWithLock(String lockKey, Runnable action) {
executeWithLock(lockKey, DEFAULT_TIMEOUT, action);
}
/**
* 使用锁执行操作
* @param lockKey 锁的key
* @param timeout 超时时间(秒)
* @param action 要执行的操作
*/
public void executeWithLock(String lockKey, long timeout, Runnable action) {
String fullLockKey = DEFAULT_LOCK_NAME_PREFIX + lockKey;
String value = tryLock(fullLockKey, timeout);
if (value != null) {
try {
action.run();
} finally {
releaseLock(fullLockKey, value);
}
} else {
logger.warn("无法获取锁,key: {}", fullLockKey);
}
}
}
2.使用方式
@RequestMapping("/testLock")
public void testLock() {
// 默认超时时间10s,自定义锁名字
redisShareLockUtil.executeWithLock("mylock", () -> {
// 需要加锁的逻辑
});
//
// 自定义超时时间30s,自定义锁名字
redisShareLockUtil.executeWithLock("mylock", 30, () -> {
// 需要加锁的逻辑
});
}
2.RedisUtil二次封装
1.RedisUtil
package com.sunxiansheng.redis.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
* Description: RedisUtil工具类
* @Author sun
* @Create 2024/6/5 14:17
* @Version 1.0
*/
@Component
public class RedisUtil {
private static final Logger logger = LoggerFactory.getLogger(RedisUtil.class);
@Resource
private RedisTemplate<String, Object> redisTemplate;
private static final String CACHE_KEY_SEPARATOR = ".";
/**
* 构建缓存key
* @param strObjs 多个字符串拼接成缓存key
* @return 拼接后的缓存key
* @example
* <pre>
* String key = redisUtil.buildKey("user", "123");
* System.out.println(key); // 输出: user.123
* </pre>
*/
public String buildKey(String... strObjs) {
return String.join(CACHE_KEY_SEPARATOR, strObjs);
}
// =============================Common============================
/**
* 是否存在key
* @param key Redis中的key
* @return true如果key存在,否则false
* @example
* <pre>
* boolean exists = redisUtil.exists("myKey");
* System.out.println(exists);
* </pre>
*/
public boolean exists(String key) {
return execute(() -> redisTemplate.hasKey(key));
}
/**
* 删除key
* @param key Redis中的key
* @return true如果删除成功,否则false
* @example
* <pre>
* boolean deleted = redisUtil.delete("myKey");
* System.out.println(deleted);
* </pre>
*/
public boolean delete(String key) {
return execute(() -> redisTemplate.delete(key));
}
// =============================String============================
/**
* 设置key-value对
* @param key Redis中的key
* @param value 要设置的值
* @example
* <pre>
* redisUtil.set("myKey", "myValue");
* </pre>
*/
public void set(String key, Object value) {
execute(() -> {
redisTemplate.opsForValue().set(key, value);
return null;
});
}
/**
* 设置key-value对,并设置过期时间
* @param key Redis中的key
* @param value 要设置的值
* @param timeout 过期时间
* @param unit 时间单位
* @example
* <pre>
* redisUtil.set("myKey", "myValue", 10, TimeUnit.MINUTES);
* </pre>
*/
public void set(String key, Object value, long timeout, TimeUnit unit) {
execute(() -> {
redisTemplate.opsForValue().set(key, value, timeout, unit);
return null;
});
}
/**
* 设置key-value对,如果key不存在,则设置成功,并指定过期时间
* @param key Redis中的key
* @param value 要设置的值
* @param timeout 过期时间
* @param unit 时间单位
* @return true如果设置成功,否则false
* @example
* <pre>
* boolean result = redisUtil.setIfAbsent("myKey", "myValue", 10, TimeUnit.MINUTES);
* System.out.println(result);
* </pre>
*/
public boolean setIfAbsent(String key, Object value, long timeout, TimeUnit unit) {
return execute(() -> redisTemplate.opsForValue().setIfAbsent(key, value, timeout, unit));
}
/**
* 获取指定key的值
* @param key Redis中的key
* @param clazz 值的类型
* @return key对应的值
* @example
* <pre>
* String value = redisUtil.get("myKey", String.class);
* System.out.println(value);
* </pre>
*/
public <T> Optional<T> get(String key, Class<T> clazz) {
return Optional.ofNullable(execute(() -> clazz.cast(redisTemplate.opsForValue().get(key))));
}
/**
* 递增
* @param key Redis中的key
* @param delta 增量
* @example
* <pre>
* redisUtil.increment("myKey", 1);
* </pre>
*/
public void increment(String key, long delta) {
execute(() -> {
redisTemplate.opsForValue().increment(key, delta);
return null;
});
}
// =============================Hash============================
/**
* 向hash中存入数据
* @param key Redis中的key
* @param hashKey hash中的小key
* @param value hash中的小value
* @example
* <pre>
* redisUtil.hPut("myHash", "field1", "value1");
* </pre>
*/
public void hPut(String key, String hashKey, Object value) {
execute(() -> {
redisTemplate.opsForHash().put(key, hashKey, value);
return null;
});
}
/**
* 获取hash中的数据
* @param key Redis中的key
* @param hashKey hash中的小key
* @param clazz 值的类型
* @return hash中的小value
* @example
* <pre>
* String value = redisUtil.hGet("myHash", "field1", String.class);
* System.out.println(value);
* </pre>
*/
public <T> Optional<T> hGet(String key, String hashKey, Class<T> clazz) {
return Optional.ofNullable(execute(() -> clazz.cast(redisTemplate.opsForHash().get(key, hashKey))));
}
/**
* 获取hash中的所有数据
* @param key Redis中的key
* @return hash中的所有数据
* @example
* <pre>
* Map<Object, Object> map = redisUtil.hGetAll("myHash");
* map.forEach((k, v) -> System.out.println(k + ": " + v));
* </pre>
*/
public Optional<Map<Object, Object>> hGetAll(String key) {
return Optional.ofNullable(execute(() -> redisTemplate.opsForHash().entries(key)));
}
/**
* 删除hash中的指定字段
* @param key Redis中的key
* @param hashKey hash中的小key
* @example
* <pre>
* redisUtil.hDelete("myHash", "field1");
* </pre>
*/
public void hDelete(String key, Object... hashKey) {
execute(() -> {
redisTemplate.opsForHash().delete(key, hashKey);
return null;
});
}
/**
* 获取并删除hash中的所有数据
* @param key Redis中的key
* @return hash中的所有数据
* @example
* <pre>
* Map<Object, Object> map =redisUtil.hGetAndDelete(“myHash”);
* map.forEach((k, v) -> System.out.println(k + “: “ + v));
* </pre>
*/
public Optional<Map<Object, Object>> hGetAndDelete(String key) {
Map<Object, Object> map = new HashMap<>();
try (Cursor<Map.Entry<Object, Object>> cursor = redisTemplate.opsForHash().scan(key, ScanOptions.NONE)) {
while (cursor.hasNext()) {
Map.Entry<Object, Object> entry = cursor.next();
Object hashKey = entry.getKey();
Object hashValue = entry.getValue();
map.put(hashKey, hashValue);
redisTemplate.opsForHash().delete(key, hashKey);
}
} catch (Exception e) {
logger.error("Redis hGetAndDelete error: key={}", key, e);
}
return Optional.of(map);
}
// =============================List============================
/**
* 向list中左侧推入数据
* @param key Redis中的key
* @param value list中的值
* @example
* <pre>
* redisUtil.lPush("myList", "value1");
* </pre>
*/
public void lPush(String key, Object value) {
execute(() -> {
redisTemplate.opsForList().leftPush(key, value);
return null;
});
}
/**
* 向list中右侧推入数据
* @param key Redis中的key
* @param value list中的值
* @example
* <pre>
* redisUtil.rPush("myList", "value1");
* </pre>
*/
public void rPush(String key, Object value) {
execute(() -> {
redisTemplate.opsForList().rightPush(key, value);
return null;
});
}
/**
* 从list中左侧弹出数据
* @param key Redis中的key
* @param clazz 值的类型
* @return list中的值
* @example
* <pre>
* String value = redisUtil.lPop("myList", String.class).orElse(null);
* System.out.println(value);
* </pre>
*/
public <T> Optional<T> lPop(String key, Class<T> clazz) {
return Optional.ofNullable(execute(() -> clazz.cast(redisTemplate.opsForList().leftPop(key))));
}
/**
* 从list中右侧弹出数据
* @param key Redis中的key
* @param clazz 值的类型
* @return list中的值
* @example
* <pre>
* String value = redisUtil.rPop("myList", String.class).orElse(null);
* System.out.println(value);
* </pre>
*/
public <T> Optional<T> rPop(String key, Class<T> clazz) {
return Optional.ofNullable(execute(() -> clazz.cast(redisTemplate.opsForList().rightPop(key))));
}
/**
* 获取list中的指定范围的数据
* @param key Redis中的key
* @param start 起始位置
* @param end 结束位置
* @return list中的值
* @example
* <pre>
* List<Object> list = redisUtil.lRange("myList", 0, -1).orElse(Collections.emptyList());
* list.forEach(System.out::println);
* </pre>
*/
public Optional<List<Object>> lRange(String key, long start, long end) {
return Optional.ofNullable(execute(() -> redisTemplate.opsForList().range(key, start, end)));
}
// =============================Set============================
/**
* 向set中添加数据
* @param key Redis中的key
* @param values set中的值
* @example
* <pre>
* redisUtil.sAdd("mySet", "value1", "value2");
* </pre>
*/
public void sAdd(String key, Object... values) {
execute(() -> {
redisTemplate.opsForSet().add(key, values);
return null;
});
}
/**
* 获取set中的所有数据
* @param key Redis中的key
* @return set中的所有值
* @example
* <pre>
* Set<Object> set = redisUtil.sMembers("mySet").orElse(Collections.emptySet());
* set.forEach(System.out::println);
* </pre>
*/
public Optional<Set<Object>> sMembers(String key) {
return Optional.ofNullable(execute(() -> redisTemplate.opsForSet().members(key)));
}
/**
* 判断set中是否存在指定的值
* @param key Redis中的key
* @param value set中的值
* @return true如果存在,否则false
* @example
* <pre>
* boolean exists = redisUtil.sIsMember("mySet", "value1").orElse(false);
* System.out.println(exists);
* </pre>
*/
public Optional<Boolean> sIsMember(String key, Object value) {
return Optional.ofNullable(execute(() -> redisTemplate.opsForSet().isMember(key, value)));
}
/**
* 从set中随机弹出一个值
* @param key Redis中的key
* @return set中的值
* @example
* <pre>
* Object value = redisUtil.sPop("mySet").orElse(null);
* System.out.println(value);
* </pre>
*/
public Optional<Object> sPop(String key) {
return Optional.ofNullable(execute(() -> redisTemplate.opsForSet().pop(key)));
}
/**
* 获取set的大小
* @param key Redis中的key
* @return set的大小
* @example
* <pre>
* long size = redisUtil.sCard("mySet").orElse(0L);
* System.out.println(size);
* </pre>
*/
public Optional<Long> sCard(String key) {
return Optional.ofNullable(execute(() -> redisTemplate.opsForSet().size(key)));
}
// =============================ZSet============================
/**
* 向有序集合中添加元素
* @param key Redis中的key
* @param value 元素的值
* @param score 元素的分数
* @return true如果添加成功,否则false
* @example
* <pre>
* boolean added = redisUtil.zAdd("myZSet", "value1", 1.0).orElse(false);
* System.out.println(added);
* </pre>
*/
public Optional<Boolean> zAdd(String key, Object value, double score) {
return Optional.ofNullable(execute(() -> redisTemplate.opsForZSet().add(key, value, score)));
}
/**
* 获取有序集合的元素数量
* @param key Redis中的key
* @return 元素数量
* @example
* <pre>
* long size = redisUtil.zCard("myZSet").orElse(0L);
* System.out.println(size);
* </pre>
*/
public Optional<Long> zCard(String key) {
return Optional.ofNullable(execute(() -> redisTemplate.opsForZSet().size(key)));
}
/**
* 获取有序集合指定范围内的元素
* @param key Redis中的key
* @param start 起始位置
* @param end 结束位置
* @return 指定范围内的元素集合
* @example
* <pre>
* Set<Object> set = redisUtil.zRange("myZSet", 0, -1).orElse(Collections.emptySet());
* set.forEach(System.out::println);
* </pre>
*/
public Optional<Set<Object>> zRange(String key, long start, long end) {
return Optional.ofNullable(execute(() -> redisTemplate.opsForZSet().range(key, start, end)));
}
/**
* 删除有序集合中的指定元素
* @param key Redis中的key
* @param value 要删除的元素
* @return 被删除的元素数量
* @example
* <pre>
* long removed = redisUtil.zRemove("myZSet", "value1").orElse(0L);
* System.out.println(removed);
* </pre>
*/
public Optional<Long> zRemove(String key, Object value) {
return Optional.ofNullable(execute(() -> redisTemplate.opsForZSet().remove(key, value)));
}
/**
* 获取有序集合中指定元素的分数
* @param key Redis中的key
* @param value 元素的值
* @return 元素的分数
* @example
* <pre>
* double score = redisUtil.zScore("myZSet", "value1").orElse(null);
* System.out.println(score);
* </pre>
*/
public Optional<Double> zScore(String key, Object value) {
return Optional.ofNullable(execute(() -> redisTemplate.opsForZSet().score(key, value)));
}
/**
* 获取有序集合中指定分数范围内的元素
* @param key Redis中的key
* @param start 起始分数
* @param end 结束分数
* @return 指定分数范围内的元素集合
* @example
* <pre>
* Set<Object> set = redisUtil.zRangeByScore("myZSet”, 0, 100).orElse(Collections.emptySet());
* set.forEach(System.out::println);
*
*/
public Optional<Set> zRangeByScore(String key, double start, double end) {
return Optional.ofNullable(execute(() -> redisTemplate.opsForZSet().rangeByScore(key, start, end)));
}
/**
* 增加有序集合中指定元素的分数
* @param key Redis中的key
* @param value 元素的值
* @param score 增加的分数
* @return 增加后的分数
* @example
* <pre>
* double newScore = redisUtil.zIncrementScore("myZSet", "value1", 10.0).orElse(null);
* System.out.println(newScore);
* </pre>
*/
public Optional<Double> zIncrementScore(String key, Object value, double score) {
return Optional.ofNullable(execute(() -> redisTemplate.opsForZSet().incrementScore(key, value, score)));
}
/**
* 获取有序集合中指定元素的排名
* @param key Redis中的key
* @param value 元素的值
* @return 元素的排名
* @example
* <pre>
* long rank = redisUtil.zRank("myZSet", "value1").orElse(null);
* System.out.println(rank);
* </pre>
*/
public Optional<Long> zRank(String key, Object value) {
return Optional.ofNullable(execute(() -> redisTemplate.opsForZSet().rank(key, value)));
}
/**
* 从有序集合中按分数范围获取成员及其分数
* @param key 排行榜的key
* @param start 起始位置(包含)
* @param end 结束位置(包含)
* @return Set<ZSetOperations.TypedTuple < Object>> 每个TypedTuple对象包含以下内容:value: 集合中的成员,score: 成员的分数。
* @example
* <pre>
* Set<ZSetOperations.TypedTuple<Object>> set = redisUtil.zRangeWithScores("myZSet", 0, 100).orElse(Collections.emptySet());
* set.forEach(tuple -> System.out.println(tuple.getValue() + ": " + tuple.getScore()));
* </pre>
*/
public Optional<Set<ZSetOperations.TypedTuple<Object>>> zRangeWithScores(String key, long start, long end) {
return Optional.ofNullable(execute(() -> redisTemplate.opsForZSet().rangeWithScores(key, start, end)));
}
/**
* 获取有序集合中指定分数范围内的成员及其分数
* @param key Redis中的key
* @param min 最小分数
* @param max 最大分数
* @return Set<ZSetOperations.TypedTuple < Object>> 每个TypedTuple对象包含以下内容:value: 集合中的成员,score: 成员的分数。
* @example
* <pre>
* Set<ZSetOperations.TypedTuple<Object>> set = redisUtil.zRangeByScoreWithScores("myZSet", 0, 100).orElse(Collections.emptySet());
* set.forEach(tuple -> System.out.println(tuple.getValue() + ": " + tuple.getScore()));
* </pre>
*/
public Optional<Set<ZSetOperations.TypedTuple<Object>>> zRangeByScoreWithScores(String key, double min, double max) {
return Optional.ofNullable(execute(() -> redisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max)));
}
/**
* 获取有序集合中指定成员的分数范围排名
* @param key Redis中的key
* @param value 成员的值
* @return 成员的分数排名
* @example
* <pre>
* long rank = redisUtil.zRevRank("myZSet", "value1").orElse(null);
* System.out.println(rank);
* </pre>
*/
public Optional<Long> zRevRank(String key, Object value) {
return Optional.ofNullable(execute(() -> redisTemplate.opsForZSet().reverseRank(key, value)));
}
/**
* 获取有序集合中指定分数范围内的元素数量
* @param key Redis中的key
* @param min 最小分数
* @param max 最大分数
* @return 元素数量
* @example
* <pre>
* long count = redisUtil.zCount("myZSet", 0, 100).orElse(0L);
* System.out.println(count);
* </pre>
*/
public Optional<Long> zCount(String key, double min, double max) {
return Optional.ofNullable(execute(() -> redisTemplate.opsForZSet().count(key, min, max)));
}
/**
* 移除有序集合中指定分数范围内的元素
* @param key Redis中的key
* @param min 最小分数
* @param max 最大分数
* @return 移除的元素数量
* @example
* <pre>
* long removed = redisUtil.zRemoveByScore("myZSet", 0, 100).orElse(0L);
* System.out.println(removed);
* </pre>
*/
public Optional<Long> zRemoveByScore(String key, double min, double max) {
return Optional.ofNullable(execute(() -> redisTemplate.opsForZSet().removeRangeByScore(key, min, max)));
}
/**
* 移除有序集合中指定排名范围内的元素
* @param key Redis中的key
* @param start 起始排名
* @param end 结束排名
* @return 移除的元素数量
* @example
* <pre>
* long removed = redisUtil.zRemoveByRank("myZSet", 0, 100).orElse(0L);
* System.out.println(removed);
* </pre>
*/
public Optional<Long> zRemoveByRank(String key, long start, long end) {
return Optional.ofNullable(execute(() -> redisTemplate.opsForZSet().removeRange(key, start, end)));
}
private <T> T execute(RedisOperation<T> operation) {
try {
return operation.execute();
} catch (Exception e) {
logger.error("Redis operation error", e);
return null;
}
}
@FunctionalInterface
private interface RedisOperation<T> {
T execute();
}
}
2.使用案例
@RequestMapping("/testRedisUtil")
public String testRedisUtil() {
// 设置key-value
redisUtil.set("testRedisUtil", "123456");
// 获取key对应的value并指定转换的类型
String res = redisUtil.get("testRedisUtil", String.class).orElse(null);
if (res == null) {
return "null";
}
return res;
}