Redis缓存优化项目

Redis数据库基础

Redis数据库概述

Redis是一个基于内存的key-value结构数据库,即非关系型数据库

(NoSql)数据库并不是要取代关系型数据库,而是关系型数据库的一种补充。

Redis是互联网技术领域使用最为广泛的存储中间件,他是用C语言开发的一个开源的高性能键值对(key-value)数据库,官方提供的数据可以达到100000+的QPS(每秒内查询次数)

主要特点

  • 基于内存存储,读写性能高
  • 适合存储热点数据
  • 企业应用广泛

Redis下载与开启服务

下载

Redis的Windows是绿色软件,在官网下载之后,之间解压即可使用

注意其中的一些目录结构

redis.windows.conf Redis配置文件

redis-cli.exe Redis客户端

redis-server.exe Redis服务器

开启服务

Window版Redis启动命令

cmd命令窗口启动命令

redis-server.exe  redis.windows.conf

也可以使用脚本命令

start cmd /k "redis-server.exe redis.windows.conf"

建立bat后缀文件放入文件位置,即可开启服务

Redis文件配置

在redis.windows.conf文件为redis的核心配置文件,可根据项目情况设置不同的参数

  • 设置Redis服务密码,添加内容为 requirepass 123456
  • 设置密码为123456.

客户端图形工具

我们通常使用Redis客户端图形工具,因为默认提供的客户端连接工具界面不太友好,同时操作也比较麻烦

我在项目中使用的是Another Redis Desktop Manager

Redis数据类型和命令

Redis数据类型

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

Redis在Java中操作

我们在Java中操作Redis

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

Spring Data Redis

Spring Data Redis是Spring中的一部分,提供了在Spring应用中,通过简单的配置就可以访问Redis服务,对Redis底层开发包进行了高度封装。

可以使用Spring Data Redis来简化Redis操作

Spring Boot中提供了对应的Starter,maven坐标:

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

在Spring Data Redis中提供了一个高度封装的类,RedisTemplate,对相应的ap进行了封装,将同一类型的操作封装成operation接口,具体分类如下:

  • ValueOperations: string数据操作
  • SetOperations: set类型数据操作
  • ZSetOperations: zet 类型数据操作
  • HashOperations: hash类型的数据操作
  • ListOperations: list类型的数据操作

在application.yml文件中,配置redis连接

spring:
  redis:
    host: localhost
    port: 6379
    password: 123456

在以上环境搭建之后,springboot框架会自动装配RedisTemplate,我们就可以在任意的位置使用这个对象

测试

下面我们来简单的新建一个测试类,并集成springboot,并且调用RedisTemplate

package com.itheima.test;

import com.itheima.CacheDemoApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.junit4.SpringRunner;

@SpringBootTest(classes = CacheDemoApplication.class)
@RunWith(SpringRunner.class)
public class RedisTest {

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @Test
    public void test(){
        System.out.println(redisTemplate);
    }
    
}

在上面测试案例中,我们直接注入了RedisTemplate,并且指定了泛型为String

  • 如果不指定泛型也可以进行操作,但是存储对象的时候需要进行序列化
  • 一般项目中存储对象都会转化为json字符串,在进行存储,所以一般会选择使用泛型为String ,避免大量的序列化操作

操作字符串数据类型

/**
 * 操作字符串类型的数据
 */
@Test
public void testString(){
    // set get setex setnx
    redisTemplate.opsForValue().set("name","小明");
    
    String name = redisTemplate.opsForValue().get("name");
    
    System.out.println(name);
    
    redisTemplate.opsForValue().set("code","1234",3, TimeUnit.MINUTES);
    redisTemplate.opsForValue().setIfAbsent("lock","1");
    redisTemplate.opsForValue().setIfAbsent("lock","2");
}

操作哈希数据类型

/**
 * 操作哈希类型的数据
 */
@Test
public void testHash(){
    //hset hget hdel hkeys hvals
    redisTemplate.opsForHash().put("heima","name","tom");
    redisTemplate.opsForHash().put("heima","age","20");
    String name = (String) redisTemplate.opsForHash().get("heima", "name");
    System.out.println(name);

    Set keys = redisTemplate.opsForHash().keys("heima");
    System.out.println(keys);

    List values = redisTemplate.opsForHash().values("heima");
    System.out.println(values);

    redisTemplate.opsForHash().delete("heima","age");
}

操作列表数据类型

/**
 * 操作列表类型的数据
 */
@Test
public void testList(){
    //lpush lrange rpop llen
    redisTemplate.opsForList().leftPushAll("mylist","a","b","c");
    redisTemplate.opsForList().leftPush("mylist","d");

    List mylist = redisTemplate.opsForList().range("mylist", 0, -1);
    System.out.println(mylist);

    redisTemplate.opsForList().rightPop("mylist");

    Long size = redisTemplate.opsForList().size("mylist");
    System.out.println(size);
}

