Redis面试总结(一)

1、除了Redis,你还知道其他分布式缓存方案吗?

redis痛点问题:内存占用高,数据可靠性差,业务维护缓存和存储一致性繁琐。

腾讯开源的Tendis也是分布式高性能KV存储数据库。

Tendis特征:

  • 完全兼容Redis协议,支持绝大多数redis的指令
  • 持久化存储:使用rocksdb作为存储引擎
  • 去中心化架构:类似于redis cluster分布式实现,所有节点通过gossip协议通讯,可指定hashtag来控制数据分布和访问,使用和运维成本极低。
  • 水平拓展:集群支持增删节点,并且数据可以按照slot在任意两节点之间歉意,扩容和缩放过程中对应用韵味人员透明,支持拓展至1000个节点。
  • 故障高可用:自动检测故障节点,当故障发生,slave会提升为master继续对外服务。
  • redis冷热混合存储关键组件:得益于Tendis存储版的设计和内部优化,RedisTendis存储版可以一起工作成为Tendis冷热混合存储。混合存储区非常适用于KV存储场景,并平衡了性能和成本。对于redis,占用大量存储空间的冷数据降冷后可以最多减少80%的成本,同时保证热数据在redis的访问性能。

选择redis原因:经历过多年不断考验、生态优秀、资料全面

为什么要用redis?

  • 访问速度快

​ redis基于内存,内存的访问速度比磁盘快很多,引入Redis后,可以把高频访问的数据放到Redis中,下次直接从内存中读取,速度提升几十倍甚至上百倍。

  • 高并发

​一般像 MySQL 这类的数据库的 QPS 大概都在 4k 左右(4 核 8g) ,但是使用 Redis 缓存之后很容易达到 5w+,甚至能达到 10w+(就单机 Redis 的情况,Redis 集群的话会更高)。

QPS(Query Per Second):服务器每秒可以执行的查询次数。

可以考虑把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里而不用经过数据库。

  • 功能全面

​ redis除了缓存之外,还可用于分布式锁、限流、消息队列、延时队列等场景。

2、常见的缓存读写策略有哪些?

包括:旁路缓存、读写穿透、异步缓存写入

1. Cashe Aside Pattern (旁路缓存模式)

缓存读写模式,比较适合读请求比较多的场景

​ 其中服务端需要同时维系 db 和 cache,并且是以db的结果为准。

写:

  • 先更新db
  • 然后直接删除cache。

读:

  • 从cache中读取数据,读取到就直接返回
  • cache中读取不到的话,就从db中读取数据返回
  • 再把数据放到cache中

问题:

在写数据的过程中,可以先删除cache,后更新db吗?

答案:不行,这样会造成db和缓存不一致问题,比如,请求 1 先把 cache 中的 A 数据删除 -> 请求 2 从 db 中读取数据->请求 1 再把 db 中的 A 数据更新

在写数据的过程中,先更新db,后删除cache就没有问题了吗?

答案:理论上来说还是有可能出现数据不一致问题,不过概率非常小,因为缓存写入的速度比数据库的写入速度快很多。简单理解为,请求 1 从 db 读数据 A-> 请求 2 更新 db 中的数据 A(此时缓存中无数据 A ,故不用执行删除缓存操作 ) -> 请求 1 将数据 A 写入 cache

Cashe Aside Pattern 存在缺陷:

  • 首次请求数据一定不在cache问题

解决办法:将热点数据提前放到cache中

  • 写操作比较繁琐的话,导致cache中的数据会被频繁删除,这样会影响缓存命中率。

解决办法:

数据库和缓存数据强一致场景:更新 db 的时候同样更新 cache,不过我们需要加一个锁/分布式锁来保证更新 cache 的时候不存在线程安全问题。

可以短暂地允许数据库和缓存数据不一致的场景:更新 db 的时候同样更新 cache,但是给缓存加一个比较短的过期时间,这样的话就可以保证即使数据不一致的话影响也比较小。

2. Read/Write Through Pattern (读写穿透)

服务端把cache视为主要数据存储,从中读取数据并将数据写入其中。cache服务负责将此数据读取和写入db,从而减轻了应用程序的职责。

写:

  • 先查 cache,cache 中不存在,直接更新 db。
  • cache 中存在,则先更新 cache,然后 cache 服务自己更新 db(同步更新 cache 和 db)。

读:

  • 从 cache 中读取数据,读取到就直接返回 。
  • 读取不到的话,先从 db 加载,写入到 cache 后返回响应。

3. Write Behind Pattern (异步缓存写入)

Read/Write Through 是同步更新 cache 和 db,而 Write Behind 则是只更新缓存,不直接更新 db,而是改为异步批量的方式来更新 db。

面临问题:比如 cache 数据可能还没异步更新 db 的话,cache 服务可能就挂掉了。

