【面试题】Redis (上篇)

1.什么是Redis,它的优缺点是什么?

Redis(Remote Dictionary Server)是一个开源的、内存中的数据结构存储系统,它可以用作数据库、缓存和消息代理。Redis 支持多种数据结构,如字符串(strings)、哈希(hashes)、列表(lists)、集合(sets)、有序集合(sorted sets)与位图(bitmaps)等类型的数据。Redis 还提供了多种操作这些数据结构的功能,如排序、事务等。

Redis 的优点:

  1. 高性能:Redis 所有的数据都存储在内存中,读写速度非常快,其性能远超数据库。

  2. 支持多种数据结构:Redis 支持丰富的数据结构,包括字符串、哈希、列表、集合、有序集合等,可以方便地满足各种场景的需求。

  3. 支持持久化:Redis 支持 RDB 和 AOF 两种持久化方式,可以将内存中的数据保存到磁盘中,避免数据丢失。

  4. 支持事务:Redis 支持简单的事务操作,可以通过MULTI、EXEC、DISCARD等命令来实现。

  5. 丰富的功能:Redis 提供了发布/订阅、管道、Lua 脚本、集群等多种功能,可以满足各种复杂的业务需求。

  6. 可扩展性:Redis 支持主从复制和分片集群,可以轻松实现水平扩展,满足大规模数据和高并发的需求。

  7. 客户端支持:Redis 支持多种编程语言的客户端,如 Java、Python、C、C++、PHP、Ruby 等,方便开发者使用。

Redis 的缺点:

  1. 内存占用:由于 Redis 的所有数据都存储在内存中,因此会占用较多的系统内存。如果数据量过大,可能会导致内存不足。

  2. 持久化的缺点:虽然 Redis 支持持久化功能,但 RDB 持久化方式可能会导致数据丢失(如服务器突然宕机),而 AOF 持久化方式则会占用较多的磁盘空间,并且恢复数据的速度较慢。

  3. 网络依赖:Redis 是基于网络的存储系统,因此会受到网络的影响。如果网络不稳定或延迟较大,可能会导致 Redis 的性能下降或数据丢失。

  4. 复杂的业务场景:虽然 Redis 提供了丰富的数据结构和功能,但在某些复杂的业务场景下,可能需要结合其他技术(如数据库、消息队列等)来实现。

  5. 学习成本:Redis 的命令和操作相对较多,对于初学者来说可能需要花费一定的时间来学习和掌握。

  6. 安全问题:Redis 默认配置下没有密码验证等安全机制,如果直接暴露在互联网上,可能会面临安全风险。因此,在使用 Redis 时需要配置好相应的安全策略。

2.Redis支持几种数据类型?

Redis 通常被提及的八种数据类型中,实际包含了五种基础数据类型和三种特殊数据类型。以下是对这些类型的详细介绍:

五种基础数据类型

  1. String(字符串)

    • 是 Redis 中最简单且最常用的数据类型。

    • 可以用来存储任何类型的数据,如字符串、整数、浮点数等。

    • 字符串类型的值最大可以存储 512MB 的数据。

  2. List(列表)

    • 是一个有序的字符串集合,可以存储多个值。

    • 列表的插入和删除操作是 O(1) 的复杂度,非常高效。

    • 列表类型支持从两端进行插入和删除操作。

  3. Set(集合)

    • 是一个无序的、唯一的字符串集合。

    • 集合类型的值可以进行交集、并集、差集等操作。

    • 适合用于存储一些不重复的数据。

  4. Hash(哈希)

    • 是一个键值对存储结构,在 Redis 中称为哈希表。

    • 每个键对应一个值,每个值可以是字符串、数字等。

    • 哈希类型适合用于存储对象,可以将一个对象的多个属性存储在一个哈希值中。

  5. Zset(有序集合)

    • 类似于集合,但每个元素都有一个分数(score)与之关联,可以对集合中的元素进行排序。

    • 有序集合类型的值适合用于存储一些需要排序的数据,如排行榜、热门文章等。

