文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

【Redis】Redis面试题详解与使用案例(金三银四面试专栏启动)

2023-09-01 08:00

关注

📫作者简介:小明java问道之路2022博客之星全国TOP3,专注于后端、中间件、计算机底层、架构设计演进与稳定性建工设优化。

文章内容兼具广度深度、大厂技术方案,对待技术喜欢推理加验证,就职于知名金融公司后端高级工程师。

        

📫 热衷分享,喜欢原创~ 关注我会给你带来一些不一样的认知和成长。

        

🏆 2022博客之星全国TOP3 | CSDN博客专家 | 后端领域优质创作者 | CSDN内容合伙人

🏆 InfoQ(极客邦)签约作者、阿里云专家 | 签约博主、51CTO专家 | TOP红人、华为云享专家

        

🔥如果此文还不错的话,还请👍关注、点赞、收藏三连支持👍一下博主~ 


🍅 文末获取联系 🍅  👇🏻 精彩专栏推荐订阅收藏 👇🏻

 

本文目录

本文导读

一、Redis接入指南

1、Redis接入集群模式(推荐)

2、Redis接入哨兵模式(仅做了解,不推荐)

二、Redis使用案例

1、Redis增删改查多种数据结构实现

2、Redis缓存穿透、雪崩、击穿与解决办法

2.1、Redis缓存穿透

2.2、Redis缓存雪崩

2.3、Redis缓存击穿

3、Redis实现限流

3.1、基于Redis的incr

3.2、基于Redis的List、zset等数据结构

4、Redis分布式锁

5、测试环境测试

总结


本文提供了两种 Redis 的接入指南,集群模式接入Redis、哨兵模式接入Redis,避免工作中的小伙伴只会背不会用。

Redis使用案例模块主要是为了能够快速开发与使用,给出了一个百万QPS写法的Redis增删改查多种数据结构实现,Redis缓存穿透、雪崩、击穿与解决办法,Redis实现简单的限流策略以及Redis分布式锁, Redis 测试和管理工具Redis Desktop Manager 

文章兼具广度深度,让许多读者对大厂技术方案有进一步了解,在底层原理方面配合推荐的的博客,使用效果更佳!

Redis 接入分为哨兵模式和集群模式。

哨兵模式,基于哨兵集群实现主从切换,可以看作是简单主从模式的扩展(哨兵模式主要用于Redis主从同步架构,主节点宕机需要哨兵节点监控、通知,选举);

集群模式下需要注意的在哨兵模式下,多个服务器在Redis中存储相同的数据,这是浪费。集群模式可以看作是Redis的分布式存储。

1、Redis接入集群模式(推荐)

JedisRedisson、spring-data-redis三者的比较

Jedis 是 Redis 官方推荐的面向 Java 的操作Redis 的客户端,通过jedis我们可以实现连接Redis,以及操作 Redis 。,如果想在 Java 环境下操作 Redis ,需要安装相应的 Redis 驱动程序,也就 jedis.jar 包,然后将该驱动添加至 Java 的 classpath 中。

spring-data-redis是spring-data模块的一部分,RedisTemplate方式是SpringBoot集成Redis的客户端方式 ,专门用来支持在spring管理项目对redis的操作,使用java操作redis最常用的是使用jedis,但并不是只有jedis可以使用,spring-data-redis提供了redis的java客户端的抽象(org.springframework.data.redis.core.RedisTemplate)并且本身就属于spring的一部分,比起单纯的使用Jedis,更加稳定,管理起来更加自动化.。

Redisson 是一个在Redis的基础上实现的Java驻内存数据网格。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务。支持的主要功能:分布式锁、分布式服务、数据序列化、分布式对象处理

   org.springframework.data   spring-data-redis   ${redis.version}   redis.clients   jedis   ${redis.client.version}   org.redisson   redisson   ${redisson.version}

通用Spring配置:${}中的“”字符串为配置

!-- JedisPool连接池 -->                                                                            ​​​​​​​

2、Redis接入哨兵模式(仅做了解,不推荐)

     redis.properties                             ${redis.hostAndPort1}            ${redis.hostAndPort2}            ${redis.hostAndPort3}                                                                                                            

1、Redis增删改查多种数据结构实现

以下类是使用RedisTemplate构件一个完整的Redis缓存操作类。

实现了很多操作 Redis 的功能,包括不同数据结构的增删改查、限流、异步添加、对存储在指定key的数值加num操作等等

推荐文章:Redis安装步骤和特性以及支持的10种数据类型Redis跳表与实现源码解析Redis事务工作原理解析与分布式事务实战Redis过期删除策略和内存淘汰策略剖析Redis布隆过滤器工作原理与实战