应用:消息队列中消息的异步写入磁盘、MySQL 的 Innodb Buffer Pool 机制都用到了这种策略。

Write Behind Pattern 下 db 的写性能非常高,非常适合一些数据经常变化又对数据一致性要求没那么高的场景,比如浏览量、点赞量。

3、Redis除了做缓存,还能做什么?

  • 分布式锁
  • 限流:通过Redis + Lua 脚本的方式实现限流
  • 消息队列:支持消息持久化、ACK机制
  • 延时队列:Redisson内置了延时队列
  • 分布式Session:利用String 或 Hash数据类型
  • 复杂业务业务场景:通过Redis 以及 Redis拓展(Redisson)提供的数据结构。业务:通过Bitmap统计活跃用户、通过Sorted Set 维护排行榜

4、如何基于Redis实现分布式锁?

SETNX如果key不存在,设置key的值,如果key已存在,SETNX啥也不做。

SETNX命令加锁:如果对应的key不存在则加锁,如果存在那么获取锁失败

基于Lua脚本释放锁:判断key(锁)对应的value是否相等 --> 执行DEL命令释放锁,如果不相等,那么释放锁失败。

存在问题:释放锁逻辑的程序突然挂掉,可能会导致锁无法被释放,造成共享资源无法再被其他线程/进程访问。

5、为什么要给锁设置一个过期时间?

解决问题:避免锁无法被释放 --> 给这个key(锁)设置一个过期时间。

127.0.0.1:6379> SET lockKey uniqueValue EX 3 NX
OK

lockKey:加锁的锁名;

uniqueValue:能够唯一标识锁的随机字符串;

NX:只有当 lockKey 对应的 key 值不存在的时候才能 SET 成功;

EX:过期时间设置(秒为单位)EX 3 标示这个锁有一个 3 秒的自动过期时间。与 EX 对应的是 PX(毫秒为单位),这两个都是过期时间设置。

存在:共享资源操作时间大于过期时间,就会出现锁提前过期的情况,如果锁的时间设置过长,会影响程序性能。

6、如何合理的设置锁的过期时间?

Redisson(Java语言Redis客户端) 中的分布式锁自带自动续期机制,使用起来非常简单,原理也比较简单,其提供了一个专门用来监控和续期锁的 Watch Dog( 看门狗),如果操作共享资源的线程还未执行完成的话,Watch Dog 会不断地延长锁的过期时间,进而保证锁不会因为超时而被释放。

//默认 30秒,支持修改
private long lockWatchdogTimeout = 30 * 1000;public Config setLockWatchdogTimeout(long lockWatchdogTimeout) {this.lockWatchdogTimeout = lockWatchdogTimeout;return this;
}
public long getLockWatchdogTimeout() {return lockWatchdogTimeout;
}

主要逻辑