三种特殊数据类型

  1. HyperLogLog

    • 用于基数统计,可以估算集合中的唯一元素数量。

    • 它的主要优势是在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。

  2. Bitmap

    • 位图是基于字符串类型实现的,可以对每个位进行操作。

    • 它可以用来处理一些位运算相关的问题,比如统计用户某段时间内的活跃天数等。

  3. Geospatial

    • 用于存储地理位置信息。

    • Redis 的地理位置数据类型在 Redis 3.2 版本中引入,允许用户根据经纬度来查询位置,以及计算位置之间的距离等。

这些数据类型为 Redis 提供了丰富的功能,使其能够灵活地处理各种数据结构和应用场景。

3.Redis中的Redis中set和zset的底层数据结构

Set(集合)

Set的底层存储结构主要有两种:intsethashtable

  1. intset:

    • 当集合对象保存的所有元素都是整数值,且集合对象保存的元素数量不超过512个时,Redis会使用intset来存储集合元素。

    • intset内部其实是一个数组(int8_t contents[]数组),并且存储数据时是有序的,这样在查找数据时可以通过二分查找来实现。

    • intset的数据结构定义如下:
      typedef struct intset {  
          // 编码方式  
          uint32_t encoding;  
          // 集合包含的元素数量  
          uint32_t length;  
          // 保存元素的数组  
          int8_t contents[];  
      } intset;
  2. hashtable:

    • 当集合对象不满足使用intset的条件时(即包含非整数值元素或元素数量超过512个),Redis会使用hashtable来存储集合元素。

    • hashtable就是普通的哈希表,其中key为set的值,value为null。

Zset(有序集合)

Zset的底层数据结构主要有两种:ziplistskiplist(结合hash)。

  1. ziplist:

    • [value,score]键值对数量少于128个,且每个元素的长度小于64字节时,Redis会使用ziplist作为zset的底层实现。

    • ziplist是一种压缩列表技术,用于存储有序集合对象。

  2. skiplist:

    • 当不满足ziplist的使用条件时,Redis会使用skiplist作为zset的底层实现。

    • skiplist内部包含字典和跳跃表,其中跳表按分值从小到大排列,每个元素对象属性保存分值,元素成员属性保存元素成员。

    • hash在zset底层用于关联元素值与权重,确保元素的唯一性,并通过元素值找到相应的分数值。

总结来说,Redis的set和zset数据类型通过不同的底层数据结构来存储和管理数据,以实现高效的数据存储和查询操作。这些底层数据结构的选择取决于集合中元素的数量和类型,以及Redis的配置和性能要求。

4.Redis主要消耗什么物理资源?

Redis主要消耗的物理资源是内存

具体来说,Redis作为一个基于内存的数据结构存储系统,其设计初衷就是为了快速、高效地处理大量数据。因此,Redis在运行过程中会占用大量的内存资源来存储数据。这些内存资源主要用于以下几个方面:

  1. 对象内存:这是Redis中用于存储实际数据的内存部分。无论是字符串、哈希、列表、集合还是有序集合,Redis都会在内存中为其分配相应的空间来存储这些数据结构及其包含的元素。

  2. 缓冲内存:Redis在处理客户端请求时,会使用一些缓冲内存来暂存数据。这些缓冲内存主要用于网络IO、排序操作等场景,以提高Redis的性能和响应速度。

  3. 自身内存:Redis进程本身也需要占用一定的内存来运行。虽然这部分内存相对较小(通常在空进程中约为3MB左右),但在实际运行中仍然需要考虑其占用情况。

此外,虽然Redis本身并不直接消耗大量的CPU资源或磁盘资源,但在实际应用中,Redis的性能和稳定性仍然会受到这些资源的影响。例如,当Redis使用的内存资源接近或超过服务器的物理内存限制时,可能会引发内存交换(swap)操作,导致Redis的性能急剧下降。同样,如果Redis的持久化配置不当或数据量过大,也可能导致磁盘IO成为性能瓶颈。

因此,在部署和运行Redis时,需要根据实际情况合理配置相关参数和资源,以确保Redis能够稳定、高效地运行。

5.为什么Redis需要把所有的数据放在内存中?