操作集合类型数据

/**
 * 操作集合类型的数据
 */
@Test
public void testSet(){
    //sadd smembers scard sinter sunion srem
    redisTemplate.opsForSet().add("set1","a","b","c","d");
    redisTemplate.opsForSet().add("set2","a","b","x","y");

    Set members = redisTemplate.opsForSet().members("set1");
    System.out.println(members);

    Long size = redisTemplate.opsForSet().size("set1");
    System.out.println(size);

    Set intersect = redisTemplate.opsForSet().intersect("set1", "set2");
    System.out.println(intersect);

    Set union = redisTemplate.opsForSet().union("set1", "set2");
    System.out.println(union);

    redisTemplate.opsForSet().remove("set1","a","b");
}

操作有序集合类型数据

/**
 * 操作有序集合类型的数据
 */
@Test
public void testZset(){
    //zadd zrange zincrby zrem
    redisTemplate.opsForZSet().add("zset1","a",10);
    redisTemplate.opsForZSet().add("zset1","b",12);
    redisTemplate.opsForZSet().add("zset1","c",9);

    Set zset1 = redisTemplate.opsForZSet().range("zset1", 0, -1);
    System.out.println(zset1);

    redisTemplate.opsForZSet().incrementScore("zset1","c",10);

    redisTemplate.opsForZSet().remove("zset1","a","b");
}

缓存框架--Spring Cache

在项目中,我们一般统一管理缓存的框架来实现缓存优化,主要原因是耦合度更低,切换缓存的成本较低

Spring Cache是一个框架,实现了基于注解的缓存功能,只需要简单的加一个注解,就能实现缓存功能

导入Redis和SpringCache依赖

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

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

添加配置支持

application.yml文件中添加redis的支持

spring:
  redis:
    host: 127.0.0.1
    port: 6379
    password: 123456

在SpringCache中提供了很多缓存操作的注解

注解 说明
@EnableCaching 开启缓存注解功能吗,通常加在启动类上
@Cacheable 在方法执行前先查询缓存中是否有数据,如果有数据,则直接返回缓存数据,如果没有缓存数据,调用方法并将方法返回值放到缓存中
@CachePut 将方法的返回值放到缓存中
@CacheEvict 将一条或多条数据从缓存中删除
@Caching 缓存的结合体,可以组合以上注解在一个方法中使用,比如有新增有删除

在方法执行前,spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;若没有数据,调用方法并将方法返回值放到缓存中,查询的时候使用

@Cacheable(cacheNames = "userCache",key="#id")
public User getById(Long id){
    User user = userMapper.getById(id);
    if(user == null){
        throw new RuntimeException("用户不存在");
    }
    return user;
}

多条件查询使用Cacheable,比如查询的方法参数有多个,我们可以使用hashcode作为缓存的key,

@Cacheable(value = "userCache",key="#userDto.hashCode()",unless = "#result.size() == 0")
public List<User> getList(UserDto userDto){
    List<User> list = userMapper.getList("%" + userDto.getName() + "%", userDto.getAge());
    return list;
}

需要提供一个hashCode方法

@Data
public class UserDto {

    private String name;
    private int age;

    @Override
    public int hashCode() {
        int result = Objects.hash(getName(),getAge());
        return result;
    }
}

hashcode的特点是,只要参数相同,则生成后的hashcode值肯定相同

如果返回结果为空,则不缓存unless="#result == null"或unless="#result.size()==0"

@CachePut

作用: 将方法返回值,放入缓存,一般保存的时候使用该注解

@CachePut(value = "userCache", key = "#user.id")//key的生成:userCache::1
public User insert(User user){
    userMapper.insert(user);
    return user;
}
  • #user.id : #user指的是方法形参的名称, id指的是user的id属性 , 也就是使用user的id属性作为key

@CacheEvict

作用: 清理指定缓存

@CacheEvict(cacheNames = "userCache",key = "#id")//删除某个key对应的缓存数据
public void deleteById(Long id){
    userMapper.deleteById(id);
}

@CacheEvict(cacheNames = "userCache",allEntries = true)//删除userCache下所有的缓存数据
public void deleteAll(){
    userMapper.deleteAll();
}

@Caching

作用: 组装其他缓存注解

  • cacheable 组装一个或多个@Cacheable注解
  • put 组装一个或多个@CachePut注解
  • evict 组装一个或多个@CacheEvict注解
@Caching(
         cacheable = {
                 @Cacheable(value = "userCache",key = "#id")
         },
         put = {
                 @CachePut(value = "userCache",key = "#result.name"),
                 @CachePut(value = "userCache",key = "#result.age")
         }
 )
 public User insert(User user){
     userMapper.insert(user);
     return user;
 }
```****
posted @ 2024-01-25 20:12  奕帆卷卷  阅读(58)  评论(0)    收藏  举报