黑马Java热门面试题Redis(九)


目录:

(1)为什么使用 Redis?

(2)Redis 常见数据结构以及使用场景?

(3)Redis 为什么快?

(4)Redis 过期删除策略?

(5)Redis 内存淘汰策略?

(6)Redis 持久化机制 RDB 和 AOF 区别?

(7)Redis 如何选择合适的持久化方式

(8)在生成 RDB 期间,Redis 可以同时处理写请求么?

(9)如何保存 Redis 数据与 DB 一致?

(10)Redis 的什么是缓存预热?

(11)什么是缓存降级?

(12)Redis 的缓存雪崩、缓存穿透、缓存击穿

(13)常见的分布式锁有哪些解决方案?

(14)Redis 主从架构数据会丢失吗,为什么?

(15)Redis 如何做内存优化?

(16)Redis 集群


(1)为什么使用 Redis?

1. 速度快,因为数据存在内存中,类似于 HashMap,HashMap 的优势就 是查找和操作的时间复杂度都是 O1)

b) 支持丰富数据类型,支持 string,list,set,Zset,hash 等

c) 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部 执行,要么全部不执行

4. 丰富的特性:可用于缓存,消息,按 key 设置过期时间,过期后将会自 动删除

(2)Redis 常见数据结构以及使用场景?

• String

–介绍 :string 数据结构是简单的 key-value 类型。虽然 Redis 是用 C 语 言写的,但是 Redis 并没有使用 C 的字符串表示,而是自己构建了一 种 简单动态字符串(simple dynamic string,SDS)。最大能存储 512MB。

–常用命令: set,get,strlen,exists,decr,incr,setex 等等。

–应用场景: 计数、缓存文章标题、微博内容等。

• List

–介绍 :list 即是 链表。链表是一种非常常见的数据结构,特点是易于数据 元素的插入和删除并且可以灵活调整链表长度,但是链表的随机访问困 难。最多可存储 232 - 1 元素(4294967295, 每个列表可存储 40 亿)

–常用命令: rpush,lpop,lpush,rpop,lrange,llen 等。

–应用场景: 发布与订阅或者说消息队列。 

• Hash

–介绍 :hash 类似于 JDK1.8 前的 HashMap,内部实现也差不多(数组 + 链 表)。不过,Redis 的 hash 做了更多优化。另外,hash 是一个 string 类 型的 field 和 value 的映射表,特别适合用于存储对象,后续操作的时 候,你可以直接仅仅修改这个对象中的某个字段的值。 比如我们可以 hash 数据结构来存储用户信息,商品信息等等。每个 hash 可以存储 232 -1 键值对(40 多亿)

–常用命令:hset,hmset,hexists,hget,hgetall,hkeys,hvals 等。

–应用场景: 系统中对象数据的存储。

• Set

–介绍 : set 类似于 Java 中的 HashSet 。Redis 中的 set 类型是一种无 序集合,集合中的元素没有先后顺序。当你需要存储一个列表数据,又 不希望出现重复数据时,set 是一个很好的选择,并且 set 提供了判断 某个成员是否在一个 set 集合内的重要接口,这个也是 list 所不能提 供的。可以基于 set 轻易实现交集、并集、差集的操作。比如:你可以 将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。 Redis 可以非常方便的实现如共同关注、共同粉丝、共同喜好等功能。 这个过程也就是求交集的过程。最大的成员数为 232 -1(4294967295, 每 个 集合可存储 40 多亿个成员)。

–常用命令:sadd,spop,smembers,sismember,scard,sinterstore,sunion 等。

–应用场景: 需要存放的数据不能重复以及需要获取多个数据源交集和并集 等场景

• SortedSet(zset)

–介绍:SortedSet 和 set 相比,SortedSet 增加了一个权重参数 score,使得 集合中的元素能够按 score 进行有序排列,还可以通过 score 的范围 来获取元素的列表。有点像是 Java 中 HashMap 和 TreeSet 的结合 体。

–常用命令:zadd,zcard,zscore,zrange,zrevrange,zrem 等。

应用场景: 需要对数据根据某个权重进行排序的场景。比如在直播系统中,实时排 行信息包含直播间在线用户列表,各种礼物排行榜,弹幕消息(可以理解为按消息 维度的消息排行榜)等信息。

• 缓存

• 排行榜

• 分布式计数器

• 分布式锁

• 消息队列

• 分布式 token

• 限流 

(3)Redis 为什么快?

• (内存操作)完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。