Redis需要把所有的数据放在内存中,主要原因有以下几点:

  1. 高速读写:内存访问速度非常快,相比于磁盘和数据库,内存操作能够更迅速地响应读写请求。Redis将数据存储在内存中,可以大大缩短读写的延迟,提高系统的响应速度和吞吐量。

  2. 简单数据结构:Redis使用简单的数据结构(如字符串、列表、哈希、集合和有序集合等)来存储数据。这些数据结构直接映射到内存,不需要进行复杂的数据转换和序列化操作,从而提高了读写效率。

  3. 原子操作:Redis的所有操作都是原子的,这意味着在并发环境下,Redis可以保证数据的一致性。将数据存储在内存中有助于减少并发操作时的磁盘IO次数,进一步提高并发性能。

  4. 内存管理:Redis通过一系列机制来管理内存,如预分配和惰性释放、哈希表、跳跃表、压缩列表等。这些机制使得Redis能够高效地利用内存资源,同时避免内存碎片化和浪费。

  5. 数据持久化:虽然Redis将数据存储在内存中,但它也支持数据的持久化。通过使用RDB快照和AOF日志两种方式,Redis可以将内存中的数据定期或实时写入磁盘,以保证数据的持久性和安全性。这样,即使在Redis重启或服务器宕机的情况下,也可以通过持久化文件来恢复数据。

  6. 扩展性:虽然内存容量有限,但Redis可以通过集群和分片等方式来扩展内存容量。这种扩展性使得Redis能够处理大规模数据集,同时保持高性能。

综上所述,将数据放在内存中是Redis实现高性能、高并发、高可靠性等特性的关键所在。通过将数据存储在内存中并结合一系列内存管理机制和持久化策略,Redis能够在各种应用场景下提供稳定、高效的数据存储和访问服务。

6.RDB快照和AOF日志

RDB快照和AOF日志是Redis中用于数据持久化的两种主要机制。以下是关于这两种机制的详细解释:

RDB快照(Redis DataBase)

定义与特点

  • RDB快照是Redis将某一时刻的内存数据以二进制形式写入磁盘的过程,生成的文件默认为dump.rdb

  • 它是全量备份,保存的是内存数据的二进制序列化形式,存储上非常紧凑。

  • RDB快照生成过程不会阻塞Redis主线程,它使用子进程进行快照生成。

触发方式

  • 手动触发:通过执行命令SAVE或BGSAVE来触发。

    • SAVE:阻塞Redis主线程,直到RDB快照生成完毕。

    • BGSAVE:在后台异步执行,不会阻塞Redis主线程。

  • 自动触发:通过配置文件中设置的条件自动触发。

优点

  • 备份和恢复速度快,适合大规模数据的备份和恢复。

  • 在恢复大数据集时速度较快。

缺点

  • 可能会丢失最后一次快照之后的数据,不适合对数据丢失要求严格的场景。

  • 在生成快照时,如果数据集较大,可能会对Redis服务器的性能产生一些负面影响。

AOF日志(Append Only File)

定义与特点

  • AOF日志是Redis以追加的方式将服务器所执行的每一个写命令记录到日志文件中的过程。

  • 它是增量备份,保存的是Redis服务器执行的原始命令,因此具有很好的可读性。

  • AOF文件在长期的运行过程中会变大,需要定期进行重写(rewrite)来压缩文件大小。

触发方式

  • 配置触发:通过Redis配置文件中的相关设置来启用AOF持久化,并配置相关参数(如同步策略)。

  • 手动触发:可以通过命令BGREWRITEAOF来手动触发AOF重写。

优点

  • 提供更好的数据持久性,可以最大程度上避免数据丢失。

  • AOF文件可读性高,便于理解和分析。

缺点

  • AOF文件通常比RDB文件大,恢复速度相对较慢。

  • AOF文件的写入频率较高,对性能产生一定影响。

总结

RDB快照和AOF日志各有优缺点,适用于不同的场景。在需要快速恢复和备份大量数据的场景下,RDB快照是一个好的选择;而在对数据丢失要求严格,需要更高数据持久性的场景下,AOF日志则是更好的选择。Redis也支持同时使用RDB和AOF两种持久化方式,以获得更全面的数据保护。

