您的位置:首页 > 新闻 > 会展 > 免费个人网站服务器樱花澳门今晚开什么_免费咨询服务合同范本_百度指数在线查询工具_长沙seo排名扣费

免费个人网站服务器樱花澳门今晚开什么_免费咨询服务合同范本_百度指数在线查询工具_长沙seo排名扣费

2024/12/22 18:12:39 来源:https://blog.csdn.net/Tomkruse11/article/details/144528761  浏览:    关键词:免费个人网站服务器樱花澳门今晚开什么_免费咨询服务合同范本_百度指数在线查询工具_长沙seo排名扣费
免费个人网站服务器樱花澳门今晚开什么_免费咨询服务合同范本_百度指数在线查询工具_长沙seo排名扣费

业务分析(思路)

一个正常的业务操作是从前端到后端再到数据库,以商城的商品详情为例,当用户点击一个商品跳转进入详情页面时,从前端传入此商品的id,通过请求发至后端,后端接收该参数后即执行相应的方法,执行数据库sql操作。

那么假定一个商城有十万或者数十万甚至更多的商品,那么商品数据之间肯定会有冷热之分,即热门商品会有很多用户点击进入详情页面,有的冷门商品可能很久才有人点进去。那么此种情景,就可以给该业务添加Redis功能。即将那些热门的商品数据存入Redis中,那么后续数据将从Redis中取,而不用走数据库,可以增加性能。

1.未加入Redis前的业务

先对直接走数据库的业务进行压力测试。此处设置为线程数1000(用户)循环10次,即1000个用户每个发送10个请求,即1000*10=10000个请求。可以看到异常率达到了81%,平均响应值达到了12S。

image.png

image.png


控制层代码

  //详情页面请求方法@RequestMapping(value = "findPhoneByPid",method={RequestMethod.GET})public Map<String,Object> findPhoneByPid(@RequestParam Integer pid){Map<String,Object> map =new HashMap<>();List<Phone> t=this.comBiz.findPhoneByPid(pid);map.put("code",1);map.put("data",t);return map;}

2.实现Redis(SpringBoot)

在使用Redis前,先要弄明白Redis是什么?为什么它可以提高效率?

Redis的数据是存在内存中的。它的读写速度非常快,每秒可以处理超过10万次读写操作。因此redis被广泛应用于缓存

Redis一共有五中基本数据类型(key,Value)

  • String(字符串)
  • Hash(哈希)
  • List(列表)
  • Set(集合)
  • zset(有序集合)

1.在Maven中导入Redis所需的依赖

    	<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>

 yml配置文件

  redis:database: 4host: 127.0.0.1port: 6379timeout: 10000ms

2.封装Redis操作类

该封装类转自CSDN:springBoot整合redis使用介绍(详细案例)_redis springboot-CSDN博客

