Redis介绍

redis是一个key-value存储系统。它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。
原子性说明: Redis的操作是单进程单线程操作,所以没有线程并发性的安全问题. 采用队列的方式一个一个操作.
Redis常见用法:
1.Redis可以当做缓存使用
2.Redis可以当做数据库使用 验证码
3.Redis可以消息中间件使用 银行转账等

Redis安装

1).解压 Redis安装包
先下载https://redis.io/
下载好压缩包移至LINUX服务器 cd /usr/local/src/
解压

[root@localhost src]# tar -zxvf redis-5.0.4.tar.gz

image.png
2). 安装Redis
说明:在Redis的根目录中执行命令
命令: 1.make

  2.make install
  ![image.png](/img/bVcHiTE)

3). 修改Redis的配置文件
命令1: 展现行号 :set nu
image.png
修改位置1: 注释IP绑定
image.png
修改位置2: 关闭保护模式
image.png
修改位置3: 开启后台启动
image.png

redis 服务器命令

1.启动命令: redis-server redis.conf
2.检索命令: ps -ef | grep redis
3.进入客户端: redis-cli -p 6379
4.关闭redis: kill -9 PID号 | redis-cli -p 6379 shutdown
image.png

Redis入门案例

引入jar包文件

image.png

编辑测试API

package com.jt.test;

import org.junit.jupiter.api.Test;
import redis.clients.jedis.Jedis;

public class TestRedis {

    /**
     * 1.测试redis程序链接是否正常
     * 步骤:
     *      1.实例化jedis工具API对象(host:port)
     *      2.根据实例 操作redis  方法就是命令
     *
     * 关于链接不通的说明:
     *      1.检查Linux防火墙
     *      2.检查Redis配置文件修改项
     *          2.1 IP绑定
     *          2.2 保护模式
     *          2.3 后台启动
     *      3.检查redis启动方式  redis-server redis.conf
     *      4.检查IP 端口 及redis是否启动...
     *
     * */
    @Test
    public void test01(){
        String host = "192.168.126.129";
        int port = 6379;
        Jedis jedis = new Jedis(host,port);
        jedis.set("cgb2006","好好学习 天天向上");
        System.out.println(jedis.get("cgb2006"));

        //2.练习是否存在key
        if(jedis.exists("cgb2006")){
            jedis.del("cgb2006");
        }else{
            jedis.set("cgb2006", "xxxx");
            jedis.expire("cgb2006", 100);
        }


    }
    
 /* 2.需求:
  *   1.向Redis中插入数据
  *   2.为key设定超时时间
  *   3.线程sleep 3秒
  *   4.获取key的剩余时间
  */
 @Test
public void test02() throws InterruptedException {
 Jedis jedis = new Jedis("192.168.126.129",6379);
 /*
 * 如果使用Redis 并且需要添加超时时间时 一般需要满足原子性要求
 * 要么同时成功 要么同时失败
 */
 jedis.setex("宝可梦",60,"小火龙");
 Thread.sleep(3000);
 System.out.println(jedis.ttl("宝可梦"));
 /* jedis.set("宝可梦","小火龙");
 int a=1/0;//出现异常 程序不会往下执行
 jedis.expire("宝可梦",60);
 Thread.sleep(3000); System.out.println(jedis.ttl("宝可梦"));*/
 
}

@Test
public void test03() throws InterruptedException {
 Jedis jedis = new Jedis("192.168.126.129", 6379);
 jedis.setnx("a","ab");//a存在不做修改 不存在则新建
 /*if (jedis.exists("a")) {
 System.out.println("key已经存在");
 } else { jedis.set("a", "ac"); }*/ System.out.println(jedis.get("a"));
 
}

@Test
public void test04() throws InterruptedException {
 Jedis jedis = new Jedis("192.168.126.129", 6379);
 SetParams setParams=new SetParams();
 setParams.nx().ex(10);//加锁操作 并设置超时时间 秒
 String result=jedis.set("b","方法四",setParams);
 System.out.println(result);//加锁成功会返回OK 反                             //之null
 System.out.println(jedis.get("b"));
 Thread.sleep(3000);
 System.out.println(jedis.ttl("b"));
 jedis.del("b");//解锁操作
 
}

@Test
public void test05() throws InterruptedException {
 Jedis jedis = new Jedis("192.168.126.129", 6379);
 //开启事务
 Transaction transaction=jedis.multi();
 try{
 transaction.set("aa","aa");
 //提交事务
 transaction.exec();
 }catch (Exception e){
 e.printStackTrace();
 //回滚事务
 transaction.discard();
 }

}

秒杀业务逻辑

image.png

SpringBoot整合Redis

编写配置文件

@Configuration
@PropertySource("classpath:/properties/redis.properties")
public class JedisConfig {

