1.系统优化策略
说明:引入缓存机制可以有效的降低用户访问物理设备的频次,从而提高响应速度.
2.如何设计缓存
1.缓存数据如何存储? 应该采用什么样的数据结构呢? K-V key的唯一性
2.缓存数据的容量大小 应该动态维护缓存数据,将不需要的数据提前删除. LRU算法/LFU算法/随机算法/TTL算法
3.缓存数据保存到内存中,但是内存的特点断电即擦除. 定期将内存数据持久化(写入磁盘中)
4.单台缓存服务器性能不足,所以一般需要搭建集群(实现高可用).
5.使用C语言开发.
3.Redis缓存服务
3.1什么是Redis
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。
速度: 读: 11.2万/秒 写:8.6万/秒 50万/秒
3.2上传Redis
3.3安装Redis
3.4 修改redis配置文件
修改redis根目录下的redis.conf文件
1).去除IP绑定
4.Redis服务器命令
说明: Redis服务在运行时,必须依赖于配置文件 redis.conf. 操作redis时最好在根目录中操作
1).启动redis
redis-server redis.conf
redis-cli -p 6379
ctrl + c 退出客户端
redis-cli -p 6379 shutdown
方法二:
说明: 如果操作的端口号是默认端口6379 则可以省略不写.
1).启动redis
redis-server redis.conf
2).进入redis客户端
redis-cli
3).关闭redis服务器
redis-cli shutdown
4.SpringBoot整合Redis
4.1 整合入门案例
4.1.1 导入jar包
`
redis.clients
jedis
org.springframework.data
spring-data-redis
`
4.1.2 完成测试案例
4.2 测试基本命令
package com.jt.test;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.SetParams;
//@SpringBootTest
public class TestRedis {
//链接服务: IP地址:端口
@Test
public void testSetGet() throws InterruptedException {
Jedis jedis = new Jedis("192.168.126.129",6379);
jedis.flushAll(); //先清空redis缓存
//1.存取redis
jedis.set("key1", "天天向上");
String value = jedis.get("key1");
System.out.println(value);
//2.判断key是否存在
if(jedis.exists("key1")){
jedis.set("key1", "好好学习,天天向上");
}else{
jedis.set("key1", "天天开心");
}
//3.为key添加超时时间
jedis.expire("key1", 10);
Thread.sleep(2000);
System.out.println("剩余存活时间:"+jedis.ttl("key1"));
//4.撤销剩余超时时间
jedis.persist("key1");
System.out.println("撤销成功!!");
}
}
4.2.1 setNx/setEx/set命令
/**
* 需求: 如果数据不存在,则将数据修改
*/
@Test
public void testSetNx(){
Jedis jedis = new Jedis("192.168.126.129",6379);
jedis.set("key1", "aaa");
//如果key不存在,则赋值
jedis.setnx("key1", "测试方法");
System.out.println(jedis.get("key1"));
}
/**
* 需求: 实现超时时间的设定与赋值操作的原子性.
* 考点: 为数据设定超时时间时,切记满足原子性要求.
* 否则会出现key永远不能删除的现象
*/
@Test
public void testSetEx(){
Jedis jedis = new Jedis("192.168.126.129",6379);
/*jedis.set("key2", "bbb");
jedis.expire("key2", 3);*/
//???数据超时之后一定会被删除吗??? 错的
jedis.setex("key2", 10, "bbb");
}
/**
* 需求: 如果数据存在时,才会修改数据,并且为数据添加超时时间10秒(原子性).
* 参数说明:
* NX: 只有数据不存在时,才会赋值.
* XX: 只有数据存在时,才会赋值.
* EX: 秒
* PX: 毫秒
*/
public void testSet(){
Jedis jedis = new Jedis("192.168.126.129",6379);
/* if(jedis.exists("key3")){
jedis.setex("key3",10 ,"ccc");
}*/
SetParams setParams = new SetParams();
setParams.xx().ex(10);
//将所有的操作采用原子性的方式进行控制
jedis.set("key3", "ccc", setParams);
}
4.2.2 测试hash
/**
* HASH测试
*/
@Test
public void testHash(){
Jedis jedis = new Jedis("192.168.126.129",6379);
jedis.hset("person", "id", "100");
jedis.hset("person", "name", "tomcat猫");
System.out.println(jedis.hgetAll("person"));
}
4.2.3 测试List
/**
* LIST集合测试
*/
@Test
public void testList(){
Jedis jedis = new Jedis("192.168.126.129",6379);
jedis.lpush("list", "1","2","3","4");
String value = jedis.rpop("list");
System.out.println(value);
}
4.2.4 测试事务
/**
* Set集合测试
* 1. sadd 新增元素
* 2. SCARD 获取元素数量
* 3. SINTER key1 key2 获取元素的交集
* 4. SMEMBERS set 获取集合元素
* demo自己补一下
*/
@Test
public void testMulti(){
Jedis jedis = new Jedis("192.168.126.129",6379);
//开启事务
Transaction transaction = jedis.multi();
try {
transaction.set("a", "a");
transaction.set("b", "b");
//提交事务
transaction.exec();
}catch (Exception e){
//回滚事务
transaction.discard();
}
}
5.SpringBoot整合Redis的实际运用
5.1 编辑properties配置文件
5.2 编辑配置类
package com.jt.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import redis.clients.jedis.Jedis;
@Configuration//标识是个配置类,一般与@Bean注解连用
@PropertySource("classpath:/properties/redis.properties")
public class RedisConfig {
@Value("${redis.host}")
private String host;
@Value("${redis.port}")
private Integer port;
@Bean
public Jedis jedis(){
return new Jedis(host,port);
}
}
6.ObjectMapperUtil实现
6.1 业务需求
说明:在业务中通常需要将业务对象转化为JSON数据.需要通过工具API进行手动的转化.
6.2 入门案例
public class TestObjectMapper {
//objectMapper入门案例
//2. 将JSON转化为对象
@Test
public void testObject() throws JsonProcessingException {
//1. 将对象转化为JSON
ItemDesc itemDesc = new ItemDesc();
itemDesc.setItemId(100L)
.setItemDesc("我是一个测试数据")
.setCreated(new Date())
.setUpdated(itemDesc.getCreated());
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(itemDesc);
System.out.println(json);
//2.将JSON转化为对象
ItemDesc desc = objectMapper.readValue(json, ItemDesc.class);
System.out.println(desc);
}
@Test
public void testList() throws JsonProcessingException {
List list = new ArrayList<>();
ItemDesc itemDesc = new ItemDesc();
itemDesc.setItemId(100L).setItemDesc("我是一个测试数据").setCreated(new Date()).setUpdated(itemDesc.getCreated());
ItemDesc itemDesc2 = new ItemDesc();
itemDesc2.setItemId(200L).setItemDesc("我是一个测试数据").setCreated(new Date()).setUpdated(itemDesc.getCreated());
list.add(itemDesc);
list.add(itemDesc2);
//1. 将对象转化为JSON
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(list);
System.out.println(json);
//2.将JSON转化为对象
List list2 = objectMapper.readValue(json,list.getClass());
System.out.println(list2);
}
}
6.3 编辑ObjectMapperUtil
public class ObjectMapperUtil {
private static final ObjectMapper MAPPER = new ObjectMapper();
public static String toJSON(Object target){
try {
return MAPPER.writeValueAsString(target);
} catch (JsonProcessingException e) {
e.printStackTrace();
//将检查异常转化为运行时异常.
throw new RuntimeException(e);
}
}
public static T toObject(String json,Class targetClass){
try {
return MAPPER.readValue(json, targetClass);
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
7.实现商品分类缓存操作
7.1 编辑ItemController
/**
* 业务需求: 实现商品分类树形结构展现
* url地址: http://localhost:8091/item/cat/list
* 参数: id= 父级节点的ID
* 返回值: List
*/
@RequestMapping("/list")
public List findItemCatList(Long id){
//暂时只查询一级商品分类信息
long parentId = (id==null?0:id);
//return itemCatService.findItemCatList(parentId);
return itemCatService.findItemCatCache(parentId);
}
7.2 编辑ItemService
package com.jt.service;
import com.baomidou.mybatisplus.core.conditions.query.Query;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.jt.mapper.ItemCatMapper;
import com.jt.pojo.ItemCat;
import com.jt.util.ObjectMapperUtil;
import com.jt.vo.EasyUITree;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import java.util.ArrayList;
import java.util.List;
@Service
public class ItemCatServiceImpl implements ItemCatService{
@Autowired
private ItemCatMapper itemCatMapper;
@Autowired(required = false) //类似于懒加载
private Jedis jedis;
@Override
public ItemCat findItemCatById(Long itemCatId) {
return itemCatMapper.selectById(itemCatId);
}
/**
* 1.根据parentId 查询子级目录信息
* 2.获取ItemCatList集合,之后将List集合转化为VOList集合
* 3.返回数据
* @param parentId
* @return
*/
@Override
public List findItemCatList(long parentId) {
QueryWrapper queryWrapper = new QueryWrapper<>();
queryWrapper.eq("parent_id", parentId);
List catList = itemCatMapper.selectList(queryWrapper);
//2 list集合转化
List treeList = new ArrayList<>(catList.size());
for (ItemCat itemCat : catList){
long id = itemCat.getId();
String text = itemCat.getName();
String state = itemCat.getIsParent()?"closed":"open"; //是父级,默认应该closed
EasyUITree easyUITree = new EasyUITree(id, text, state);
treeList.add(easyUITree);
}
return treeList;
}
/**
* 实现步骤:
* 1.先定义key ITEM_CAT_PARENT::0
* 2.先查询缓存
* 有 true 获取缓存数据之后,将JSON转化为对象 之后返回
* 没有 false 应该查询数据库, 之后将数据保存到redis中. 默认30天超时.
* * @param parentId
* @return
*/
@Override
public List findItemCatCache(long parentId) {
Long startTime = System.currentTimeMillis();
List treeList = new ArrayList<>();
//1.定义key
String key = "ITEM_CAT_PARENT::"+parentId;
//2.从缓存中获取对象
if(jedis.exists(key)){
//存在 直接获取缓存数据,之后转化为对象
String json = jedis.get(key);
treeList = ObjectMapperUtil.toObject(json,treeList.getClass());
System.out.println("查询redis缓存,获取数据");
long endTime = System.currentTimeMillis();
System.out.println("耗时:"+(endTime-startTime)+"毫秒");
}else{
//不存在 应该先查询数据库,之后将数据保存到缓存中.
treeList = findItemCatList(parentId);
String json = ObjectMapperUtil.toJSON(treeList);
jedis.setex(key, 7*24*60*60, json);
System.out.println("查询数据库获取结果");
long endTime = System.currentTimeMillis();
System.out.println("耗时:"+(endTime-startTime)+"毫秒");
}
return treeList;
}
}