package com.yc.util;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;/*** @author Redis 业务类* @date 2021-08-15 18:44*/
@Slf4j
@Component
public class CacheService {@Autowiredprivate StringRedisTemplate redisTemplate;private final String DEFAULT_KEY_PREFIX = "";   //key字符串非空处理private final int EXPIRE_TIME = 1;private final TimeUnit EXPIRE_TIME_TYPE = TimeUnit.DAYS;/*** 数据缓存至redis** @param key* @param value* @return*/public <K, V> void add(K key, V value) {try {if (value != null) {redisTemplate.opsForValue().set(DEFAULT_KEY_PREFIX + key, JSON.toJSONString(value));}} catch (Exception e) {log.error(e.getMessage(), e);throw new RuntimeException("数据缓存至redis失败");}}/*** 数据缓存至redis并设置过期时间** @param key* @param value* @return*/public <K, V> void add(K key, V value, long timeout, TimeUnit unit) {try {if (value != null) {redisTemplate.opsForValue().set(DEFAULT_KEY_PREFIX + key, JSON.toJSONString(value), timeout, unit);}} catch (Exception e) {log.error(e.getMessage(), e);throw new RuntimeException("数据缓存至redis失败");}}/*** 写入 hash-set,已经是key-value的键值,不能再写入为hash-set** @param key    must not be {@literal null}.* @param subKey must not be {@literal null}.* @param value  写入的值*/public <K, SK, V> void addHashCache(K key, SK subKey, V value) {redisTemplate.opsForHash().put(DEFAULT_KEY_PREFIX + key, subKey, value);}/*** 写入 hash-set,并设置过期时间** @param key    must not be {@literal null}.* @param subKey must not be {@literal null}.* @param value  写入的值*/public <K, SK, V> void addHashCache(K key, SK subKey, V value, long timeout, TimeUnit unit) {redisTemplate.opsForHash().put(DEFAULT_KEY_PREFIX + key, subKey, value);   //添加redisTemplate.expire(DEFAULT_KEY_PREFIX + key, timeout, unit);       //设置过期时间}/*** 获取 hash-setvalue** @param key    must not be {@literal null}.* @param subKey must not be {@literal null}.*/public <K, SK> Object getHashCache(K key, SK subKey) {return  redisTemplate.opsForHash().get(DEFAULT_KEY_PREFIX + key, subKey);}/*** 从redis中获取缓存数据,转成对象** @param key   must not be {@literal null}.* @param clazz 对象类型* @return*/public <K, V> V getObject(K key, Class<V> clazz) {String value = this.get(key);V result = null;if (!StringUtils.isEmpty(value)) {result = JSONObject.parseObject(value, clazz);}return result;}/*** 从redis中获取缓存数据,转成list** @param key   must not be {@literal null}.* @param clazz 对象类型* @return*/public <K, V> List<V> getList(K key, Class<V> clazz) {String value = this.get(key);List<V> result = Collections.emptyList();if (!StringUtils.isEmpty(value)) {result = JSONArray.parseArray(value, clazz);}return result;}/*** 功能描述:Get the value of {@code key}.** @param key must not be {@literal null}.* @return java.lang.String* @date 2021/9/19**/public <K> String get(K key) {String value;try {value = redisTemplate.opsForValue().get(DEFAULT_KEY_PREFIX + key);} catch (Exception e) {log.error(e.getMessage(), e);throw new RuntimeException("从redis缓存中获取缓存数据失败");}return value;}/*** 删除key*/public void delete(String key) {redisTemplate.delete(key);}/*** 批量删除key*/public void delete(Collection<String> keys) {redisTemplate.delete(keys);}/*** 序列化key*/public byte[] dump(String key) {return redisTemplate.dump(key);}/*** 是否存在key*/public Boolean hasKey(String key) {return redisTemplate.hasKey(key);}/*** 设置过期时间*/public Boolean expire(String key, long timeout, TimeUnit unit) {return redisTemplate.expire(key, timeout, unit);}/*** 设置过期时间*/public Boolean expireAt(String key, Date date) {return redisTemplate.expireAt(key, date);}/*** 移除 key 的过期时间,key 将持久保持*/public Boolean persist(String key) {return redisTemplate.persist(key);}/*** 返回 key 的剩余的过期时间*/public Long getExpire(String key, TimeUnit unit) {return redisTemplate.getExpire(key, unit);}/*** 返回 key 的剩余的过期时间*/public Long getExpire(String key) {return redisTemplate.getExpire(key);}
}

 

在该封装类中不难发现,其使用的是StringRedisTemplate而不是RedisTemplate类。

image.png


其含义是当我们在JAVA中以对象的形式作为value的值存入时,那么value中会有一个@class值用作于序列化与反序列化。这样一来我们在IDEA的控制器上很直观的就可以看到数据,但是也存在了一个缺点->浪费内存,因为我们要存放@class这一段额外的数据来反序列化JSON字符串。
那为了节约内存,我们在处理Value时不使用GenericJackson2JsonRedisSerializer序列化器,使用RedisSerializer.string序列化器。这样一下,我们只需要在存入数据时,手动的把JAVA对象转变为JSON格式字符串,然后取数据时,再把JSON转回JAVA对象就好了。
而StringRedisTemplate它的key和Value默认就是String方式,我们不用自己再去定义RedisTemplate的配置类。
那么我们使用时即注意一下三点即可:
1.使用 StringRedisTemplate
2.写入 Redis 时,手动把对象序列化为 JSON
3.读取 Redis 时,手动把读取到的 JSON 反序列化为对象