重点

RDB文件存储的是数据 AOF存储的是写操作

7.Redis默认是哪种持久化机制

Redis的默认持久化机制是RDB(Redis DataBase)快照

8.Redis中key过期时间和永久有效怎么设置?

在Redis中,你可以通过特定的命令来设置key的过期时间或使其永久有效。以下是具体的设置方法:

设置key的过期时间

Redis提供了多种方式来设置key的过期时间:

  1. EXPIRE命令:

    • 语法:EXPIRE <key> <ttl>

    • 描述:该命令用于将key的生存时间设置为ttl秒。如果key已经存在并且设置了过期时间,那么EXPIRE命令会更新该key的过期时间。

    • 示例:EXPIRE mykey 3600 将名为“mykey”的key的过期时间设置为3600秒(即1小时)。

  2. PEXPIRE命令:

    • 语法:PEXPIRE <key> <ttl>

    • 描述:与EXPIRE命令类似,但ttl是以毫秒为单位的。

    • 示例:PEXPIRE mykey 3600000 将名为“mykey”的key的过期时间设置为3600000毫秒(即1小时)。

  3. EXPIREAT命令:

    • 语法:EXPIREAT <key> <timestamp>

    • 描述:该命令用于将key的过期时间设置为指定的时间戳。timestamp是UNIX时间戳。

    • 示例:假设你想在2024-06-18 10:30:00让key过期,你需要先计算出该时间的时间戳,然后使用EXPIREAT命令设置。

  4. PEXPIREAT命令:

    • 语法:PEXPIREAT <key> <timestamp>

    • 描述:与EXPIREAT命令类似,但timestamp是以毫秒为单位的UNIX时间戳。

设置key永久有效

在Redis中,没有直接的命令可以使key永久有效,但你可以通过设置一个非常大的过期时间来实现这一效果。然而,从实际角度考虑,Redis并不支持真正的“永久有效”key,因为服务器重启、内存不足或其他因素都可能导致数据丢失。

但如果你想要一个key看起来是永久有效的,你可以使用EXPIREPEXPIRE命令设置一个非常大的过期时间,例如几十年或几百年。但请注意,这样做并不保证key会永远存在。

使key永久有效的替代方法

  • 使用PERSIST命令:如果你之前为某个key设置了过期时间,但后来又想让它永久有效,可以使用PERSIST <key>命令来移除该key的过期时间。

总结

  • 使用EXPIRE、PEXPIRE、EXPIREAT或PEXPIREAT命令来设置key的过期时间。

  • 虽然没有直接的命令可以使key永久有效,但你可以通过设置一个非常大的过期时间或使用PERSIST命令来模拟永久有效。但请注意,这样做并不保证key会永远存在。

9.什么是缓存击穿、缓存雪崩、缓存穿透,他们的解决方案分别是什么?

缓存击穿、缓存雪崩和缓存穿透是Redis缓存系统中常见的三个问题,以下是它们的定义及相应的解决方案:

缓存击穿

定义: 缓存击穿问题也叫热点Key问题,即一个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击。

缓存击穿是指某个热点数据的缓存过期,而此时有大量的请求同时访问这个热点数据,导致大量请求直接打到数据库上,增加数据库负载(例子:就好比一个商品原本不是很火只有几个星期的上架时间,但是突然被带火了成为了大热款,但是没等开卖或者刚卖不久商品就下架了,这时候大量的请求直接打到数据库上,最后导致数据库响应不及时直接挂掉)

解决方案

  1. 使用锁:通过互斥锁保证同一时间只有一个线程去查询数据库,其他线程等待锁释放后再从缓存中获取数据。

    • 优点:能有效防止大量请求直接冲击数据库。

    • 缺点:会降低系统的并发性能,因为查询从并行变成了串行。

  2. 预加载热点数据:提前将可能被频繁访问的数据加载到缓存中,并设置较长的过期时间。

    • 优点:能降低缓存击穿发生的概率。

    • 缺点:需要预先知道哪些数据是热点数据,并且增加了缓存的存储压力。

  3. 分布式锁:在分布式系统中使用分布式锁来保证同一时间只有一个节点去查询数据库。

