1
1
# Redis面试题
2
2
3
- ### redis是什么
3
+ ### Redis是什么
4
4
5
5
Redis是C语言开发的一个开源的(遵从BSD协议)高性能键值对(key-value)的内存数据库,可以用作数据库、缓存、消息中间件等。它是一种NoSQL(not-only sql,泛指非关系型数据库)的数据库。
6
6
@@ -14,7 +14,14 @@ Redis作为一个内存数据库。 性能优秀,数据在内存中,读写
14
14
15
15
可以用作分布式锁; 可以作为消息中间件使用,支持发布订阅
16
16
17
- ### redis 和 memcached 的区别
17
+ ### 应用场景⭐
18
+
19
+ 1 . 缓存
20
+ 2 . 共享Session
21
+ 3 . 消息队列系统
22
+ 4 . 分布式锁
23
+
24
+ ### Redis 和 memcached 的区别
18
25
19
26
1 . ** redis支持更丰富的数据类型(支持更复杂的应用场景)** :Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储。memcache支持简单的数据类型,String。
20
27
2 . ** Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用,而Memecache把数据全部存在内存之中。**
@@ -23,11 +30,11 @@ Redis作为一个内存数据库。 性能优秀,数据在内存中,读写
23
30
24
31
### redis 为什么是单线程的?
25
32
26
- 因为 cpu 不是 Redis 的瓶颈,Redis 的瓶颈最有可能是机器内存或者网络带宽。既然单线程容易实现,而且 cpu 又不会成为瓶颈,那就顺理成章地采用单线程的方案了。 可以避免多线程上下文切换。
33
+ 因为 cpu 不是 Redis 的瓶颈,Redis 的瓶颈最有可能是机器内存或者网络带宽。既然单线程容易实现,而且 cpu 又不会成为瓶颈,那就顺理成章地采用单线程的方案了。 可以避免多线程上下文切换。
27
34
28
35
29
36
30
- ### 为什么redis这么快?
37
+ ### 为什么Redis这么快?⭐
31
38
32
39
完全基于内存,绝大部分请求是纯粹的内存操作,执行效率高
33
40
采用单线程,单线程也能处理高并发请求,想多核也可启动多实例
@@ -36,9 +43,13 @@ Redis作为一个内存数据库。 性能优秀,数据在内存中,读写
36
43
37
44
核心是基于非阻塞的 IO 多路复用机制。
38
45
39
- ### redis 支持的数据类型有哪些?应用?
46
+ ### Redis 支持的数据类型有哪些?应用?⭐
40
47
41
- string、list、hash、set、zset。
48
+ 1 . String字符串:字符串类型是 Redis 最基础的数据结构,首先键都是字符串类型, Value 不仅是 String,也可以是数字。常用在缓存、计数、共享Session、限速等。
49
+ 2 . Hash哈希:在Redis中,哈希类型是指键值本身又是一个键值对结构,哈希可以用来存放用户信息,比如实现购物车。
50
+ 3 . List列表(双向链表):列表(list)类型是用来存储多个有序的字符串。可以做简单的消息队列的功能。 数据结构:List 就是链表,可以用来当消息队列用。Redis 提供了 List 的 Push 和 Pop 操作,还提供了操作某一段的 API,可以直接查询或者删除某一段的元素。 实现方式:Redis List 的是实现是一个双向链表,既可以支持反向查找和遍历,更方便操作,不过带来了额外的内存开销。
51
+ 4 . Set集合:集合(set)类型也是用来保存多个的字符串元素,集合是通过 hashtable 实现的。 但和列表类型不一样的是,集合中不允许有重复元素,并且集合中的元素是无序的,不能通过索引下标获取元素。利用 Set 的交集、并集、差集等操作,可以计算共同喜好,全部的喜好,自己独有的喜好等功能。
52
+ 5 . Sorted Set有序集合(跳表实现):Sorted Set 多了一个权重参数 Score,集合中的元素能够按 Score 进行排列。实现方式:Redis Sorted Set 的内部使用 HashMap 和跳跃表(skipList)来保证数据的存储和有序,HashMap 里放的是成员到 Score 的映射。
42
53
43
54
** String 在你们项目怎么用的?**
44
55
@@ -92,6 +103,10 @@ eg: user:id:3506728370
92
103
93
104
在直播系统中,实时排行信息包含直播间在线用户列表,各种礼物排行榜,弹幕消息(可以理解为按消息维度的消息排行榜)等信息,适合使用 Redis 中的 Sorted Set 结构进行存储。
94
105
106
+ ### zset调表的数据结构⭐
107
+
108
+
109
+
95
110
### redis 设置过期时间
96
111
97
112
Redis中有个设置时间过期的功能,即对存储在 redis 数据库中的值可以设置一个过期时间。作为一个缓存数据库,这是非常实用的。如我们一般项目中的 token 或者一些登录信息,尤其是** 短信验证码都是有时间限制的** ,按照传统的数据库处理方式,一般都是自己判断过期,这样无疑会严重影响项目性能。
@@ -100,6 +115,8 @@ Redis中有个设置时间过期的功能,即对存储在 redis 数据库中
100
115
101
116
如果假设你设置了一批 key 只能存活1个小时,那么接下来1小时后,redis是怎么对这批key进行删除的?
102
117
118
+ ### 数据过期策略⭐
119
+
103
120
** 定期删除+惰性删除。**
104
121
105
122
通过名字大概就能猜出这两个删除方式的意思了。
@@ -109,47 +126,31 @@ Redis中有个设置时间过期的功能,即对存储在 redis 数据库中
109
126
110
127
但是仅仅通过设置过期时间还是有问题的。我们想一下:如果定期删除漏掉了很多过期 key,然后你也没及时去查,也就没走惰性删除,此时会怎么样?如果大量过期key堆积在内存里,导致redis内存块耗尽了。怎么解决这个问题呢? ** redis 内存淘汰机制。**
111
128
112
- ### 数据淘汰机制
129
+ ### 数据淘汰机制⭐
113
130
114
131
当内存到达最大内存限制时进行的数据淘汰策略
115
132
116
- ** 配置**
117
-
118
- 最大可用内存:maxmemory //默认为0,一般设置全部内存50%以上
119
- 每次选取带删除数据个数:maxmemory-samples //采用随机获取方式
120
- 删除策略:maxmemory-policy //达到最大内存后,对被选取带数据进行的删除策略
121
-
122
- ** 检测易失数据集( ** 设置了过期时间的键空间)
123
- volatile-lru:挑选最近最少使用的数据淘汰(最近数据中使用时间离当前最远的数据)。** 常用**
124
- volatile-lfu:挑选最近使用次数最少的数据淘汰(最近数据中使用次数最少的数据)
125
- volatile-ttl:挑选将要过期数据淘汰
126
- volatile-random:任意挑选数据淘汰
127
-
128
- ** 检测全库数据(所有数据集)**
129
- allkeys-lru:挑选最近最少使用的数据淘汰
130
- allkeys-lfu:挑选最近使用次数最少的数据淘汰
131
- allkeys-random:任意挑选数据淘汰
132
-
133
- ** 放弃数据驱逐**
134
-
135
- no-enviction //禁止驱逐数据 4.0中默认策略,会引发OOM
133
+ 1 . 新写入操作会报错。(Redis 默认策略)
134
+ 2 . 在键空间中,移除最近最少使用的 Key。(LRU推荐使用)
135
+ 3 . 在键空间中,随机移除某个 Key。
136
+ 4 . 在设置了过期时间的键空间中,移除最近最少使用的 Key。这种情况一般是把 Redis 既当缓存,又做持久化存储的时候才用。
137
+ 5 . 在设置了过期时间的键空间中,随机移除某个 Key。
138
+ 6 . 在设置了过期时间的键空间中,有更早过期时间的 Key 优先移除。
136
139
137
140
** LRU 算法实现** :1.通过双向链表来实现,新数据插入到链表头部;2.每当缓存命中(即缓存
138
141
数据被访问),则将数据移到链表头部;3.当链表满的时候,将链表尾部的数据丢弃。
139
142
LinkedHashMap:HashMap 和双向链表合二为一即是 LinkedHashMap。HashMap 是无序
140
143
的,LinkedHashMap 通过维护一个额外的双向链表保证了迭代顺序。该迭代顺序可以是插
141
144
入顺序(默认),也可以是访问顺序。
142
145
146
+ ### Redis的LRU具体实现:
143
147
148
+ 传统的LRU是使用栈的形式,每次都将最新使用的移入栈顶,但是用栈的形式会导致执行select * 的时候大量非热点数据占领头部数据,所以需要改进。Redis每次按key获取一个值的时候,都会更新value中的lru字段为当前秒级别的时间戳。Redis初始的实现算法很简单,随机从dict中取出五个key,淘汰一个lru字段值最小的。在3.0的时候,又改进了一版算法,首先第一次随机选取的key都会放入一个pool中(pool的大小为16),pool中的key是按lru大小顺序排列的。接下来每次随机选取的keylru值必须小于pool中最小的lru才会继续放入,直到将pool放满。放满之后,每次如果有新的key需要放入,需要将pool中lru最大的一个key取出。淘汰的时候,直接从pool中选取一个lru最小的值然后将其淘汰。
144
149
145
- ### redis 持久化的两种方式
146
-
147
- - RDB:RDB 持久化机制,是对 redis 中的数据执行** 周期性** 的持久化。
148
- - AOF:AOF 机制对每条写入命令作为日志,以 ` append-only ` 的模式写入一个日志文件中,在 redis 重启的时候,可以通过** 回放** AOF 日志中的写入指令来重新构建整个数据集。
150
+ ### Redis 持久化的两种方式⭐
149
151
150
- 通过 RDB 或 AOF,都可以将 redis 内存中的数据给持久化到磁盘上面来,然后可以将这些数据备份到别的地方去,比如说阿里云等云服务。
151
-
152
- 如果 redis 挂了,服务器上的内存和磁盘上的数据都丢了,可以从云服务上拷贝回来之前的数据,放到指定的目录中,然后重新启动 redis,redis 就会自动根据持久化数据文件中的数据,去恢复内存中的数据,继续对外提供服务。
152
+ - RDB:快照形式是直接把内存中的数据保存到一个dump的文件中,定时保存,保存策略。 当Redis需要做持久化时,Redis会fork一个子进程,子进程将数据写到磁盘上一个临时RDB文件中。当子进程完成写临时文件后,将原来的RDB替换掉。
153
+ - AOF:把所有的对Redis的服务器进行修改的命令都存到一个文件里,命令的集合。 使用AOF做持久化,每一个写命令都通过write函数追加到appendonly.aof中。aof的默认策略是每秒钟fsync一次,在这种配置下,就算发生故障停机,也最多丢失一秒钟的数据。 缺点是对于相同的数据集来说,AOF的文件体积通常要大于RDB文件的体积。根据所使用的fsync策略,AOF的速度可能会慢于RDB。 Redis默认是快照RDB的持久化方式。对于主从同步来说,主从刚刚连接的时候,进行全量同步(RDB);全同步结束后,进行增量同步(AOF)。
153
154
154
155
如果同时使用 RDB 和 AOF 两种持久化机制,那么在 redis 重启的时候,会使用 ** AOF** 来重新构建数据,因为 AOF 中的** 数据更加完整** 。
155
156
@@ -165,9 +166,9 @@ RDB恢复数据速度比AOF快
165
166
166
167
** AOF 优点**
167
168
168
- - AOF 可以更好的保护 数据不丢失,一般 AOF 会每隔 1 秒, 最多丢失 1 秒钟的数据。
169
- - 写入性能非常高,而且文件不容易破损
170
- - ** 适合做灾难性的误删除的紧急恢复** 。
169
+ - AOF 可以更好的保护 数据不丢失,一般 AOF 会每隔 1 秒,最多丢失 1 秒钟的数据。
170
+ - 写入性能非常高,而且文件不容易破损
171
+ - ** 适合做灾难性的误删除的紧急恢复** 。
171
172
172
173
** AOF 缺点**
173
174
@@ -179,18 +180,11 @@ RDB恢复数据速度比AOF快
179
180
180
181
对数据非常敏感,建议使用默认的AOF持久化方案
181
182
AOF策略使用everysec,每秒fsync一次,该策略仍可保持很好性能,出现问题最多丢失一秒内的数据
182
- 数据呈现阶段有效性,建议使用RDB持久化方案
183
183
数据可以做到阶段内无丢失,且恢复较快,阶段点数据恢复通常使用RDB方案
184
184
185
- 注意:
186
- AOF文件存储体积较大,恢复速度较慢
187
- 利用RDB使用线紧凑的数据持久化会使Redis性能降低
188
-
189
185
综合:
190
- RDB与AOF选择实际上是在一种权衡,每种都有利有弊
191
186
如果不能承受分钟内的数据丢失,对业务数据非常敏感,选用AOF
192
- 如果能承受分钟内的数据丢失,且追求大数据集的恢复速度选用RDB
193
- 灾难恢复选用RDB
187
+ 如果能承受分钟内的数据丢失,且追求大数据集的恢复速度选用RDB,RDB 非常适合灾难恢复。
194
188
双保险策略,同时开启RDB和AOF,重启后Redis优先使用AOF来恢复数据,降低丢失数据量
195
189
196
190
@@ -205,10 +199,15 @@ RDB与AOF选择实际上是在一种权衡,每种都有利有弊
205
199
206
200
207
201
208
- ### 怎么保证缓存和数据库数据的一致性?
202
+ ### 怎么保证缓存和数据库数据的一致性?⭐
203
+
204
+ 分布式环境下非常容易出现缓存和数据库间数据一致性问题,针对这一点,如果项目对缓存的要求是强一致性的,那么就不要使用缓存。
205
+
206
+ 我们只能采取合适的策略来降低缓存和数据库间数据不一致的概率,而无法保证两者间的强一致性。
209
207
210
208
- 合理设置缓存的过期时间。
211
209
- 新增、更改、删除数据库操作时同步更新 Redis,可以使用事务机制来保证数据的一致性。
210
+ - 缓存失败时增加重试机制。
212
211
213
212
### redis 怎么实现分布式锁?
214
213
@@ -223,12 +222,11 @@ Redis 分布式锁其实就是在系统里面占一个“坑”,其他程序
223
222
### 缓存雪崩
224
223
225
224
226
-
227
225
** 在一个较短的时间内,缓存中较多的key集中过期或者缓存挂了** ,导致了** 数据库服务器崩溃**
228
226
229
227
缓存雪崩的事前事中事后的解决方案如下:
230
228
231
- 在批量往Redis存数据的时候,把每个Key的失效时间都加个随机值就好了,这样可以保证数据不会再同一时间大面积失效。
229
+ 在批量往Redis存数据的时候,把每个Key的失效时间都加个随机值就好了,这样可以保证数据不会再同一时间大面积失效。如果 Redis 是集群部署,将热点数据均匀分布在不同的 Redis 库中也能避免全部失效。或者设置热点数据永不过期,有更新操作就更新缓存就好了
232
230
233
231
### 缓存穿透
234
232
@@ -239,13 +237,13 @@ Redis 分布式锁其实就是在系统里面占一个“坑”,其他程序
239
237
240
238
解决方案:最简单粗暴的方法如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们就把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。
241
239
242
- ** 布隆过滤器(Bloom Filter)** 这个也能很好的预防缓存穿透的发生,他的原理也很简单, 就是利用高效的数据结构和算法快速判断出你这个Key是否在数据库中存在,不存在你return就好了,存在你就去查DB刷新KV再return
240
+ ** 布隆过滤器(Bloom Filter)** 这个也能很好的预防缓存穿透的发生,就是利用高效的数据结构和算法快速判断出你这个Key是否在数据库中存在,不存在你return就好了,存在你就去查DB刷新KV再return
243
241
244
242
### 缓存击穿
245
243
246
244
缓存击穿是指一个Key非常热点,在不停地扛着大量的请求,大并发集中对这一个点进行访问,当这个Key在失效的瞬间,持续的大并发直接落到了数据库上,就在这个Key的点上击穿了缓存。
247
245
248
- 解决:设置热点数据永不过期,或者加上互斥锁就搞定了 。
246
+ 解决:设置热点数据永不过期,或者加上个锁就搞定了 。
249
247
250
248
** 假如 Redis 里面有 1 亿个 key ,其中有 10w 个 个 key 是以某个固定的已知的前缀开头的,如**
251
249
** 果将它们全部找出来?**
@@ -257,4 +255,53 @@ Redis 分布式锁其实就是在系统里面占一个“坑”,其他程序
257
255
令,scan 指令可以无阻塞的提取出指定模式的 key 列表,但是会有一定的重复概率,在客
258
256
户端做一次去重就可以了,但是整体所花费的时间会比直接用 keys 指令长。
259
257
260
-
258
+ ### 实际项目中使用缓存有遇到什么问题或者会遇到什么问题你知道吗?
259
+
260
+ 缓存和数据库数据一致性问题
261
+
262
+ ### 主从复制
263
+
264
+ ** 作用:**
265
+ 读写分离:master写、slave读,提高服务器的读写负载能力
266
+ 负载均衡:基于主从结构,配合读写分离,由slave分担master负载,并根据需求的变化,改变slave的数量,通过多个从节点分担数据读取负载,大大提高Redis服务器并发量与数据吞吐量
267
+ 故障恢复:当master出现问题时,由slave提供服务,实现快速的故障恢复
268
+ 数据冗余:实现数据热备份,是持久化之外的一种数据冗余方式
269
+ 高可用基石:基于主从复制,构建哨兵模式与集群,实现Redis的高可用方案
270
+
271
+ ** 过程:**
272
+
273
+ - 从节点执行 ** slaveof IP,port** 发送指令
274
+ - 主节点响应
275
+ - 从节点保存主节点信息(IP,port),建立和主节点的 Socket 连接。
276
+ - 从节点发送 Ping 信号,主节点返回 Pong,确定两边能互相通信。
277
+ - 连接建立后,主节点将所有数据发送给从节点(数据同步)。
278
+ - 主节点把当前的数据同步给从节点后,便完成了复制的建立过程。接下来,主节点就会持续的把写命令发送给从节点,保证主从数据一致性。
279
+
280
+ ** 复制/数据同步过程分为两个阶段**
281
+
282
+ 1 . 全量复制:
283
+ slave接收到master生成的RDB文件,先清空自身的旧数据,然后执行RDB恢复过程,然后告知master已经恢复完毕。
284
+ 2 . 部分复制(增量复制)
285
+ 主节点发送数据给从节点过程中,主节点还会进行一些写操作,这时候的数据存储在复制缓冲区中。master把自己之前创建的复制缓冲区的数据发送到slave,slave接收到aof指令后执行重写操作,恢复数据。
286
+
287
+ ** 主从复制会存在以下问题:**
288
+
289
+ - 一旦主节点宕机,从节点晋升为主节点,同时需要修改应用方的主节点地址,还需要命令所有从节点去复制新的主节点,整个过程需要人工干预。
290
+ - 主节点的写能力受到单机的限制。
291
+ - 主节点的存储能力受到单机的限制。
292
+
293
+ ** 哨兵:**
294
+
295
+ 哨兵(sentinel) 是一个分布式系统,用于对主从结构中的每台服务器进行监控,当出现故障时通过投票机制选择新的master并将所有slave连接到新的master。
296
+
297
+ ** 作用:**
298
+
299
+ ** 监控**
300
+ 不断的检查master和slave是否正常运行。
301
+ master存活检测、master与slave运行情况检测
302
+
303
+ ** 通知(提醒)**
304
+ 当被监控的服务器出现问题时,向其他(哨兵间,客户端)发送通知。
305
+
306
+ ** 自动故障转移**
307
+ 断开master与slave连接,选取一个slave作为master,将其他slave连接到新的master,并告知客户端新的服务器地址
0 commit comments