redis是一个基于内存的高性能key-value数据库,具有极高的读写速度。本文介绍 SpringBoot 和 Redis 的整合,以及如何在项目中具体应用。
1、实时快速获得动态数据
如图,下图中的通知数5和消息数8在每个页面都是有的,不可能每次都从 MySQL 数据库里查询一下吧(下拉的详细信息是点击事件后才ajax加载)。我们知道 MySQL 是从硬盘读取,Redis 是从内存读取,速度比较可想而知。
所以我们这里可以使用 Redis 数据库,第一次先把计数从 MySQL 查出来,然后放到 Redis 数据库里,下次直接从 Redis 数据库读取。张三给李四发一条消息,只需要给李四的 Redis 数据库里该 key 记录 + 1,李四读取了这条消息,只需要给李四的 Redis 数据库里该 key -1。
2、排行榜或推荐内容(每天变动一次或七天变一次)
比如我这里右边侧边栏有本周用户排行榜(按文章数排序),推荐关键词排行榜(按文章关键词次数排序),热门文章排行榜(按评论数点赞数访问数等加权排序)。MySQL查询到数据后,我们还有进行处理排序,时间比较长。如果我们第一次查询后,然后将现成的数据存到 Redis 数据库中,下次访问就很快了,无论用户如何刷新页面,加载都很快。
Redis 也可以设置缓存过期时间,比如用户排行榜设置1小时更新一次,推荐关键词只有增删改的时候才更新,热门文章2小时更新一次。
1、本地安装了 Redis 数据库
Linux 或 Mac 可以参考这篇文章:点此
2、启动查看端口
3、下载可视化客户端 rdm
通常情况下,我们可以在命令行下查看 Redis 数据库,但是可视化工具能更真实地让我们看到数据的结构。
注意:本文用的 Redis 数据结构都是 String 类型,没有用到 List 类型
本文使用的 SpringBoot 版本 1.5.9
如果你的是 2.0 版本,在 RedisConfig.java 需要修改一下序列化工具部分
1、Maven
2、封装 redisTemplate 工具类
3、application.properties
4、RedisConfig.java
新建一个控制器,测试一下之前封装的 RedisOperator
1、Sevice 中统计某个用户未阅读的消息数量,并添加到Redis
2、发布私信(好友的未读消息+1)
3、查看私信(自己的未读消息-N)
注意:key 必须为String类型, @Cacheable中不能使用 #Result
除了上述使用方法参数作为key之外,Spring还为我们提供了一个root对象可以用来生成key。通过该root对象我们可以获取到以下信息。
参考这里:https://www.cnblogs.com/fashflying/p/6908028.html
Mac环境下安装配置Redis
一、我的需求
1、实时快速获得动态数据
如图,下图中的通知数5和消息数8在每个页面都是有的,不可能每次都从 MySQL 数据库里查询一下吧(下拉的详细信息是点击事件后才ajax加载)。我们知道 MySQL 是从硬盘读取,Redis 是从内存读取,速度比较可想而知。
所以我们这里可以使用 Redis 数据库,第一次先把计数从 MySQL 查出来,然后放到 Redis 数据库里,下次直接从 Redis 数据库读取。张三给李四发一条消息,只需要给李四的 Redis 数据库里该 key 记录 + 1,李四读取了这条消息,只需要给李四的 Redis 数据库里该 key -1。
2、排行榜或推荐内容(每天变动一次或七天变一次)
比如我这里右边侧边栏有本周用户排行榜(按文章数排序),推荐关键词排行榜(按文章关键词次数排序),热门文章排行榜(按评论数点赞数访问数等加权排序)。MySQL查询到数据后,我们还有进行处理排序,时间比较长。如果我们第一次查询后,然后将现成的数据存到 Redis 数据库中,下次访问就很快了,无论用户如何刷新页面,加载都很快。
Redis 也可以设置缓存过期时间,比如用户排行榜设置1小时更新一次,推荐关键词只有增删改的时候才更新,热门文章2小时更新一次。
二、基本准备
1、本地安装了 Redis 数据库
Linux 或 Mac 可以参考这篇文章:点此
2、启动查看端口
3、下载可视化客户端 rdm
通常情况下,我们可以在命令行下查看 Redis 数据库,但是可视化工具能更真实地让我们看到数据的结构。
三、开始代码
注意:本文用的 Redis 数据结构都是 String 类型,没有用到 List 类型
本文使用的 SpringBoot 版本 1.5.9
如果你的是 2.0 版本,在 RedisConfig.java 需要修改一下序列化工具部分
1、Maven
- <!-- 里面依赖了spring-data-redis -->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-redis</artifactId>
- </dependency>
2、封装 redisTemplate 工具类
- package com.liuyanzhao.forum.util;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.data.redis.core.StringRedisTemplate;
- import org.springframework.stereotype.Component;
- import java.util.Map;
- import java.util.Set;
- import java.util.concurrent.TimeUnit;
- /**
- * @author 言曌
- * @date 2018/3/18 下午1:11
- */
- @Component
- public class RedisOperator {
- // @Autowired
- // private RedisTemplate<String, Object> redisTemplate;
- @Autowired
- private StringRedisTemplate redisTemplate;
- // Key(键),简单的key-value操作
- /**
- * 实现命令:TTL key,以秒为单位,返回给定 key的剩余生存时间(TTL, time to live)。
- *
- * @param key
- * @return
- */
- public long ttl(String key) {
- return redisTemplate.getExpire(key);
- }
- /**
- * 实现命令:expire 设置过期时间,单位秒
- *
- * @param key
- * @return
- */
- public void expire(String key, long timeout) {
- redisTemplate.expire(key, timeout, TimeUnit.SECONDS);
- }
- /**
- * 实现命令:INCR key,增加key一次
- *
- * @param key
- * @return
- */
- public long incr(String key, long delta) {
- return redisTemplate.opsForValue().increment(key, delta);
- }
- /**
- * 实现命令: key,减少key一次
- *
- * @param key
- * @return
- */
- public long decr(String key, long delta) {
- if(delta<0){
- // throw new RuntimeException("递减因子必须大于0");
- del(key);
- return 0;
- }
- return redisTemplate.opsForValue().increment(key, -delta);
- }
- /**
- * 实现命令:KEYS pattern,查找所有符合给定模式 pattern的 key
- */
- public Set<String> keys(String pattern) {
- return redisTemplate.keys(pattern);
- }
- /**
- * 实现命令:DEL key,删除一个key
- *
- * @param key
- */
- public void del(String key) {
- redisTemplate.delete(key);
- }
- // String(字符串)
- /**
- * 实现命令:SET key value,设置一个key-value(将字符串值 value关联到 key)
- *
- * @param key
- * @param value
- */
- public void set(String key, String value) {
- redisTemplate.opsForValue().set(key, value);
- }
- /**
- * 实现命令:SET key value EX seconds,设置key-value和超时时间(秒)
- *
- * @param key
- * @param value
- * @param timeout (以秒为单位)
- */
- public void set(String key, String value, long timeout) {
- redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
- }
- /**
- * 实现命令:GET key,返回 key所关联的字符串值。
- *
- * @param key
- * @return value
- */
- public String get(String key) {
- return (String) redisTemplate.opsForValue().get(key);
- }
- // Hash(哈希表)
- /**
- * 实现命令:HSET key field value,将哈希表 key中的域 field的值设为 value
- *
- * @param key
- * @param field
- * @param value
- */
- public void hset(String key, String field, Object value) {
- redisTemplate.opsForHash().put(key, field, value);
- }
- /**
- * 实现命令:HGET key field,返回哈希表 key中给定域 field的值
- *
- * @param key
- * @param field
- * @return
- */
- public String hget(String key, String field) {
- return (String) redisTemplate.opsForHash().get(key, field);
- }
- /**
- * 实现命令:HDEL key field [field ...],删除哈希表 key 中的一个或多个指定域,不存在的域将被忽略。
- *
- * @param key
- * @param fields
- */
- public void hdel(String key, Object... fields) {
- redisTemplate.opsForHash().delete(key, fields);
- }
- /**
- * 实现命令:HGETALL key,返回哈希表 key中,所有的域和值。
- *
- * @param key
- * @return
- */
- public Map<Object, Object> hgetall(String key) {
- return redisTemplate.opsForHash().entries(key);
- }
- // List(列表)
- /**
- * 实现命令:LPUSH key value,将一个值 value插入到列表 key的表头
- *
- * @param key
- * @param value
- * @return 执行 LPUSH命令后,列表的长度。
- */
- public long lpush(String key, String value) {
- return redisTemplate.opsForList().leftPush(key, value);
- }
- /**
- * 实现命令:LPOP key,移除并返回列表 key的头元素。
- *
- * @param key
- * @return 列表key的头元素。
- */
- public String lpop(String key) {
- return (String) redisTemplate.opsForList().leftPop(key);
- }
- /**
- * 实现命令:RPUSH key value,将一个值 value插入到列表 key的表尾(最右边)。
- *
- * @param key
- * @param value
- * @return 执行 LPUSH命令后,列表的长度。
- */
- public long rpush(String key, String value) {
- return redisTemplate.opsForList().rightPush(key, value);
- }
- }
3、application.properties
- #### Redis
- # Redis数据库索引,默认为0
- spring.redis.database=0
- # Redis 服务器地址
- spring.redis.host=127.0.0.1
- # Redis 端口
- spring.redis.port=6379
- # Redis 密码,默认为空
- spring.redis.password=
- # 连接池中最大连接数(使用负值表示没有限制)
- spring.redis.pool.max-active=1000
- # 连接池中最大阻塞等待时间(使用负值表示没有限制)
- spring.redis.pool.max-wait=-1
- # 连接池中最大空闲连接
- spring.redis.pool.max-idle=10
- # 连接池中最小空闲连接
- spring.redis.pool.min-idle=2
- # 连接超时时间(毫秒)
- spring.redis.timeout=0
4、RedisConfig.java
- package com.liuyanzhao.forum.config;
- import com.fasterxml.jackson.annotation.JsonAutoDetect;
- import com.fasterxml.jackson.annotation.PropertyAccessor;
- import com.fasterxml.jackson.databind.ObjectMapper;
- import org.springframework.cache.CacheManager;
- import org.springframework.cache.annotation.EnableCaching;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.data.redis.cache.RedisCacheManager;
- import org.springframework.data.redis.connection.RedisConnectionFactory;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.data.redis.core.StringRedisTemplate;
- import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
- import java.util.HashMap;
- import java.util.Map;
- /**
- * @author 言曌
- * @date 2018/5/21 上午10:02
- */
- @Configuration
- //@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 3600 * 12)//最大过期时间
- @EnableCaching
- public class RedisConfig {
- //缓存管理器
- @Bean
- public CacheManager cacheManager(@SuppressWarnings("rawtypes") RedisTemplate redisTemplate) {
- RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
- //设置缓存过期时间
- Map<String, Long> expires = new HashMap<>();
- expires.put("7d", 7 * 3600 * 24L);
- expires.put("12h", 3600 * 12L);
- expires.put("1h", 3600 * 1L);
- expires.put("10m", 60 * 10L);
- cacheManager.setExpires(expires);
- cacheManager.setDefaultExpiration(7 * 3600 * 24);//默认缓存七天
- return cacheManager;
- }
- @Bean
- public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
- StringRedisTemplate template = new StringRedisTemplate(factory);
- setSerializer(template);//设置序列化工具
- template.afterPropertiesSet();
- return template;
- }
- private void setSerializer(StringRedisTemplate template) {
- @SuppressWarnings({"rawtypes", "unchecked"})
- Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
- ObjectMapper om = new ObjectMapper();
- om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
- om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
- jackson2JsonRedisSerializer.setObjectMapper(om);
- template.setValueSerializer(jackson2JsonRedisSerializer);
- }
- }
四、简单的测试
新建一个控制器,测试一下之前封装的 RedisOperator
- package com.liuyanzhao.forum.controller;
- import com.alibaba.fastjson.JSON;
- import com.liuyanzhao.forum.entity.Article;
- import com.liuyanzhao.forum.entity.User;
- import com.liuyanzhao.forum.repository.ArticleRepository;
- import com.liuyanzhao.forum.util.RedisOperator;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.ResponseBody;
- import org.springframework.web.bind.annotation.RestController;
- import java.util.List;
- /**
- * @author 言曌
- * @date 2018/3/18 上午11:18
- */
- @RestController
- @RequestMapping("/redis")
- public class RedisController {
- @Autowired
- private RedisOperator redis;
- @Autowired
- private ArticleRepository articleRepository;
- @RequestMapping("/set")
- @ResponseBody
- public String set(){
- redis.set("name","琪仔");
- redis.incr("age",20);
- User user = new User();
- user.setId(234);
- List<Article> articleList = articleRepository.findAll();
- System.out.println(articleList);
- redis.set("json:articleList", JSON.toJSONString(articleList));
- return "success";
- }
- @RequestMapping("/get")
- @ResponseBody
- public String get(){
- return redis.get("json:articleList");
- }
- }
五、具体应用到项目中
1、Sevice 中统计某个用户未阅读的消息数量,并添加到Redis
- @Override
- @Cacheable(value = "12h", key = "'messageSize:'+#p0.id")
- public Integer countNotReadMessageSize(User user) {
- return messageRepository.countByUserAndStatus(user, MessageStatusEnum.NOT_READ_MESSAGE.getCode());
- }
2、发布私信(好友的未读消息+1)
- @PostMapping("/messages")
- public ResponseEntity<Response> createMessage(Integer friendId, String content) {
- .......
- //3、Redis 中 好友的未读消息+1
- redisOperator.incr("messageSize:" + friendId, 1L);
- .....
- }
3、查看私信(自己的未读消息-N)
- @GetMapping("/manage/messages")
- public ModelAndView messages( Integer uid) {
- ......
- // 查看好友发来的N消息,Redis中未读消息-N
- redisOperator.decr("messageSize:" + user.getId(), notReadSize);
- ......
- }
六、@Cacheable、@CachePut、@CacheEvict 注解的使用
- @Transactional
- @Override
- //执行下面的方法,最终将结果添加(如果这个key存在,则覆盖)到Redis中
- @CachePut(value = "7d", key = "'tags:'+#p0.id", unless = "#tag eq null")
- public Tag saveTag(Tag tag) {
- return tagRepository.save(tag);
- }
- @Override
- //如果Redis数据库中没有这个key,执行下面方法,最终结果添加到Redis。如果key已存在,则直接从Redis获取,不执行下面方法
- @Cacheable(value = "7d", key = "'tags:'+#id.toString()")
- public Tag getTagById(Integer id) {
- return tagRepository.findOne(id);
- }
- @Override
- //condition为true执行@CacheEvict,将该key从Redis删除
- @CacheEvict(value = "7d", key = "'tags:'+#id.toString()", condition = "#result eq true")
- public Boolean removeTag(Integer id) {
- tagRepository.delete(id);
- return true;
- }
注意:key 必须为String类型, @Cacheable中不能使用 #Result
除了上述使用方法参数作为key之外,Spring还为我们提供了一个root对象可以用来生成key。通过该root对象我们可以获取到以下信息。
参考这里:https://www.cnblogs.com/fashflying/p/6908028.html
属性名称 | 描述 | 示例 |
methodName | 当前方法名 | #root.methodName |
method | 当前方法 | #root.method.name |
target | 当前被调用的对象 | #root.target |
targetClass | 当前被调用的对象的class | #root.targetClass |
args | 当前方法参数组成的数组 | #root.args[0] |
caches | 当前被调用的方法使用的Cache | #root.caches[0].name |
七、更多文章
Mac环境下安装配置Redis
您可以选择一种方式赞助本站
支付宝扫一扫赞助
微信钱包扫描赞助
赏