Redis基于内存的key-value结构化NOSQL(非关系型)数据库

Redis介绍

Redis基于内存的key-value结构的NOSQL(非关系型)数据库

非关系型数据库:表与表之间没有复杂的关系

Redis的优点

  • 基于内存存储,读写性能高 – Redis读的速度是110000次/S
  • 适合存储热点数据(商品、新闻资讯)
  • 它存储的value类型比较丰富,也称为结构化NoSQL数据库

Redis的缺点

在这里插入图片描述

Redis的安装

直接解压windows版压缩包就可以

启动:
①双击启动 无密码的形式

redis-server.exe

②cmd命令行启动

redis-server.exe  redis.windows.conf

前提是修改了配置文件

在配置文件中搜索 ctrl + f

foobared 

将注释 # 去掉 , 将 foobared 替换为 你的密码即可

Redis的连接

①双击启动 无密码的形式

redis-cli.exe

②cmd命令行启动

redis-cli.exe -a 你的密码

或远程连接

redis-cli.exe -h 192.168.166.60 -p 6379 -a 你的密码

验证登录

使用 ping 返回 pang 为成功

192.168.166.60:6379> ping
PONG

Redis的使用

更多命令可以参考Redis中文网:https://www.redis.net.cn

Redis中的数据类型

key 数据类型为 String
Value的数据类型有5种
① String *常用
② Hash *常用
③ List 有序
④ Set 无序
⑤ ZSet 有序

String的使用

get set
192.168.166.60:6379> set name zs
OK
192.168.166.60:6379> get name
"zs"
192.168.166.60:6379>

重复set会覆盖

setex(expire)ttl
192.168.166.60:6379> setex age 10 18
OK
192.168.166.60:6379> get age
"18"
192.168.166.60:6379> ttl age
(integer) -2
192.168.166.60:6379> setex age 10 18   //10 秒过期
OK
192.168.166.60:6379> ttl age
(integer) 8
192.168.166.60:6379> ttl age
(integer) 6
192.168.166.60:6379> get age
"18"
192.168.166.60:6379> ttl age
(integer) -2
192.168.166.60:6379> get age
(nil)
192.168.166.60:6379>

过期是-2
永久是-1
正数是倒计时过期时间

setnx(not exit)

分布式锁(应用场景)

不能覆盖value

192.168.166.60:6379> setnx sex female
(integer) 1
192.168.166.60:6379> setnx sex male
(integer) 0
192.168.166.60:6379> get sex
"female"
192.168.166.60:6379>

Hash

Hash类型极其类似于java中的Map,值里面可以存放一组组的键值对

该类型非常适合于存储java中对象的信息

192.168.166.60:6379> hset user1 name zs
(integer) 1
192.168.166.60:6379> hset user1 age 18
(integer) 1
192.168.166.60:6379> hgetall user1
1) "name"
2) "zs"
3) "age"
4) "18"
192.168.166.60:6379> hkeys user1
1) "name"
2) "age"
192.168.166.60:6379> hvals user1
1) "zs"
2) "18"
192.168.166.60:6379> hdel user1 name [可以删除多个字段 空格 隔开]
(integer) 1
192.168.166.60:6379> hkeys user1
1) "age"
192.168.166.60:6379> hvals user1
1) "18"

List列表(队列)

List类型底层是一个双向字符串链表。里面的元素是有序的,可重复的

我们可以从链表的任何一端进行元素的增删

* 新增
	左压入
		lpush key value1 value2 ...
	右压入
		rpush key value1 value2 ...
		
* 查询元素(闭合区间)
		lrange key  开始索引  结束索引
		
* 列表长度
		llen key
* 删除元素
	左弹出
		lpop key
	右弹出
		rpop key

Set集合

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

* 新增
		sadd key value1 value2 ...
		
* 查询元素
		smembers key
		
* 查询集合数量
		scard key
		
* 删除元素	
		srem key value1 value2 ...
* 求集合的交集:
	sinter key1 key2
		
* 求集合的并集:
		sunion key1 key2	

* 求集合的差集:
		sdiff key1 key2

ZSet集合

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

* 新增
		zadd key score1 value1 score2 value2 ...
		
* 查询
	升序
		zrange key start end [withscores] 
	降序	
		zrevrange key start end [withscores] 
		
* 加分
		zincrby key increment member 

* 删除
		zrem key value

Redis 通用命令

192.168.166.60:6379> exists sex
(integer) 1
192.168.166.60:6379> get sex
"female"
192.168.166.60:6379> keys *
1) "sex"
2) "name"
3) "user1"
192.168.166.60:6379> type sex
string
192.168.166.60:6379> type name
string
192.168.166.60:6379> type user1
hash
192.168.166.60:6379> select 1
OK
192.168.166.60:6379[1]> select 0
OK
192.168.166.60:6379> select 1
OK
192.168.166.60:6379[1]> type name
none
192.168.166.60:6379[1]> keys *
(empty list or set)
192.168.166.60:6379[1]> select 0
OK
192.168.166.60:6379> keys *
1) "sex"
2) "name"
3) "user1"
192.168.166.60:6379>