因此在封装类中可以看到value值即对象进行了手动JSON处理

redisTemplate.opsForValue().set(DEFAULT_KEY_PREFIX + key, JSON.toJSONString(value));

因此存入的对象在Redis中是一段JSON格式的字符串。

那么我们在取出数据时,则需要将这段JSON格式的字符串转为对象形式。那么需要导入jackson依赖

<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.12.5</version>
</dependency>

使用其提供的ObjectMapper类中的readValue(String,对象.class)来将json字符串转为对象。

注意:JSON格式中的属性要严格对应对象中的属性

3.编写测试类测试其功能

package com.fastdfs;import cn.hutool.json.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yc.ImgApp;
import com.yc.bean.Manager;
import com.yc.util.CacheService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;@RunWith(SpringRunner.class)
@SpringBootTest(classes = ImgApp.class)
public class TestRedis {@Autowiredprivate CacheService cacheService;@Autowiredprivate ObjectMapper objectMapper;   //字符串转对象//测试Redis的缓存方法@Testpublic void testAdd() throws JsonProcessingException {Manager manager=new Manager(1,"22","3");Manager manager0=new Manager(2,"哈哈","333");List<Manager> list=new ArrayList<>();list.add(manager);list.add(manager0);//1.测试单个对象cacheService.add("key",manager);String manager1=cacheService.get("key");Manager manager2=objectMapper.readValue(manager1,Manager.class);System.out.println(manager2.getUsername());System.out.println(manager.getId());//2.测试集合对象       //key    集合       过期时间    时间单位cacheService.add("list",list,1, TimeUnit.HOURS);List<Manager> list2=null;list2= cacheService.getList("list",Manager.class);for (Manager manager3 : list2) {System.out.println(manager3);}}}

运行结果:

4.业务实现(思路)

1.当某个商品还没有被点过时(即该商品数据未被查询过),此时正常走数据库查询。

2.当执行过一次后,那么以该商品的pid值作为键key,该商品对象作为值value存入redis中

3.后续当再此执行同一商品的方法时,那么以该商品pid去Redis查,若有直接从Redis中取出该商品对象直接传回前端,而不再继续走到DAO层中。

4.如若没有查到,则正常走DAO层。

业务类代码实现

    //详情业务   TODO: 加入Redis@Overridepublic List<Phone> findPhoneByPid(Integer pid) {List<Phone> list=null;if (this.cacheService.hasKey(String.valueOf(pid))) {    //hasKey(key) 判断是否存在该keylist=this.cacheService.getList(pid,Phone.class);   //如果存在该Key 则从Redis中拿取}else {list=comDao.selectPhoneByPid(pid);   //否则走DB 从数据库中拿取this.cacheService.add(pid,list,EXPIRE_TIME, EXPIRE_TIME_TYPE);     // 然后将该List数据存入Redis 并设置过期时间//那么下次则会走Redis 而不会走DB}return list;}

💡注意:此处为某个商品的查询业务增加了Redis。那么当该商品数据发生了修改或删除操作时,那么应该直接清除或设置为过期

在实现Redis后,我们再进行测试。这时可以看到其异常率降低到了0%,平均响应时间降低到了2S

由此可见,其Redis功能的强大。

5.总结

本次是以电商项目中的一个业务为例,对Redis的理解与实现,在本项目中,还有许多类似业务可以增加Redis,此处不再复述。其Redis还有许多业务场景可以使用,而该案例所使用目标为减轻了DB服务器压力。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com