redis面试题
redis
01 什么是redis?
Redis
是一种基于内存的数据库,对数据的读写操作都非常快,常用于缓存,消息队列,分布式锁等场景。其内部提供了多种不同的数据类型来支持不同的业务场景,比如String, Hash, List, Set, Zset, BitMap, HyperLogLog, GEO, Stream
,并且对数据类型的操作都是原子性的,因为执行命令由单线程负责,不存在并发竞争的问题。除此之外,Redis
还支持事务,持久化,Lua脚本,多种集群方案(主从复制模式/哨兵模式,切片集群模式),发布/订阅模式,内存淘汰机制,过期删除机制。
02 为什么redis快?
- 纯内存访问
完全给予内存,绝大部分请求是纯粹的内存操作,非常快速,数据存在内存中,类似于HashMap,HashMap
的优势就是查找和操作的时间复杂度都是O(1)。
- 单线程避免行下文切换
采用单线程,避兔了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗
- 数据结构设计思路是空间换时间
跳表查询(虽然浪费了空间但是提升了查询效率),将O(n)的时间复杂度降到了O(logn)
为什么不是使用平衡树查找呢?
① 跳表的特性:
- 时间复杂度和平衡树是相当的,都是对数的量级
- 平衡二叉树在调整旋转操作时,跳表可能对这种情况效率更高
② 对比平衡树的优势:
- 不用旋转操作,可能代码量更少,调试和维护成本更低
- 虽然Redis采用单线程模型,但是调表的无锁设计在理论上更容易拓展为多线程数据结构,保留了优化的空间
③ 契合 Redis 的设计需求
- 空间换时间的权衡,消耗约 33% 的额外内存,但换取了更快的操作速度。对于以内存换性能的 Redis 来说,这种权衡是合理的。
- Redis 同时使用哈希表(存储 value 到 score 的映射)和跳表(按 score 排序),两者结合既保证了 O(1) 时间的单值查询(哈希表),又实现了高效的范围操作(跳表),综合性能优于单一结构。
- 使用多路复用I/O,复用(epoll)模型,非阻塞I/O
单线程同时监听所有连接,哪个连接有数据就立刻处理,不卡在空等上(非阻塞)。就像快递员用智能扫描枪,一次知道所有包裹到没到,不用挨个查。
- 渐近式ReHash,缓存时间戳
**渐近式ReHash
**主要解决,传统ReHash
一次性迁移全部数据会阻塞线程
- 用 两个哈希表(旧表+新大表)
- 每次处理请求时 顺带迁移1个桶的数据(如处理第N个请求迁移旧表第N个桶)
- 数据访问时 同时查新旧表,直到迁移完成
将海量数据迁移的耗时分摊到每次请求,避免卡顿
详细解释:什么是渐进式
ReHash
呢?这个与
Redis
的Key
和Value
的数据结构有关,为了实现从键到值的快速访问,Redis
使用了一个哈希表来保存所有的键值对,哈希表的本质就是一个数组,数组的每个元素称为一个哈希桶,每个哈希桶中保存了键值对数据。但是假设不断的往redis
数据库中添加元素,就会涉及到扩容问题,扩容必然会面临到元素移动的问题,但是,加入元素总量为6w个时,总之,很多很多元素,就会阻塞我们的IO,为了解决这个问题,所以采用了渐近式的rehash
(+斜体字)注意:
不过这里要注意,如果
Redis
中有海量的key值的话,这个Rehash
过程会很长很长,虽然采用渐进式Rehash
,但在Rehash
的过程中还是会导致请求有不小的卡顿。并且像一些统计命令也会非常卡顿:比如keys
按照Redis
的配置每个实例 能存储的最大的key
的数量为2的32次方,即2.5亿,但是尽量把key
的数量控制在千万以下,这样就可以避免Rehash
导致的卡顿问题,如果数量确实比较多,建议采用分区hash
存储。
缓存时间戳解决直接调用系统时间戳(如gettimeofday
)需陷入内核,效率低
- 单线程缓存时间:主线程每毫秒更新一次缓存时间戳
- 所有时间操作直接读缓存,减少系统调用
时间精度从纳秒级变毫秒级,但性能提升显著
详细解释:为什么缓存时间戳
redis
需要频繁检查键是否过期,每次检查都要获取当前的时间戳。我们平常使用系统时间戳时,常常是使用System.currentTimeInMillis
或者time.time()
来获取系统的毫秒时间戳。Redis
不能这样,因为每一次获取系统时间戳都是一次系统调用,系统调用相对来说是比较费时间的,作为单线程的Redis
承受不起,所以他需要对时间进行缓存,有一个定时任务,每毫秒更新一次时间缓存,获取时间都是从缓存中直接拿。不用访问主存,所以更快。(简单来说+斜体字)时间戳是什么?
时间戳本质上是一个数字,假设今天是 2024年1月1日 00:00:00,你给自己定了一个闹钟:2024年1月1日 08:30:00(早上八点半)。用时间戳表示,这个时间可能是 1704061800(假设这是从某个固定起点到八点半经过的总秒数)。
03 redis 优缺点
Redis
是一个键值(Key-Value)了些的NoSQL
数据库。
Redis优点:
- 基于内存操作,因此读写速度非常快;
- 支持丰富的数据结构,string、hash、list、set、zset(sorted set);
- 支持事务,而且操作都是原子性;
- 按key设置过期时间,到期后自动删除;
- 支持主从(master-slave)复制来实现数据备份,主机会自动将数据同步到从机。
Redis缺点:
内存成本相对硬盘较高。
什么是NoSQL?
见名思意:
SOL:structured Query Language
NoSQL:Not Only sQL/non-relational
比如常见的
mysql
数据库中,有的表内容是,有的数据的内容列为(id,name,address),而有的数据为(id,name,address,telegraph),每数据之间的结构是不一样的。
04 redis五种数据类型,每种适用于什么场景
redis
常见的数据类型有五种:String(字符串),Hash(哈希),List(列表),Set(集合)、Zset(有序集合)。
后续加入了新的四种: BitMap(2.2 版新增)、HyperLogLog(2.8 版新增)、GEO(3.2 版新增)、Stream(5.0 版新增)
结构类型 | 结构存储的值 | 结构读写能力 | 结构适用场景 |
---|---|---|---|
string字符串 | 可以是字符串、整数或浮点数 | 对整个字符串或字符串的一部分进行操作;对整数或浮点数进行自增或自减操作; | 缓存对象、常规计数、分布式锁、共享 session 信息等 |
List列表 | 一个链表,链表上的每个节点都包含一个字符串 | 对链表的两端进行push和pop操作,读取单个或多个元素;根据值查找或删除元素; | 消息队列(但是有两个问题:1. 生产者需要自行实现全局唯一 ID;不能以消费组形式消费数据)等 |
Set集合 | 包含字符串的无序集合 | 字符串的集合,包含基础的方法有看是否存在添加、获取、删除;还包含计算交集、并集、差集等 | 缓存对象、购物车等。 |
Hash散列 | 包含键值对的无序散列表 | 包含方法有添加、获取、删除单个元素 | 聚合计算(并集、交集、差集)场景,比如点赞、共同关注、抽奖活动等。 |
Zset有序集合 | 和散列一样,用于存储键值对 | 字符串成员与浮点数分数之间的有序映射;元素的排列顺序由分数的大小决定;包含方法有添加、获取、删除单个元素以及根据分值范围或成员来获取元素 | 排序场景,比如排行榜、电话和姓名排序等。 |
05 缓冲击穿、缓存穿透、缓存雪崩
06 redis数据持久化两种方式:RDB、AOF
Redis 提供了两种数据持久化方式:RDB 和 AOF。RDB 是通过生成数据快照来实现持久化,适合对性能要求高、允许少量数据丢失的场景,比如备份和灾难恢复;它的优点是文件紧凑、恢复速度快,但缺点是可能会丢失最后一次快照之后的数据。AOF 则是通过记录每次写操作日志来实现持久化,适合对数据安全性要求高、需要实时持久化的场景;它的优点是数据丢失风险低,缺点是文件较大且频繁同步可能影响性能。实际应用中,可以同时开启 RDB 和 AOF,结合两者的优点,既保证性能又提高数据安全性。
为什么会有混合持久化
RDB 优点是数据恢复速度快,但是快照的频率不好把握。频率太低,丢失的数据就会比较多,频率太高,就会影响性能。
AOF 优点是丢失数据少,但是数据恢复不快。
为了集成了两者的优点, Redis 4.0 提出了混合使用 AOF 日志和内存快照,也叫混合持久化,既保证了 Redis 重启速度,又降低数据丢失风险。
混合持久化工作在 AOF 日志重写过程,当开启了混合持久化时,在 AOF 重写日志时,fork 出来的重写子进程会先将与主线程共享的内存数据以 RDB 方式写入到 AOF 文件,然后主线程处理的操作命令会被记录在重写缓冲区里,重写缓冲区里的增量命令会以 AOF 方式写入到 AOF 文件,写入完成后通知主进程将新的含有 RDB 格式和 AOF 格式的 AOF 文件替换旧的的 AOF 文件。
也就是说,使用了混合持久化,AOF 文件的前半部分是 RDB 格式的全量数据,后半部分是 AOF 格式的增量数据
Redis 提供了两种数据持久化方式:RDB 和 AOF。
1. RDB(Redis Database)
RDB 是 Redis 默认的持久化方式,它通过生成数据快照来实现持久化。具体来说,Redis 会将某一时刻内存中的数据保存到一个二进制文件中(默认文件名为
dump.rdb
)。特点:
- 触发方式:可以通过配置自动触发(例如在指定时间内有多少次写操作),也可以通过
SAVE
或BGSAVE
命令手动触发。- 优点
- 性能高:生成快照时对性能影响较小,适合大规模数据恢复。
- 文件紧凑:RDB 文件是二进制格式,文件较小,适合备份和传输。
- 缺点
- 数据丢失风险:如果 Redis 宕机,可能会丢失最后一次快照之后的数据。
- 不适合实时持久化:RDB 是定期生成快照,无法实现秒级数据持久化。
2. AOF(Append-Only File)
AOF 通过记录每次写操作日志来实现持久化。Redis 会将所有的写操作追加到一个日志文件中(默认文件名为
appendonly.aof
),重启时通过重放日志来恢复数据。特点:
- 触发方式:可以配置不同的同步策略,例如
appendfsync everysec
(每秒同步一次)或appendfsync always
(每次写操作都同步)。- 优点
- 数据安全性高:AOF 可以记录每一次写操作,数据丢失风险较低。
- 支持实时持久化:通过配置可以实现秒级或实时持久化。
- 缺点
- 文件较大:AOF 文件是文本格式,随着写操作增多,文件会不断增长。
- 性能开销:频繁的同步操作可能会对性能有一定影响。
总结:
- RDB 适合对性能要求较高、允许少量数据丢失的场景,例如备份和灾难恢复。
- AOF 适合对数据安全性要求较高、需要实时持久化的场景。
实际应用中,Redis 可以同时开启 RDB 和 AOF,结合两者的优点,既保证性能又提高数据安全性。
07 mysql数据与redis进行同步?(双写一致性问题)
什么叫做一致性问题?
当修改了数据库的数据也要同时更新缓存的数据,缓存和数据库的数据要保持一致,再学习时候我了解到了4种场景下的缓存模式:
1.缓存更新策略(旁路缓存模式)
场景:
在大部分业务系统中,Redis 作为缓存层用于提升系统的读取性能,而MySQL作为持久化存储,用于保证数据的可靠性。最常见的场景是:
- 系统先查询 Redis 缓存,如果缓存中没有数据,再从 MySQL 中查询井将数据写入 Redis 缓存。
- 更新数据时,更新 MySQL 并删除 Redis 缓存,使缓存数据失效,保证下次读取时能拿到最新数据。
典型业务场景:
商品详情页面:当用户请求某个商品详情时,首先查询 Redis 缓存,如果须存中没有,则查询 MySQL,将查询结果缓存到 Redis 中;如果商品信息发生变更时,更新 MySQL 并删除 Redis 中的缓存。
可能存在并发问题,同时有两个数用户更新同一条数据,数据本来是100,用户a改为了90,用户b改成了100,改回去了。
为了保证数据的一致性:
- **缓存淘汰策略:**MySQL数据更新后立即删除 Redis 缓存,确保下次读取时能获取到最新数据。即通过“删除缓存” 的方式避免胜数据存在于缓存中。
- **并发问题:**当并发请求较高时,可能会出现“缓存雪崩”或“缓存击穿”问题,例如:A更新 MySQL数据,B 在缓存失效的瞬间读取了旧数据,再次缓存到 Redis,为解决此问题,可以采用 延迟双删策略:
- a.删除 Redis 缓存。
- b.更新 MySQL.
- c.适当延迟(如 500ms),再次删除 Redis 缓存,确保在并发情况下不存在缓存不一致问题。
2.先更新缓存再更新数据库
场景:
在某些实时性要求较高的场景中,可以考先更新 Redis 缓存,然后再异步更新 MySQL数据库。
典型业务场景:
**秒杀系统:**例如商品库存的扣减,用户购买商品时,首先更新 Redis 中的库存数量,保证极低延退的实时性体验。然后将变更异步写入 MySQL,确保持久化存储的一致性
方案分析:
- 读取路径:读取 Redis 缓存的库存信息,能够提供快速的读取响应。
- 写入路径:更新 Redis 中的库存数量后,使用消息队列或其他异步机制将更新同步到 MySQL
如何保障一致性:(保证的最终一致性)
- 数据最终一致性:Redis 作为前端实时数据的缓存,MySQL作为后端数据的持久化存储,采用异步更新策略时,一致性无法保证是强一致性,但可以通过使用消息队列等手段来保证最终一致性。异步写入MySQL时,如果操作失败,可以通过重试机制或补偿机制恢复一致性。
3.双写操作
双写操作是指在更新数据时,同时写入 MySQL 和 Redis,以确保两者数据一致。具体来说,当应用程序执行写操作时,会先更新 MySQL 数据库,然后同步更新 Redis 缓存。为了避免脏数据或一致性问题,可以采用先更新数据库,再删除缓存的策略,这样下次读取时 Redis 会重新从 MySQL 加载最新数据。为了提升性能和可靠性,可以通过消息队列异步处理双写操作,或者使用分布式锁来保证写操作的原子性。
4.数据回写的方案
数据回写方案是指在缓存失效或数据不一致时,通过回写机制将数据从 MySQL 同步到 Redis。例如,当 Redis 中某个缓存数据失效时,应用程序会从 MySQL 中读取最新数据并写回 Redis,确保缓存数据的准确性。这种方案通常结合缓存穿透保护和缓存预热策略,进一步优化系统性能和数据一致性。
08 过期删除策略
09 数据淘汰策略
Redis 提供了多种数据淘汰策略,用于在内存不足时决定哪些数据需要被移除。常见的策略包括:noeviction(默认策略,不淘汰数据,直接返回错误)、allkeys-lru(从所有键中移除最近最少使用的数据)、volatile-lru(从设置了过期时间的键中移除最近最少使用的数据)、allkeys-random(随机移除所有键中的数据)、volatile-random(随机移除设置了过期时间的键中的数据)以及 volatile-ttl(优先移除剩余生存时间最短的数据)。选择哪种策略取决于具体业务需求,例如如果需要优先保留热点数据,可以选择 LRU 策略;如果需要公平性,可以选择随机策略。合理配置淘汰策略可以有效管理内存资源,提升 Redis 的性能和稳定性。
10 哨兵机制
Redis 如何实现服务高可用?
要想设计一个高可用的 Redis 服务,一定要从 Redis 的多服务节点来考虑,比如 Redis 的主从复制、哨兵模式、切片集群。
Redis 哨兵模式通过引入哨兵节点来监控主节点和从节点的健康状态,哨兵会定期向主从节点发送 PING 命令来检测它们的响应情况,确保系统正常运行。当主节点出现故障时,哨兵会自动触发故障转移流程,从从节点中选举一个新的主节点,并通过 写同步 机制保证数据一致性,从而实现高可用性和自动恢复服务。这种架构解决了主从模式中需要手动恢复故障的问题,极大地提升了 Redis 的可靠性和稳定性。
具体流程
- 哨兵:它就像个“保安”,负责监控主节点和从节点的状态,发现问题就立刻处理。
- 主节点:负责处理写操作,同时把数据同步给从节点。
- 从节点:负责处理读操作,数据是从主节点同步过来的。
- 故障转移:如果主节点挂了,哨兵会从从节点里选一个当新的主节点,保证服务不中断。
11 主从复制
12 -了解redis么?
Redis 简介与使用场景
在这一章中,我们主要学习 Redis。Redis 可以显著提升应用程序中许多功能的性能。因此,学完 Redis 后,我们可以利用它来开发对性能要求较高的功能。此外,我们还可以用它重构现有的一部分功能,从而提升这些功能的性能。
Redis 的概念
Redis 是一款基于键值对的 NoSQL 数据库。它的值支持多种数据结构。首先,我们来了解什么是 NoSQL 数据库。
- NoSQL 数据库:NoSQL 是指非关系型数据库的统称,除了像 MySQL、Oracle 这类关系型数据库之外的其他数据库,都称为 NoSQL 数据库。NoSQL 是 “Not Only SQL” 的缩写,意思是“不仅仅是 SQL”。这是因为其他类型的数据库存储数据的方式多种多样,不仅仅依赖于 SQL。例如,有的数据库按照键值对存储,有的按照列存储,有的按照文档或图形存储,形式各异。
- Redis 的存储方式:Redis 采用键值对的方式存储数据。它的键(Key)是字符串类型,而值(Value)支持多种数据结构,包括字符串、哈希、列表、集合和有序集合等。这些数据结构在后续课程中会详细讲解。
Redis 的高性能
Redis 之所以能够以极高的性能存储数据,是因为它将所有数据存储在内存中。内存的读写速度远快于硬盘,因此 Redis 的速度非常快。
数据持久化
虽然 Redis 将数据存储在内存中,但如果服务器关闭,数据是否会丢失呢?这一点无需担心。Redis 在将数据存储到内存的同时,还可以通过快照(RDB)或日志(AOF)的形式将数据持久化到硬盘上,从而保证数据的安全性。
- 快照(RDB):快照是将当前内存中的数据直接保存到硬盘上。它的优点是数据体积小,恢复速度快,但缺点是存储时会阻塞其他操作,影响性能。因此,快照适合每隔几小时进行一次数据备份。
- 日志(AOF):日志是将每次执行的 Redis 命令以追加的形式保存到硬盘上。它的优点是实时性好,适合实时存储,但缺点是日志文件体积较大,恢复速度较慢。通常,可以将 RDB 和 AOF 结合使用,以兼顾性能和安全性。
Redis 的优势
Redis 是一款非常好用的工具,具有以下优势:
- 性能优异:由于数据存储在内存中,读写速度非常快。
- 数据结构丰富:支持多种数据结构,满足不同的业务需求。
- 易于使用:Redis 的学习和使用非常简单。
Redis 的使用场景
Redis 在以下场景中表现尤为出色:
- 缓存:将频繁访问的数据缓存到内存中,显著提升访问速度。
- 排行榜:例如热门帖子排行榜,Redis 可以高效地处理排名和访问。
- 计数器:例如帖子浏览量的统计,Redis 可以快速处理频繁的计数操作。
- 社交网络功能:例如点赞、关注等功能,Redis 可以高效地存储和处理这些数据。
- 消息队列:虽然 Redis 不是专门的消息队列工具,但对于要求不高的场景,Redis 也能满足需求。如果对消息队列要求较高,可以使用更专业的工具,例如 Kafka。
以上就是 Redis 的简介及其主要使用场景。通过学习和使用 Redis,我们可以显著提升应用程序的性能,并优化现有功能。
13 - redis命令行常用命令
通用指令:
select 0/1/2,换库
flushdb,清空
keys,看有哪些key,比如keys ,查看所有key;keys test,正则匹配看test开头的key
type/exists/del key,查看key类型、是否存在、删除
expire key time,设置过期时间
操作字符串:
set key value
get key
incr/decr key
操作hash表
hset key field value
hget key field
操作list
lpush/rpop listname,左进、右出,也可以左出右进
llen listname,长度
lindex/lrange listname,下标访问/区间访问
操作set
sadd setname item1 item2 …, 添加元素
scard setname,大小
spop setname,随机弹出,可用于抽奖
smembers setname,显示全部元素
操作ordered set
zadd setname score1 item1 score2 item2 …,添加元素
zcard setname,大小
zscore setname item,查分
zrank setname item,查元素的大小顺序
14 -还了解其他分布式缓存数据库么
下面简单聊聊常见的分布式缓存技术选型。
分布式缓存的话,比较老牌同时也是使用的比较多的还是 Memcached 和 Redis。不过,现在基本没有看过还有项目使用 Memcached 来做缓存,都是直接用 Redis。
Memcached 是分布式缓存最开始兴起的那会,比较常用的。后来,随着 Redis 的发展,大家慢慢都转而使用更加强大的 Redis 了。
有一些大厂也开源了类似于 Redis 的分布式高性能 KV 存储数据库,例如,腾讯开源的 Tendis。Tendis 基于知名开源项目 RocksDB 作为存储引擎 ,100% 兼容 Redis 协议和 Redis4.0 所有数据模型。关于 Redis 和 Tendis 的对比,腾讯官方曾经发过一篇文章:Redis vs Tendis:冷热混合存储版架构揭秘,可以简单参考一下。
不过,从 Tendis 这个项目的 Github 提交记录可以看出,Tendis 开源版几乎已经没有被维护更新了,加上其关注度并不高,使用的公司也比较少。因此,不建议你使用 Tendis 来实现分布式缓存。