笔记redis

发布于:2024-08-23 ⋅ 阅读:(60) ⋅ 点赞:(0)

Redis

介绍

Redis(Remote Dictionary Server)是用C语言开发的一个基于内存键值对数据库

  • 所有数据都在内存中,访问速度非常快:读的速度是110000次/s,写的速度是81000次/s
  • 适合存储热点数据(商品、新闻资讯)
  • 存储的value类型比较丰富,也称为结构化NoSQL数据库(非关系型数据库)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

安装

下载

中文网地址:https://www.redis.net.cn/

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

解压

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

目录或文件 作用
redis-benchmark 性能测试工具
redis-check-aof AOF文件修复工具
redis-check-dump RDB文件检查工具(快照持久化文件)
redis-cli 命令行客户端
redis-server 启动redis服务器
redis.windows.conf redis核心配置文件

修改配置

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

启动服务

使用管理员身份运行,注意:必须指定配置文件,窗口启动之后,不能关闭

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

测试联通性

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图形客户端

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

数据结构

键值 <String,5种数据类型>

Redis采用的是键值对存储,键的类型只能为字符串,值支持五种数据类型

  • 字符串(string):普通字符串,Redis中最简单的数据类型
  • 哈希(hash):也叫散列,类似于Java中的HashMap结构
  • 列表(list):按照插入顺序排序,可以有重复元素,类似于Java中的LinkedList
  • 集合(set):无序集合,没有重复元素,类似于Java中的HashSet
  • 有序集合(sorted set/zset):集合中每个元素关联一个分数(score),根据分数升序排序,没有重复元素

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

命令

String 字符串

字符串类型是Redis中最为基础的数据存储类型。

* 增加数据:set key value
* 获取数据:get key
* 删除数据:del key


* 增加数据的时候设置过期时间:setex key 存活时间(单位是s) value
* 增加的时候判断可以是否存在:setnx key value

* 自增(减)操作:incr/decr key      incrby/decrby  key  step

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Hash 哈希

Hash类型极其类似于java中的Map,值里面可以存放一组组的键值对,该类型非常适合于存储对象类型的信息。

* 增加数据:hset key hkey hvalue

* 获取数据(单个):hget key hkey
* 获取数据(所有):hgetall key
* 获取所有hkey:   hkeys key 
* 获取所有hvalue: hvals key 

* 删除数据(单个): hdel key hkey
* 删除数据(所有): del key

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

List 列表

List类型底层是一个双向字符串链表,里面的元素是有序的,可重复的,我们可以从链表的任何一端进行元素的增删。

* 添加数据:lpush(rpush)  key  value
* 查询数据:lrange key  [开始索引  结束索引]
* 列表长度:llen key
* 删除数据:lpop(rpop)  key

* 移出并获取列表的最后一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止:BRPOP key timeout       

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Set 集合

Set类型底层是一张hash表,里面的元素是无序的,不可重复的

* 添加数据:sadd key value
* 查看数据:smembers key
* 获取元素数量:scard key
* 移除集合元素:srem  key value 

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

ZSet 有序集合

ZSet,也称sortedSet,在Set的基础上,加入了有序功能,在添加元素的时候,允许指定一个分数,它会按照这个分数排序。

* 添加数据:zadd key score value
* 查询数据:zrange key [开始索引  结束索引]
* 删除数据:zrem key value

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

通用命令

通用命令指的是不受数据类型限制的一批命令

1. 模糊查询键:keys 模糊匹配规则      

2. 根据键判断记录是否存在:exists key  

3. 根据键判断值类型:type key     

4. 返回key的剩余生存时间:TTL key

5. 选择数据库: select 库索引[从0开始]      

6. 清空当前数据库: flushdb      

7. 清空所有数据库: flushall

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Java操作

Spring对Redis客户端进行了整合,提供了 SpringDataRedis,在SpringBoot项目中还提供了对应的starter,即spring-boot-starter-data-redis

SpringDataRedis是Spring的一部分,对Redis底层开发包进行了高度封装,提供了一个高度封装的类:RedisTemplate,用于操作各种数据类型

  • ValueOperations:简单K-V操作
  • SetOperations: set类型数据操作
  • ZSetOperations: zset类型数据操作
  • HashOperations: hash类型的数据操作
  • ListOperations: list类型的数据操作

环境准备

① 在项目中添加依赖(已完成)

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

② application.yaml中添加配置

