Springboot之项目技术集成

}

第一次执行,出现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)]

由于内容太多,这里只截取部分的内容。

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

  • 23
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot是一个基于Spring框架的开发框架,因此Spring Boot技术架构也是基于Spring框架的技术架构。Spring Boot项目技术架构包括以下几个方面: 1. Spring Framework:Spring Boot基于Spring Framework进行开发,使用Spring Framework提供的IoC、AOP等特性来构建应用程序。 2. Spring Boot Starter:Spring Boot Starter是Spring Boot的一个组件,用于简化Spring应用程序的依赖关系管理。Spring Boot Starter提供了一组预定义的依赖关系,可以直接在应用程序中使用。 3. Spring Boot Auto-configuration:Spring Boot Auto-configuration是Spring Boot的另一个组件,用于自动配置应用程序。它基于应用程序的classpath和其他条件,自动配置应用程序的各个组件。 4. Spring Boot Actuator:Spring Boot Actuator是Spring Boot的监控和管理组件,可以提供应用程序的健康检查、性能指标、日志管理等功能。 5. Spring Boot Web:Spring Boot Web是Spring Boot的Web组件,用于构建Web应用程序。它基于Spring MVC框架,提供了一组预定义的Web组件,可以直接在应用程序中使用。 6. 数据库:Spring Boot可以集成多种数据库,包括MySQL、Oracle、MongoDB等。它提供了一组预定义的数据访问组件,可以直接在应用程序中使用。 7. 缓存:Spring Boot可以集成多种缓存,包括Redis、Ehcache等。它提供了一组预定义的缓存管理组件,可以直接在应用程序中使用。 8. 测试:Spring Boot提供了一组预定义的测试工具,包括JUnit、Mockito等。可以方便地进行单元测试和集成测试。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值