缓存雪崩

定义: 当大量缓存数据在同一时间过期(失效)或者Redis故障宕机时,如果此时有大量的用户请求,都无法在Redis中处理,于是全部请求都直接访问数据库,从而导致数据库的压力骤增,严重的会造成数据库宕机,形成一系列连锁反应,造成整个系统崩溃。

解决方案

  1. 均匀设置过期时间:避免大量数据同时过期,可以在设置过期时间时加上一个随机数。

  2. 互斥锁:同缓存击穿中的互斥锁解决方案。

  3. 后台更新缓存:将更新缓存的工作交由后台线程定时更新,而不是由业务线程在缓存失效时立即更新。

  4. 熔断降级:在缓存失效或Redis宕机时,暂时停止对数据库的访问,返回默认数据或空数据,等待缓存恢复后再继续提供服务。

缓存穿透

定义: 缓存穿透是指查询一个不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。

解决方案

  1. 布隆过滤器:将所有可能存在的数据使用布隆过滤器进行预先过滤,不存在的数据直接返回,不进行后续查询。

  2. 设置缓存空值:当一个请求查询的数据在数据库中不存在时,将该空结果设置到缓存中,并设置一个较短的过期时间。

  3. 热点数据缓存:提前加载热点数据到缓存中。

  4. 异步加载缓存:当数据不在缓存中时,通过异步的方式去加载数据,避免同步加载造成的性能损耗。

  5. 限流和防刷机制:通过限流和防刷机制来控制请求的访问频率,防止恶意请求对系统造成过大的压力。

10.布隆过滤器

布隆过滤器(Bloom Filter)是一种高效的空间效率型数据结构,主要用于快速判断一个元素是否存在于一个集合中。以下是关于布隆过滤器的详细解释:

基本原理

  1. 数据结构:布隆过滤器由一个很长的二进制向量(即位数组)和一系列随机映射函数(即哈希函数)组成。

  2. 元素插入:当一个元素需要插入到布隆过滤器时,它会被多个哈希函数映射到位数组中的多个位置,并将这些位置的值设置为1。

  3. 元素查询:要判断一个元素是否存在于布隆过滤器中,同样使用这些哈希函数将元素映射到位数组中的多个位置。如果所有这些位置的值都为1,那么该元素可能存在于集合中(存在误判的可能);如果任何一个位置的值不为1,那么该元素肯定不存在于集合中。

优缺点

优点
  • 空间效率高:布隆过滤器只需要一个位数组和若干个哈希函数,因此其空间效率很高。

  • 查询效率高:布隆过滤器的查询时间复杂度为O(K),其中K为哈希函数的个数,一般较小,与数据量大小无关。

  • 支持并行运算:哈希函数相互之间没有关系,方便硬件并行运算。

  • 不需要存储元素本身:在某些对保密要求比较严格的场合有很大优势。

  • 表示全集:在数据量很大时,布隆过滤器可以表示全集,其他数据结构不能。

缺点
  • 有误判:布隆过滤器是基于概率的数据结构,存在一定的误判率。即有可能将不存在的元素误判为存在。

  • 不支持删除操作:由于布隆过滤器的位数组被多个元素共享,删除某个元素会影响其他元素的判断结果。因此,布隆过滤器不支持删除操作。

应用场景

布隆过滤器被广泛应用于各种需要快速判断元素是否存在于集合中的场景,如:

  • 网页URL去重:在爬虫或搜索引擎中,可以使用布隆过滤器来快速判断一个URL是否已经被爬取或索引过。

  • 垃圾邮件过滤:在邮件系统中,可以使用布隆过滤器来快速判断一封邮件是否为垃圾邮件。

  • 数据库查询加速:在数据库中,可以使用布隆过滤器来减少不必要的磁盘查找操作,提高查询效率。

  • 网络安全:在网络安全领域,布隆过滤器可以用于检测恶意请求或流量等。

总之,布隆过滤器以其高效的空间利用和快速的查询性能,成为了一种广泛使用的数据结构。

  • 23
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱编程的小猴

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值