}
第一次执行,出现sql语句:
第二次无,直接从缓存中得到:
4、连接redis
要下载redis插件
5、增加缓存管理器方法
RedisConfiguration文件:
@Bean
CacheManager yzmManager(RedisConnectionFactory factory) {
RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.computePrefixWith(cacheName -> cacheName + “:-yzm-:”)
/设置缓存过期时间/
.entryTtl(Duration.ofSeconds(60))
/禁用缓存空值,不缓存null校验/
.disableCachingNullValues()
/设置CacheManager的值序列化方式为json序列化,可使用加入@Class属性/
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(
new GenericJackson2JsonRedisSerializer()
));
/使用RedisCacheConfiguration创建RedisCacheManager/
RedisCacheManager manager = RedisCacheManager.builder(factory)
.cacheDefaults(cacheConfiguration)
.build();
return manager;
}
StudentServiceImpl修改缓存管理器名字:
package com.lv.code.service;
import com.lv.code.mapper.StudentMapper;
import com.lv.code.pojo.Student;
import com.lv.code.util.PageBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
@CacheConfig(cacheNames = “student”)
public class StudentServiceImpl implements StudentService {
@Autowired
private StudentMapper mapper;
@Override
// @Cacheable:第一次访问,根据参数拿到键,根据键去缓存中寻找,
// 若未找到,则会去数据库中执行下面的方法,再将结果储存到缓存中;下一次就可以直接从缓存中拿数据
// cacheNames:缓存名字,用于分类
@Cacheable(cacheManager = “yzmManager”)
public List findPager(PageBean pageBean) {
return mapper.selectAll();
}
}
执行:
6、增加主键生成器方法
RedisConfiguration文件:
@Bean
@Primary
public KeyGenerator k1() {
return (Object target, Method method, Object… params) -> {
return LocalDateTime.now()+“”;
};
}
在StudentServiceImpl方法中调用
// key:存放在缓存中的名字,可自己指定,也可由键生成器生成
// 键生成器生成
@Cacheable(keyGenerator = “k1”)
// 指定键
@Cacheable(key = “123”)
7、注解的使用
@Cacheable:第一次访问,根据参数拿到键,根据键去缓存中寻找,
若未找到,则会去数据库中执行下面的方法,再将结果储存到缓存中;下一次就可以直接从缓存中拿数据,一般用于查询
**@CachePut:**用于更新缓存,不管有没有都进数据库,然后把数据库结果拿出来之后,再根据这个键放到缓存中,一般用于做修改
**@CacheEvict:**删除缓存,allEntries:删除该区块底下的所有缓存
@CacheConfig: 一个类级别的注解,允许共享缓存的cacheNames、KeyGenerator、CacheManager 和CacheResolver.
**@Configuration:**用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法
@Bean:用于定义加载类型
四、Template的使用
=============
1、Springboot05ApplicationTests :
package com.lv.code;
import com.lv.code.service.StudentService;
import com.lv.code.util.PageBean;
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 javax.sql.DataSource;
import java.util.concurrent.TimeUnit;
@SpringBootTest
class Springboot05ApplicationTests {
@Autowired
private StudentService service;
@Autowired
RedisTemplate redisTemplate;
@Test
void contextLoads() {
// 将验证码放入缓存
redisTemplate.opsForValue().set(“yzm”,“8080”);
// 这个验证码60秒后过期
redisTemplate.expire(“yzm”,60, TimeUnit.SECONDS);
//
}
}
2、若不使用注解的方式,可直接使用RedisTemplate来操作:
引入RedisUtil文件:
package com.lv.code.util;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.redis.RedisSystemException;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.connection.ReturnType;
import org.springframework.data.redis.connection.jedis.JedisConnection;
import org.springframework.data.redis.connection.lettuce.LettuceConnection;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;
/**
-
Redis工具类
-
-
声明: 此工具只简单包装了redisTemplate的大部分常用的api,没有包装redisTemplate所有的api
-
如果对此工具类中的功能不太满意,或对StringRedisTemplate提供的api不太满意,
-
那么可自行实现相应的{@link StringRedisTemplate}类中的对应execute方法,以达
-
到自己想要的效果; 至于如何实现,则可参考源码或{@link LockOps}中的方法
-
-
注: 此工具类依赖spring-boot-starter-data-redis类库
-
注: 更多javadoc细节,可详见{@link RedisOperations}
-
-
统一说明一: 方法中的key、 value都不能为null
-
统一说明二: 不能跨数据类型进行操作,否者会操作失败/操作报错
-
如: 向一个String类型的做Hash操作,会失败/报错…等等
*/
@Slf4j
@Component
@SuppressWarnings(“unused”)
public class RedisUtil implements ApplicationContextAware {
/**
- 使用StringRedisTemplate(,其是RedisTemplate的定制化升级)
*/
private static StringRedisTemplate redisTemplate;
private static final ObjectMapper mapper = new ObjectMapper();
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
RedisUtil.redisTemplate = applicationContext.getBean(StringRedisTemplate.class);
}
/**
- key相关操作
*/
public static class KeyOps {
/**
-
根据key,删除redis中的对应key-value
-
-
注: 若删除失败,则返回false
-
-
若redis中,不存在该key,那么返回的也是false
-
所以,不能因为返回了false,就认为redis中一定还存
-
在该key对应的key-value
-
@param key 要删除的key
-
@return 删除是否成功
*/
public static Boolean delete(String key) {
log.info(“delete(…) => key -> {}”, key);
// 返回值只可能为true/false,不可能为null
Boolean result = redisTemplate.delete(key);
log.info(“delete(…) => result -> {}”, result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
-
根据keys,批量删除key-value
-
-
注: 若redis中,不存在对应的key,那么计数不会加1,即:
-
redis中存在的key-value里,有名为a1、a2的key,
-
删除时,传的集合是a1、a2、a3,那么返回结果为2
-
@param keys 要删除的key集合
-
@return 删除了的key-value个数
*/
public static long delete(Collection keys) {
log.info(“delete(…) => keys -> {}”, keys);
Long count = redisTemplate.delete(keys);
log.info(“delete(…) => count -> {}”, count);
if (count == null) {
throw new RedisOpsResultIsNullException();
}
return count;
}
/**
-
将key对应的value值进行序列化,并返回序列化后的value值
-
-
注: 若不存在对应的key,则返回null
-
注: dump时,并不会删除redis中的对应key-value
-
注: dump功能与restore相反
-
@param key 要序列化的value的key
-
@return 序列化后的value值
*/
public static byte[] dump(String key) {
log.info(“dump(…) =>key -> {}”, key);
byte[] result = redisTemplate.dump(key);
log.info(“dump(…) => result -> {}”, result);
return result;
}
/**
-
将给定的value值,反序列化到redis中,形成新的key-value
-
@param key value对应的key
-
@param value 要反序列的value值
-
注: 这个值可以由{@link this#dump(String)}获得
-
@param timeToLive 反序列化后的key-value的存活时长
-
@param unit timeToLive的单位
-
@throws RedisSystemException 如果redis中已存在同样的key时,抛出此异常
*/
public static void restore(String key, byte[] value, long timeToLive, TimeUnit unit) {
restore(key, value, timeToLive, unit, false);
}
/**
-
将给定的value值,反序列化到redis中,形成新的key-value
-
@param key value对应的key
-
@param value 要反序列的value值
-
注: 这个值可以由{@link this#dump(String)}获得
-
@param timeout 反序列化后的key-value的存活时长
-
@param unit timeout的单位
-
@param replace 若redis中已经存在了相同的key,是否替代原来的key-value
-
@throws RedisSystemException 如果redis中已存在同样的key,且replace为false时,抛出此异常
*/
public static void restore(String key, byte[] value, long timeout, TimeUnit unit, boolean replace) {
log.info(“restore(…) => key -> {},value -> {},timeout -> {},unit -> {},replace -> {}”,
key, value, timeout, unit, replace);
redisTemplate.restore(key, value, timeout, unit, replace);
}
/**
-
redis中是否存在,指定key的key-value
-
@param key 指定的key
-
@return 是否存在对应的key-value
*/
public static boolean hasKey(String key) {
log.info(“hasKey(…) => key -> {}”, key);
Boolean result = redisTemplate.hasKey(key);
log.info(“hasKey(…) => result -> {}”, result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
-
给指定的key对应的key-value设置: 多久过时
-
-
注:过时后,redis会自动删除对应的key-value
-
注:若key不存在,那么也会返回false
-
@param key 指定的key
-
@param timeout 过时时间
-
@param unit timeout的单位
-
@return 操作是否成功
*/
public static boolean expire(String key, long timeout, TimeUnit unit) {
log.info(“expire(…) => key -> {},timeout -> {},unit -> {}”, key, timeout, unit);
Boolean result = redisTemplate.expire(key, timeout, unit);
log.info(“expire(…) => result is -> {}”, result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
-
给指定的key对应的key-value设置: 什么时候过时
-
-
注:过时后,redis会自动删除对应的key-value
-
注:若key不存在,那么也会返回false
-
@param key 指定的key
-
@param date 啥时候过时
-
@return 操作是否成功
*/
public static boolean expireAt(String key, Date date) {
log.info(“expireAt(…) => key -> {},date -> {}”, key, date);
Boolean result = redisTemplate.expireAt(key, date);
log.info(“expireAt(…) => result is -> {}”, result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
-
找到所有匹配pattern的key,并返回该key的结合.
-
-
提示:若redis中键值对较多,此方法耗时相对较长,慎用!慎用!慎用!
-
@param pattern 匹配模板
-
注: 常用的通配符有:
-
? 有且只有一个;
-
* >=0个;
-
@return 匹配pattern的key的集合 可能为null
*/
public static Set keys(String pattern) {
log.info(“keys(…) => pattern -> {}”, pattern);
Set keys = redisTemplate.keys(pattern);
log.info(“keys(…) => keys -> {}”, keys);
return keys;
}
/**
-
将当前数据库中的key对应的key-value,移动到对应位置的数据库中
-
-
注:单机版的redis,默认将存储分为16个db,index为0 到 15
-
注:同一个db下,key唯一; 但是在不同db中,key可以相同
-
注:若目标db下,已存在相同的key,那么move会失败,返回false
-
@param key 定位要移动的key-value的key
-
@param dbIndex 要移动到哪个db
-
@return 移动是否成功
-
注: 若目标db下,已存在相同的key,那么move会失败,返回false
*/
public static boolean move(String key, int dbIndex) {
log.info(“move(…) => key -> {},dbIndex -> {}”, key, dbIndex);
Boolean result = redisTemplate.move(key, dbIndex);
log.info(“move(…) =>result -> {}”, result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
-
移除key对应的key-value的过期时间,使该key-value一直存在
-
-
注: 若key对应的key-value,本身就是一直存在(无过期时间的),那么persist方法会返回false;
-
若没有key对应的key-value存在,本那么persist方法会返回false;
-
@param key 定位key-value的key
-
@return 操作是否成功
*/
public static boolean persist(String key) {
log.info(“persist(…) => key -> {}”, key);
Boolean result = redisTemplate.persist(key);
log.info(“persist(…) => result -> {}”, result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
-
获取key对应的key-value的过期时间
-
-
注: 若key-value永不过期,那么返回的为-1
-
注: 若不存在key对应的key-value,那么返回的为-2
-
注:若存在零碎时间不足1 SECONDS,则(大体上)四舍五入到SECONDS级别
-
@param key 定位key-value的key
-
@return 过期时间(单位s)
*/
public static long getExpire(String key) {
return getExpire(key, TimeUnit.SECONDS);
}
/**
-
获取key对应的key-value的过期时间
-
-
注: 若key-value永不过期,那么返回的为-1
-
注: 若不存在key对应的key-value,那么返回的为-2
-
注:若存在零碎时间不足1 unit,则(大体上)四舍五入到unit别
-
@param key 定位key-value的key
-
@return 过期时间(单位unit)
*/
public static long getExpire(String key, TimeUnit unit) {
log.info(“getExpire(…) =>key -> {},unit is -> {}”, key, unit);
Long result = redisTemplate.getExpire(key, unit);
log.info(“getExpire(…) => result -> {}”, result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
-
从redis的所有key中,随机获取一个key
-
-
注: 若redis中不存在任何key-value,那么这里返回null
-
@return 随机获取到的一个key
*/
public static String randomKey() {
String result = redisTemplate.randomKey();
log.info(“randomKey(…) => result is -> {}”, result);
return result;
}
/**
-
重命名对应的oldKey为新的newKey
-
-
注: 若oldKey不存在,则会抛出异常.
-
注: 若redis中已存在与newKey一样的key,
-
那么原key-value会被丢弃,
-
只留下新的key,以及原来的value
-
示例说明: 假设redis中已有 (keyAlpha,valueAlpha) 和 (keyBeta,valueBeat),
-
在使用rename(keyAlpha,keyBeta)替换后,redis中只会剩下(keyBeta,valueAlpha)
-
@param oldKey 旧的key
-
@param newKey 新的key
-
@throws RedisSystemException 若oldKey不存在时,抛出此异常
*/
public static void rename(String oldKey, String newKey) {
log.info(“rename(…) => oldKey -> {},newKey -> {}”, oldKey, newKey);
redisTemplate.rename(oldKey, newKey);
}
/**
-
当redis中不存在newKey时,重命名对应的oldKey为新的newKey
-
否者不进行重命名操作
-
-
注: 若oldKey不存在,则会抛出异常.
-
@param oldKey 旧的key
-
@param newKey 新的key
-
@throws RedisSystemException 若oldKey不存在时,抛出此异常
*/
public static boolean renameIfAbsent(String oldKey, String newKey) {
log.info(“renameIfAbsent(…) => oldKey -> {},newKey -> {}”, oldKey, newKey);
Boolean result = redisTemplate.renameIfAbsent(oldKey, newKey);
log.info(“renameIfAbsent(…) => result -> {}”, result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
-
获取key对应的value的数据类型
-
-
注: 若redis中不存在该key对应的key-value,那么这里返回NONE
-
@param key 用于定位的key
-
@return key对应的value的数据类型
*/
public static DataType type(String key) {
log.info(“type(…) => key -> {}”, key);
DataType result = redisTemplate.type(key);
log.info(“type(…) => result -> {}”, result);
return result;
}
}
/**
- string相关操作
*/
public static class StringOps {
/**
-
设置key-value
-
-
注: 若已存在相同的key,那么原来的key-value会被丢弃
-
@param key key
-
@param value key对应的value
*/
public static void set(String key, String value) {
log.info(“set(…) => key -> {},value -> {}”, key, value);
redisTemplate.opsForValue().set(key, value);
}
/**
-
处理redis中key对应的value值,将第offset位的值,设置为1或0
-
-
说明: 在redis中,存储的字符串都是以二级制的进行存在的; 如存储的key-value里,值为abc,实际上,
-
在redis里面存储的是011000010110001001100011,前8为对应a,中间8为对应b,后面8位对应c
-
示例:这里如果setBit(key,6,true)的话,就是将索引位置6的那个数,设置值为1,值就变成
-
了011000110110001001100011
-
追注:offset即index,从0开始
-
-
注: 参数value为true,则设置为1;参数value为false,则设置为0
-
-
注: 若redis中不存在对应的key,那么会自动创建新的
-
注: offset可以超过value在二进制下的索引长度
-
@param key 定位value的key
-
@param offset 要改变的bit的索引
-
@param value 改为1或0,true - 改为1,false - 改为0
-
@return set是否成功
*/
public static boolean setBit(String key, long offset, boolean value) {
log.info(“setBit(…) => key -> {},offset -> {},value -> {}”, key, offset, value);
Boolean result = redisTemplate.opsForValue().setBit(key, offset, value);
log.info(“setBit(…) => result -> {}”, result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
-
设置key-value
-
-
注: 若已存在相同的key,那么原来的key-value会被丢弃
-
@param key key
-
@param value key对应的value
-
@param timeout 过时时长
-
@param unit timeout的单位
*/
public static void setEx(String key, String value, long timeout, TimeUnit unit) {
log.info(“setEx(…) => key -> {},value -> {},timeout -> {},unit -> {}”,
key, value, timeout, unit);
redisTemplate.opsForValue().set(key, value, timeout, unit);
}
/**
-
若不存在key时,向redis中添加key-value,返回成功/失败
-
若存在,则不作任何操作,返回false
-
@param key key
-
@param value key对应的value
-
@return set是否成功
*/
public static boolean setIfAbsent(String key, String value) {
log.info(“setIfAbsent(…) => key -> {},value -> {}”, key, value);
Boolean result = redisTemplate.opsForValue().setIfAbsent(key, value);
log.info(“setIfAbsent(…) => result -> {}”, result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
-
若不存在key时,向redis中添加一个(具有超时时长的)key-value,返回成功/失败
-
若存在,则不作任何操作,返回false
-
@param key key
-
@param value key对应的value
-
@param timeout 超时时长
-
@param unit timeout的单位
-
@return set是否成功
*/
public static boolean setIfAbsent(String key, String value, long timeout, TimeUnit unit) {
log.info(“setIfAbsent(…) => key -> {},value -> {},key -> {},value -> {}”, key, value, timeout, unit);
Boolean result = redisTemplate.opsForValue().setIfAbsent(key, value, timeout, unit);
log.info(“setIfAbsent(…) => result -> {}”, result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
-
从(redis中key对应的)value的offset位置起(包含该位置),用replaceValue替换对应长度的值
-
-
举例说明:
-
1.假设redis中存在key-value (“ds”,“0123456789”); 调
-
用setRange(“ds”,“abcdefghijk”,3)后,redis中该value值就变为了[012abcdefghijk]
-
-
2.假设redis中存在key-value (“jd”,“0123456789”);调
-
用setRange(“jd”,“xyz”,3)后,redis中该value值就变为了[012xyz6789]
-
-
3.假设redis中存在key-value (“ey”,“0123456789”);调
-
用setRange(“ey”,“qwer”,15)后,redis中该value值就变为了[0123456789 qwer]
-
注:case3比较特殊,offset超过了原value的长度了,中间就会有一些空格来填充,但是如果在程序
-
中直接输出的话,中间那部分空格可能会出现乱码
-
@param key 定位key-value的key
-
@param replaceValue 要替换的值
-
@param offset 起始位置
*/
public static void setRange(String key, String replaceValue, long offset) {
log.info(“setRange(…) => key -> {},replaceValue -> {},offset -> {}”, key, replaceValue, offset);
redisTemplate.opsForValue().set(key, replaceValue, offset);
}
/**
-
获取到key对应的value的长度
-
-
注: 长度等于{@link String#length}
-
注: 若redis中不存在对应的key-value,则返回值为0.
-
@param key 定位value的key
-
@return value的长度
*/
public static long size(String key) {
log.info(“size(…) => key -> {}”, key);
Long result = redisTemplate.opsForValue().size(key);
log.info(“size(…) => result -> {}”, result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
-
批量设置 key-value
-
-
注: 若存在相同的key,则原来的key-value会被丢弃
-
@param maps key-value 集
*/
public static void multiSet(Map<String, String> maps) {
log.info(“multiSet(…) => maps -> {}”, maps);
redisTemplate.opsForValue().multiSet(maps);
}
/**
-
当redis中,不存在任何一个keys时,才批量设置 key-value,并返回成功/失败.
-
否者,不进行任何操作,并返回false
-
-
即: 假设调用此方法时传入的参数map是这样的: {k1=v1,k2=v2,k3=v3}
-
那么redis中,k1、k2、k3都不存在时,才会批量设置key-value;
-
否则不会设置任何key-value
-
-
注: 若存在相同的key,则原来的key-value会被丢弃
-
-
注:
-
@param maps key-value 集
-
@return 操作是否成功
*/
public static boolean multiSetIfAbsent(Map<String, String> maps) {
log.info(“multiSetIfAbsent(…) => maps -> {}”, maps);
Boolean result = redisTemplate.opsForValue().multiSetIfAbsent(maps);
log.info(“multiSetIfAbsent(…) => result -> {}”, result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
-
增/减 整数
-
-
注: 负数则为减
-
注: 若key对应的value值不支持增/减操作(即: value不是数字),那么会
-
抛出org.springframework.data.redis.RedisSystemException
-
@param key 用于定位value的key
-
@param increment 增加多少
-
@return 增加后的总值
-
@throws RedisSystemException key对应的value值不支持增/减操作时
*/
public static long incrBy(String key, long increment) {
log.info(“incrBy(…) => key -> {},increment -> {}”, key, increment);
Long result = redisTemplate.opsForValue().increment(key, increment);
log.info(“incrBy(…) => result -> {}”, result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
-
增/减 浮点数
-
-
注: 慎用浮点数,会有精度问题
-
如: 先 RedisUtil.StringOps.set(“ds”,“123”);
-
然后再RedisUtil.StringOps.incrByFloat(“ds”,100.6);
-
就会看到精度问题
-
注: 负数则为减
-
注: 若key对应的value值不支持增/减操作(即: value不是数字),那么会
-
抛出org.springframework.data.redis.RedisSystemException
-
@param key 用于定位value的key
-
@param increment 增加多少
-
@return 增加后的总值
-
@throws RedisSystemException key对应的value值不支持增/减操作时
*/
public static double incrByFloat(String key, double increment) {
log.info(“incrByFloat(…) => key -> {},increment -> {}”, key, increment);
Double result = redisTemplate.opsForValue().increment(key, increment);
log.info(“incrByFloat(…) => result -> {}”, result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
-
追加值到末尾
-
-
注: 当redis中原本不存在key时,那么(从效果上来看)此方法就等价于{@link this#set(String, String)}
-
@param key 定位value的key
-
@param value 要追加的value值
-
@return 追加后, 整个value的长度
*/
public static int append(String key, String value) {
log.info(“append(…) => key -> {},value -> {}”, key, value);
Integer result = redisTemplate.opsForValue().append(key, value);
log.info(“append(…) => result -> {}”, result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
-
根据key,获取到对应的value值
-
@param key key-value对应的key
-
@return 该key对应的值
-
注: 若key不存在,则返回null
*/
public static String get(String key) {
log.info(“get(…) => key -> {}”, key);
String result = redisTemplate.opsForValue().get(key);
log.info("get(…) => result -> {} ", result);
return result;
}
/**
-
对(key对应的)value进行截取,截取范围为[start,end]
-
-
注: 若[start,end]的范围不在value的范围中,那么返回的是空字符串 “”
-
注: 若value只有一部分在[start,end]的范围中,那么返回的是value对应部分的内容(即:不足的地方,并不会以空来填充)
-
@param key 定位value的key
-
@param start 起始位置 (从0开始)
-
@param end 结尾位置 (从0开始)
-
@return 截取后的字符串
*/
public static String getRange(String key, long start, long end) {
log.info(“getRange(…) => kry -> {}”, key);
String result = redisTemplate.opsForValue().get(key, start, end);
log.info("getRange(…) => result -> {} ", result);
return result;
}
/**
-
给指定key设置新的value,并返回旧的value
-
-
注: 若redis中不存在key,那么此操作仍然可以成功,不过返回的旧值是null
-
@param key 定位value的key
-
@param newValue 要为该key设置的新的value值
-
@return 旧的value值
*/
public static String getAndSet(String key, String newValue) {
log.info(“getAndSet(…) => key -> {},value -> {}”, key, newValue);
String oldValue = redisTemplate.opsForValue().getAndSet(key, newValue);
log.info(“getAndSet(…) => oldValue -> {}”, oldValue);
return oldValue;
}
/**
-
获取(key对应的)value在二进制下,offset位置的bit值
-
-
注: 当offset的值在(二进制下的value的)索引范围外时,返回的也是false
-
-
示例:
-
RedisUtil.StringOps.set(“akey”,“a”);
-
字符串a,转换为二进制为01100001
-
那么getBit(“akey”,6)获取到的结果为false
-
@param key 定位value的key
-
@param offset 定位bit的索引
-
@return offset位置对应的bit的值(true - 1, false - 0)
*/
public static boolean getBit(String key, long offset) {
log.info(“getBit(…) => key -> {},offset -> {}”, key, offset);
Boolean result = redisTemplate.opsForValue().getBit(key, offset);
log.info(“getBit(…) => result -> {}”, result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
-
批量获取value值
-
-
注: 若redis中,对应的key不存在,那么该key对应的返回的value值为null
-
@param keys key集
-
@return value值集合
*/
public static List multiGet(Collection keys) {
log.info(“multiGet(…) => keys -> {}”, keys);
List result = redisTemplate.opsForValue().multiGet(keys);
log.info(“multiGet(…) => result -> {}”, result);
return result;
}
}
/**
-
hash相关操作
-
-
提示: 简单的,可以将redis中hash的数据结构看作是 Map<String,Map<HK,HV>>
*/
public static class HashOps {
/**
-
向key对应的hash中,增加一个键值对entryKey-entryValue
-
-
注: 同一个hash里面,若已存在相同的entryKey,那么此操作将丢弃原来的entryKey-entryValue,
-
而使用新的entryKey-entryValue
-
@param key 定位hash的key
-
@param entryKey 要向hash中增加的键值对里的 键
-
@param entryValue 要向hash中增加的键值对里的 值
*/
public static void hPut(String key, String entryKey, String entryValue) {
log.info(“hPut(…) => key -> {},entryKey -> {},entryValue -> {}”, key, entryKey, entryValue);
redisTemplate.opsForHash().put(key, entryKey, entryValue);
}
/**
-
向key对应的hash中,增加maps(即: 批量增加entry集)
-
-
注: 同一个hash里面,若已存在相同的entryKey,那么此操作将丢弃原来的entryKey-entryValue,
-
而使用新的entryKey-entryValue
-
@param key 定位hash的key
-
@param maps 要向hash中增加的键值对集
*/
public static void hPutAll(String key, Map<String, String> maps) {
log.info(“hPutAll(…) => key -> {},maps -> {}”, key, maps);
redisTemplate.opsForHash().putAll(key, maps);
}
/**
-
当key对应的hash中,不存在entryKey时,才(向key对应的hash中,)增加entryKey-entryValue
-
否则,不进行任何操作
-
@param key 定位hash的key
-
@param entryKey 要向hash中增加的键值对里的 键
-
@param entryValue 要向hash中增加的键值对里的 值
-
@return 操作是否成功
*/
public static boolean hPutIfAbsent(String key, String entryKey, String entryValue) {
log.info(“hPutIfAbsent(…) => key -> {},entryKey -> {},entryValue -> {}”,
key, entryKey, entryValue);
Boolean result = redisTemplate.opsForHash().putIfAbsent(key, entryKey, entryValue);
log.info(“hPutIfAbsent(…) => result -> {}”, result);
if (result != null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
-
获取到key对应的hash里面的对应字段的值
-
-
注: 若redis中不存在对应的key,则返回null
-
若key对应的hash中不存在对应的entryKey,也会返回null
-
@param key 定位hash的key
-
@param entryKey 定位hash里面的entryValue的entryKey
-
@return key对应的hash里的entryKey对应的entryValue值
*/
public static Object hGet(String key, String entryKey) {
log.info(“hGet(…) => key -> {},entryKey -> {}”, key, entryKey);
Object entryValue = redisTemplate.opsForHash().get(key, entryKey);
log.info(“hGet(…) => entryValue -> {}”, entryValue);
return entryValue;
}
/**
-
获取到key对应的hash(即: 获取到key对应的Map<HK,HV>)
-
-
注: 若redis中不存在对应的key,则返回一个没有任何entry的空的Map(,而不是返回null)
-
@param key 定位hash的key
-
@return key对应的hash
*/
public static Map<Object, Object> hGetAll(String key) {
log.info(“hGetAll(…) => key -> {}”, key);
Map<Object, Object> result = redisTemplate.opsForHash().entries(key);
log.info(“hGetAll(…) => result -> {}”, result);
return result;
}
/**
-
批量获取(key对应的)hash中的entryKey的entryValue
-
-
注: 若hash中对应的entryKey不存在,那么返回的对应的entryValue值为null
-
注: redis中key不存在,那么返回的List中,每个元素都为null
-
追注: 这个List本身不为null,size也不为0,只是每个list中的每个元素为null而已
-
@param key 定位hash的key
-
@param entryKeys 需要获取的hash中的字段集
-
@return hash中对应entryKeys的对应entryValue集
*/
public static List hMultiGet(String key, Collection entryKeys) {
log.info(“hMultiGet(…) => key -> {},entryKeys -> {}”, key, entryKeys);
List entryValues = redisTemplate.opsForHash().multiGet(key, entryKeys);
log.info(“hMultiGet(…) => entryValues -> {}”, entryValues);
return entryValues;
}
/**
-
(批量)删除(key对应的)hash中的对应entryKey-entryValue
-
-
注: 1、若redis中不存在对应的key,则返回0;
-
2、若要删除的entryKey,在key对应的hash中不存在,在count不会+1,如:
-
RedisUtil.HashOps.hPut(“ds”,“name”,“邓沙利文”);
-
RedisUtil.HashOps.hPut(“ds”,“birthday”,“1994-02-05”);
-
RedisUtil.HashOps.hPut(“ds”,“hobby”,“女”);
-
则调用RedisUtil.HashOps.hDelete(“ds”,“name”,“birthday”,“hobby”,“non-exist-entryKey”)
-
的返回结果为3
-
注: 若(key对应的)hash中的所有entry都被删除了,那么该key也会被删除
-
@param key 定位hash的key
-
@param entryKeys 定位要删除的entryKey-entryValue的entryKey
-
@return 删除了对应hash中多少个entry
*/
public static long hDelete(String key, Object… entryKeys) {
log.info(“hDelete(…) => key -> {},entryKeys -> {}”, key, entryKeys);
Long count = redisTemplate.opsForHash().delete(key, entryKeys);
log.info(“hDelete(…) => count -> {}”, count);
if (count == null) {
throw new RedisOpsResultIsNullException();
}
return count;
}
/**
-
查看(key对应的)hash中,是否存在entryKey对应的entry
-
-
注: 若redis中不存在key,则返回false
-
注: 若key对应的hash中不存在对应的entryKey,也会返回false
-
@param key 定位hash的key
-
@param entryKey 定位hash中entry的entryKey
-
@return hash中是否存在entryKey对应的entry.
*/
public static boolean hExists(String key, String entryKey) {
log.info(“hDelete(…) => key -> {},entryKeys -> {}”, key, entryKey);
Boolean exist = redisTemplate.opsForHash().hasKey(key, entryKey);
log.info(“hDelete(…) => exist -> {}”, exist);
return exist;
}
/**
-
增/减(hash中的某个entryValue值) 整数
-
-
注: 负数则为减
-
注: 若key不存在,那么会自动创建对应的hash,并创建对应的entryKey、entryValue,entryValue的初始值为increment
-
注: 若entryKey不存在,那么会自动创建对应的entryValue,entryValue的初始值为increment
-
注: 若key对应的value值不支持增/减操作(即: value不是数字),那么会
-
抛出org.springframework.data.redis.RedisSystemException
-
@param key 用于定位hash的key
-
@param entryKey 用于定位entryValue的entryKey
-
@param increment 增加多少
-
@return 增加后的总值
-
@throws RedisSystemException key对应的value值不支持增/减操作时
*/
public static long hIncrBy(String key, Object entryKey, long increment) {
log.info(“hIncrBy(…) => key -> {},entryKey -> {},increment -> {}”,
key, entryKey, increment);
Long result = redisTemplate.opsForHash().increment(key, entryKey, increment);
log.info(“hIncrBy(…) => result -> {}”, result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
-
增/减(hash中的某个entryValue值) 浮点数
-
-
注: 负数则为减
-
注: 若key不存在,那么会自动创建对应的hash,并创建对应的entryKey、entryValue,entryValue的初始值为increment
-
注: 若entryKey不存在,那么会自动创建对应的entryValue,entryValue的初始值为increment
-
注: 若key对应的value值不支持增/减操作(即: value不是数字),那么会
-
抛出org.springframework.data.redis.RedisSystemException
-
注: 因为是浮点数,所以可能会和{@link StringOps#incrByFloat(String, double)}一样,出现精度问题
-
追注: 本人简单测试了几组数据,暂未出现精度问题
-
@param key 用于定位hash的key
-
@param entryKey 用于定位entryValue的entryKey
-
@param increment 增加多少
-
@return 增加后的总值
-
@throws RedisSystemException key对应的value值不支持增/减操作时
*/
public static double hIncrByFloat(String key, Object entryKey, double increment) {
log.info(“hIncrByFloat(…) => key -> {},entryKey -> {},increment -> {}”,
key, entryKey, increment);
Double result = redisTemplate.opsForHash().increment(key, entryKey, increment);
log.info(“hIncrByFloat(…) => result -> {}”, result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
-
获取(key对应的)hash中的所有entryKey
-
-
注: 若key不存在,则返回的是一个空的Set(,而不是返回null)
-
@param key 定位hash的key
-
@return hash中的所有entryKey
*/
public static Set hKeys(String key) {
log.info(“hKeys(…) => key -> {}”, key);
Set entryKeys = redisTemplate.opsForHash().keys(key);
log.info(“hKeys(…) => entryKeys -> {}”, entryKeys);
return entryKeys;
}
/**
-
获取(key对应的)hash中的所有entryValue
-
-
注: 若key不存在,则返回的是一个空的List(,而不是返回null)
-
@param key 定位hash的key
-
@return hash中的所有entryValue
*/
public static List hValues(String key) {
log.info(“hValues(…) => key -> {}”, key);
List entryValues = redisTemplate.opsForHash().values(key);
log.info(“hValues(…) => entryValues -> {}”, entryValues);
return entryValues;
}
/**
-
获取(key对应的)hash中的所有entry的数量
-
-
注: 若redis中不存在对应的key,则返回值为0
-
@param key 定位hash的key
-
@return (key对应的)hash中, entry的个数
*/
public static long hSize(String key) {
log.info(“hSize(…) => key -> {}”, key);
Long count = redisTemplate.opsForHash().size(key);
log.info(“hSize(…) => count -> {}”, count);
if (count == null) {
throw new RedisOpsResultIsNullException();
}
return count;
}
/**
-
根据options匹配到(key对应的)hash中的对应的entryKey,并返回对应的entry集
-
-
-
注: ScanOptions实例的创建方式举例:
-
1、ScanOptions.NONE
-
2、ScanOptions.scanOptions().match(“n??e”).build()
-
@param key 定位hash的key
-
@param options 匹配entryKey的条件
-
注: ScanOptions.NONE表示全部匹配
-
注: ScanOptions.scanOptions().match(pattern).build()表示按照pattern匹配,
-
其中pattern中可以使用通配符 * ? 等,
-
* 表示>=0个字符
-
? 表示有且只有一个字符
-
此处的匹配规则与{@link KeyOps#keys(String)}处的一样
-
@return 匹配到的(key对应的)hash中的entry
*/
@SneakyThrows
public static Cursor<Entry<Object, Object>> hScan(String key, ScanOptions options) {
log.info(“hScan(…) => key -> {},options -> {}”, key, mapper.writeValueAsString(options));
Cursor<Entry<Object, Object>> cursor = redisTemplate.opsForHash().scan(key, options);
log.info(“hScan(…) => cursor -> {}”, mapper.writeValueAsString(cursor));
return cursor;
}
}
/**
-
list相关操作
-
-
提示: 列表中的元素,可以重复
-
-
提示: list是有序的
-
-
提示: redis中的list中的索引,可分为两类,这两类都可以用来定位list中元素:
-
类别一: 从left到right,是从0开始依次增大: 0, 1, 2, 3…
-
类别二: 从right到left,是从-1开始依次减小: -1,-2,-3,-4…
*/
public static class ListOps {
/**
-
从左端推入元素进列表
-
-
注: 若redis中不存在对应的key,那么会自动创建
-
@param key 定位list的key
-
@param item 要推入list的元素
-
@return 推入后, (key对应的)list的size
*/
public static long lLeftPush(String key, String item) {
log.info(“lLeftPush(…) => key -> {},item -> {}”, key, item);
Long size = redisTemplate.opsForList().leftPush(key, item);
log.info(“lLeftPush(…) => size -> {}”, size);
if (size == null) {
throw new RedisOpsResultIsNullException();
}
return size;
}
/**
-
从左端批量推入元素进列表
-
-
注: 若redis中不存在对应的key,那么会自动创建
-
注: 这一批item中,先push左侧的,后push右侧的
-
@param key 定位list的key
-
@param items 要批量推入list的元素集
-
@return 推入后, (key对应的)list的size
*/
public static long lLeftPushAll(String key, String… items) {
log.info(“lLeftPushAll(…) => key -> {},items -> {}”, key, items);
Long size = redisTemplate.opsForList().leftPushAll(key, items);
log.info(“lLeftPushAll(…) => size -> {}”, size);
if (size == null) {
throw new RedisOpsResultIsNullException();
}
return size;
}
/**
-
从左端批量推入元素进列表
-
-
注: 若redis中不存在对应的key,那么会自动创建
-
注: 这一批item中,那个item先从Collection取出来,就先push哪个
-
@param key 定位list的key
-
@param items 要批量推入list的元素集
-
@return 推入后, (key对应的)list的size
*/
public static long lLeftPushAll(String key, Collection items) {
log.info(“lLeftPushAll(…) => key -> {},items -> {}”, key, items);
Long size = redisTemplate.opsForList().leftPushAll(key, items);
log.info(“lLeftPushAll(…) => size -> {}”, size);
if (size == null) {
throw new RedisOpsResultIsNullException();
}
return size;
}
/**
-
如果redis中存在key,则从左端批量推入元素进列表;
-
否则,不进行任何操作
-
@param key 定位list的key
-
@param item 要推入list的项
-
@return 推入后, (key对应的)list的size
*/
public static long lLeftPushIfPresent(String key, String item) {
log.info(“lLeftPushIfPresent(…) => key -> {},item -> {}”, key, item);
Long size = redisTemplate.opsForList().leftPushIfPresent(key, item);
log.info(“lLeftPushIfPresent(…) => size -> {}”, size);
if (size == null) {
throw new RedisOpsResultIsNullException();
}
return size;
}
/**
-
若key对应的list中存在pivot项,那么将item放入第一个pivot项前(即:放在第一个pivot项左边);
-
若key对应的list中不存在pivot项,那么不做任何操作,直接返回-1
-
-
注: 若redis中不存在对应的key,那么会自动创建
-
@param key 定位list的key
-
@param item 要推入list的元素
-
@return 推入后, (key对应的)list的size
*/
public static long lLeftPush(String key, String pivot, String item) {
log.info(“lLeftPush(…) => key -> {},pivot -> {},item -> {}”, key, pivot, item);
Long size = redisTemplate.opsForList().leftPush(key, pivot, item);
log.info(“lLeftPush(…) => size -> {}”, size);
if (size == null) {
throw new RedisOpsResultIsNullException();
}
return size;
}
/**
- 与{@link ListOps#lLeftPush(String, String)}类比即可,不过是从list右侧推入元素
*/
public static long lRightPush(String key, String item) {
log.info(“lRightPush(…) => key -> {},item -> {}”, key, item);
Long size = redisTemplate.opsForList().rightPush(key, item);
log.info(“lRightPush(…) => size -> {}”, size);
if (size == null) {
throw new RedisOpsResultIsNullException();
}
return size;
}
/**
- 与{@link ListOps#lLeftPushAll(String, String…)}类比即可,不过是从list右侧推入元素
*/
public static long lRightPushAll(String key, String… items) {
log.info(“lRightPushAll(…) => key -> {},items -> {}”, key, items);
Long size = redisTemplate.opsForList().rightPushAll(key, items);
log.info(“lRightPushAll(…) => size -> {}”, size);
if (size == null) {
throw new RedisOpsResultIsNullException();
}
return size;
}
/**
- 与{@link ListOps#lLeftPushAll(String, Collection)}类比即可,不过是从list右侧推入元素
*/
public static long lRightPushAll(String key, Collection items) {
log.info(“lRightPushAll(…) => key -> {},items -> {}”, key, items);
Long size = redisTemplate.opsForList().rightPushAll(key, items);
log.info(“lRightPushAll(…) => size -> {}”, size);
if (size == null) {
throw new RedisOpsResultIsNullException();
}
return size;
}
/**
- 与{@link ListOps#lLeftPushIfPresent(String, String)}类比即可,不过是从list右侧推入元素
*/
public static long lRightPushIfPresent(String key, String item) {
log.info(“lRightPushIfPresent(…) => key -> {},item -> {}”, key, item);
Long size = redisTemplate.opsForList().rightPushIfPresent(key, item);
log.info(“lRightPushIfPresent(…) => size -> {}”, size);
if (size == null) {
throw new RedisOpsResultIsNullException();
}
return size;
}
/**
- 与{@link ListOps#lLeftPush(String, String, String)}类比即可,不过是从list右侧推入元素
*/
public static long lRightPush(String key, String pivot, String item) {
log.info(“lLeftPush(…) => key -> {},pivot -> {},item -> {}”, key, pivot, item);
Long size = redisTemplate.opsForList().rightPush(key, pivot, item);
log.info(“lLeftPush(…) => size -> {}”, size);
if (size == null) {
throw new RedisOpsResultIsNullException();
}
return size;
}
/**
-
【非阻塞队列】 从左侧移出(key对应的)list中的第一个元素,并将该元素返回
-
-
注: 此方法是非阻塞的,即: 若(key对应的)list中的所有元素都被pop移出了,此时,再进行pop的话,会立即返回null
-
注: 此方法是非阻塞的,即: 若redis中不存在对应的key,那么会立即返回null
-
注: 若将(key对应的)list中的所有元素都pop完了,那么该key会被删除
-
@param key 定位list的key
-
@return 移出的那个元素
*/
public static String lLeftPop(String key) {
log.info(“lLeftPop(…) => key -> {}”, key);
String item = redisTemplate.opsForList().leftPop(key);
log.info(“lLeftPop(…) => item -> {}”, item);
return item;
}
/**
-
【阻塞队列】 从左侧移出(key对应的)list中的第一个元素,并将该元素返回
-
-
注: 此方法是阻塞的,即: 若(key对应的)list中的所有元素都被pop移出了,此时,再进行pop的话,
-
会阻塞timeout这么久,然后返回null
-
注: 此方法是阻塞的,即: 若redis中不存在对应的key,那么会阻塞timeout这么久,然后返回null
-
注: 若将(key对应的)list中的所有元素都pop完了,那么该key会被删除
-
-
提示: 若阻塞过程中,目标key-list出现了,且里面有item了,那么会立马停止阻塞,进行元素移出并返回
-
@param key 定位list的key
-
@param timeout 超时时间
-
@param unit timeout的单位
-
@return 移出的那个元素
*/
public static String lLeftPop(String key, long timeout, TimeUnit unit) {
log.info(“lLeftPop(…) => key -> {},timeout -> {},unit -> {}”, key, timeout, unit);
String item = redisTemplate.opsForList().leftPop(key, timeout, unit);
log.info(“lLeftPop(…) => item -> {}”, item);
return item;
}
/**
- 与{@link ListOps#lLeftPop(String)}类比即可,不过是从list右侧移出元素
*/
public static String lRightPop(String key) {
log.info(“lRightPop(…) => key -> {}”, key);
String item = redisTemplate.opsForList().rightPop(key);
log.info(“lRightPop(…) => item -> {}”, item);
return item;
}
/**
- 与{@link ListOps#lLeftPop(String, long, TimeUnit)}类比即可,不过是从list右侧移出元素
*/
public static String lRightPop(String key, long timeout, TimeUnit unit) {
log.info(“lRightPop(…) => key -> {},timeout -> {},unit -> {}”, key, timeout, unit);
String item = redisTemplate.opsForList().rightPop(key, timeout, unit);
log.info(“lRightPop(…) => item -> {}”, item);
return item;
}
/**
-
【非阻塞队列】 从sourceKey对应的sourceList右侧移出一个item,并将这个item推
-
入(destinationKey对应的)destinationList的左侧
-
-
注: 若sourceKey对应的list中没有item了,则立马认为(从sourceKey对应的list中pop出来的)item为null,
-
null并不会往destinationKey对应的list中push
-
追注: 此时,此方法的返回值是null
-
-
注: 若将(sourceKey对应的)list中的所有元素都pop完了,那么该sourceKey会被删除
-
@param sourceKey 定位sourceList的key
-
@param destinationKey 定位destinationList的key
-
@return 移动的这个元素
*/
public static String lRightPopAndLeftPush(String sourceKey, String destinationKey) {
log.info(“lRightPopAndLeftPush(…) => sourceKey -> {},destinationKey -> {}”,
sourceKey, destinationKey);
String item = redisTemplate.opsForList().rightPopAndLeftPush(sourceKey, destinationKey);
log.info(“lRightPopAndLeftPush(…) => item -> {}”, item);
return item;
}
/**
-
【阻塞队列】 从sourceKey对应的sourceList右侧移出一个item,并将这个item推
-
入(destinationKey对应的)destinationList的左侧
-
-
注: 若sourceKey对应的list中没有item了,则阻塞等待,直到能从sourceList中移出一个非null的item(或等待时长超时);
-
case1: 等到了一个非null的item,那么继续下面的push操作,并返回这个item
-
case2: 超时了,还没等到非null的item,那么pop出的结果就未null,此时并不会往destinationList进行push
-
此时,此方法的返回值是null
-
-
注: 若将(sourceKey对应的)list中的所有元素都pop完了,那么该sourceKey会被删除
-
@param sourceKey 定位sourceList的key
-
@param destinationKey 定位destinationList的key
-
@param timeout 超时时间
-
@param unit timeout的单位
-
@return 移动的这个元素
*/
public static String lRightPopAndLeftPush(String sourceKey, String destinationKey, long timeout,
TimeUnit unit) {
log.info(“lRightPopAndLeftPush(…) => sourceKey -> {},destinationKey -> {},timeout -> {},”
- " unit -> {}", sourceKey, destinationKey, timeout, unit);
String item = redisTemplate.opsForList().rightPopAndLeftPush(sourceKey, destinationKey, timeout, unit);
log.info(“lRightPopAndLeftPush(…) => item -> {}”, item);
return item;
}
/**
-
设置(key对应的)list中对应索引位置index处的元素为item
-
-
注: 若key不存在,则会抛出org.springframework.data.redis.RedisSystemException
-
注: 若索引越界,也会抛出org.springframework.data.redis.RedisSystemException
-
@param key 定位list的key
-
@param index 定位list中的元素的索引
-
@param item 要替换成的值
*/
public static void lSet(String key, long index, String item) {
log.info(“lSet(…) => key -> {},index -> {},item -> {}”, key, index, item);
redisTemplate.opsForList().set(key, index, item);
}
/**
-
通过索引index,获取(key对应的)list中的元素
-
-
注: 若key不存在 或 index超出(key对应的)list的索引范围,那么返回null
-
@param key 定位list的key
-
@param index 定位list中的item的索引
-
@return list中索引index对应的item
*/
public static String lIndex(String key, long index) {
log.info(“lIndex(…) => key -> {},index -> {}”, key, index);
String item = redisTemplate.opsForList().index(key, index);
log.info(“lIndex(…) => item -> {}”, item);
return item;
}
/**
-
获取(key对应的)list中索引在[start,end]之间的item集
-
-
注: 含start、含end
-
注: 当key不存在时,获取到的是空的集合
-
注: 当获取的范围比list的范围还要大时,获取到的是这两个范围的交集
-
-
提示: 可通过RedisUtil.ListOps.lRange(key,0,-1)来获取到该key对应的整个list
-
@param key 定位list的key
-
@param start 起始元素的index
-
@param end 结尾元素的index
-
@return 对应的元素集合
*/
public static List lRange(String key, long start, long end) {
log.info(“lRange(…) => key -> {},start -> {},end -> {}”, key, start, end);
List result = redisTemplate.opsForList().range(key, start, end);
log.info(“lRange(…) => result -> {}”, result);
return result;
}
/**
-
获取(key对应的)list
-
@param key 定位list的key
-
@return (key对应的)list
-
@see ListOps#lRange(String, long, long)
*/
public static List lWholeList(String key) {
log.info(“lWholeList(…) => key -> {}”, key);
List result = redisTemplate.opsForList().range(key, 0, -1);
log.info(“lWholeList(…) => result -> {}”, result);
return result;
}
/**
-
获取(key对应的)list的size
-
-
注: 当key不存在时,获取到的size为0.
-
@param key 定位list的key
-
@return list的size
*/
public static long lSize(String key) {
log.info(“lSize(…) => key -> {}”, key);
Long size = redisTemplate.opsForList().size(key);
log.info(“lSize(…) => size -> {}”, size);
if (size == null) {
throw new RedisOpsResultIsNullException();
}
return size;
}
/**
-
删除(key对应的)list中,前expectCount个值等于item的项
-
-
注: 若expectCount == 0,则表示删除list中所有的值等于item的项.
-
注: 若expectCount > 0, 则表示删除从左往右进行
-
注: 若expectCount < 0, 则表示删除从右往左进行
-
-
注: 若list中,值等于item的项的个数少于expectCount时,那么会删除list中所有的值等于item的项
-
注: 当key不存在时,返回0
-
注: 若lRemove后,将(key对应的)list中没有任何元素了,那么该key会被删除
-
@param key 定位list的key
-
@param expectCount 要删除的item的个数
-
@param item 要删除的item
-
@return 实际删除了的item的个数
*/
public static long lRemove(String key, long expectCount, String item) {
log.info(“lRemove(…) => key -> {},expectCount -> {},item -> {}”, key, expectCount, item);
Long actualCount = redisTemplate.opsForList().remove(key, expectCount, item);
log.info(“lRemove(…) => actualCount -> {}”, actualCount);
if (actualCount == null) {
throw new RedisOpsResultIsNullException();
}
return actualCount;
}
/**
-
裁剪(即: 对list中的元素取交集)
-
-
举例说明: list中的元素索引范围是[0,8],而这个方法传入的[start,end]为 [3,10],
-
那么裁剪就是对[0,8]和[3,10]进行取交集,得到[3,8],那么裁剪后
-
的list中,只剩下(原来裁剪前)索引在[3,8]之间的元素了
-
-
注: 若裁剪后的(key对应的)list就是空的,那么该key会被删除
-
@param key 定位list的key
-
@param start 要删除的item集的起始项的索引
-
@param end 要删除的item集的结尾项的索引
*/
public static void lTrim(String key, long start, long end) {
log.info(“lTrim(…) => key -> {},start -> {},end -> {}”, key, start, end);
redisTemplate.opsForList().trim(key, start, end);
}
}
/**
-
set相关操作
-
-
提示: set中的元素,不可以重复
-
提示: set是无序的
-
提示: redis中String的数据结构可参考resources/data-structure/Set(集合)的数据结构(示例一).png
-
redis中String的数据结构可参考resources/data-structure/Set(集合)的数据结构(示例二).png
*/
public static class SetOps {
/**
-
向(key对应的)set中添加items
-
-
注: 若key不存在,则会自动创建
-
注: set中的元素会去重
-
@param key 定位set的key
-
@param items 要向(key对应的)set中添加的items
-
@return 此次添加操作, 添加到set中的元素的个数
*/
public static long sAdd(String key, String… items) {
log.info(“sAdd(…) => key -> {},items -> {}”, key, items);
Long count = redisTemplate.opsForSet().add(key, items);
log.info(“sAdd(…) => count -> {}”, count);
if (count == null) {
throw new RedisOpsResultIsNullException();
}
return count;
}
/**
-
从(key对应的)set中删除items
-
-
注: 若key不存在,则返回0
-
注: 若已经将(key对应的)set中的项删除完了,那么对应的key也会被删除
-
@param key 定位set的key
-
@param items 要移除的items
-
@return 实际删除了的个数
*/
public static long sRemove(String key, Object… items) {
log.info(“sRemove(…) => key -> {},items -> {}”, key, items);
Long count = redisTemplate.opsForSet().remove(key, items);
log.info(“sRemove(…) => count -> {}”, count);
if (count == null) {
throw new RedisOpsResultIsNullException();
}
return count;
}
/**
-
从(key对应的)set中随机移出一个item,并返回这个item
-
-
注: 因为set是无序的,所以移出的这个item,是随机的; 并且,哪怕
-
是数据一样的set,多次测试移出操作,移除的元素也是随机的
-
-
注: 若已经将(key对应的)set中的项pop完了,那么对应的key会被删除
-
@param key 定位set的key
-
@return 移出的项
*/
public static String sPop(String key) {
log.info(“sPop(…) => key -> {}”, key);
String popItem = redisTemplate.opsForSet().pop(key);
log.info(“sPop(…) => popItem -> {}”, popItem);
return popItem;
}
/**
-
将(sourceKey对应的)sourceSet中的元素item,移动到(destinationKey对应的)destinationSet中
-
-
注: 当sourceKey不存在时,返回false
-
注: 当item不存在时,返回false
-
注: 若destinationKey不存在,那么在移动时会自动创建
-
注: 若已经将(sourceKey对应的)set中的项move出去完了,那么对应的sourceKey会被删除
-
@param sourceKey 定位sourceSet的key
-
@param item 要移动的项目
-
@param destinationKey 定位destinationSet的key
-
@return 移动成功与否
*/
public static boolean sMove(String sourceKey, String item, String destinationKey) {
Boolean result = redisTemplate.opsForSet().move(sourceKey, item, destinationKey);
log.info(“sMove(…) => sourceKey -> {},destinationKey -> {},item -> {}”,
sourceKey, destinationKey, item);
log.info(“sMove(…) => result -> {}”, result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
-
获取(key对应的)set中的元素个数
-
-
注: 若key不存在,则返回0
-
@param key 定位set的key
-
@return (key对应的)set中的元素个数
*/
public static long sSize(String key) {
log.info(“sSize(…) => key -> {}”, key);
Long size = redisTemplate.opsForSet().size(key);
log.info(“sSize(…) => size -> {}”, size);
if (size == null) {
throw new RedisOpsResultIsNullException();
}
return size;
}
/**
-
判断(key对应的)set中是否含有item
-
-
注: 若key不存在,则返回false
-
@param key 定位set的key
-
@param item 被查找的项
-
@return (key对应的)set中是否含有item
*/
public static boolean sIsMember(String key, Object item) {
log.info(“sSize(…) => key -> {},size -> {}”, key, item);
Boolean result = redisTemplate.opsForSet().isMember(key, item);
log.info(“sSize(…) => result -> {}”, result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
-
获取两个(key对应的)Set的交集
-
-
注: 若不存在任何交集,那么返回空的集合(,而不是null)
-
注: 若其中一个key不存在(或两个key都不存在),那么返回空的集合(,而不是null)
-
@param key 定位其中一个set的键
-
@param otherKey 定位其中另一个set的键
-
@return item交集
*/
public static Set sIntersect(String key, String otherKey) {
log.info(“sIntersect(…) => key -> {},otherKey -> {}”, key, otherKey);
Set intersectResult = redisTemplate.opsForSet().intersect(key, otherKey);
log.info(“sIntersect(…) => intersectResult -> {}”, intersectResult);
return intersectResult;
}
/**
-
获取多个(key对应的)Set的交集
-
-
注: 若不存在任何交集,那么返回空的集合(,而不是null)
-
注: 若>=1个key不存在,那么返回空的集合(,而不是null)
-
@param key 定位其中一个set的键
-
@param otherKeys 定位其它set的键集
-
@return item交集
*/
public static Set sIntersect(String key, Collection otherKeys) {
log.info(“sIntersect(…) => key -> {},otherKeys -> {}”, key, otherKeys);
Set intersectResult = redisTemplate.opsForSet().intersect(key, otherKeys);
log.info(“sIntersect(…) => intersectResult -> {}”, intersectResult);
return intersectResult;
}
/**
-
获取两个(key对应的)Set的交集,并将结果add到storeKey对应的Set中
-
-
case1: 交集不为空,storeKey不存在,则 会创建对应的storeKey,并将交集添加到(storeKey对应的)set中
-
case2: 交集不为空,storeKey已存在,则 会清除原(storeKey对应的)set中所有的项,然后将交集添加到(storeKey对应的)set中
-
case3: 交集为空,则不进行下面的操作,直接返回0
-
-
注: 求交集的部分,详见{@link SetOps#sIntersect(String, String)}
-
@param key 定位其中一个set的键
-
@param otherKey 定位其中另一个set的键
-
@param storeKey 定位(要把交集添加到哪个)set的key
-
@return add到(storeKey对应的)Set后, 该set对应的size
*/
public static long sIntersectAndStore(String key, String otherKey, String storeKey) {
log.info(“sIntersectAndStore(…) => key -> {},otherKey -> {},storeKey -> {}”,
key, otherKey, storeKey);
Long size = redisTemplate.opsForSet().intersectAndStore(key, otherKey, storeKey);
log.info(“sIntersectAndStore(…) => size -> {}”, size);
if (size == null) {
throw new RedisOpsResultIsNullException();
}
return size;
}
/**
-
获取多个(key对应的)Set的交集,并将结果add到storeKey对应的Set中
-
-
case1: 交集不为空,storeKey不存在,则 会创建对应的storeKey,并将交集添加到(storeKey对应的)set中
-
case2: 交集不为空,storeKey已存在,则 会清除原(storeKey对应的)set中所有的项,然后将交集添加到(storeKey对应的)set中
-
case3: 交集为空,则不进行下面的操作,直接返回0
-
-
注: 求交集的部分,详见{@link SetOps#sIntersect(String, Collection)}
*/
public static long sIntersectAndStore(String key, Collection otherKeys, String storeKey) {
log.info(“sIntersectAndStore(…) => key -> {},otherKeys -> {},storeKey -> {}”, key, otherKeys, storeKey);
Long size = redisTemplate.opsForSet().intersectAndStore(key, otherKeys, storeKey);
log.info(“sIntersectAndStore(…) => size -> {}”, size);
if (size == null) {
throw new RedisOpsResultIsNullException();
}
return size;
}
/**
-
获取两个(key对应的)Set的并集
-
-
注: 并集中的元素也是唯一的,这是Set保证的
-
@param key 定位其中一个set的键
-
@param otherKey 定位其中另一个set的键
-
@return item并集
*/
public static Set sUnion(String key, String otherKey) {
log.info(“sUnion(…) => key -> {},otherKey -> {}”, key, otherKey);
Set unionResult = redisTemplate.opsForSet().union(key, otherKey);
log.info(“sUnion(…) => unionResult -> {}”, unionResult);
return unionResult;
}
/**
-
获取两个(key对应的)Set的并集
-
-
注: 并集中的元素也是唯一的,这是Set保证的
-
@param key 定位其中一个set的键
-
@param otherKeys 定位其它set的键集
-
@return item并集
*/
public static Set sUnion(String key, Collection otherKeys) {
log.info(“sUnion(…) => key -> {},otherKeys -> {}”, key, otherKeys);
Set unionResult = redisTemplate.opsForSet().union(key, otherKeys);
log.info(“sUnion(…) => unionResult -> {}”, unionResult);
return unionResult;
}
/**
-
获取两个(key对应的)Set的并集,并将结果add到storeKey对应的Set中
-
-
case1: 并集不为空,storeKey不存在,则 会创建对应的storeKey,并将并集添加到(storeKey对应的)set中
-
case2: 并集不为空,storeKey已存在,则 会清除原(storeKey对应的)set中所有的项,然后将并集添加到(storeKey对应的)set中
-
case3: 并集为空,则不进行下面的操作,直接返回0
-
-
注: 求并集的部分,详见{@link SetOps#sUnion(String, String)}
-
@param key 定位其中一个set的键
-
@param otherKey 定位其中另一个set的键
-
@param storeKey 定位(要把并集添加到哪个)set的key
-
@return add到(storeKey对应的)Set后, 该set对应的size
*/
public static long sUnionAndStore(String key, String otherKey, String storeKey) {
log.info(“sUnionAndStore(…) => key -> {},otherKey -> {},storeKey -> {}”,
key, otherKey, storeKey);
Long size = redisTemplate.opsForSet().unionAndStore(key, otherKey, storeKey);
log.info(“sUnionAndStore(…) => size -> {}”, size);
if (size == null) {
throw new RedisOpsResultIsNullException();
}
return size;
}
/**
-
获取两个(key对应的)Set的并集,并将结果add到storeKey对应的Set中
-
-
case1: 并集不为空,storeKey不存在,则 会创建对应的storeKey,并将并集添加到(storeKey对应的)set中
-
case2: 并集不为空,storeKey已存在,则 会清除原(storeKey对应的)set中所有的项,然后将并集添加到(storeKey对应的)set中
-
case3: 并集为空,则不进行下面的操作,直接返回0
-
-
注: 求并集的部分,详见{@link SetOps#sUnion(String, Collection)}
-
@param key 定位其中一个set的键
-
@param otherKeys 定位其它set的键集
-
@param storeKey 定位(要把并集添加到哪个)set的key
-
@return add到(storeKey对应的)Set后, 该set对应的size
*/
public static long sUnionAndStore(String key, Collection otherKeys, String storeKey) {
log.info(“sUnionAndStore(…) => key -> {},otherKeys -> {},storeKey -> {}”,
key, otherKeys, storeKey);
Long size = redisTemplate.opsForSet().unionAndStore(key, otherKeys, storeKey);
log.info(“sUnionAndStore(…) => size -> {}”, size);
if (size == null) {
throw new RedisOpsResultIsNullException();
}
return size;
}
/**
-
获取 (key对应的)Set 减去 (otherKey对应的)Set 的差集
-
-
注: 如果被减数key不存在,那么结果为空的集合(,而不是null)
-
注: 如果被减数key存在,但减数key不存在,那么结果即为(被减数key对应的)Set
-
@param key 定位"被减数set"的键
-
@param otherKey 定位"减数set"的键
-
@return item差集
*/
public static Set sDifference(String key, String otherKey) {
log.info(“sDifference(…) => key -> {},otherKey -> {}”,
key, otherKey);
Set differenceResult = redisTemplate.opsForSet().difference(key, otherKey);
log.info(“sDifference(…) => differenceResult -> {}”, differenceResult);
return differenceResult;
}
/**
-
获取 (key对应的)Set 减去 (otherKeys对应的)Sets 的差集
-
-
注: 如果被减数key不存在,那么结果为空的集合(,而不是null)
-
注: 如果被减数key存在,但减数key不存在,那么结果即为(被减数key对应的)Set
-
-
提示: 当有多个减数时,被减数先减去哪一个减数,后减去哪一个减数,是无所谓的,是不影响最终结果的
-
@param key 定位"被减数set"的键
-
@param otherKeys 定位"减数集sets"的键集
-
@return item差集
*/
public static Set sDifference(String key, Collection otherKeys) {
log.info(“sDifference(…) => key -> {},otherKeys -> {}”, key, otherKeys);
Set differenceResult = redisTemplate.opsForSet().difference(key, otherKeys);
log.info(“sDifference(…) => differenceResult -> {}”, differenceResult);
return differenceResult;
}
/**
-
获取 (key对应的)Set 减去 (otherKey对应的)Set 的差集,并将结果add到storeKey对应的Set中
-
-
case1: 差集不为空,storeKey不存在,则 会创建对应的storeKey,并将差集添加到(storeKey对应的)set中
-
case2: 差集不为空,storeKey已存在,则 会清除原(storeKey对应的)set中所有的项,然后将差集添加到(storeKey对应的)set中
-
case3: 差集为空,则不进行下面的操作,直接返回0
-
-
注: 求并集的部分,详见{@link SetOps#sDifference(String, String)}
-
@param key 定位"被减数set"的键
-
@param otherKey 定位"减数set"的键
-
@param storeKey 定位(要把差集添加到哪个)set的key
-
@return add到(storeKey对应的)Set后, 该set对应的size
*/
public static long sDifferenceAndStore(String key, String otherKey, String storeKey) {
log.info(“sDifferenceAndStore(…) => key -> {},otherKey -> {},storeKey -> {}”,
key, otherKey, storeKey);
Long size = redisTemplate.opsForSet().differenceAndStore(key, otherKey, storeKey);
log.info(“sDifferenceAndStore(…) => size -> {}”, size);
if (size == null) {
throw new RedisOpsResultIsNullException();
}
return size;
}
/**
-
获取 (key对应的)Set 减去 (otherKey对应的)Set 的差集,并将结果add到storeKey对应的Set中
-
-
case1: 差集不为空,storeKey不存在,则 会创建对应的storeKey,并将差集添加到(storeKey对应的)set中
-
case2: 差集不为空,storeKey已存在,则 会清除原(storeKey对应的)set中所有的项,然后将差集添加到(storeKey对应的)set中
-
case3: 差集为空,则不进行下面的操作,直接返回0
-
-
注: 求并集的部分,详见{@link SetOps#sDifference(String, String)}
-
@param key 定位"被减数set"的键
-
@param otherKeys 定位"减数集sets"的键集
-
@param storeKey 定位(要把差集添加到哪个)set的key
-
@return add到(storeKey对应的)Set后, 该set对应的size
*/
public static long sDifferenceAndStore(String key, Collection otherKeys, String storeKey) {
log.info(“sDifferenceAndStore(…) => key -> {},otherKeys -> {},storeKey -> {}”,
key, otherKeys, storeKey);
Long size = redisTemplate.opsForSet().differenceAndStore(key, otherKeys, storeKey);
log.info(“sDifferenceAndStore(…) => size -> {}”, size);
if (size == null) {
throw new RedisOpsResultIsNullException();
}
return size;
}
/**
-
获取key对应的set
-
-
注: 若key不存在,则返回的是空的set(,而不是null)
-
@param key 定位set的key
-
@return (key对应的)set
*/
public static Set sMembers(String key) {
log.info(“sMembers(…) => key -> {}”, key);
Set members = redisTemplate.opsForSet().members(key);
log.info(“sMembers(…) => members -> {}”, members);
return members;
}
/**
-
从key对应的set中随机获取一项
-
@param key 定位set的key
-
@return 随机获取到的项
*/
public static String sRandomMember(String key) {
log.info(“sRandomMember(…) => key -> {}”, key);
String randomItem = redisTemplate.opsForSet().randomMember(key);
log.info(“sRandomMember(…) => randomItem -> {}”, randomItem);
return randomItem;
}
/**
-
从key对应的set中获取count次随机项(,set中的同一个项可能被多次获取)
-
-
注: count可大于set的size
-
注: 取出来的结果里可能存在相同的值
-
@param key 定位set的key
-
@param count 要取多少项
-
@return 随机获取到的项集
*/
public static List sRandomMembers(String key, long count) {
log.info(“sRandomMembers(…) => key -> {},count -> {}”, key, count);
List randomItems = redisTemplate.opsForSet().randomMembers(key, count);
log.info(“sRandomMembers(…) => randomItems -> {}”, randomItems);
return randomItems;
}
/**
-
从key对应的set中随机获取count个项
-
-
注: 若count >= set的size,那么返回的即为这个key对应的set
-
注: 取出来的结果里没有重复的项
-
@param key 定位set的key
-
@param count 要取多少项
-
@return 随机获取到的项集
*/
public static Set sDistinctRandomMembers(String key, long count) {
log.info(“sDistinctRandomMembers(…) => key -> {},count -> {}”, key, count);
Set distinctRandomItems = redisTemplate.opsForSet().distinctRandomMembers(key, count);
log.info(“sDistinctRandomMembers(…) => distinctRandomItems -> {}”, distinctRandomItems);
return distinctRandomItems;
}
/**
-
根据options匹配到(key对应的)set中的对应的item,并返回对应的item集
-
-
-
注: ScanOptions实例的创建方式举例:
-
1、ScanOptions.NONE
-
2、ScanOptions.scanOptions().match(“n??e”).build()
-
@param key 定位set的key
-
@param options 匹配set中的item的条件
-
注: ScanOptions.NONE表示全部匹配
-
注: ScanOptions.scanOptions().match(pattern).build()表示按照pattern匹配,
-
其中pattern中可以使用通配符 * ? 等,
-
* 表示>=0个字符
-
? 表示有且只有一个字符
-
此处的匹配规则与{@link KeyOps#keys(String)}处的一样
-
@return 匹配到的(key对应的)set中的项
*/
@SneakyThrows
public static Cursor sScan(String key, ScanOptions options) {
log.info(“sScan(…) => key -> {},options -> {}”, key, mapper.writeValueAsString(options));
Cursor cursor = redisTemplate.opsForSet().scan(key, options);
log.info(“sScan(…) => cursor -> {}”, mapper.writeValueAsString(cursor));
return cursor;
}
}
/**
-
ZSet相关操作
-
-
特别说明: ZSet是有序的,
-
不仅体现在: redis中的存储上有序
-
还体现在: 此工具类ZSetOps中返回值类型为Set<?>的方法,实际返回类型是LinkedHashSet<?>
-
-
提示: redis中的ZSet,一定程度等于redis中的Set + redis中的Hash的结合体
-
提示: redis中String的数据结构可参考resources/data-structure/ZSet(有序集合)的数据结构(示例一).png
-
redis中String的数据结构可参考resources/data-structure/ZSet(有序集合)的数据结构(示例二).png
-
提示: ZSet中的entryKey即为成员项,entryValue即为这个成员项的分值,ZSet根据成员的分值,来堆成员进行排序
*/
public static class ZSetOps {
/**
-
向(key对应的)zset中添加(item,score)
-
-
注: item为entryKey成员项,score为entryValue分数值
-
-
注: 若(key对应的)zset中已存在(与此次要添加的项)相同的item项,那么此次添加操作会失败,返回false;
-
但是!!! zset中原item的score会被更新为此次add的相同item项的score
-
所以,也可以通过zAdd达到更新item对应score的目的
-
-
注: score可为正、可为负、可为0; 总之,double范围内都可以
-
-
注: 若score的值一样,则按照item排序
-
@param key 定位set的key
-
@param item 要往(key对应的)zset中添加的成员项
-
@param score item的分值
-
@return 是否添加成功
*/
public static boolean zAdd(String key, String item, double score) {
log.info(“zAdd(…) => key -> {},item -> {},score -> {}”, key, item, score);
Boolean result = redisTemplate.opsForZSet().add(key, item, score);
log.info(“zAdd(…) => result -> {}”, result);
if (result == null) {
throw new RedisOpsResultIsNullException();
}
return result;
}
/**
-
批量添加entry<item,score>
-
-
注: 若entry<item,score>集中存在item相同的项(,score不一样),那么redis在执行真正的批量add操作前,会
-
将其中一个item过滤掉
-
注: 同样的,若(key对应的)zset中已存在(与此次要添加的项)相同的item项,那么此次批量添加操作中,
-
对该item项的添加会失败,会失败,成功计数器不会加1;但是!!! zset中原item的score会被更新为此
-
次add的相同item项的score所以,也可以通过zAdd达到更新item对应score的目的
-
@param key 定位set的key
-
@param entries 要添加的entry<item,score>集
-
@return 本次添加进(key对应的)zset中的entry的个数
*/
@SneakyThrows
public static long zAdd(String key, Set<TypedTuple> entries) {
log.info(“zAdd(…) => key -> {},entries -> {}”, key, mapper.writeValueAsString(entries));
Long count = redisTemplate.opsForZSet().add(key, entries);
log.info(“zAdd(…) => count -> {}”, count);
if (count == null) {
throw new RedisOpsResultIsNullException();
}
return count;
}
/**
-
从(key对应的)zset中移除项
-
-
注:若key不存在,则返回0
-
@param key 定位set的key
-
@param items 要移除的项集
-
@return 实际移除了的项的个数
*/
public static long zRemove(String key, Object… items) {
log.info(“zRemove(…) => key -> {},items -> {}”, key, items);
Long count = redisTemplate.opsForZSet().remove(key, items);
log.info(“zRemove(…) => count -> {}”, count);
if (count == null) {
throw new RedisOpsResultIsNullException();
}
return count;
}
/**
-
移除(key对应的)zset中,排名范围在[startIndex,endIndex]内的item
-
-
注:默认的,按score.item升序排名,排名从0开始
-
-
注: 类似于List中的索引,排名可以分为多个方式:
-
从前到后(正向)的排名: 0、1、2…
-
从后到前(反向)的排名: -1、-2、-3…
-
-
注: 不论是使用正向排名,还是使用反向排名,使用此方法时,应保证 startRange代表的元素的位置
-
在endRange代表的元素的位置的前面,如:
-
示例一: RedisUtil.ZSetOps.zRemoveRange(“name”,0,2);
-
示例二: RedisUtil.ZSetOps.zRemoveRange(“site”,-2,-1);
-
示例三: RedisUtil.ZSetOps.zRemoveRange(“foo”,0,-1);
-
-
注:若key不存在,则返回0
-
@param key 定位set的key
-
@param startRange 开始项的排名
-
@param endRange 结尾项的排名
-
@return 实际移除了的项的个数
*/
public static long zRemoveRange(String key, long startRange, long endRange) {
log.info(“zRemoveRange(…) => key -> {},startRange -> {},endRange -> {}”,
key, startRange, endRange);
Long count = redisTemplate.opsForZSet().removeRange(key, startRange, endRange);
log.info(“zRemoveRange(…) => count -> {}”, count);
if (count == null) {
throw new RedisOpsResultIsNullException();
}
return count;
}
/**
-
移除(key对应的)zset中,score范围在[minScore,maxScore]内的item
-
-
提示: 虽然删除范围包含两侧的端点(即:包含minScore和maxScore),但是由于double存在精度问题,所以建议:
-
设置值时,minScore应该设置得比要删除的项里,最小的score还小一点
-
maxScore应该设置得比要删除的项里,最大的score还大一点
-
追注: 本人简单测试了几组数据,暂未出现精度问题
-
-
注:若key不存在,则返回0
-
@param key 定位set的key
-
@param minScore score下限(含这个值)
-
@param maxScore score上限(含这个值)
-
@return 实际移除了的项的个数
*/
public static long zRemoveRangeByScore(String key, double minScore, double maxScore) {
log.info(“zRemoveRangeByScore(…) => key -> {},startIndex -> {},startIndex -> {}”,
key, minScore, maxScore);
Long count = redisTemplate.opsForZSet().removeRangeByScore(key, minScore, maxScore);
log.info(“zRemoveRangeByScore(…) => count -> {}”, count);
if (count == null) {
throw new RedisOpsResultIsNullException();
}
return count;
}
/**
-
增/减 (key对应的zset中,)item的分数值
-
@param key 定位zset的key
-
@param item 项
-
@param delta 变化量(正 - 增,负 - 减)
-
@return 修改后的score值
*/
public static double zIncrementScore(String key, String item, double delta) {
log.info(“zIncrementScore(…) => key -> {},item -> {},delta -> {}”, key, item, delta);
Double scoreValue = redisTemplate.opsForZSet().incrementScore(key, item, delta);
log.info(“zIncrementScore(…) => scoreValue -> {}”, scoreValue);
if (scoreValue == null) {
throw new RedisOpsResultIsNullException();
}
return scoreValue;
}
/**
-
返回item在(key对应的)zset中的(按score从小到大的)排名
-
-
注: 排名从0开始 即意味着,此方法等价于: 返回item在(key对应的)zset中的位置索引
-
注: 若key或item不存在,返回null
-
注: 排序规则是score,item,即:优先以score排序,若score相同,则再按item排序
-
@param key 定位zset的key
-
@param item 项
-
@return 排名(等价于 : 索引)
*/
public static long zRank(String key, Object item) {
log.info(“zRank(…) => key -> {},item -> {}”, key, item);
Long rank = redisTemplate.opsForZSet().rank(key, item);
log.info(“zRank(…) => rank -> {}”, rank);
if (rank == null) {
总目录展示
该笔记共八个节点(由浅入深),分为三大模块。
高性能。 秒杀涉及大量的并发读和并发写,因此支持高并发访问这点非常关键。该笔记将从设计数据的动静分离方案、热点的发现与隔离、请求的削峰与分层过滤、服务端的极致优化这4个方面重点介绍。
一致性。 秒杀中商品减库存的实现方式同样关键。可想而知,有限数量的商品在同一时刻被很多倍的请求同时来减库存,减库存又分为“拍下减库存”“付款减库存”以及预扣等几种,在大并发更新的过程中都要保证数据的准确性,其难度可想而知。因此,将用一个节点来专门讲解如何设计秒杀减库存方案。
高可用。 虽然介绍了很多极致的优化思路,但现实中总难免出现一些我们考虑不到的情况,所以要保证系统的高可用和正确性,还要设计一个PlanB来兜底,以便在最坏情况发生时仍然能够从容应对。笔记的最后,将带你思考可以从哪些环节来设计兜底方案。
篇幅有限,无法一个模块一个模块详细的展示(这些要点都收集在了这份《高并发秒杀顶级教程》里),麻烦各位转发一下(可以帮助更多的人看到哟!)
由于内容太多,这里只截取部分的内容。
entries) {
log.info(“zAdd(…) => key -> {},entries -> {}”, key, mapper.writeValueAsString(entries));
Long count = redisTemplate.opsForZSet().add(key, entries);
log.info(“zAdd(…) => count -> {}”, count);
if (count == null) {
throw new RedisOpsResultIsNullException();
}
return count;
}
/**
-
从(key对应的)zset中移除项
-
-
注:若key不存在,则返回0
-
@param key 定位set的key
-
@param items 要移除的项集
-
@return 实际移除了的项的个数
*/
public static long zRemove(String key, Object… items) {
log.info(“zRemove(…) => key -> {},items -> {}”, key, items);
Long count = redisTemplate.opsForZSet().remove(key, items);
log.info(“zRemove(…) => count -> {}”, count);
if (count == null) {
throw new RedisOpsResultIsNullException();
}
return count;
}
/**
-
移除(key对应的)zset中,排名范围在[startIndex,endIndex]内的item
-
-
注:默认的,按score.item升序排名,排名从0开始
-
-
注: 类似于List中的索引,排名可以分为多个方式:
-
从前到后(正向)的排名: 0、1、2…
-
从后到前(反向)的排名: -1、-2、-3…
-
-
注: 不论是使用正向排名,还是使用反向排名,使用此方法时,应保证 startRange代表的元素的位置
-
在endRange代表的元素的位置的前面,如:
-
示例一: RedisUtil.ZSetOps.zRemoveRange(“name”,0,2);
-
示例二: RedisUtil.ZSetOps.zRemoveRange(“site”,-2,-1);
-
示例三: RedisUtil.ZSetOps.zRemoveRange(“foo”,0,-1);
-
-
注:若key不存在,则返回0
-
@param key 定位set的key
-
@param startRange 开始项的排名
-
@param endRange 结尾项的排名
-
@return 实际移除了的项的个数
*/
public static long zRemoveRange(String key, long startRange, long endRange) {
log.info(“zRemoveRange(…) => key -> {},startRange -> {},endRange -> {}”,
key, startRange, endRange);
Long count = redisTemplate.opsForZSet().removeRange(key, startRange, endRange);
log.info(“zRemoveRange(…) => count -> {}”, count);
if (count == null) {
throw new RedisOpsResultIsNullException();
}
return count;
}
/**
-
移除(key对应的)zset中,score范围在[minScore,maxScore]内的item
-
-
提示: 虽然删除范围包含两侧的端点(即:包含minScore和maxScore),但是由于double存在精度问题,所以建议:
-
设置值时,minScore应该设置得比要删除的项里,最小的score还小一点
-
maxScore应该设置得比要删除的项里,最大的score还大一点
-
追注: 本人简单测试了几组数据,暂未出现精度问题
-
-
注:若key不存在,则返回0
-
@param key 定位set的key
-
@param minScore score下限(含这个值)
-
@param maxScore score上限(含这个值)
-
@return 实际移除了的项的个数
*/
public static long zRemoveRangeByScore(String key, double minScore, double maxScore) {
log.info(“zRemoveRangeByScore(…) => key -> {},startIndex -> {},startIndex -> {}”,
key, minScore, maxScore);
Long count = redisTemplate.opsForZSet().removeRangeByScore(key, minScore, maxScore);
log.info(“zRemoveRangeByScore(…) => count -> {}”, count);
if (count == null) {
throw new RedisOpsResultIsNullException();
}
return count;
}
/**
-
增/减 (key对应的zset中,)item的分数值
-
@param key 定位zset的key
-
@param item 项
-
@param delta 变化量(正 - 增,负 - 减)
-
@return 修改后的score值
*/
public static double zIncrementScore(String key, String item, double delta) {
log.info(“zIncrementScore(…) => key -> {},item -> {},delta -> {}”, key, item, delta);
Double scoreValue = redisTemplate.opsForZSet().incrementScore(key, item, delta);
log.info(“zIncrementScore(…) => scoreValue -> {}”, scoreValue);
if (scoreValue == null) {
throw new RedisOpsResultIsNullException();
}
return scoreValue;
}
/**
-
返回item在(key对应的)zset中的(按score从小到大的)排名
-
-
注: 排名从0开始 即意味着,此方法等价于: 返回item在(key对应的)zset中的位置索引
-
注: 若key或item不存在,返回null
-
注: 排序规则是score,item,即:优先以score排序,若score相同,则再按item排序
-
@param key 定位zset的key
-
@param item 项
-
@return 排名(等价于 : 索引)
*/
public static long zRank(String key, Object item) {
log.info(“zRank(…) => key -> {},item -> {}”, key, item);
Long rank = redisTemplate.opsForZSet().rank(key, item);
log.info(“zRank(…) => rank -> {}”, rank);
if (rank == null) {
总目录展示
该笔记共八个节点(由浅入深),分为三大模块。
高性能。 秒杀涉及大量的并发读和并发写,因此支持高并发访问这点非常关键。该笔记将从设计数据的动静分离方案、热点的发现与隔离、请求的削峰与分层过滤、服务端的极致优化这4个方面重点介绍。
一致性。 秒杀中商品减库存的实现方式同样关键。可想而知,有限数量的商品在同一时刻被很多倍的请求同时来减库存,减库存又分为“拍下减库存”“付款减库存”以及预扣等几种,在大并发更新的过程中都要保证数据的准确性,其难度可想而知。因此,将用一个节点来专门讲解如何设计秒杀减库存方案。
高可用。 虽然介绍了很多极致的优化思路,但现实中总难免出现一些我们考虑不到的情况,所以要保证系统的高可用和正确性,还要设计一个PlanB来兜底,以便在最坏情况发生时仍然能够从容应对。笔记的最后,将带你思考可以从哪些环节来设计兜底方案。
篇幅有限,无法一个模块一个模块详细的展示(这些要点都收集在了这份《高并发秒杀顶级教程》里),麻烦各位转发一下(可以帮助更多的人看到哟!)
[外链图片转存中…(img-lVUYMJxX-1714461930023)]
[外链图片转存中…(img-ShLrIOrv-1714461930024)]
由于内容太多,这里只截取部分的内容。