 @Value("${redis.host}")
 private String host;
 @Value("${redis.port}")
 private Integer port;
 
 @Bean
 public Jedis jedis(){
 return new Jedis(host,port);
 }
}

image.png

对象与JSON转化

ObjectMapper介绍

入门案例

public class TestObjectMapper {
 @Test
 public void test01() throws JsonProcessingException {
 ObjectMapper objectMapper = new ObjectMapper();
 ItemDesc itemDesc = new ItemDesc();
 itemDesc.setItemId(100L).setItemDesc("商品详情");
 //对象转化为json
 String json=objectMapper.writeValueAsString(itemDesc);
 System.out.println(json);
 //json转化为对象
 ItemDesc itemDesc1=objectMapper.readValue(json,ItemDesc.class);
 System.out.println(itemDesc1.getItemDesc());
 }
}

编辑对象API

public class ObjectMapperUtil {
 private static final ObjectMapper mapper=new ObjectMapper();
 //1.将用户传递的数据转化为json
 public static String toJson(Object object){
 if(object==null) {
 throw  new RuntimeException("传递的数据为null 请检查");
 }
 try {
 String json = mapper.writeValueAsString(object);
 return json ;
 } catch (JsonProcessingException e) {
 e.printStackTrace();
 throw new RuntimeException(e);
 }
 }
 
 //要求用户传递什么样的类型,就返回什么样的对象
public static <T> T toObject(String json,Class<T> targer){
 if (StringUtils.isEmpty(json)||targer==null) {
 throw new RuntimeException("参数不能为空");
 }
 try {
 return mapper.readValue(json, targer);
 } catch (JsonProcessingException e) {
 e.printStackTrace();
 throw new RuntimeException(e);
 }
}
 }

商品的缓存实现

@Override
public List<EasyUiTree> findAll(long parentId) {
 QueryWrapper<ItemCat>queryWrapper=new QueryWrapper<>();
 queryWrapper.eq("parent_id",parentId);
 //1.查询数据库一级的记录
 List<ItemCat> itemCats = itemCatMapper.selectList(queryWrapper);
 //需要把List<ItemCat> 集合转成 List<EasyUiTree>
 List<EasyUiTree> tree=new ArrayList<>();
 //1.遍历List<ItemCat> 集合
 for (ItemCat it:itemCats
         ) {
 long id = it.getId();
 String name = it.getName();
 String state=it.getIsParent()?"closed":"open";
 //含参构造
 EasyUiTree easyUiTree=new EasyUiTree(id,name,state);
 //转换
 tree.add(easyUiTree);
 }
 return tree;
 }
@Override
public List<EasyUiTree> findAllCache(Long parentId) {
 List<EasyUiTree> treeList =new ArrayList<>();
 //定义key
 String key="ITEM_CAT_PARENTID::"+parentId;
 //检索是否包含key
 if(jedis.exists(key)){
 //数据存在
 String json = jedis.get(key);
 //串转化为对象
 treeList = ObjectMapperUtil.toObject(json, treeList.getClass());
 }else {
 //数据不存在 查询数据库
 treeList=findAll(parentId);
 //将数据保存到缓存中
 String json = ObjectMapperUtil.toJson(treeList);
 jedis.set(key,json);
 }
 return treeList;
}

AOP实现Redis缓存

理解AOP

名称:面向切面编程
作用:降低系统中代码的耦合性,并且在不改变原有代码的条件下对原有的方法进行功能的扩展
公式:AOP=切入点表达式+通知方法

通知类型

1.前置通知 目标方法执行之前执行
2.后置通知 目标方法执行之后执行
3.异常通知 目标方法执行过程中抛出异常时执行
4.最终通知 无论什么时候都要执行的通知
特点: 上述的四大通知类型 不能干预目标方法是否执行.一般用来做程序运行状态的记录.监控

5.环绕通知 在目标方法执行前后都要执行的通知方法 该方法可以控制目标方法是否运行.joinPoint.proceed(); 功能最为强大的.

切入点表达式

理解: 切入点表达式就是一个程序是否进入通知的一个判断(IF)
作用: 当程序运行过程中 ,满足了切入点表达式时才会去执行通知方法,实现业务的扩展.
种类(写法):
1). bean(bean的名称 bean的ID) 只能拦截具体的某个bean对象 只能匹配一个对象
lg: bean(“itemServiceImpl”)

2). within(包名.类名) within(“com.jt.service.*”) 可以匹配多个对象
粗粒度的匹配原则 按类匹配

3). execution(返回值类型 包名.类名.方法名(参数列表)) 最为强大的用法
lg : execution( com.jt.service...*(..))
返回值类型任意 com.jt.service包下的所有的类的所有的方法都会被拦截.

4). @annotation(包名.注解名称) 按照注解匹配.


Silver
13 声望11 粉丝