Redis图形客户端

在我的资源中有Redis-DeskTop软件,解压安装即可使用

Redis在Java中的使用

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

RedisTemplate

  • ValueOperations:简单的键值对数据操作
  • SetOperations:set类型数据操作
  • ZSetOperations:zset类型数据操作
  • HashOperations:hash类型的数据操作
  • ListOperations:list类型的数据操作

导入坐标

在sky-server模块导入spring data redis依赖

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

添加redis配置

application-dev.yml

sky:
  redis:
    host: localhost
    port: 6379
    password: itheima

application.yml

spring:
  redis:
    host: ${
    
    sky.redis.host}
    port: ${
    
    sky.redis.port}
    password: ${
    
    sky.redis.password}

RedisTemplate测试类


@SpringBootTest 
public class RedisTest {
    
    

    @Autowired
    private RedisTemplate redisTemplate;



    // 获取五种操作类型的对象
    @Test
    public void test()throws Exception{
    
    
        // 1.字符串类型
        ValueOperations valueOperations = redisTemplate.opsForValue();
        // 2.哈希类型
        HashOperations hashOperations = redisTemplate.opsForHash();
        // 3.列表类型
        ListOperations listOperations = redisTemplate.opsForList();
        // 4.集合类型
        SetOperations setOperations = redisTemplate.opsForSet();
        // 5.有序集合类型
        ZSetOperations zSetOperations = redisTemplate.opsForZSet();
    }
}

API

操作字符串类型数据

// 字符串类型
@Test
public void test02()throws Exception{
    
    
    // 1.字符串操作对象
    ValueOperations valueOperations = redisTemplate.opsForValue();

    // 存
    // valueOperations.set("mystr","i am a string");
    // 取
    // System.out.println(valueOperations.get("mystr"));

    // 存短信验证码,5分钟有效时间
    // valueOperations.set("sms:13500135001","223355", Duration.ofSeconds(300));


    //存一个java对象(要求实现序列化接口)
    Category category = Category.builder()
        .name("烤鱼套餐")
        .status(1)
        .createTime(LocalDateTime.now())
        .build();


    // valueOperations.set("category",category);

    // 查询java对象
    System.out.println(valueOperations.get("category"));
}

操作哈希类型数据

// 哈希类型
@Test
public void test03()throws Exception{
    
    
    // 2.哈希操作对象
    HashOperations hashOperations = redisTemplate.opsForHash();

    // 存
    hashOperations.put("user:1","name","饭岛爱");
    hashOperations.put("user:1","age",18);

    // 取一个
    System.out.println(hashOperations.get("user:1", "age"));

    System.out.println("--------------------------");
    // 取出所有元素
    Set hkeys = hashOperations.keys("user:1");
    hkeys.forEach(hkey->{
    
    
        System.out.println(hashOperations.get("user:1", hkey));
    });

    // 删除
    hashOperations.delete("user:1","age");
}

操作列表类型数据

// 列表类型
@Test
public void test04()throws Exception{
    
    
    // 3.列表操作对象
    ListOperations listOperations = redisTemplate.opsForList();
    // 左压入
    listOperations.leftPushAll("mylist","a","b","c");

    // 查询    c b a
    List mylist = listOperations.range("mylist", 0, -1);
    System.out.println(mylist);

    // 右弹出
    System.out.println(listOperations.rightPop("mylist"));


    // 查询  c b
    List mylist1 = listOperations.range("mylist", 0, -1);
    System.out.println(mylist1);
}

操作集合类型数据

// 集合类型
@Test
public void test05()throws Exception{
    
    
    // 4.集合操作对象
    SetOperations setOperations = redisTemplate.opsForSet();

    // 存 
    // setOperations.add("tom","唱","跳","rap","打篮球");
    // 存 
    // setOperations.add("jack","爱坤","唱","rap");


    // 查询
    Set set = setOperations.members("jack");
    System.out.println(set);


    // 交集
    System.out.println(setOperations.intersect("tom", "jack"));

    // 并集
    System.out.println(setOperations.union("tom", "jack"));

    // 删除
    setOperations.remove("tom","rap");
}

操作有序集合类型数据

// 有序集合类型
@Test
public void test06()throws Exception{
    
    
    // 5.有序集合操作对象
    ZSetOperations zSetOperations = redisTemplate.opsForZSet();

    // 存
    // zSetOperations.add("java","tom",66.4);
    // zSetOperations.add("java","jack",45.2);
    // zSetOperations.add("java","飞机",23.2);


    // 苦练安其拉
    zSetOperations.incrementScore("java","tom",30);

    // 查询 降序
    System.out.println(zSetOperations.reverseRange("java", 0, -1));

}

通用操作

