【Redis】面试题(持续更新……)

为什么要用 Redis(为什么要用缓存)

主要从“高性能”和“高并发”这两点来看待这个问题

  • 高性能

    假如用户第一次访问数据库中的某些数据。这个过程会比较慢,因为是从硬盘上读取的。将该用户访问的数据存在数缓存中,这样下一次再访问这些数据的时候就可以直接从缓存中获取了。操作缓存就是直接操作内存,所以速度相当快。如果数据库中的对应数据改变的之后,同步改变缓存中相应的数据即可!

  • 高并发

    直接操作缓存能够承受的请求是远远大于直接访问数据库的,所以我们可以考虑把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里而不用经过数据库。

为什么要用 Redis 而不用 map/guava 做缓存

缓存分为本地缓存分布式缓存

以 Java 为例,使用自带的 map 或者 guava 实现的是本地缓存,最主要的特点是轻量以及快速生命周期随着 JVM 的销毁而结束,并且在多实例的情况下,每个实例都需要各自保存一份缓存,缓存不具有一致性

使用 Redis 或 Memcached 之类的称为分布式缓存,在多实例的情况下,各实例共用一份缓存数据,缓存具有一致性。缺点是需要保持 Redis 或 Memcached服务的高可用,整个程序架构上较为复杂。

Redis 为什么这么快

  • 完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于 HashMap,HashMap 的优势就是查找和操作的时间复杂度都是 O ( 1 ) O(1) O(1)
  • 数据结构简单,对数据操作也简单,Redis 中的数据结构是专门进行设计的(比如 ziplist、跳跃表)
  • 采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗
  • 使用多路 I/O 复用模型,非阻塞 IO

Redis 基本数据类型

Redis 主要有 5 种数据类型,包括 String,List,Set,Zset,Hash,满足大部分的使用要求,具体每个数据类型可以干嘛要知道

Zset 底层的跳跃表

参考 【Redis】基本操作

Redis 持久化机制及各自的优缺点

参考 【Redis】持久化

Redis 过期键的删除策略

我们都知道,Redis 是 key-value 数据库,我们可以设置 Redis 中缓存的 key 的过期时间。Redis 的过期策略就是指当 Redis 中缓存的 key 过期了,Redis 如何处理。

  • 被动清理

    只有当访问一个 key 时,才会判断该 key 是否已过期,过期则清除并且返回 nil。该策略可以最大化地节省 CPU 资源,却对内存非常不友好。极端情况可能出现大量的过期 key 没有再次被访问,从而不会被清除,占用大量内存。

  • 定期清理

    在 Redis 的持久化中,我们知道 Redis 为了保持系统的稳定性,健壮性,会周期性的执行一个函数。在这个过程中,会进行之前已经提到过的自动的持久化操作,同时也会进行内存的主动清理。
    在内存主动清理的过程中,Redis 采用了一个随机算法来进行这个过程:简单来说,Redis 会随机的抽取 N (默认100) 个被设置了过期时间的 key,检查这其中已经过期的 key,将其清除。同时,如果这其中已经过期的 key 超过了一定的百分比 M (默认是 25),则将继续执行一次主动清理,直至过期 key 的百分比在概率上降低到 M 以下。

  • 内存不足时触发主动清理

    在 Redis 的内存不足时,也会触发主动清理。

    配置文件中 maxmemory-policy 是用来设置内存超限时的删除策略的

    在这里插入图片描述

    • volatile-lru:使用 LRU 算法移除 key,只对设置了过期时间的键(最近最少使用)
    • allkeys-lru:在所有集合 key 中,使用 LRU 算法移除 key
    • volatile-random:在过期集合中移除随机的 key,只对设置了过期时间的键
    • allkeys-random:在所有集合 key 中,移除随机的 key
    • volatile-ttl:移除那些 TTL 值最小的 key,即那些最近要过期的 key
    • noeviction:不进行移除,针对写操作,只是返回错误信息

    同时,还可以在配置文件中配置这些策略的采样数量 maxmemory-samples

    在这里插入图片描述

    • LRU 算法和最小 TTL 算法都并非是精确的算法,而是估算值,所以你可以设置样本的大小,Redis 默认会检查这么多个 key 并选择其中 LRU 的那个
    • 一般设置 3 到 7 的数字,数值越小样本越不准确,但性能消耗越小。

Redis 如何设置过期时间