• (单线程,省去线程切换、锁竞争的开销)采用单线程,避免了不必要的上下 文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用 去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致 的性能消耗;

• (NIO 的 IO 多路复用模型)使用多路 I/O 复用模型,非阻塞 IO;这里“多路”指 的是多个网络连接,“复用”指的是复用同一个线程

(4)Redis 过期删除策略?

常用的过期数据的删除策略就两个:

• 惰性删除 :只会在取出 key 的时候才对数据进行过期检查。这样对 CPU 最友 好,但是可能会造成太多过期 key 没有被删除。

• 定期删除 : 每隔一段时间抽取一批 key 执行删除过期 key 操作。并且,Redis 底层会通过限制删除操作执行的时长和频率来减少删除操作对 CPU 时间的影 响。

定期删除对内存更加友好,惰性删除对 CPU 更加友好。两者各有千秋,所以 Redis 采用的是 定期删除+惰性/懒汉式删除 。 但是,仅仅通过给 key 设置过期时间还是有问题的。因为还是可能存在定期删除和 惰性删除漏掉了很多过期 key 的情况。这样就导致大量过期 key 堆积在内存里, 然后就 Out of memory 了。 怎么解决这个问题呢?答案就是:Redis 内存淘汰机制。

(5)Redis 内存淘汰策略?

Redis 提供 6 种数据淘汰策略:

• volatile-lru(least recently used):从已设置过期时间的数据集 (server.db[i].expires)中挑选最近最少使用的数据淘汰

• volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期 的数据淘汰

• volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择 数据淘汰

• allkeys-lru(least recently used):当内存不足以容纳新写入数据时,在键空间 中,移除最近最少使用的 key(这个是最常用的)

• allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰

• no-eviction:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入 操作会报错。这个应该没人使用吧! 4.0 版本后增加以下两种:

• volatile-lfu(least frequently used):从已设置过期时间的数据集 (server.db[i].expires)中挑选最不经常使用的数据淘汰

• allkeys-lfu(least frequently used):当内存不足以容纳新写入数据时,在键空 间中,移除最不经常使用的 key 

(6)Redis 持久化机制 RDB 和 AOF 区别?

 (7)Redis 如何选择合适的持久化方式

• 如果是数据不那么敏感,且可以从其他地方重新生成补回的,那么可以关闭持 久化。

• 如果是数据比较重要,不想再从其他地方获取,且可以承受数分钟的数据丢失, 比如缓存等,那么可以只使用 RDB。

• 如果是用做内存数据库,要使用 Redis 的持久化,建议是 RDB 和 AOF 都开启, 或者定期执行 bgsave 做快照备份,RDB 方式更适合做数据的备份,AOF 可以保 证数据的不丢失。

补充:Redis4.0 对于持久化机制的优化 Redis4.0 相对与 3.X 版本其中一个比较大的变化是 4.0 添加了新的混合持久化方式。 简单的说:新的 AOF 文件前半段是 RDB 格式的全量数据后半段是 AOF 格式的增量 数据

• 优势:混合持久化结合了 RDB 持久化 和 AOF 持久化的优点, 由于绝大部分 都是 RDB 格式,加载速度快,同时结合 AOF,增量的数据以 AOF 方式保存了, 数据更少的丢失。

• 劣势:兼容性差,一旦开启了混合持久化,在 4.0 之前版本都不识别该 aof 文件, 同时由于前部分是 RDB 格式,阅读性较差。 

(8)在生成 RDB 期间,Redis 可以同时处理写请求么?

可以的,Redis 使用操作系统的多进程写时复制技术 COW(Copy On Write) 来实现 快照持久化,保证数据一致性。Redis 在持久化时会调用 glibc 的函数 fork 产生一 个子进程,快照持久化完全交给子进程来处理,父进程继续处理客户端请求。当主 线程执行写指令修改数据的时候,这个数据就会复制一份副本, bgsave 子进程读 取这个副本数据写到 RDB 文件。这既保证了快照的完整性,也允许主线程同时对 数据进行修改,避免了对正常业务的影响。

(9)如何保存 Redis 数据与 DB 一致?

• 方案 1:同步双写,即更新完 DB 后立即同步更新 redis

• 方案 2:异步监听,即通过 Canal 监听 MySQL 变化的表,同步更新数据到 Redis

• 方案 3:MQ 异步,即更新完 DB 后生产消息到 MQ,MQ 消费者更新数据到 Redis

(10)Redis 的什么是缓存预热?