private void renewExpiration() {//......Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask() {@Overridepublic void run(Timeout timeout) throws Exception {//......// 异步续期,基于 Lua 脚本CompletionStage<Boolean> future = renewExpirationAsync(threadId);future.whenComplete((res, e) -> {if (e != null) {// 无法续期log.error("Can't update lock " + getRawName() + " expiration", e);EXPIRATION_RENEWAL_MAP.remove(getEntryName());return;}if (res) {// 递归调用实现续期renewExpiration();} else {// 取消续期cancelExpirationRenewal(null);}});}// 延迟 internalLockLeaseTime/3(默认 10s,也就是 30/3) 再调用}, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);ee.setTimeout(task);}

默认情况下,没过10s看门狗就会执行续期操作,将锁的超时时间设置为30s。

protected CompletionStage<Boolean> renewExpirationAsync(long threadId) {return evalWriteAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,// 判断是否为持锁线程,如果是就执行续期操作,就锁的过期时间设置为 30s(默认)"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +"redis.call('pexpire', KEYS[1], ARGV[1]); " +"return 1; " +"end; " +"return 0;",Collections.singletonList(getRawName()),internalLockLeaseTime, getLockName(threadId));
}

调用renewExpirationAsync()方法实现锁的异步续期

protected CompletionStage<Boolean> renewExpirationAsync(long threadId) {return evalWriteAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,// 判断是否为持锁线程,如果是就执行续期操作,就锁的过期时间设置为 30s(默认)"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +"redis.call('pexpire', KEYS[1], ARGV[1]); " +"return 1; " +"end; " +"return 0;",Collections.singletonList(getRawName()),internalLockLeaseTime, getLockName(threadId));
}

使用Redisson分布式可重入锁RLock为例来说明如何使用Redisson实现分布式锁:

// 1.获取指定的分布式锁对象
RLock lock = redisson.getLock("lock");
// 2.拿锁且不设置锁超时时间,具备 Watch Dog 自动续期机制
lock.lock();
// 3.执行业务
...
// 4.释放锁
lock.unlock();

只有未指定锁超时时间,才会使用到 Watch Dog 自动续期机制。

// 手动给锁设置过期时间,不具备 Watch Dog 自动续期机制
lock.lock(10, TimeUnit.SECONDS);

7、如何基于Redis实现延时任务?

场景:订单在 10 分钟后未支付就失效、红包 24 小时未被查收自动退还

两种方案:1. Redis过期时间监听;2. Redisson内置的延时队列

Redis 过期事件监听的存在时效性较差、丢消息、多服务实例下消息重复消费等问题,不被推荐使用。

Redisson 内置的延时队列具备下面这些优势:

  1. 减少了丢消息的可能:DelayedQueue 中的消息会被持久化,即使 Redis 宕机了,根据持久化机制,也只可能丢失一点消息,影响不大。也可以使用扫描数据库的方法作为补偿机制。
  2. 消息不存在重复消费问题:每个客户端都是从同一个目标队列中获取任务的,不存在重复消费的问题。

8、Redisson延迟队列原理是什么?有什么优势?

Redisson 是一个开源的 Java 语言 Redis 客户端,提供了很多开箱即用的功能,比如多种分布式锁的实现、延时队列。

借助 Redisson 内置的延时队列 RDelayedQueue 来实现延时任务功能。

Redisson 的延迟队列 RDelayedQueue 是基于 Redis 的 SortedSet 来实现的。SortedSet 是一个有序集合,其中的每个元素都可以设置一个分数,代表该元素的权重。Redisson 利用这一特性,将需要延迟执行的任务插入到 SortedSet 中,并给它们设置相应的过期时间作为分数。

Redisson 使用 zrangebyscore 命令扫描 SortedSet 中过期的元素,然后将这些过期元素从 SortedSet 中移除,并将它们加入到就绪消息列表中。就绪消息列表是一个阻塞队列,有消息进入就会被监听到。这样做可以避免对整个 SortedSet 进行轮询,提高了执行效率。

跟 Redisson 内置的延时队列相比,消息队列可以通过保障消息消费的可靠性、控制消息生产者和消费者的数量等手段来实现更高的吞吐量和更强的可靠性,实际项目中首选使用消息队列的延时消息这种方案。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/883906.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python 爬虫抓取百度热搜

实现思路&#xff1a; 第1步、在百度热搜页获取热搜元素 元素类名为category-wrap_iQLoo 即我们只需要获取类名category-wrap_为前缀的元素 第2步、编写python脚本实现爬虫 import requests from bs4 import BeautifulSoupurl https://top.baidu.com/board?tabrealtime he…

【保姆级教程】Linux服务器本地部署Trilium+Notes笔记结合内网穿透远程在线协作

文章目录 前言1. 安装docker与docker-compose2. 启动容器运行镜像3. 本地访问测试4.安装内网穿透5. 创建公网地址6. 创建固定公网地址 前言 今天和大家分享一款在G站获得了26K的强大的开源在线协作笔记软件&#xff0c;Trilium Notes的中文版如何在Linux环境使用docker本地部署…

整合 flatten-maven-plugin 插件:解决子模块单独打包失败问题

整合 flatten-maven-plugin 插件&#xff1a;解决子模块单独打包失败问题 解决问题 我们来解决 Maven 多模块工程中&#xff0c;如果在父 pom 中定义了统一版本号 revision &#xff0c;单独对某个子模块执行 clean package 打包失败的问题。 [ERROR] Failed to execute goa…

PLC是如何扫描程序的?各位电气人都了解吗?

学习PLC必须要深刻理解PLC的扫描过程和执行原理&#xff0c;才能可靠无误的编写程序。通俗的讲PLC程序是从上往下&#xff0c;从左往右顺序循环扫描执行&#xff0c;它需要三个过程才真正输出实现外部动作。 第一步&#xff0c;先把外接的开关信号状态批量刷新到I输入映像区。 …

基于BLE的商业综合体室内定位导航系统的设计与实现

在大型商业综合体中&#xff0c;消费者常常因复杂的布局而感到困惑&#xff0c;如何高效、精准地引导顾客到达目标位置&#xff0c;成为提升购物体验的关键。BLE技术凭借其低功耗、高稳定性和广泛的设备兼容性&#xff0c;成为构建室内定位导航系统的理想选择。本文将详细探讨商…

CSS浮雕效果

效果图&#xff1a; HTML源码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Documen…

音视频入门基础:FLV专题(18)——Audio Tag简介

一、引言 根据《video_file_format_spec_v10_1.pdf》第75页&#xff0c;如果某个Tag的Tag header中的TagType值为8&#xff0c;表示该Tag为Audio Tag&#xff1a; 这时StreamID之后紧接着的就是AudioTagHeader&#xff0c;也就是说这时Tag header之后的就是AudioTagHeader&…

《掌控Linux:全面解析用户与组管理的奥秘》

目录 引言 用户与组管理 一、理解用户账户和组 二、Linux用户账户及其类型 三、超级用户权限 &#xff08;一&#xff09;Ubuntu的sudo命令 1、使用su命令临时改变用户身份 2、sudo命令用于切换用户身份执行 四、用户配置文件 &#xff08;一&#xff09;用户账户配置…

exp:CVE-2024-2961将phpfilter任意文件读取提升为远程代码执行(RCE)

该exp来自于https://raw.githubusercontent.com/ambionics/cnext-exploits/main/cnext-exploit.py在原基础上添加了一个小改动&#xff0c;使其更加通用 修改后的exp顶部资源失效则https://www.123865.com/s/kN7jVv-uccLd 之前的命令行参数为 使用方式是python exp.py url com…

玄机-应急响应- Linux入侵排查

一、web目录存在木马&#xff0c;请找到木马的密码提交 到web目录进行搜索 find ./ type f -name "*.php" | xargs grep "eval(" 发现有三个可疑文件 1.php看到密码 1 flag{1} 二、服务器疑似存在不死马&#xff0c;请找到不死马的密码提交 被md5加密的…

Thread类及线程的核心操作

一. Thread类的常见构造方法 1. Thread() Thread类无参的构造方法, 用于创建Thread类的实例对象. 2. Thread(String name) 带一个参数的Thread类构造方法, 创建一个线程对象, 并给其命名. [注]: 如果不专门给线程命名, 那么线程默认的名字就是Thread-0, Thread-1, Thread-…

云腾五洲的智联引擎是什么?

智联引擎是成都云腾五洲科技有限公司旗下的数智化转型服务平台&#xff0c;它提供云边协同的分布式物联网平台引擎服务。这一平台以其强大的功能和灵活性&#xff0c;为全行业提供数智化转型的新动力&#xff0c;帮助企业在数智化升级中实现持续增长。 核心能力 智联引擎的核心…

网络编程 TCP编程 Linux环境 C语言实现

所有基于数据传输通信的程序&#xff0c;都会被分成两种角色&#xff1a; 1. 服务端&#xff1a;又称为服务器 server 提供一种通信服务的进程 基本工作过程是&#xff1a;1> 接收请求数据 2> 处理请求数据 3> 发送处理结果 2. 客户端&#xff1a;client 使用一种通…

【Qt聊天室客户端】个人信息界面功能开发

1. 加载个人信息 从DataCenter数据类中拿到个人信息相关数据&#xff0c;然后显示到窗口中即可 个人信息界面中所有功能的实现&#xff0c;都是通过按钮触发操作&#xff0c;然后通过网络请求与后台交互完成信息更新 实现 2. 修改昵称 实现逻辑分析 获取输入框的昵称&#xf…

加油-加油

A 时刻注意A可逆&#xff0c;直接除去

十分钟Linux中的epoll机制

epoll机制 epoll是Linux内核提供的一种高效I/O事件通知机制&#xff0c;用于处理大量文件描述符的I/O操作。它适合高并发场景&#xff0c;如网络服务器、实时数据处理等&#xff0c;是select和poll的高效替代方案。 1. epoll的工作原理 epoll通过内核中的事件通知接口和文件…

60V恒流IC SL8443B内置功率MOS 支持2.5A电流 降压LED恒流驱动芯片

一、概述 SL8443B是一款高性能的LED恒流驱动芯片&#xff0c;具有60V的耐压能力&#xff0c;适用于高电压应用场景。它内置了5A&#xff08;或说5V&#xff0c;根据上下文理解为功率等级&#xff09;的功率MOS&#xff0c;可以减少外部元件数量&#xff0c;降低成本&#xff0…

获得淘宝app商品详情原数据 API 券后价获取API

item_get_app_pro-获得淘宝app商品详情原数据 通过此API可以实现通过商品id获取商品详情页数据&#xff0c;包括券后价、主图、详情等等。 公共参数 名称类型必须描述keyString是调用key&#xff08;必须以GET方式拼接在URL中&#xff09;secretString是调用密钥api_nameStr…

得计算题者得天下!软考系统集成计算题详解!

软考中级系统集成项目管理工程师考试一共有《综合知识》和《案例分析》两门科目&#xff0c;而在这两科中都会涉及到计算题&#xff0c;特别是案例分析中&#xff0c;计算题每次考试都会占到一道大题&#xff0c;共25分&#xff0c;占到了科目总分的1/4&#xff0c;所以对于系统…

访问jenkins页面报错

安装fontconfig 即可 yum install fontconfig -y 安装完之后重启jenkins systemctl restart jenkins 再访问