day 11 Redis
此文档是依据上课流程编写,更多细节及图片请参见刘老师的专栏
江哥的专栏
《cgb2008- 京淘 day11》
一. Redis 客户端命令
-
Hash 类型
i. 阐明
能够用散列类型保留 ” 对象 ” 和属性值,个别在工作中存储的都是基于一个业务对象的数据。
user{“name”:”aa”,”age”:5}
ii. Hash 类型命令
hset user name aa hset user age 5 hget user age => "5" hgetall user => "name" "aa" "age" "5" hkeys user => "name" "age"
-
List 类型
i. 阐明
双端循环链表,能够从首尾两端拜访数据 L===R
ii. List 类型命令
队列:lpush list 1 2 3 4 5 rpop ==> 1 栈:lpush list 1 2 3 4 5 lpop ==> 5
-
Set 类型
sadd set1 a sadd set1 b scard set1 sinter set1 set2 sinterstore set3 set1 set2 sismember set1 c smembers set1
-
事务命令
Redis 中的事务是弱事务,如果在分布式系统中无奈对事务进行管制。
multi exec discard
二. 应用程序操作 redis
-
整合入门案例
i. 导入 jar 包
<!--spring 整合 redis --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> </dependency>
ii. 创立 TestRedis 测试类
public class TestRedis { @Test // 连贯服务:IP 地址: 端口 public void testSetGet() throws InterruptedException {Jedis jedis = new Jedis("192.168.126.129",6379); jedis.flushAll(); // 先清空 redis 缓存 //1. 存取 redis jedis.set("好好学习", "天天向上"); String value = jedis.get("好好学习"); System.out.println(value); //2. 判断 key 是否存在 if(jedis.exists("好好学习")){jedis.set("好好学习", "天天敲代码"); }else {jedis.set("好好学习", "天天开心"); }; //3. 为 key 增加超时工夫 jedis.expire("好好学习", 10); Thread.sleep(2000); System.out.println("残余存活工夫:"+jedis.ttl("好好学习")); //4. 撤销剩余时间 jedis.persist("好好学习"); System.out.println("撤销胜利"); } }
-
高级 API
i. 测试 setnx()
@Test // 如果数据不存在, 则批改数据 public void testSetNx() throws InterruptedException {Jedis jedis = new Jedis("192.168.126.129",6379); jedis.set("key1", "aaa"); // 如果 key 不存在则赋值 jedis.setnx("key1", "测试方法"); System.out.println(jedis.get("key1")); //"aaa" }
ii. 测试 setex()
@Test // 实现超时工夫的设定与赋值操作原子性 public void testSetEx() throws InterruptedException {Jedis jedis = new Jedis("192.168.126.129",6379); jedis.set("key2", "bbb"); jedis.expire("key2", 3); // 数据超时之后肯定会被删除吗? 错! // 考点: 为数据设定超时工夫时, 切记满足原子性操作, 否则会呈现 key 永远不能删除的景象 jedis.setex("key2", 3, "bbb"); }
iii. 测试 SetParams
@Test // 如果 key 存在, 则为其赋值并增加超时工夫 public void testSet() throws InterruptedException {Jedis jedis = new Jedis("192.168.126.129",6379); /*if(jedis.exists("key3")){jedis.setex("key3", 10, "cc"); }*/ //NX - 只有数据不存在时才会赋值 XX - 只有数据存在时才会赋值 //EX - 设置超时工夫:s PX - 设置超时工夫:ms SetParams setParams = new SetParams(); setParams.xx().ex(10); // 将所有的操作采纳原子性的形式进行管制 jedis.set("key3", "ccc", setParams); }
iv. 测试 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")); }
v. 测试 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); }
vi. 测试事务
@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.set("c", "c"); // 提交事务 transaction.exec();}catch (Exception e){ // 回滚事务 transaction.discard();} }
三. SpringBoot 整合 redis
-
编写 RedisConfig 配置类
@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); } }
-
批改 TestRedis 测试类
@SpringBootTest public class TestRedis { @Autowired private Jedis jedis; ... }
四. 基于业务实现缓存
-
ObjectMapperUtil 实现
i. 业务需要
在业务中通常须要将业务对象
ii. 入门案例 - 编写 TestObjectMapper 测试类
public class TestObjectMapper { //objectMapper 入门案例 @Test public void test01() 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); } }
//objectMapper 入门案例 @Test public void test02() throws JsonProcessingException {List<ItemDesc> list = new ArrayList<>(); //1. 将汇合转化为 JSON ItemDesc itemDesc = new ItemDesc(); itemDesc.setItemId(100L).setItemDesc("测试数据") .setCreated(new Date()).setUpdated(itemDesc.getCreated()); ItemDesc itemDesc2 = new ItemDesc(); itemDesc2.setItemId(50L).setItemDesc("测试数据 2") .setCreated(new Date()).setUpdated(itemDesc.getCreated()); list.add(itemDesc); list.add(itemDesc2); ObjectMapper objectMapper = new ObjectMapper(); String json = objectMapper.writeValueAsString(list); System.out.println(json); //2. 将 JSON 转化为汇合 List<ItemDesc> desc = objectMapper.readValue(json, list.getClass()); System.out.println(desc); }
iii. 编辑 ObjectMapperUtil 工具 API
public class ObjectMapperUtil {private static final ObjectMapper MAPPER = new ObjectMapper(); private static String toJSON(Object target){ try {return MAPPER.writeValueAsString(target); } catch (JsonProcessingException e) {e.printStackTrace(); // 将查看异样转化为运行时异样 throw new RuntimeException(e); } } private static <T> T toObj(String json,Class<T> clazz){ try {return MAPPER.readValue(json, clazz); } catch (JsonProcessingException e) {e.printStackTrace(); throw new RuntimeException(e); } } }
-
实现商品种类的缓存
i. 编辑 ItemCatController
/* 业务需要: 实现商品分类树形构造的展现 url: http://localhost:8091/item/cat/list 参数: id = 父级节点的 id 返回值: List<EasyUITree> */ @RequestMapping("/list") public List<EasyUITree> showTree(Long id){ // 临时只查问一级菜单信息 long pid = (id==null)?0L:id; // 数据库记录 ItemCat 对象, 页面中要的数据 EasyUITree // 须要将 pojo 对象转换为 vo 对象 get/set... //List<EasyUITree> list = itemCatService.findAllCat(pid); //return list; return itemCatService.findItemCatCache(pid); }
ii. 编辑 ItemCatService/Impl
/* 实现步骤: 1. 先定义 key ITEM_CAT_PARENT::0 2. 先查问缓存 T - 通过获取缓存数据之后, 将 JSON 转化为对象, 之后返回 F - 应该查询数据库, 之后将数据保留到 redis 中, 默认 30 天超时 */ @Autowired(required = false) private Jedis jedis; @Override public List<EasyUITree> findItemCatCache(long pid) {List<EasyUITree> treeList = new ArrayList<>(); //1. 定义 key String key = "ITEM_CAT_PARENT::"+pid; //2. if(jedis.exists(key)){String json = jedis.get(key); System.out.println("== 查问缓存 =="); treeList = ObjectMapperUtil.toObj(json,treeList.getClass()); }else { // 不存在 treeList = findAllCat(pid); String json = ObjectMapperUtil.toJSON(treeList); jedis.setex(key,7*24*60*60, json); System.out.println("== 查数据库 =="); } return treeList; }
-
AOP 实现 Redis 缓存
i. AOP 温习
1) 利用 AOP 能够实现对办法 (性能) 的扩大,实现代码的解耦;
2) 切面的组成因素:
切面 = 切入点表达式 + 告诉办法
3) 切入点表达式
bean(bean 的 id) 拦挡 bean 的所有办法, 是粗粒度的 --> 具体的某个类 within(包名. 类名) 扫描某个包下的某些类中的所有办法 com.jt.* 粗粒度的 execution(返回值类型 包名. 类名. 办法名(参数列表)) 细粒度的 !!! @annotation(包名. 注解名) 细粒度的
4) 告诉办法
before 指标办法执行前 afterReturning 指标办法返回后 afterThrowing 指标办法抛出异样后 after 不论什么状况, 最初都要执行 around 指标办法执行前后都要执行
前四种告诉类型个别用于记录程序运行的状态。如果要对程序运行的轨迹产生影响,首选 around。
ii. AOP 入门案例 – 编写 CacheAop
@Aspect @Component public class CacheAop { // 切面 = 切入点表达式 + 告诉办法 // 表达式 1: ItemCatServiceImpl 类 //@Pointcut("within(com.jt.service.*)") //.* 一级包下的类 ..* 所有子孙后代包中的类 //@Pointcut("execution(* com.jt.service..*.*(**))") @Pointcut("bean(itemCatServiceImpl)") // l 默认值类名首字母小写 public void pointcut(){} @Before("pointcut()") public void before(JoinPoint joinPoint){ //jointPoint 代表连接点的对象 - 程序执行的办法实用于前四大类型 //1. 获取指标对象 Object target = joinPoint.getTarget(); System.out.println(target); //2. 获取指标对象的门路 包名. 类名. 办法名 String className = joinPoint.getSignature().getDeclaringTypeName(); String methodName = joinPoint.getSignature().getName(); System.out.println("指标办法的门路:"+className+methodName); //3. 获取参数类型 System.out.println(Arrays.toString(joinPoint.getArgs())); } @Around("pointcut()") public Object around(ProceedingJoinPoint joinPoint){System.out.println("== 盘绕告诉 =="); Object data = null; try {data = joinPoint.proceed(); } catch (Throwable throwable) {throwable.printStackTrace(); } return data; } }