缓存预热是指系统上线后,提前将相关的缓存数据加载到缓存系统。避免在用户请 求的时候,先查询数据库,然后再将数据缓存的问题,用户直接查询事先被预热的 缓存数据。 如果不进行预热,那么 Redis 初始状态数据为空,系统上线初期,对于高并发的流 量,都会访问到数据库中, 对数据库造成流量的压力。

缓存预热解决方案:

• 数据量不大的时候,工程启动的时候进行加载缓存动作;

• 数据量大的时候,设置一个定时任务脚本,进行缓存的刷新;

• 数据量太大的时候,优先保证热点数据进行提前加载到缓存。

(11)什么是缓存降级?

缓存降级是指缓存失效或缓存服务器挂掉的情况下,不去访问数据库,直接返回默 认数据或访问服务的内存数据。降级一般是有损的操作,所以尽量减少降级对于业 务的影响程度。 在进行降级之前要对系统进行梳理,看看系统是不是可以丢卒保帅;从而梳理出哪 些必须誓死保护,哪些可降级;比如可以参考日志级别设置预案:

• 一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降 级;

• 警告:有些服务在一段时间内成功率有波动(如在 95~100%之间),可以自动 降级或人工降级,并发送告警;

• 错误:比如可用率低于 90%,或者数据库连接池被打爆了,或者访问量突然猛 增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;

• 严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。

(12)Redis 的缓存雪崩、缓存穿透、缓存击穿

• 缓存穿透

–缓存穿透:缓存中和数据库中都没有所查询的东西,从而使数据库崩掉。

–解决方案:将一条数据库不存在的数据也放入缓存中这样即使数据库不存在 但缓存中有,还可 以使用布隆过滤器。

• 缓存击穿

–缓存击穿:缓存中没有但数据库中有,如果同一时间访问量过大会使数据崩 掉。

–解决方案:加分布式锁,一条数据访问数据库;将数据存储到缓存中,其他 线程从缓存中拿。

• 缓存雪崩

–缓存雪崩:缓存中的数据正好在一个时间删除,当请求来时穿过缓存访问数 据库。

–解决方案:1.提前预热;2.设置随机缓存中数据的过期时间;3.做缓存备份

(13)常见的分布式锁有哪些解决方案?

 

 

 

 

(14)Redis 主从架构数据会丢失吗,为什么?

有两种数据丢失的情况:

• 异步复制导致的数据丢失:因为 master -> slave 的复制是异步的,所以可能有 部分数据还没复制到 slave,master 就宕机了,此时这些部分数据就丢失了。

• 脑裂导致的数据丢失:某个 master 所在机器突然脱离了正常的网络,跟其他 slave 机器不能连接,但是实际上 master 还运行着,此时哨兵可能就会认为 master 宕机了,然后开启选举,将其他 slave 切换成了 master。这个时候,集 群里就会有两个 master,也就是所谓的脑裂。此时虽然某个 slave 被切换成了 master,但是可能 client 还没来得及切换到新的 master,还继续写向旧 master 的数据可能也丢失了。因此旧 master 再次恢复的时候,会被作为一个 slave 挂 到新的 master 上去,自己的数据会清空,重新从新的 master 复制数据。 

 (15)Redis 如何做内存优化?

• 控制 key 的数量:当使用 Redis 存储大量数据时,通常会存在大量键,过多的键 同样会消耗大量内存。Redis 本质是一个数据结构服务器,它为我们提供多种数 据结构,如 hash,list,set,zset 等结构。使用 Redis 时不要进入一个误区,大 量使用 get/set 这样的 API,把 Redis 当成 Memcached 使用。对于存储相同的数 据内容利用 Redis 的数据结构降低外层键的数量,也可以节省大量内存。

• 缩减键值对象,降低 Redis 内存使用最直接的方式就是缩减键(key)和值(value) 的长度。 –key 长度:如在设计键时,在完整描述业务情况下,键值越短越好。 –value 长度:值对象缩减比较复杂,常见需求是把业务对象序列化成二进制 数组放入 Redis。首先应该在业务上精简业务对象,去掉不必要的属性避 免存储无效数据。其次在序列化工具选择上,应该选择更高效的序列化 工具来降低字节数组大小。

• 编码优化。Redis 对外提供了 string,list,hash,set,zet 等类型,但是 Redis 内部针对 不同类型存在编码的概念,所谓编码就是具体使用哪种底层数据结构来实现。 编码不同将直接影响数据的内存占用和读写效率。

(16)Redis 集群

1.主从模式

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

喵俺第一专栏

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

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

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

打赏作者

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

抵扣说明:

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

余额充值