import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.scheduling.annotation.Async;import org.springframework.stereotype.Component;import java.util.List;import java.util.Map;import java.util.Objects;import java.util.concurrent.TimeUnit;import java.util.stream.Collectors;@Componentpublic class RedisExecutorImpl implements CacheExecutor {    private static final Log logger = LogFactory.getLog(RedisExecutorImpl.class);        @Value("${redis.systemPrefix}")    private String systemPrefix;    @Autowired    private RedisTemplate redisTemplate;    @Autowired    private RedisTemplate redisTemplate4Num;    @Override    public void set(String key, int timeout, Object value) {        String redisKey = systemPrefix + key;        logger.info("RedisExecutorImpl set redisKey=" + redisKey + ",value=" + value + ",timeout=" + timeout);        redisTemplate.opsForValue().set(redisKey, value, timeout, TimeUnit.SECONDS);    }    @Override    public Object getData(String key) {        logger.info("RedisExecutorImpl getData key=" + key);        return redisTemplate.opsForValue().get(key);    }    @Override    public void setData(String key, long timeout, Object value) {        logger.info("RedisExecutorImpl setData key=" + key + ",value=" + value + ",timeout=" + timeout);        redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.MILLISECONDS);    }    @Override    public Object get(String key) {        logger.info("RedisExecutorImpl get key=" + key);        return redisTemplate.opsForValue().get(systemPrefix + key);    }    @Override    public Object get4Num(String key) {        logger.info("RedisExecutorImpl get key=" + key);        return redisTemplate4Num.opsForValue().get(systemPrefix + key);    }    @Override    public Object hGet(String key, Object field) {        return redisTemplate.opsForHash().get(systemPrefix + key, field);    }    @Override    @Async(value = "redisExecutor")    public void hSet(String key, String hKey, Object value, Long timeout) {        redisTemplate.opsForHash().put(systemPrefix + key, hKey, value);        redisTemplate.expire(systemPrefix + key, timeout, TimeUnit.SECONDS);    }        @Override    public void hDel(String key, String hKey) {        redisTemplate.opsForHash().delete(systemPrefix + key, hKey);    }    @SuppressWarnings("static-access")    @Override    public boolean delete(String key) {        try {            redisTemplate.delete(systemPrefix + key);            return true;        } catch (Exception e) {            logger.error("redis delete failed, key =" + systemPrefix + key + ", e=", e);            return false;        }    }    @Override    public List mget(List keyList) {        List finalKeys = keyList.stream().filter(Objects::nonNull).distinct().map(e -> systemPrefix + e).collect(Collectors.toList());        return redisTemplate.opsForValue().multiGet(finalKeys);    }    @Override    @Async(value = "redisExecutor")    public void mset(Map map) {        redisTemplate.opsForValue().multiSet(map);    }      @SuppressWarnings("static-access")    @Override    public boolean keyExistsIgnoreException(String key) {        try {            return redisTemplate.hasKey(systemPrefix + key);        } catch (Exception e) {            logger.error("redis keyExistsIgnoreException failed, key =" + systemPrefix + key + ", e=", e);            return false;        }    }    @SuppressWarnings("static-access")    @Override    public boolean deleteCacheKey(String key) {        try {            redisTemplate.delete(key);            return true;        } catch (Exception e) {            logger.error("redis deleteCacheKey failed, key =" + key + ", e=", e);            return false;        }    }    @Override    @Async(value = "redisExecutor")    public void asyncSet(String key, int timeout, Object value) {        redisTemplate.opsForValue().set(systemPrefix + key, value, timeout, TimeUnit.SECONDS);    }    @Override    public boolean expireKey(String key, long timeout) {        logger.info("RedisExecutorImpl expireKey key=" + key + " timeout=" + timeout);        boolean result = false;        result = redisTemplate.expire(key, timeout, TimeUnit.MILLISECONDS);        return result;    }        @Override    public Long incr(String key, long delta) {        logger.info("RedisExecutorImpl expireKey key=" + key + " delta=" + delta);        Long increment = redisTemplate4Num.opsForValue().increment(systemPrefix + key, delta);        logger.info("current value" + increment);        return increment;    }        @Override    public Long incr(String key) {        return incr(key, 1L);    }        @Override    public Long getExpireTime(String key, TimeUnit timeUnit) {        return redisTemplate.getExpire(systemPrefix + key, timeUnit);    }        @Override    public void set(String key, Object value) {        logger.info("RedisExecutorImpl set redisKey=" + key + ",value=" + value);        redisTemplate.opsForValue().set(key, value);    }} 

2、Redis缓存穿透、雪崩、击穿与解决办法

推荐文章:【Redis】Redis缓存穿透、缓存雪崩、缓存击穿详解与解决办法(Redis专栏启动) 

2.1、Redis缓存穿透

缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。

缓存穿透通常发生在以下两种情况下:缓存中的数据和数据库中的数据被错误删除,导致缓存和数据库中没有数据;黑客恶意攻击并故意访问大量读取不存在数据的企业;

解决方案:

1、非法请求的限制:在接收参数时过滤业务接口中的非法值、空值、负值和空值

