day 11 Redis
此文档是依据上课流程编写,更多细节及图片请参见刘老师的专栏
江哥的专栏
《cgb2008-京淘day11》
一. Redis客户端命令
Hash类型
i. 阐明
能够用散列类型保留"对象"和属性值,个别在工作中存储的都是基于一个业务对象的数据。
user{"name":"aa","age":5}
ii. Hash类型命令
hset user name aahset user age 5hget 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 asadd set1 bscard set1sinter set1 set2sinterstore set3 set1 set2sismember set1 csmembers set1
事务命令
Redis中的事务是弱事务,如果在分布式系统中无奈对事务进行管制。
multiexecdiscard
二. 应用程序操作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
@Testpublic 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
@Testpublic 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. 测试事务
@Testpublic 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测试类
@SpringBootTestpublic 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入门案例@Testpublic 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;@Overridepublic 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@Componentpublic 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; }}