2、EXPIREAT 和 PEXPIREAT

  • EXPIREAT

    接口定义:EXPIREAT key timestamp
    接口描述:设置一个 key 在 timestamp (时间戳(秒)) 之后过期。返回 1 代表设置成功,返回 0 代表 key 不存在或者无法设置过期时间。

  • PEXPIREAT

    接口定义:PEXPIREAT key milliseconds-timestamp
    接口描述:设置一个 key 在 milliseconds-timestamp (时间戳(毫秒)) 之后过期。返回 1 代表设置成功,返回 0 代表 key 不存在或者无法设置过期时间。

3、TTL 和 PTTL

  • TTL

    接口定义:TTL key
    接口描述:获取 key 的过期时间。如果 key 存在过期时间,返回剩余生存时间 (秒);如果 key 是永久的,返回 -1;如果 key 不存在或者已过期,返回 -2。

  • PTTL

    接口定义:PTTL key
    接口描述:获取 key 的过期时间。如果 key 存在过期时间,返回剩余生存时间(毫秒);如果 key 是永久的,返回 -1;如果 key 不存在或者已过期,返回 -2。

4、PERSIST

  • PERSIST

    接口定义:PERSIST key
    接口描述:移除 key 的过期时间,将其转换为永久状态。如果返回 1,代表转换成功。如果返回 0,代表 key 不存在或者之前就已经是永久状态。

5、SETEX

  • SETEX

    接口定义:SETEX key seconds value
    接口描述:SETEX 在逻辑上等价于 SET 和 EXPIRE 合并的操作,区别之处在于 SETEX 是一条命令,而命令的执行是原子性的,所以不会出现并发问题。

Redis 的内存用完了会发生什么

如果达到设置的上限,Redis 的写命令会返回错误信息(但是读命令还可以正常返回。)或者你可以配置内存淘汰机制,当 Redis 达到内存上限时会冲刷掉旧的内容。

Redis 线程模型

Redis 6 加入多线程,但跟 Memcached 这种从 IO 处理到数据访问多线程的实现模式有些差异。Redis 的多线程部分只是用来处理网络数据的读写和协议解析,执行命令仍然是单线程。之所以这么设计是不想因为多线程而变得复杂,需要去控制 key、lua、事务,LPUSH、LPOP 等等的并发问题。整体的设计大体如下:

在这里插入图片描述

另外,多线程IO默认也是不开启的,需要再配置文件中配置

  • io-threads-do-reads yes

  • io-threads 4

Redis 事务

参考 【Redis】事务与锁

Redis 主从架构

Redis 哨兵模式

参考【Redis】主从复制

Redis 集群

参考 【Redis】集群

一致性哈希算法

参考 一致性哈希

Redis 实现分布式锁

参考 【Redis】常见问题

缓存异常

包括缓存穿透缓存击穿缓存雪崩等,参考 【Redis】常见问题

布隆过滤器

直观的说,bloom 算法类似一个 hash set,用来判断某个元素(key)是否在某个集合中。

和一般的 hash set 不同的是,这个算法无需存储 key 的值,对于每个 key,只需要 k 个比特位,每个存储一个标志,用来判断 key 是否在集合中。

算法:

  1. 首先需要 k 个 hash 函数,每个函数可以把 key 散列成为 1 个整数
  2. 初始化时,需要一个长度为 n 比特的数组,每个比特位初始化为 0
  3. 某个 key 加入集合时,用 k 个 hash 函数计算出 k 个散列值,并把数组中对应的比特位置为 1
  4. 判断某个 key 是否在集合时,用 k 个 hash 函数计算出 k 个散列值,并查询数组中对应的比特位,如果所有的比特位都是 1,认为在集合中

优点:

  • 相比于其它的数据结构,布隆过滤器在空间和时间方面都有巨大的优势。布隆过滤器存储空间和插入/查询时间都是常数。另外, Hash 函数相互之间没有关系,方便由硬件并行实现。布隆过滤器不需要存储元素本身,在某些对保密要求非常严格的场合有优势。

缺点:

  • 随着存入的元素数量增加,误算率随之增加,但是如果元素数量太少,则使用散列表足矣。

  • 另外,一般情况下不能从布隆过滤器中删除元素。我们很容易想到把位列阵变成整数数组,每插入一个元素相应的计数器加 1, 这样删除元素时将计数器减掉就可以了。然而要保证安全的删除元素并非如此简单。首先我们必须保证删除的元素的确在布隆过滤器里面。这一点单凭这个过滤器是无法保证的。另外计数器回绕也会造成问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值