关于redis的内容,我之前已经分享过了很多了,今天这篇算是为了springboot nosql整合中的凑数篇吧,哈哈,虽然这么说,但如果点进来了,蛮看下,说不定会有一些新发现
Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库。
Redis 与其他 key - value 缓存产品有以下三个特点:
1.在主页中显示最新的项目列表。
Redis使用的是常驻内存的缓存,速度非常快。LPUSH用来插入一个内容ID,作为关键字存储在列表头部。LTRIM用来限制列表中的项目数最多为5000。如果用户需要的检索的数据量超越这个缓存容量,这时才需要把请求发送到数据库。
2.删除和过滤。
如果一篇文章被删除,可以使用LREM从缓存中彻底清除掉。
3.排行榜及相关问题。
排行榜(leader board)按照得分进行排序。ZADD命令可以直接实现这个功能,而ZREVRANGE命令可以用来按照得分来获取前100名的用户,ZRANK可以用来获取用户排名,非常直接而且操作容易。
4.按照用户投票和时间排序。
这就像Reddit的排行榜,得分会随着时间变化。LPUSH和LTRIM命令结合运用,把文章添加到一个列表中。一项后台任务用来获取列表,并重新计算列表的排序,ZADD命令用来按照新的顺序填充生成列表。列表可以实现非常快速的检索,即使是负载很重的站点。
5.过期项目处理。
使用unix时间作为关键字,用来保持列表能够按时间排序。对currenttime和timeto_live进行检索,完成查找过期项目的艰巨任务。另一项后台任务使用ZRANGE...WITHSCORES进行查询,删除过期的条目。
6.计数。
进行各种数据统计的用途是非常广泛的,比如想知道什么时候封锁一个IP地址。INCRBY命令让这些变得很容易,通过原子递增保持计数;GETSET用来重置计数器;过期属性用来确认一个关键字什么时候应该删除。
7.特定时间内的特定项目。
这是特定访问者的问题,可以通过给每次页面浏览使用SADD命令来解决。SADD不会将已经存在的成员添加到一个集合。
8.实时分析正在发生的情况,用于数据统计与防止垃圾邮件等。
使用Redis原语命令,更容易实施垃圾邮件过滤系统或其他实时跟踪系统。
9.Pub/Sub。
在更新中保持用户对数据的映射是系统中的一个普遍任务。Redis的pub/sub功能使用了SUBSCRIBE、UNSUBSCRIBE和PUBLISH命令,让这个变得更加容易。
10.队列。
在当前的编程中队列随处可见。除了push和pop类型的命令之外,Redis还有阻塞队列的命令,能够让一个程序在执行时被另一个程序添加到队列。你也可以做些更有趣的事情,比如一个旋转更新的RSS feed队列。
11.缓存。
Redis缓存使用的方式与memcache相同。
网络应用不能无休止地进行模型的战争,看看这些Redis的原语命令,尽管简单但功能强大,把它们加以组合,所能完成的就更无法想象。当然,你可以专门编写代码来完成所有这些操作,但Redis实现起来显然更为轻松。
12.分布式会话
集群模式下,在应用不多的情况下一般使用容器自带的session复制功能就能满足,当应用增多相对复杂的系统中,一般都会搭建以Redis等内存数据库为中心的session服务,session不再由容器管理,而是由session服务及内存数据库管理。
13.分布式锁
例子整合的内容,主要为如下:
1、方案一:通过与spring cache集成,配合@Cacheable、@CachePut 和 @CacheEvict注解
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
spring:
redis:
host: localhost
port: 6379
password: 123456
lettuce:
pool:
max-active: 20
max-idle: 20
min-idle: 10
cache:
redis:
cache-null-values: false
time-to-live: 30m #redis缓存过期统一配置的时间,但实际情况,我们要按不同业务配置不同的过期时间,因此可以在RedisCacheManager中实现
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
// 生成一个默认配置,通过config对象即可对缓存进行自定义配置
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
// 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(om);
RedisCacheConfiguration defaultConfig = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(timeToLive)
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer))
.disableCachingNullValues();
// // 设置一个初始化的缓存空间set集合
// Set<String> cacheNames = new HashSet<>();
// cacheNames.add("book");
// 对每个缓存空间应用不同的配置
Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
RedisCacheConfiguration bookCacheConfig = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(180))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer))
.disableCachingNullValues();
configMap.put("book",bookCacheConfig);
RedisCacheManager cacheManager = RedisCacheManager.builder(RedisCacheWriter.lockingRedisCacheWriter
(redisConnectionFactory)).cacheDefaults(defaultConfig).withInitialCacheConfigurations(configMap)
.transactionAware().build();
return cacheManager;
}
@Cacheable(cacheNames = "book",key="'add_'+#bookDTO.bookName")
public BookDTO addBook(BookDTO bookDTO) {
Book book = dozerMapper.map(bookDTO,Book.class);
boolean isExitBookByName = ObjectUtils.isNotEmpty(getBookByName(bookDTO.getBookName()));
if(isExitBookByName){
throw new BizException("书名已经存在");
}
book.setCreateDate(new Date());
book.setUpdateDate(new Date());
baseMapper.insert(book);
bookDTO = dozerMapper.map(book,BookDTO.class);
return bookDTO;
}
2、方案二:自定义缓存,通过AOP和自定义注解RedisCache搭配实现。
具体实现方法,可以查看如下链接
https://github.com/lyb-geek/springboot-learning/blob/master/springboot-redis/src/main/java/com/github/lybgeek/redis/aspect/RedisCacheAspect.java
1、方案一 通过org.springframework.integration.redis.util.RedisLockRegistry进行实现
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-redis</artifactId>
</dependency>
@Bean
public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory) {
return new RedisLockRegistry(redisConnectionFactory, "bookLock");
}
Lock lock = redisLockRegistry.obtain("bookStock");
if(lock.tryLock()) {
try {
log.info("updateStockById走db");
num = bookMapper.updateStockById(id, count);
} catch (Exception e) {
log.error("updateStockById error:" + e.getMessage(), e);
} finally {
lock.unlock();
}
}
利用RedisLockRegistry来实现redis分布式锁的相关内容介绍,可以查看
https://blog.csdn.net/l18767118724/article/details/85261092
http://www.itmuch.com/spring-boot/global-lock/
2、方案二,通过lua脚本配合redis来实现分布式锁
由于篇幅原因,我就把相关实现的代码的链接贴在下方
https://github.com/lyb-geek/springboot-learning/blob/master/springboot-redis/src/main/java/com/github/lybgeek/redis/util/RedisLockUtils.java
redis之前已经介绍很多,所以这篇可以算是一篇水文吧。在使用redis做缓存时,还要关注下Redis可能引起缓存穿透、缓存雪崩和缓存击穿的问题。其解决方案可以查看之前的文章 缓存三大问题及解决方案
redis能干啥?细看11种Web应用场景
https://blog.csdn.net/u014386474/article/details/51838190
redis教程
https://www.runoob.com/redis/redis-intro.html
https://github.com/lyb-geek/springboot-learning/tree/master/springboot-redis