Skip to content

Commit

Permalink
Redis面试题补充
Browse files Browse the repository at this point in the history
  • Loading branch information
Tyson0314 committed Mar 20, 2022
1 parent 24b957b commit 76efb6e
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 16 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@
- [200多本经典的计算机书籍](https://github.com/Tyson0314/java-books)
- [谷歌师兄刷题笔记](https://t.1yb.co/A6id)(推荐 :+1:
- [BAT大佬总结的刷题手册](https://t.1yb.co/yMbo)(推荐 :+1:
- [Java项目推荐](https://www.zhihu.com/question/325011850/answer/2257046656)
- [Java优质项目推荐](https://www.zhihu.com/question/325011850/answer/2257046656)
- [优质视频教程推荐](https://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247487149&idx=1&sn=aa883c9f020945d3f210550bd688c7d0&chksm=ce98f3ebf9ef7afdae0b37c4d0751806b0fbbf08df783fba536e5ec20ec6a6e1512198dc6206&token=104697471&lang=zh_CN#rd)(推荐 :+1:

# 大厂面试系列

Expand Down
75 changes: 60 additions & 15 deletions 中间件/Redis面试题.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,16 @@ Redis(`Remote Dictionary Server`)是一个使用 C 语言编写的,高性

- **高效的数据结构**:Redis 每种数据类型底层都做了优化,目的就是为了追求更快的速度。

## 讲讲Redis的线程模型?

## Redis为何选择单线程
Redis基于Reactor模式开发了网络事件处理器,这个处理器被称为文件事件处理器。它的组成结构为4部分:多个套接字、IO多路复用程序、文件事件分派器、事件处理器。因为文件事件分派器队列的消费是单线程的,所以Redis才叫单线程模型。

- 文件事件处理器使用I/O多路复用(multiplexing)程序来同时监听多个套接字, 并根据套接字目前执行的任务来为套接字关联不同的事件处理器。
- 当被监听的套接字准备好执行连接accept、read、write、close等操作时, 与操作相对应的文件事件就会产生, 这时文件事件处理器就会调用套接字之前关联好的事件处理器来处理这些事件。

虽然文件事件处理器以单线程方式运行, 但通过使用 I/O 多路复用程序来监听多个套接字, 文件事件处理器既实现了高性能的网络通信模型, 又可以很好地与 redis 服务器中其他同样以单线程方式运行的模块进行对接, 这保持了 Redis 内部单线程设计的简单性。

## Redis为何选择单线程模型?

* 避免过多的**上下文切换开销**。程序始终运行在进程中单个线程内,没有多线程切换的场景。
* **避免同步机制的开销**:如果 Redis选择多线程模型,需要考虑数据同步的问题,则必然会引入某些同步机制,会导致在操作数据过程中带来更多的开销,增加程序复杂度的同时还会降低性能。
Expand Down Expand Up @@ -105,6 +113,18 @@ Redis支持多线程主要有两个原因:
5. Redis 的速度比 Memcached 快很多。
6. Redis 使用**单线程的多路 IO 复用模型**,Memcached使用多线程的非阻塞 IO 模型。

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

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

使用 redis 或 memcached 之类的称为**分布式缓存**,在多实例的情况下,各实例共用一份缓存数据,缓存具有一致性。

## Redis的内存用完了会怎样?

如果达到设置的上限,Redis的写命令会返回错误信息(但是读命令还可以正常返回)。

也可以配置内存淘汰机制,当Redis达到内存上限时会冲刷掉旧的内容。

## Redis 数据类型有哪些?

**基本数据类型**
Expand Down Expand Up @@ -209,6 +229,14 @@ QUEUED

使用`UNWATCH`可以取消`WATCH`命令对`key`的监控,所有监控锁将会被取消。

## Redis事务支持隔离性吗?

Redis 是单进程程序,并且它保证在执行事务时,不会对事务进行中断,事务可以运行直到执行完所有事务队列中的命令为止。因此,Redis 的事务是总是带有隔离性的。

## Redis事务保证原子性吗,支持回滚吗?

Redis单条命令是原子性执行的,但事务不保证原子性,且没有回滚。事务中任意命令执行失败,其余的命令仍会被执行。

## 持久化机制

持久化就是把**内存的数据写到磁盘中**,防止服务宕机导致内存数据丢失。
Expand Down Expand Up @@ -294,24 +322,15 @@ appendfsync no //由操作系统决定何时进行同步操作

当RDB与AOF两种方式都开启时,Redis会优先使用AOF恢复数据,因为AOF保存的文件比RDB文件更完整。

## Redis常见的部署方式有哪些?

Redis的几种常见使用方式包括:
## Redis有哪些部署方案?

* 单机版
* Redis主从
* Redis Sentinel(哨兵)
* Redis Cluster
**单机版**:单机部署,单机redis能够承载的 QPS 大概就在上万到几万不等。这种部署方式很少使用。存在的问题:1、内存容量有限 2、处理能力有限 3、无法高可用。

**使用场景**
**主从模式**一主多从,主负责写,并且将数据复制到其它的 slave 节点,从节点负责读。所有的读请求全部走从节点。这样也可以很轻松实现水平扩容,支撑读高并发。master 节点挂掉后,需要手动指定新的 master,可用性不高,基本不用。

单机版:很少使用。存在的问题:1、内存容量有限 2、处理能力有限 3、无法高可用
**哨兵模式**:主从复制存在不能自动故障转移、达不到高可用的问题。哨兵模式解决了这些问题。通过哨兵机制可以自动切换主从节点。master 节点挂掉后,哨兵进程会主动选举新的 master,可用性高,但是每个节点存储的数据是一样的,浪费内存空间。数据量不是很多,集群规模不是很大,需要自动容错容灾的时候使用

主从模式:master 节点挂掉后,需要手动指定新的 master,可用性不高,基本不用。

哨兵模式:master 节点挂掉后,哨兵进程会主动选举新的 master,可用性高,但是每个节点存储的数据是一样的,浪费内存空间。数据量不是很多,集群规模不是很大,需要自动容错容灾的时候使用。

Redis cluster:主要是针对海量数据+高并发+高可用的场景,如果是海量数据,如果你的数据量很大,那么建议就用Redis cluster,所有主节点的容量总和就是Redis cluster可缓存的数据容量。
**Redis cluster**:服务端分片技术,3.0版本开始正式提供。Redis Cluster并没有使用一致性hash,而是采用slot(槽)的概念,一共分成16384个槽。将请求发送到任意节点,接收到请求的节点会将查询请求发送到正确的节点上执行。主要是针对海量数据+高并发+高可用的场景,如果是海量数据,如果你的数据量很大,那么建议就用Redis cluster,所有主节点的容量总和就是Redis cluster可缓存的数据容量。

## 主从复制

Expand Down Expand Up @@ -478,6 +497,31 @@ public String get(String key) {
}
```

## 缓存预热

缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!

解决方案:

1. 直接写个缓存刷新页面,上线时手工操作一下;
2. 数据量不大,可以在项目启动的时候自动进行加载;
3. 定时刷新缓存;

## 缓存降级

当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。

缓存降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。

在进行降级之前要对系统进行梳理,看看系统是不是可以丢卒保帅;从而梳理出哪些必须誓死保护,哪些可降级;比如可以参考日志级别设置预案:

1. 一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
2. 警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;
3. 错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;
4. 严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。

服务降级的目的,是为了防止Redis服务故障,导致数据库跟着一起发生雪崩问题。因此,对于不重要的缓存数据,可以采取服务降级策略,例如一个比较常见的做法就是,Redis出现问题,不去数据库查询,而是直接返回默认值给用户。

## Redis 怎么实现消息队列?

使用一个列表,让生产者将任务使用LPUSH命令放进列表,消费者不断用RPOP从列表取出任务。
Expand Down Expand Up @@ -584,3 +628,4 @@ Redis 官方站提出了一种权威的基于 Redis 实现分布式锁的方式
1. 安全特性:互斥访问,即永远只有一个 client 能拿到锁
2. 避免死锁:最终 client 都可能拿到锁,不会出现死锁的情况,即使原本锁住某资源的 client 挂掉了
3. 容错性:只要大部分 Redis 节点存活就可以正常提供服务

0 comments on commit 76efb6e

Please sign in to comment.