2021-06-29

学习目标:

1.java连接redis。
2.springboot整合redis。
3.springboot使用redis作为缓存。
4.redis常见的面试题。


1.java连接redis。

1.1 引入redis的依赖------>jedis

<dependencies>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.6.0</version>
        </dependency>
    </dependencies>

1.2 java代码操作redis

package com.test;

import redis.clients.jedis.Jedis;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class TestRedis {
    public static void main(String[] args) {
        //1.获取连接对象
        Jedis jedis=new Jedis("192.168.223.128",6379); //默认连接的为localhost  端口号为6379
        //2.操作redis数据库 -----String类型
        jedis.set("k1","v1");
        jedis.set("k2","2");
        jedis.setnx("k1","v3");
        jedis.mset("k3","v3","k4","v4");
        List<String> mget = jedis.mget("k1", "k2");
        System.out.println(mget);
        jedis.flushAll();

        //3.操作hash类型
        Map<String,String> map=new HashMap<>();
        map.put("name","闫克起");
        map.put("age","17");
        map.put("sex","男");
        jedis.hset("k1",map);

        Map<String, String> k1 = jedis.hgetAll("k1");
        System.out.println(k1);

        Set<String> keys = jedis.keys("*");
        System.out.println(keys);
    }
}

2.springboot整合redis。

2.1 依赖---->启动依赖---->自动装配---->自动读取配置

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

2.2 代码测试--------StringRedistemplate

@Autowired
    private StringRedisTemplate stringRedisTemplate;//存放的数据格式为string类型
    @Test
    void contextLoads() {
//        //使用ValueOperations -----操作的为字符串类型
//        ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
//        ops.set("k1","springboot-v1");
//        System.out.println(ops.get("k1"));
//        Boolean aBoolean = ops.setIfAbsent("k2", "王沛他很听话");//setnx
//        System.out.println(aBoolean);
//        Map<String,String> map=new HashMap<>();
//        map.put("name","张三");
//        map.put("age","18");
//        map.put("name","李四");
//        ops.multiSet(map);
//        System.out.println(stringRedisTemplate.keys("*"));
//        //操作hash类型 HashOperations<String, Object, Object>
        HashOperations<String, Object, Object> hash = stringRedisTemplate.opsForHash();
        hash.put("k1","name","赵露露");
        hash.put("k1","age","18");
        Object o = hash.get("k1", "name");
        System.out.println(hash.keys("k1"));
        System.out.println(hash.values("k1"));
        System.out.println(o);
        System.out.println(hash.entries("k1"));
    }

2.3 代码测试----------RedisTemplate

它的key和value可以存放Object类型,那么它的key和value默认采用的序列化方式为JDK序列化方式。一般我们需要为key和value指定序列化方式。

        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new StringRedisSerializer());

@Autowired
    private RedisTemplate redisTemplate;

    @Test
    void contextLoadsHash() {
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new StringRedisSerializer());
        HashOperations hash = redisTemplate.opsForHash();
        hash.put("u","n","崔梦");
        hash.put("u","a","25");
    }


    @Test
    void contextLoads() {
        //因为RedisTemplate默认会为key和value进行序列  按照JDK的序列化方式进行的,
        //再实际开发过程中不用默认的序列化方式。空间少  而且不用为实体类实现序列化接口
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        ValueOperations valueOperations = redisTemplate.opsForValue();
        //可以指定key的序列化方式
        valueOperations.set("k4",new User("王五",25,"郑州"));
        Object k4 = valueOperations.get("k4");
        System.out.println(k4);
//        Object k = valueOperations.get("k");
//        System.out.println(k);

    }

3. 连接集群

哨兵

2021-06-29

 集群:

4. springboot使用redis作为缓存。

作用: 为了减少对数据库的访问频率。从而提高项目的性能。

 2021-06-29

 什么样的数据适合放入缓存中。

1. 查询频率高
2. 修改频率低
3. 数据安全行要求低的

package com.yyh.redis1.sercice;