// 通用操作
@Test
public void test07()throws Exception{
    
    
    // 查询  keys c*
    Set keys = redisTemplate.keys("c*");
    System.out.println(keys);

    // 批量删除
    redisTemplate.delete(keys);

    // 判断key是否存在
    System.out.println(redisTemplate.hasKey("tom"));
}

统一处理时间格式配置类

主要是为了方便使用图像化客户端进行对redis中存储数据的查看和对日期格式的处理(不是必须的)

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.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

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;
    }
}

在项目中的应用

ShopController设置状态

@RestController
@RequestMapping("/admin/shop")
public class ShopController {
    
    

    @Autowired
    private RedisTemplate redisTemplate;

    // 设置店铺状态
    @PutMapping("/{status}")
    public Result setStatus(@PathVariable("status") Integer status) {
    
    
        // 调用redis设置
        ValueOperations valueOperations = redisTemplate.opsForValue();
        valueOperations.set("SHOP_STATUS", status);
        return Result.success();
    }
}

用户端查询营业状态

// 店铺管理
@RestController
@RequestMapping("/user/shop")
public class AppShopController {
    
    

    @Autowired
    private RedisTemplate redisTemplate;


    // 查询店铺状态
    @GetMapping("/status")
    public Result getShopStatus(){
    
    
        Integer status = (Integer) redisTemplate.opsForValue().get("SHOP_STATUS");
        return Result.success(status);
    }
}

管理端查询营业状态

// 获取店铺的营业状态
@GetMapping("/status")
public Result getStatus(){
    
    
    Integer status = (Integer) redisTemplate.opsForValue().get("SHOP_STATUS");
    return Result.success(status);
}

菜品缓存

在这里插入图片描述
读取数据的时候:

  • 先判断缓存中有无数据,
    • 若有直接返回
    • 若无,再查询mysql,将数据放入redis中一份,最后返回

写数据的时候:

  • 保证redis和mysql中数据一致.一旦有写操作的时候,删除缓存中数据

redis数据存储设计

首先明确在redis中存储的数据格式,必须确定的是key部分要包含查询条件

redis缓存的key(DISH:分类的id) redis缓存的value
DISH:1 List<DishVo>的json

缓存菜品数据

@RestController
@RequestMapping("/user/dish")
@Slf4j
public class AppDishController {
    
    

    @Autowired
    private DishService dishService;

    @Autowired
    private RedisTemplate redisTemplate;

    // 根据分类id查询菜品列表(小程序段)
    @GetMapping("/list")
    public Result getList(@RequestParam Long categoryId) {
    
    
        List<DishVO> voList = null;
        // ---------------------start
        // 先查询redis中是否有该分类的菜品缓存
        String dishKey = "DISH:" + categoryId;
        if (redisTemplate.hasKey(dishKey)) {
    
    
            log.info("查询缓存...");
            voList = (List<DishVO>) redisTemplate.opsForValue().get(dishKey);
            return Result.success(voList);
        }
        // ---------------------end
        // 封装dto条件
        DishPageDTO dishPageDTO = DishPageDTO.builder()
                .categoryId(categoryId)
                .status(1)
                .build();

        // 调用service查询,返回结果
        log.info("查询数据库...");
        voList = dishService.getParamList(dishPageDTO);
        // --------------------------  start
        // 将数据库数据同步到缓存
        redisTemplate.opsForValue().set(dishKey, voList);
        // --------------------------  end

        return Result.success(voList);
    }
}

缓存一致性(了解什么是延迟双删)

@Autowired
private RedisTemplate redisTemplate;

// 菜品新增
@PostMapping
public Result saveDishWithFlavor(@RequestBody DishDTO dishDTO) {
    
    
    dishService.saveDishWithFlavor(dishDTO);

    // 清理缓存
    redisTemplate.delete("DISH:"+dishDTO.getCategoryId());


    return Result.success();
}


// 修改菜品
@PutMapping
public Result updateById(@RequestBody DishDTO dishDTO) {
    
    
    dishService.updateById(dishDTO);

    // 清理缓存
    Set dishKey = redisTemplate.keys("DISH*");
    redisTemplate.delete(dishKey);

    return Result.success();
}


// 删除菜品
@DeleteMapping
public Result deleteBatch(@RequestParam List<Long> ids) {
    
    
    dishService.deleteBatch(ids);

    // 清理缓存
    Set dishKey = redisTemplate.keys("DISH*");
    redisTemplate.delete(dishKey);


    return Result.success();
}


// 启用禁用
@PostMapping("/status/{status}")
public Result startOrStop(
    @PathVariable("status") Integer status,
    @RequestParam Long id) {
    
    
    dishService.startOrStop(status, id);

    // 清理缓存
    Set dishKey = redisTemplate.keys("DISH*");
    redisTemplate.delete(dishKey);

    return Result.success();
}

猜你喜欢

转载自blog.csdn.net/PY_XAT_SFZL/article/details/132269631