spring:
  redis:
    host: localhost
    port: 6379
    password: itheima
    database: 0 # 操作的是0号数据库

③ 提供配置类

package com.sky.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

// Redis配置类
@Configuration
public class RedisConfig extends CachingConfigurerSupport {

    @Bean
    @SuppressWarnings("all")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        //原生RedisTemplate
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(factory);

        //json序列化
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = getJackson2JsonRedisSerializer();

        // -------- 设置key value 序列化方式 --------
        // key采用String的序列化方式
        template.setKeySerializer(RedisSerializer.string());
        // hash的key也采用String的序列化方式
        template.setHashKeySerializer(RedisSerializer.string());
        // value序列化方式采用jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash的value序列化方式采用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }

    private Jackson2JsonRedisSerializer getJackson2JsonRedisSerializer() {
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        //设置ObjectMapper访问权限
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        //记录序列化之后的数据类型,方便反序列化
        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
                ObjectMapper.DefaultTyping.NON_FINAL);

        //LocalDatetime序列化,默认不兼容jdk8日期序列化
        JavaTimeModule timeModule = new JavaTimeModule();
        timeModule.addDeserializer(LocalDate.class,
                new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        timeModule.addDeserializer(LocalDateTime.class,
                new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        timeModule.addSerializer(LocalDate.class,
                new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        timeModule.addSerializer(LocalDateTime.class,
                new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        //关闭默认的日期格式化方式,默认UTC日期格式 yyyy-MM-dd’T’HH:mm:ss.SSS
        om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        om.registerModule(timeModule);

        jackson2JsonRedisSerializer.setObjectMapper(om);
        return jackson2JsonRedisSerializer;
    }
}

④ 提供测试类

package com.sky.test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;

@SpringBootTest
public class SpringDataRedisTest {

    @Autowired
    private RedisTemplate redisTemplate;
       
}

String

    @Test
    public void testString() {
        //0 获取操作简单kv子对象
        ValueOperations ops = redisTemplate.opsForValue();

        //1. 增加
        ops.set("1001", "zhangsan");

        //2. 获取
        System.out.println(ops.get("1001"));

        //3. 创建的时候设置有效时间 setex
        ops.set("1002", "李四", 300, TimeUnit.SECONDS);

        //4. 添加的时候判断是否有值(没有值的时候再添加) setnx
        System.out.println(ops.setIfAbsent("1003", "王五"));//true
        System.out.println(ops.setIfAbsent("1003", "王五"));//false

        //5. 删除 del
        redisTemplate.delete("1003");

        //6. 保存对象
        Dept dept = new Dept(1,"开发部", LocalDateTime.now(),LocalDateTime.now());
        ops.set("dept:1", dept);
        System.out.println(ops.get("dept:1"));
    }

Hash

    //哈希
    @Test
    public void testHash(){
        //获取当前数据类型对应的操作对象
        HashOperations ops = redisTemplate.opsForHash();

        //1. 新增 hset 2001 name zhangsan
        ops.put("2001","name","zhangsan");

        HashMap<Object, Object> map = new HashMap<>();
        map.put("name","李四");
        map.put("age",18);
        map.put("password",123456);
        ops.putAll("2002",map);

        Dept dept = new Dept(1, "开发部", LocalDateTime.now(), LocalDateTime.now());
        ops.putAll("2003", BeanUtil.beanToMap(dept));

        //2. 获取
        //hget 2002 name
        System.out.println(ops.get("2002", "name"));
        //hgetall 2002
        System.out.println(ops.entries("2002"));
        Map m = ops.entries("2003");
        System.out.println(BeanUtil.mapToBean(m, Dept.class, true));

        //hkeys 2002
        System.out.println(ops.keys("2002"));
        //hvals 2002
        System.out.println(ops.values("2002"));

        //3. 删除
        //hdel 2002 name
        ops.delete("2002","name","password");
    }

List

    //操作列表类型数据
    @Test
    public void testList() {
        //0. 获取操作具体类型对象
        ListOperations ops = redisTemplate.opsForList();

        //1. 添加数据
        ops.leftPush("3001", "张三"); //lpush 3001 张三
        ops.leftPushAll("3001", "李四", "王五"); //lpush 3001 李四  王五

        //2. 查询长度
        System.out.println(ops.size("3001"));//llen 3001

        //3. 查询数据
        List list = ops.range("3001", 0, -1);  //lrange 3001 0 -1
        for (Object o : list) {
            System.out.println(o);
        }

        //4. 删除
        ops.rightPop("3001"); //rpop 2001
        redisTemplate.delete("3001");//del 3001
    }

Set

    //操作集合类型数据
    @Test
    public void testSet() {
        //0. 获取操作具体类型对象
        SetOperations ops = redisTemplate.opsForSet();

        //1. 添加数据 
        ops.add("4001", "张三", "李四", "王五"); //sadd 4001 张三 李四 王五

        //2. 查询
        Set members = ops.members("4001");  //smembers 4001
        for (Object member : members) {
            System.out.println(member);
        }

        //3. 长度
        System.out.println(ops.size("4001")); //scard 4001

        //4. 移除
        ops.remove("4001", "张三", "李四"); //srem 4001  张三 李四 
    }

ZSet

    //操作有效集合类型数据
    @Test
    public void testZSet() {
        //0. 获取操作具体类型对象
        ZSetOperations ops = redisTemplate.opsForZSet();

        //1. 添加数据 zadd 5001 10 张三
        ops.add("5001", "张三", 10);
        ops.add("5001", "李四", 30);
        ops.add("5001", "王五", 20);

        //2. 查询 zrange 5001 0 -1
        Set set = ops.range("5001", 0, -1);
        for (Object o : set) {
            System.out.println(o);
        }

        //3. 长度 zcard 5001
        System.out.println(ops.size("5001"));

        //4. 移除 zrem 5001 张三
        ops.remove("5001", "张三", "李四");
    }

通用

    //测试通用
    @Test
    public void testCommon() {
        //键模糊查询 keys  *
        Set keys = redisTemplate.keys("*");
        for (Object key : keys) {
            System.out.println(key);
        }

        //判断键是否存在 exists 5001
        System.out.println(redisTemplate.hasKey("5001"));//true
        System.out.println(redisTemplate.hasKey("6001"));//false

        //判断键类型 type 5001
        System.out.println(redisTemplate.type("5001"));//zset

        //存活时间 ttl 5001
        System.out.println(redisTemplate.getExpire("5001"));// -1
    }

案例

将前面做的项目中的部门信息缓存到redis中

package com.itheima.controller;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.itheima.anno.LogAnno;
import com.itheima.pojo.Dept;
import com.itheima.service.DeptService;
import com.itheima.vo.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
public class DeptController {

    @Autowired
    private DeptService deptService;

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    //查询列表
    @GetMapping("/depts")
    public Result findAll() {
        //线程Redis中查询
        String listJsonFromRedis = redisTemplate.opsForValue().get("DEPT_LIST");
        List<Dept> deptList = null;
        if (StrUtil.isNotEmpty(listJsonFromRedis)){
            deptList = JSONUtil.toList(listJsonFromRedis, D	ept.class);
        }else{
            deptList = deptService.findAll();
            //保存到redis
            redisTemplate.opsForValue().set("DEPT_LIST",JSONUtil.toJsonStr(deptList));
        }
        return Result.success(deptList);
    }

    //新增
    @LogAnno(methodDesc = "新增部门")
    @PostMapping("/depts")
    public Result save(@RequestBody Dept dept) {
        deptService.save(dept);
        
        //清空缓存中的数据列表
        redisTemplate.delete("DEPT_LIST");
        return Result.success();
    }

    //删除
    @LogAnno(methodDesc = "删除部门")
    @DeleteMapping("/depts/{id}")
    public Result deleteById(@PathVariable("id") Integer id) {
        //清空缓存中的数据列表
        redisTemplate.delete("DEPT_LIST");
        
        deptService.deleteById(id);
        return Result.success();
    }
}
  1. redis命令 Java操作 工具提示完成操作
    新增部门")
    @PostMapping(“/depts”)
    public Result save(@RequestBody Dept dept) {
    deptService.save(dept);
    //清空缓存中的数据列表
    redisTemplate.delete("DEPT_LIST");
    return Result.success();
}

//删除
@LogAnno(methodDesc = "删除部门")
@DeleteMapping("/depts/{id}")
public Result deleteById(@PathVariable("id") Integer id) {
    //清空缓存中的数据列表
    redisTemplate.delete("DEPT_LIST");
    
    deptService.deleteById(id);
    return Result.success();
}

}




>1. redis命令 Java操作  工具提示完成操作
>2. 部门数据的缓存