import com.yyh.redis1.entity.User;
import com.yyh.redis1.mapper.UserMapper;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

@Service
public class UserService2 {
    @Resource
    private UserMapper userMapper;
    @Resource
    private RedisTemplate redisTemplate;

    public User selectId(Long id){
        //1.序列化
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        //2.存放key value
        ValueOperations valueOperations = redisTemplate.opsForValue();
        //3.先获取redis缓存里数据
        Object o=valueOperations.get("user::selectid::"+id);
        //4.如果存在,把缓存里的数据返回
        if(o!=null){
            return (User) o;
        }
        //5.否则查询数据库 并把查询结果放入redis缓存里 然后把查询结果返回
        User user= userMapper.selectById(id);
        if(user!=null){
            valueOperations.set("user::selectid::"+id,user,30, TimeUnit.MINUTES);

        }
        return user;
    }
    public void delete(Long id){
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        ValueOperations valueOperations = redisTemplate.opsForValue();
        //1.先删除redis里的数据
        redisTemplate.delete("user::findById::"+id);
        //2.再删除数据库里的数据
        userMapper.deleteById(id);
    }
    public void update(User user){
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        ValueOperations valueOperations = redisTemplate.opsForValue();
        //1.先修改数据库里的数据
        userMapper.updateById(user);
        //2.再修改缓存里的数据
        valueOperations.set("user::findById::"+user.getId(),user);
    }
    public void insert(User user){
        userMapper.insert(user);
    }
}

缓存注解: 我只需要再相应方法上加入相应的缓存注解则可以完成缓存的功能

1.加入缓存的配置类

package com.yyh.redis1.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
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.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;

@Configuration
public class MyConfig {
    //比如验证码
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setConnectionFactory(factory);
        //key序列化方式
        template.setKeySerializer(redisSerializer);
        //value序列化
        template.setValueSerializer(jackson2JsonRedisSerializer);
        //value hashmap序列化
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        return template;
    }

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        //解决查询缓存转换异常的问题
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // 配置序列化(解决乱码的问题),过期时间600秒
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(600)) //缓存过期10分钟 ---- 业务需求。
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))//设置key的序列化方式
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)) //设置value的序列化
                .disableCachingNullValues();
                 RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
        return cacheManager;
    }

}

 2.再相应的方法上加入缓存注解

package com.yyh.redis1.sercice;

import com.yyh.redis1.entity.User;
import com.yyh.redis1.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
@Service
public class UserService {
    @Resource
    private UserMapper userMapper;
    @Resource
    private RedisTemplate redisTemplate;

    @Cacheable(cacheNames = "user::selectById",key="#id")
    public User selectById(Long id){
        User user= userMapper.selectById(id);
        return user;
    }
    @CacheEvict(cacheNames = "user::delete",key = "#id")
    public void delete(Long id){

        userMapper.deleteById(id);
    }
    @CachePut(cacheNames = "user::update",key = "#user.id")
    public User update(User user){

        userMapper.updateById(user);
        return user;
        //2.再修改缓存里的数据

    }
    public void insert(User user){
        userMapper.insert(user);
    }
}

 3.启动缓存注解

2021-06-29

5. redis缓存穿透

1. 缓存没有该数据并且数据库中也没有该数据, 这是如果有大量的恶意请求来访问这种数据时,这些请求穿过缓存,到达数据库。

1. 如果数据库中不存在该数据时,往缓存中存放一个空对象。该对象什么也不做。(缓存中会存在大量的冗余数据。设置很短的过期时间)。

2. 使用布隆过滤器。理解为一个容器
       把数据库中的所有数据id放入到布隆过滤器中

 2021-06-29

6. 分布式锁

synchronized: 可以加载方法上,方法体内,类上。 自动获取锁,自动是否释放锁。 不会造成死锁。

lock: 方法体内。 手动获取锁  手动是否锁  如果出现异常而没有手动释放锁  则造成死锁。

 使用redis中的setNx判断是否获取锁资源    使用del释放锁资源

上一篇:redis 锁


下一篇:Springboot集成Redis举例