2、布隆过滤器:一种类似于哈希表的算法。它使用所有可能的查询条件来生成位图,该位图将用于在数据库查询之前进行过滤。如果没有,它将被直接过滤,以减轻数据库级别的压力;

3、缓存空值:一个相对简单的解决方案。在第一次查询不存在的数据后,密钥和相应的空值也被放入缓存,但设置为较短的过期时间

2.2、Redis缓存雪崩

缓存服务挂掉或者热点缓存失效,所有请求都去查数据库,导致数据库连接不够或者数据库处理不过来,从而导致整个系统不可用。

缓存雪崩两个原因:

1、大量(热点)数据同时过期,导致本应请求缓存的数据,需要从数据库中检索;

解决方案:1、随机、微调甚至设置来设置过期时间;2、添加一个互斥锁,构建缓存后,释放锁,返回空值或默认值;3、双 key 策略,主key是原始缓存, 备key是副本缓存。当主key失败时,可以访问备份key;4、后台更新缓存策略,用定时任务或者消息队列更新或删除Redis缓存。

2、Redis故障宕机(服务挂掉),无法处理请求,再次请求数据库。

解决方案:1、服务熔断或请求限流机制;2、使用主从节点来构建集群

2.3、Redis缓存击穿

缓存击穿是指缓存中没有数据,但数据库中有数据(通常缓存时间到期)。此时,由于并发用户太多,读取缓存并不能读取数据,同时又去数据库检索数据,数据库压力瞬间增加,导致压力过大。击穿和雪崩的区别在于,击穿是针对特定的热点数据,而雪崩是所有数据。

解决方案:

1、缓存设置不过期,并且后台异步更新缓存

2、添加一个互斥锁,构建缓存后,释放锁,返回空值或默认值

3、Redis实现限流

推荐文章: Redis分布式限流与Redis实现限流的四种方式(Redis专栏启动)

在生产中的限流包括网关层的限流与Redis实现的限流策略,主要有基于Redisincrement方法作为vaule实现次数的递增,然后设置缓存过期时间来实现固定时间窗口、Listzset实现的滑动窗口,以及RedisLua脚本实现分布式限流。

3.1、基于Redis的incr

@SuppressWarnings("rawtypes")private RedisTemplate redisTemplate;// key自增Long count = redisTemplate.opsForValue().increment(key, 1);// 设置时间窗口boolean expire = redisTemplate.expire(key, cacheTimeSecond, TimeUnit.SECONDS);

3.2、基于Redis的List、zset等数据结构

// 获取令牌,返回 null 代表当前令牌桶中无令牌,则拒绝请求Object result = redisTemplate.opsForList().leftPop("limit_list");// 在令牌桶中添加令牌(令牌需要唯一)redisTemplate.opsForList().rightPush("limit_list",UUID.randomUUID().toString());

4、Redis分布式锁

推荐文章:Redis实现分布式锁解析与应用(Redis专栏启动) 

Redis分布式锁被广泛使用。本质上,分布式锁的目标是在Redis中占据一把“钥匙”。当其他进程想要占用一个密钥时,它们必须放弃,或者在发现已经存在密钥时重试。

@SuppressWarnings("rawtypes")private RedisTemplate redisTemplate;// 加锁boolean isGetLock = redisTemplate.opsForValue().setIfAbsent(key, value, timeout, TimeUnit.MILLISECONDS);// 锁是自己的,才释放if (isGetLock)     redisTemplate.delete(key);

5、测试环境测试

Redis缓存数据库工具:Redis Desktop Manager

Redis Desktop Manager 类似于 Navicat 的 Redis缓存数据库管理工具,点击进入后和 Navicat 一样首先是连接数据库服务器,输入名字/地址/端口号等等。

连接成功后就可以看到服务器中的数据库,在Redis中我们的数据都是存在一个键里面,就可以操作,鼠标停留在小图标上即可显示功能,使用起来很简单,建议对照官网操作一遍。

本文提供了两种 Redis 的接入指南,集群模式接入Redis、哨兵模式接入Redis,避免工作中的小伙伴只会背不会用。

Redis使用案例模块主要是为了能够快速开发与使用,给出了一个百万QPS写法的Redis增删改查多种数据结构实现,Redis缓存穿透、雪崩、击穿与解决办法,Redis实现简单的限流策略以及Redis分布式锁, Redis 测试和管理工具Redis Desktop Manager 

文章兼具广度深度,让许多读者对大厂技术方案有进一步了解,在底层原理方面配合推荐的的博客,使用效果更佳!

🔥如果此文还不错的话,还请👍关注、点赞、收藏三连支持👍一下博主~ 

🍅 文末获取联系 🍅  👇🏻 精彩专栏推荐订阅收藏 👇🏻

来源地址:https://blog.csdn.net/FMC_WBL/article/details/128822511

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 资料下载
  • 历年真题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容
咦!没有更多了?去看看其它编程学习网 内容吧