Redis客户端
- Jedis
- Lettuce
为什么用Lettuce
1.Jedis在实现上是直连redis server,连接实例可以在多个线程间共享,多线程环境下非线程安全,除非使用连接池,为每个Jedis实例增加物理连接。当连接数量增多时,物理连接成本就较高了。
2.Lettuce基于Netty的连接实例(StatefulRedisConnection),可以在多个线程间并发访问,且线程安全,满足多线程环境下的并发访问,同时它是可伸缩的设计,一个连接实例不够的情况也可以按需增加连接实例。netty 是一个多线程、事件驱动的 I/O 框架,可以让我们更好的利用系统资源,而不用浪费线程等待网络或磁盘I/O。
整合Lettuce
1.导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- lettuce pool 缓存连接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.5.0</version>
</dependency>
<!-- jackson json 优化缓存对象序列化 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.6</version>
</dependency>
2.修改配置application.yml
spring:
redis:
host: 192.168.11.125 # IP
port: 6379 # 端口号
password: wangcheng123 # 密码
timeout: 10000
lettuce:
pool:
max-active: 8 # 连接池最大连接数
max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
min-idle: 0 # 连接池中的最小空闲连接
max-idle: 8 # 连接池中的最大空闲连接
3.编写缓存配置类CacheConfig
/**
* @author wang
*/
/**
* 缓存配置-使用Lettuce客户端,自动注入配置的方式
*/
@Configuration
@EnableCaching //启用缓存
public class CacheConfig extends CachingConfigurerSupport {
/**
* 自定义缓存key的生成策略。默认的生成策略是看不懂的(乱码内容)
* 通过Spring 的依赖注入特性进行自定义的配置注入并且此类是一个配置类可以更多程度的自定义配置
* @return
*/
@Bean
@Override
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append(method.getName());
for(Object object : params){
sb.append(object.toString());
}
return sb.toString();
}
};
}
/**
* 缓存配置管理器
*/
@Bean
public CacheManager cacheManager(LettuceConnectionFactory factory) {
//以锁写入的方式创建RedisCacheWriter对象
RedisCacheWriter writer = RedisCacheWriter.lockingRedisCacheWriter(factory);
/*
设置CacheManager的Value序列化方式为JdkSerializationRedisSerializer,
但其实RedisCacheConfiguration默认就是使用
StringRedisSerializer序列化key,
JdkSerializationRedisSerializer序列化value,
所以以下注释代码就是默认实现,没必要写,直接注释掉
*/
// RedisSerializationContext.SerializationPair pair = RedisSerializationContext.SerializationPair.fromSerializer(new JdkSerializationRedisSerializer(this.getClass().getClassLoader()));
// RedisCacheConfiguration redis = RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair);
//创建默认缓存配置对象
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
RedisCacheManager cacheManager = new RedisCacheManager(writer, config);
return cacheManager;
}
/**
* 获取缓存操作助手对象
*
* @return
*/
@Bean
public RedisTemplate<String,Object> redisTemplate(LettuceConnectionFactory factory){
//创建Redis缓存操作助手RedisTemplate对象
RedisTemplate<String,Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
//以下代码为将RedisTemplate的Value序列化方式由JdkSerializationRedisSerializer更换为Jackson2JsonRedisSerializer
//此种序列化方式结果清晰、容易阅读、存储字节少、速度快,所以推荐更换
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
//在使用注解@Bean返回RedisTemplate的时候,同时配置hashKey与hashValue的序列化方式
//key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
//value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
//hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
//hash的value也采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
4.编写缓存操作提供类RedisService(用于给开发提供缓存操作)
/**
* @author wang
*/
@Service
public class RedisService {
@Autowired
private RedisTemplate redisTemplate;
/**
* 写入缓存
*
* @param key
* @param value
* @return
*/
public boolean set(final String key, Object value) {
boolean result = false;
try {
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
operations.set(key, value);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 写入缓存设置时效时间
*
* @param key
* @param value
* @return
*/
public boolean set(final String key, Object value, Long expireTime) {
boolean result = false;
try {
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
operations.set(key, value);
redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 批量删除对应的value
*
* @param keys
*/
public void remove(final String... keys) {
for (String key : keys) {
remove(key);
}
}
/**
* 批量删除key
* @param pattern
*/
public void removePattern(final String pattern) {
Set<Serializable> keys = redisTemplate.keys(pattern);
if (keys.size() > 0) {
redisTemplate.delete(keys);
}
}
/**
* 删除对应的value
*
* @param key
*/
public void remove(final String key) {
if (exists(key)) {
redisTemplate.delete(key);
}
}
/**
* 判断缓存中是否有对应的value
*
* @param key
* @return
*/
public boolean exists(final String key) {
return redisTemplate.hasKey(key);
}
/**
* 读取缓存
*
* @param key
* @return
*/
public Object get(final String key) {
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
return operations.get(key);
}
/**
* 哈希 添加
*
* @param key
* @param hashKey
* @param value
*/
public void hmSet(String key, Object hashKey, Object value) {
HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
hash.put(key, hashKey, value);
}
/**
* 哈希获取数据
*
* @param key
* @param hashKey
* @return
*/
public Object hmGet(String key, Object hashKey) {
HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
return hash.get(key, hashKey);
}
/**
* 列表添加
*
* @param k
* @param v
*/
public void lPush(String k, Object v) {
ListOperations<String, Object> list = redisTemplate.opsForList();
list.rightPush(k, v);
}
/**
* 列表获取
*
* @param k
* @param l
* @param l1
* @return
*/
public List<Object> lRange(String k, long l, long l1) {
ListOperations<String, Object> list = redisTemplate.opsForList();
return list.range(k, l, l1);
}
/**
* 集合添加
*
* @param key
* @param value
*/
public void add(String key, Object value) {
SetOperations<String, Object> set = redisTemplate.opsForSet();
set.add(key, value);
}
/**
* 集合获取
*
* @param key
* @return
*/
public Set<Object> setMembers(String key) {
SetOperations<String, Object> set = redisTemplate.opsForSet();
return set.members(key);
}
/**
* 有序集合添加
*
* @param key
* @param value
* @param scoure
*/
public void zAdd(String key, Object value, double scoure) {
ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
zset.add(key, value, scoure);
}
/**
* 有序集合获取
*
* @param key
* @param scoure
* @param scoure1
* @return
*/
public Set<Object> rangeByScore(String key, double scoure, double scoure1) {
ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
return zset.rangeByScore(key, scoure, scoure1);
}
}
5.编写测试控制类 RedisTestController
/**
* @author wang
*/
@RestController
public class RedisTestController {
@Autowired
private RedisService redisService;
/**
* 设置Redis数据
*
* @param key
* @param value
* @return
*/
@PostMapping("/set")
public Object setRedis(String key, String value) {
redisService.set(key, value);
return "设置成功";
}
/**
* 根据key删除Redis数据
*
* @param key
* @return
*/
@GetMapping("/del")
public Object delRedis(String key) {
redisService.remove(key);
return "删除成功";
}
/**
* 根据key获取Redis数据
*
* @param key
* @return
*/
@GetMapping("/get")
public Object getRedis(String key) {
Object value = redisService.get(key);
Map<String, Object> map = new HashMap<String, Object>();
map.put("value", value);
return map;
}
}
测试
操作其他类型
下列的就是Redis其它类型所对应的操作方式
- opsForValue: 对应 String(字符串)
- opsForZSet: 对应 ZSet(有序集合)
- opsForHash: 对应 Hash(哈希)
- opsForList: 对应 List(列表)
- opsForSet: 对应 Set(集合)
- opsForGeo: 对应 GEO(地理位置)
事务(Transaction)
使用
RedisTemplate通过execute方法提供事务功能,但不保证所有的事务操作在同一个连接中进行。
//执行一次事务操作
//调用RedisTemplte的execute(SessionBack<T> session)方法
//需要实现SessionBack中的execute(RedisOperations<K,V> operations)方法
//使用lamda表达式
List<Object> txResults = redisTemplate.execute((RedisOperations<K, V> operations)->{
//开启事务
operations.multi();
//添加操作
operations.opsForSet().add("key", "value1");
//这个将会返回事务操作的所有结果
return operations.exec();
}
});
//打印结果
System.out.println("Number of items added to set: " + txResults.get(0));
结束!!!