-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Expand file tree
/
Copy pathredis.md
More file actions
5093 lines (3478 loc) · 261 KB
/
redis.md
File metadata and controls
5093 lines (3478 loc) · 261 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
---
title: Redis面试题,57道Redis八股文(4.6万字286张手绘图),面渣逆袭必看👍
shortTitle: 面渣逆袭-Redis
description: 下载次数超 1 万次,4.6 万字 286 张手绘图,详解 57 道 Redis 面试高频题(让天下没有难背的八股),面渣背会这些 Redis 八股文,这次吊打面试官,我觉得稳了(手动 dog)。
author: 三分恶
date: 2026-03-29
category:
- 面渣逆袭
tag:
- 面渣逆袭
head:
- - meta
- name: keywords
content: Redis面试题,Redis,八股文,面试题
---

## 前言
4.6 万字 286 张手绘图,详解 57 道 Redis 面试高频题(让天下没有难背的八股),面渣背会这些 Redis 八股文,这次吊打面试官,我觉得稳了(手动 dog)。整理:沉默王二,戳[转载链接](https://mp.weixin.qq.com/s/19u34NXALB1nOlBCE6Eg-Q),作者:三分恶,戳[原文链接](https://mp.weixin.qq.com/s/iJtNJYgirRugNBnzxkbB4Q)。
亮白版本更适合拿出来打印,这也是很多学生党喜欢的方式,打印出来背诵的效率会更高。

2025 年 04 月 27 日开始着手第二版更新。
- 对于高频题,会标注在《[Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)》中出现的位置,哪家公司,原题是什么,并且会加🌟,目录一目了然;如果你想节省时间的话,可以优先背诵这些题目,尽快做到知彼知己,百战不殆。
- 区分八股精华回答版本和原理底层解释,让大家知其然知其所以然,同时又能做到面试时的高效回答。
- 结合项目([技术派](https://javabetter.cn/zhishixingqiu/paicoding.html)、[pmhub](https://javabetter.cn/zhishixingqiu/pmhub.html))来组织语言,让面试官最大程度感受到你的诚意,而不是机械化的背诵。
- 修复第一版中出现的问题,包括球友们的私信反馈,网站留言区的评论,以及 [GitHub 仓库](https://github.com/itwanger/toBeBetterJavaer/issues)中的 issue,让这份面试指南更加完善。
- 增加[二哥编程星球](https://javabetter.cn/zhishixingqiu/)的球友们拿到的一些 offer,对面渣逆袭的感谢,以及对简历修改的一些认可,以此来激励大家,给大家更多信心。
- 优化排版,增加手绘图,重新组织答案,使其更加口语化,从而更贴近面试官的预期。

由于 PDF 没办法自我更新,所以需要最新版的小伙伴,可以微信搜【**沉默王二**】,或者扫描/长按识别下面的二维码,关注二哥的公众号,回复【**222**】即可拉取最新版本。
<div style="text-align: center; margin: 20px 0;">
<img src="https://cdn.paicoding.com/tobebetterjavaer/images/gongzhonghao.png" alt="微信扫码或者长按识别,或者微信搜索“沉默王二”" style="max-width: 100%; height: auto; border-radius: 10px;" />
</div>
当然了,请允许我的一点点私心,那就是星球的 PDF 版本会比公众号早一个月时间,毕竟星球用户都付费过了,我有必要让他们先享受到一点点福利。相信大家也都能理解,毕竟在线版是免费的,CDN、服务器、域名、OSS 等等都是需要成本的。
更别说我付出的时间和精力了,大家觉得有帮助还请给个口碑,让你身边的同事、同学都能受益到。

我把二哥的 Java 进阶之路、JVM 进阶之路、并发编程进阶之路,以及所有面渣逆袭的版本都放进来了,涵盖 Java基础、Java集合、Java并发、JVM、Spring、MyBatis、计算机网络、操作系统、MySQL、Redis、RocketMQ、分布式、微服务、设计模式、Linux 等 16 个大的主题,共有 40 多万字,2000+张手绘图,可以说是诚意满满。
展示一下暗黑版本的 PDF 吧,排版清晰,字体优雅,更加适合夜服,晚上看会更舒服一点。

## 基础
### 1.🌟说说什么是 Redis?
[Redis](https://javabetter.cn/redis/rumen.html) 是一种基于键值对的 NoSQL 数据库。

它主要的特点是把数据放在内存当中,相比直接访问磁盘的关系型数据库,读写速度会快很多,基本上能达到微秒级的响应。
所以在一些对性能要求很高的场景,比如缓存热点数据、防止接口爆刷,都会用到 Redis。
不仅如此,Redis 还支持持久化,可以将内存中的数据异步落盘,以便服务宕机重启后能恢复数据。
#### Redis 和 MySQL 的区别?
Redis 属于非关系型数据库,数据是通过键值对的形式放在内存当中的;MySQL 属于关系型数据库,数据以行和列的形式存储在磁盘当中。

实际开发中,会将 MySQL 作为主存储,Redis 作为缓存,通过先查 Redis,未命中再查 MySQL 并写回Redis 的方式来提高系统的整体性能。
#### 项目里哪里用到了 Redis?
在[技术派实战项目](https://javabetter.cn/zhishixingqiu/paicoding.html)当中,有很多地方都用到了 Redis,比如说用户活跃排行榜用到了 zset,作者白名单用到了 set。

还有用户登录后的 Session、站点地图 SiteMap,分别用到了 Redis 的字符串和哈希表两种数据类型。

其中比较有挑战性的一个应用是,通过 Lua 脚本封装 Redis 的 setnex 命令来实现分布式锁,以保证在高并发场景下,热点文章在短时间内的高频访问不会击穿 MySQL。

#### 部署过 Redis 吗?
第一种回答版本:
我只在本地部署过单机版,下载 Redis 的安装包,解压后运行 `redis-server` 命令即可。
第二种回答版本:
我有在生产环境中部署单机版 Redis,从官网下载源码包解压后执行 `make && make install` 编译安装。然后编辑 `redis.conf` 文件,开启远程访问、设置密码、限制内存、设置内存过期淘汰策略、开启 AOF 持久化等:
```
bind 0.0.0.0 # 允许远程访问
requirepass your_password # 设置密码
maxmemory 4gb # 限制内存,避免 OOM
maxmemory-policy allkeys-lru # 内存淘汰策略
appendonly yes # 开启 AOF 持久化
```
第三种回答版本:
我有使用 Docker 拉取 Redis 镜像后进行容器化部署。
```shell
docker run -d --name redis -p 6379:6379 redis:7.0-alpine
```
#### Redis 的高可用方案有部署过吗?
有部署过哨兵机制,这是一个相对成熟的高可用解决方案,我们生产环境部署的是一主两从的 Redis 实例,再加上三个 Sentinel 节点监控它们。Sentinel 的配置相对简单,主要设置了故障转移的判定条件和超时阈值。
主节点配置:
```shell
port 6379
appendonly yes
```
从节点配置:
```shell
replicaof 192.168.1.10 6379
```
哨兵节点配置:
```shell
sentinel monitor mymaster 192.168.1.10 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
sentinel parallel-syncs mymaster 1
```
当主节点发生故障时,Sentinel 能够自动检测并协商选出新的主节点,这个过程大概需要 10-15 秒。
另一个大型项目中,我们使用了 Redis Cluster 集群方案。该项目数据量大且增长快,需要水平扩展能力。我们部署了 6 个主节点,每个主节点配备一个从节点,形成了一个 3主3从 的初始集群。Redis Cluster 的设置比 Sentinel 复杂一些,需要正确配置集群节点间通信、分片映射等。
```shell
redis-server redis-7000.conf
redis-server redis-7001.conf
...
# 使用 redis-cli 创建集群
# Redis 会自动将 key 哈希到 16384 个槽位
# 主节点均分槽位,从节点自动跟随
redis-cli --cluster create \
127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 \
127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 \
--cluster-replicas 1
```
Redis Cluster 最大的优势是数据自动分片,我们可以通过简单地增加节点来扩展集群容量。此外,它的故障转移也很快,通常在几秒内就能完成。
对于一些轻量级应用,我也使用过主从复制加手动故障转移的方案。主节点负责读写操作,从节点负责读操作。手动故障转移时,我们会先将从节点提升为主节点,然后重新配置其他从节点。
```shell
# 1. 取消从节点身份
redis-cli -h <slave-ip> slaveof no one
# 2. 将其他从节点指向新的主节点
redis-cli -h <other-slave-ip> slaveof <new-master-ip> <port>
```
#### 用过哪些缓存数据库,除redis以外?
[技术派实战项目](https://javabetter.cn/zhishixingqiu/paicoding.html)中还用到了 Guava Cache 和 Caffeine 作为本地缓存,Guava Cache 适合小规模缓存,Caffeine 性能更好,支持更多高级特性。

Caffeine 通常用来作为二级缓存来使用,主要用于存储一些不经常变动的数据,以减轻 Redis 的压力。
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的华为一面原题:说下 Redis 和 HashMap 的区别
> 2. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的字节跳动商业化一面的原题:Redis 和 MySQL 的区别
> 3. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的农业银行面经同学 7 Java 后端面试原题:Redis 相关的基础知识
> 4. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的华为 OD 面经同学 1 一面面试原题:Redis 的了解, 部署方案?
> 5. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的农业银行面经同学 3 Java 后端面试原题:项目里哪里用到了 Redis
> 6. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的 360 面经同学 3 Java 后端技术一面面试原题:用过 redis 吗 用来干什么
> 7. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的招商银行面经同学 6 招银网络科技面试原题:了解 MySQL、Redis 吗?
> 8. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的百度面经同学 1 文心一言 25 实习 Java 后端面试原题:项目中什么地方使用了 redis 缓存,redis 为什么快?
> 9. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的国企零碎面经同学 9 面试原题:数据库用什么多(说了 Mysql 和 Redis)
> 10. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的荣耀面经同学 4 面试原题:Redis和MySQL的区别?
> 11. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的海康威视同学 4面试原题:Redis部署
> 12. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的华为 OD 面经同学 1 一面面试原题:Redis 的了解, 部署方案?
> 13. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的同学 30 腾讯音乐面试原题:redis的部署方式都有哪些呢,各自有什么优缺点?
memo:2025 年 9 月 23 日修改至此,今天[帮球友修改简历](https://javabetter.cn/zhishixingqiu/jianli.html)的时候,收到一位球友的反馈说,从 8.16 加入星球以来,每天都在星球里充电学习,学到了很多东西。对于这种正反馈我是非常开心的。

### 2.Redis 可以用来干什么?
Redis 可以用来做缓存,比如说把高频访问的文章详情、商品信息、用户信息放入 Redis 当中,并通过设置过期时间来保证数据一致性,这样就可以减轻数据库的访问压力。

Redis 的 Zset 还可以用来实现积分榜、热搜榜,通过 score 字段进行排序,然后取前 N 个元素,就能实现 TOPN 的榜单功能。

利用 Redis 的 SETNX 命令或者 Redisson 还可以实现分布式锁,确保同一时间只有一个节点可以持有锁;为了防止出现死锁,可以给锁设置一个超时时间,到期后自动释放;并且最好开启一个监听线程,当任务尚未完成时给锁自动续期。

如果是秒杀接口,还可以使用 Lua 脚本来实现令牌桶算法,限制每秒只能处理 N 个请求。
```lua
-- KEYS[1]: 令牌桶的key
-- ARGV[1]: 桶容量
-- ARGV[2]: 令牌生成速率(每秒)
-- ARGV[3]: 当前时间戳(秒)
local bucket = redis.call('HMGET', KEYS[1], 'tokens', 'timestamp')
local tokens = tonumber(bucket[1]) or ARGV[1]
local last_time = tonumber(bucket[2]) or ARGV[3]
local rate = tonumber(ARGV[2])
local capacity = tonumber(ARGV[1])
local now = tonumber(ARGV[3])
-- 计算新令牌数
local delta = math.max(0, now - last_time)
local add_tokens = delta * rate
tokens = math.min(capacity, tokens + add_tokens)
last_time = now
local allowed = 0
if tokens >= 1 then
tokens = tokens - 1
allowed = 1
end
redis.call('HMSET', KEYS[1], 'tokens', tokens, 'timestamp', last_time)
redis.call('EXPIRE', KEYS[1], 3600) -- 过期时间可自定义
return allowed
```
在 Java 中调用 Lua 脚本:
```java
// 令牌桶参数
int capacity = 10; // 桶容量
int rate = 2; // 每秒2个令牌
long now = System.currentTimeMillis() / 1000;
String key = "token_bucket:user:123";
// 调用 Lua 脚本,返回 1 表示通过,0 表示被限流
Long allowed = (Long) redis.eval(luaScript, 1, key, String.valueOf(capacity), String.valueOf(rate), String.valueOf(now));
```
#### redis做缓存要考虑哪些问题,在业务方面呢
一类是经典的缓存系统设计问题(穿透、击穿、雪崩),另一类是与业务逻辑紧密相关的业务缓存问题(数据一致性、缓存粒度等)。
当修改了数据库的数据后,如何保证缓存里的数据也同步更新?如果处理不好,用户就会看到“脏数据”。
另外就是我们应该缓存一个完整的、包含各种关联信息的复杂对象,还是只缓存那些最常用的基础字段?
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的农业银行面经同学 7 Java 后端面试原题:Redis 相关的基础知识
> 2. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的字节跳动同学 7 Java 后端实习一面的原题:讲一下为什么要用 Redis 去存权限列表?
> 3. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的字节跳动同学 20 测开一面的原题:redis 有什么好处,为什么用 redis
memo:2025 年 4 月 28 日修改至此,今天[帮球友修改简历](https://javabetter.cn/zhishixingqiu/jianli.html)的时候,碰到一位东南大学本硕连读的球友,星球能来这么多优秀的球友,真的很开心啊。

### 3.🌟Redis有哪些数据类型?
Redis 支持五种基本数据类型,分别是字符串、列表、哈希、集合和有序集合。

还有三种扩展数据类型,分别是用于位级操作的 Bitmap、用于基数估算的 HyperLogLog、支持存储和查询地理坐标的 GEO。
#### 详细介绍下字符串?
字符串是最基本的数据类型,可以存储文本、数字或者二进制数据,最大容量是 512 MB。

适合缓存单个对象,比如验证码、token、计数器等。
#### 详细介绍下列表?
列表是一个有序的元素集合,支持从头部或尾部插入/删除元素,常用于消息队列或任务列表。

#### 详细介绍下哈希?
哈希是一个键值对集合,适合存储对象,如商品信息、用户信息等。比如说 `value = {name: '沉默王二', age: 18}`。

#### 详细介绍下集合?
集合是无序且不重复的,支持交集、并集操作,查询效率能达到 `O(1)` 级别,主要用于去重、标签、共同好友等场景。

#### 详细介绍下有序集合?
有序集合的元素按分数进行排序,支持范围查询,适用于排行榜或优先级队列。

#### 详细介绍下Bitmap?
Bitmap 可以把一组二进制位紧凑地存储在一块连续内存中,每一位代表一个对象的状态,比如是否签到、是否活跃等。

比如用户 0 的已签到 1、用户 1 未签到 0、用户 2 已签到,Redis 就会把这些状态放进一个连续的二进制串 `101`,1 亿用户签到仅需 `100,000,000 / 8 / 1024 ≈ 12MB` 的空间,真的省到离谱。
#### 详细介绍下HyperLogLog?
HyperLogLog 是一种用于基数统计的概率性数据结构,可以在仅有 12KB 的内存空间下,统计海量数据集中不重复元素的个数,误差率仅 0.81%。

底层基于 LogLog 算法改进,先把每个元素哈希成一个二进制串,然后取前 14 位进行分组,放到 16384 个桶中,记录每组最大的前导零数量,最后用一个近似公式推算出总体的基数。

>$2^{14}$个桶,每个桶 6 Bit,刚好 `16384 * 6 /8 / 1024 K = 12KB`,8 bit = 1 byte。
举个超简单的例子,假设有一个神奇的哈希函数,可以把元素散列成一个二进制数,比如:
元素| 哈希值| 前导零个数
---|---|---
userA| 000100101…| 3
userB| 001010011…| 2
userC| 000000101…| 6
可以发现,哈希值越长前导零越多,也就说明集合里的元素越多。
大型网站 UV 统计系统示例:
```java
public class UVCounter {
private Jedis jedis;
public void recordVisit(String date, String userId) {
String key = "uv:" + date;
jedis.pfadd(key, userId);
}
public long getUV(String date) {
return jedis.pfcount("uv:" + date);
}
public long getUVBetween(String startDate, String endDate) {
List<String> keys = getDateKeys(startDate, endDate);
return jedis.pfcount(keys.toArray(new String[0]));
}
}
```
#### 详细介绍下GEO?
GEO 用于存储和查询地理位置信息,可以用来计算两点之间的距离,查找某位置半径内的其他元素。
常见的应用场景包括:附近的人或者商家、计算外卖员和商家的距离、判断用户是否进入某个区域等。
底层基于 ZSet 实现,通过 Geohash 算法把经纬度编码成 score。

比如说查询附近的商家时,Redis 会根据中心点经纬度反推可能的 Geohash 范围,
在 ZSet 上做范围查询,拿到候选点后,用 Haversine 公式精确计算球面距离,筛选出最终符合要求的位置。
```java
public class NearbyShopService {
private Jedis jedis;
private static final String SHOP_KEY = "shops:geo";
// 添加商铺
public void addShop(String shopId, double longitude, double latitude) {
jedis.geoadd(SHOP_KEY, longitude, latitude, shopId);
}
// 查询附近的商铺
public List<GeoRadiusResponse> getNearbyShops(
double longitude,
double latitude,
double radiusKm) {
return jedis.georadius(SHOP_KEY,
longitude,
latitude,
radiusKm,
GeoUnit.KM,
GeoRadiusParam.geoRadiusParam()
.withCoord()
.withDist()
.sortAscending()
.count(20));
}
// 计算两个商铺之间的距离
public double getShopDistance(String shop1Id, String shop2Id) {
return jedis.geodist(SHOP_KEY,
shop1Id,
shop2Id,
GeoUnit.KILOMETERS);
}
}
```
#### 为什么使用 hash 类型而不使用 string 类型序列化存储?
Hash 可以只读取或者修改某一个字段,而 String 需要一次性把整个对象取出来。

比如说有一个用户对象 `user = {name: '沉默王二', age: 18}`,如果使用 Hash 存储,可以直接修改 `age` 字段:
```java
redis.hset("user:1", "age", 19);
```
如果使用 String 存储,需要先取出整个对象,修改后再存回去:
```java
String userJson = redis.get("user:1");
User user = JSON.parseObject(userJson, User.class);
user.setAge(19);
redis.set("user:1", JSON.toJSONString(user));
```
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的字节跳动商业化一面的原题:说说 Redis 的 zset,什么是跳表,插入一个节点要构建几层索引
> 2. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的字节跳动面经同学 9 飞书后端技术一面面试原题:Redis 的数据类型,ZSet 的实现
> 3. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的小米暑期实习同学 E 一面面试原题:你对 Redis 了解多少,说说常见的数据结构和应用场景
> 4. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的腾讯面经同学 23 QQ 后台技术一面面试原题:Redis 的数据类型
> 5. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的快手面经同学 7 Java 后端技术一面面试原题:说一下 Redis 常用的数据结构
> 6. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的农业银行面经同学 7 Java 后端面试原题:Redis 相关的基础知识
> 7. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的华为面经同学 11 面试原题:项目中使用了 redis,redis 有哪些数据类型?分别使用的场景是什么?什么使用 hash 类型而不使用 string 类型序列化存储?
> 8. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的 OPPO 面经同学 1 面试原题:Redis常见数据结构
> 9. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的美团同学 9 一面面试原题:redis的数据结构类型?
> 10. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的阿里云面经同学 22 面经:redis高级数据结构的使用场景
> 11. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的腾讯面经同学 29 Java 后端一面原题:Redis保证incr命令原子性的原理是什么?
memo:2025 年 4 月 29 日修改至此,今天[有球友发信息](https://javabetter.cn/zhishixingqiu/)说拿到了亚马逊的 offer,工资还给的很高,问我要不要选? 真的恭喜了🎉。

### 4.🌟Redis 为什么快呢?
第一,Redis 的所有数据都放在内存中,而内存的读写速度本身就比磁盘快几个数量级。

第二,Redis 采用了基于 IO 多路复用技术的事件驱动模型来处理客户端请求和执行 Redis 命令。

其中的 IO 多路复用技术可以在只有一个线程的情况下,同时监听成千上万个客户端连接,解决传统 IO 模型中每个连接都需要一个独立线程带来的性能开销。

IO 多路复用会持续监听请求,然后把准备好的请求压入到一个队列当中,并将其有序地传递给文件事件分派器,最后由事件处理器来执行对应的 accept、read 和 write 请求。

Redis 会根据操作系统选择最优的 IO 多路复用技术,比如 Linux 下使用 epoll,macOS 下使用 kqueue 等。
```c
// epoll 的创建和使用
int epfd = epoll_create(1024); // 创建 epoll 实例
struct epoll_event ev, events[MAX_EVENTS];
// 添加监听事件
ev.events = EPOLLIN;
ev.data.fd = listen_sock;
epoll_ctl(epfd, EPOLL_CTL_ADD, listen_sock, &ev);
// 等待事件发生
while (1) {
int nfds = epoll_wait(epfd, events, MAX_EVENTS, -1);
for (int i = 0; i < nfds; i++) {
// 处理就绪的文件描述符
}
}
```
在 Redis 6.0 之前,包括连接建立、请求读取、响应发送,以及命令执行都是在主线程中顺序执行的,这样可以避免多线程环境下的锁竞争和上下文切换,因为 Redis 的绝大部分操作都是在内存中进行的,性能瓶颈主要是内存操作和网络通信,而不是 CPU。

为了进一步解决网络 IO 的性能瓶颈,Redis 6.0 引入了多线程机制,把网络 IO 和命令执行分开,网络 IO 交给线程池来处理,而命令执行仍然在主线程中进行,这样就可以充分利用多核 CPU 的性能。

主线程专注于命令执行,网络IO 由其他线程分担,在多核 CPU 环境下,Redis 的性能可以得到显著提升。

第三,Redis 对底层数据结构做了极致的优化,比如说 String 的底层数据结构动态字符串支持动态扩容、预分配冗余空间,能够减少内存碎片和内存分配的开销。

总结:

> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的腾讯 Java 后端实习一面原题:Redis 为什么读写性能高?
> 2. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的小米春招同学 K 一面面试原题:为什么 redis 快,淘汰策略 持久化
> 3. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的字节跳动面经同学 1 Java 后端技术一面面试原题:单线程的 Redis 为什么这么快?
> 4. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的微众银行同学 1 Java 后端一面的原题:Redis 为什么这么快?
> 5. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的百度面经同学 1 文心一言 25 实习 Java 后端面试原题:项目中什么地方使用了 redis 缓存,redis 为什么快?
> 6. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的得物面经同学 8 一面面试原题:Redis 为什么快
> 7. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的字节跳动面经同学 21 抖音商城一面面试原题:redis为什么能处理高并发
memo:2025 年 4 月 30 日修改至此,今天[有球友发信息](https://javabetter.cn/zhishixingqiu/)说拿到了滴滴的实习 offer,真的恭喜了🎉。

### 5.能详细说一下IO多路复用吗?
IO 多路复用是一种允许单个进程同时监视多个文件描述符的技术,使得程序能够高效处理多个并发连接而无需创建大量线程。

IO 多路复用的核心思想是:让单个线程可以等待多个文件描述符就绪,然后对就绪的描述符进行操作。这样可以在不使用多线程或多进程的情况下处理并发连接。

主要的实现机制包括 select、poll、epoll、kqueue 和 IOCP 等。
#### 请说说 select、poll、epoll、kqueue 和 IOCP 的区别?
select 的缺点是单个进程能监视的文件描述符数量有限,一般为 1024 个,且每次调用都需要将文件描述符集合从用户态复制到内核态,然后遍历找出就绪的描述符,性能较差。
```c
// select 的基本使用
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
// 示例代码
fd_set readfds;
FD_ZERO(&readfds); // 清空集合
FD_SET(sockfd, &readfds); // 添加监听套接字
select(sockfd + 1, &readfds, NULL, NULL, NULL);
if (FD_ISSET(sockfd, &readfds)) { // 检查是否就绪
// 处理读事件
}
```
poll 的优点是没有最大文件描述符数量的限制,但是每次调用仍然需要将文件描述符集合从用户态复制到内核态,依然需要遍历,性能仍然较差。
```c
// poll 的基本使用
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
// 示例代码
struct pollfd fds[MAX_EVENTS];
fds[0].fd = sockfd;
fds[0].events = POLLIN; // 监听读事件
poll(fds, 1, -1);
if (fds[0].revents & POLLIN) {
// 处理读事件
}
```
epoll 是 Linux 特有的 IO 多路复用机制,支持大规模并发连接,使用事件驱动模型,性能更高。其工作原理是将文件描述符注册到内核中,然后通过事件通知机制来处理就绪的文件描述符,不需要轮询,也不需要数据拷贝,更没有数量限制,所以性能非常高。
```c
// epoll 的基本使用
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
// 示例代码
int epfd = epoll_create(1);
struct epoll_event ev, events[MAX_EVENTS];
ev.events = EPOLLIN;
ev.data.fd = sockfd;
epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev);
while (1) {
int nfds = epoll_wait(epfd, events, MAX_EVENTS, -1);
for (int i = 0; i < nfds; i++) {
if (events[i].data.fd == sockfd) {
// 处理读事件
}
}
}
```
kqueue 是 BSD/macOS 系统下的 IO 多路复用机制,类似于 epoll,支持大规模并发连接,使用事件驱动模型。
```c
int kqueue(void);
int kevent(int kq, const struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout);
```
IOCP 是 Windows 系统下的 IO 多路复用机制,使用使用完成端口模型而非事件通知。
```c
HANDLE CreateIoCompletionPort(HANDLE FileHandle, HANDLE ExistingCompletionPort, ULONG_PTR CompletionKey, DWORD NumberOfConcurrentThreads);
```
#### 举个例子说一下 IO 多路复用?
比如说我是一名数学老师,上课时提出了一个问题:“今天谁来证明一下勾股定律?”
同学小王举手,我就让小王回答;小李举手,我就让小李回答;小张举手,我就让小张回答。
这种模式就是 IO 多路复用,我只需要在讲台上等,谁举手谁回答,不需要一个一个去问。

Redis 就是使用 epoll 这样的 IO 多路复用机制,在单线程模型下实现高效的网络 IO,从而支持高并发的请求处理。
#### 举例子说一下阻塞 IO和 IO 多路复用的差别?
假设我是一名老师,让学生解答一道题目。
我的第一种选择:按顺序逐个检查,先检查 A同学,然后是 B,之后是 C、D。。。这中间如果有一个学生卡住,全班都会被耽误。
这种就是阻塞 IO,不具有并发能力。

我的第二种选择,我站在讲台上等,谁举手我去检查谁。C、D 举手,我去检查 C、D 的答案,然后继续回到讲台上等。此时 E、A 又举手,然后去处理 E 和 A。
#### select、poll 和 epoll 的实现原理?
select 和 poll 都是通过把所有文件描述符传递给内核,由内核遍历判断哪些就绪。
select 将文件描述符 FD 通过 BitsMap 传入内核,轮询所有的 FD,通过调用 file->poll 函数查询是否有对应事件,没有就将 task 加入 FD 对应 file 的待唤醒队列,等待事件来临被唤醒。

poll 改进了连接数上限问题,不再用 BitsMap 来传入 FD,取而代之的是动态数组 pollfd,但本质上仍是线性遍历,性能没有提升太多。

select和poll的模式都是,一次将参数拷贝到内核空间,等有结果了再一次拷贝出去。
epoll 将监听的 FD 注册进内核的红黑树,由内核在事件触发时将就绪的 FD 放入 ready list。应用程序通过 epoll_wait 获取就绪的 FD,从而避免遍历所有连接的开销。

epoll 最大的优点是:支持事件驱动 + 边缘触发,ADD 时拷贝一次,epoll_wait 时利用 MMAP 和用户共享空间,直接拷贝数据到用户空间,因此在高并发场景下性能远高于 select 和 poll。
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的字节跳动面经同学 21 抖音商城一面面试原题:io多路复用了解吗?
> 2. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的快手同学 4 一面原题:IO多路复用中select/poll/epoll各自的实现原理和区别?
> 3. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的字节跳动面经同学19番茄小说一面面试原题:Linux中的IO多路复用
memo:2025 年 5 月 1 日修改至此,今天[帮球友修改简历时](https://javabetter.cn/zhishixingqiu/jianli.html) 时,碰到一名北京交通大学的同学,又一所 211 院校,星球真的是人才济济,大家一起加油吧(骄傲)。

### 6.Redis为什么早期选择单线程?
第一,单线程模型不需要考虑复杂的锁机制,不存在多线程环境下的死锁、竞态条件等问题,开发起来更快,也更容易维护。

第二,Redis 是IO 密集型而非 CPU 密集型,主要受内存和网络 IO 限制,而非 CPU 的计算能力,单线程可以避免线程上下文切换的开销。
哪怕我们在一个普通的 Linux 服务器上启动 Redis 服务,它也能在 1s 内处理 1000000 个用户请求。
第三,单线程可以保证命令执行的原子性,无需额外的同步机制。

Redis 虽然最初采用了单线程设计,但后续的版本中也在特定方面引入了多线程,比如说 Redis 4.0 就引异步多线程,用于清理脏数据、释放无用连接、删除大 Key 等。
```c
/* 从数据库中删除一个键、值以及相关的过期条目(如果有的话)。
* 如果释放值对象需要大量的内存分配操作,该对象可能会被放入
* 延迟释放列表中,而不是同步释放。延迟释放列表将在
* bio.c 的另一个线程中进行回收。 */
#define LAZYFREE_THRESHOLD 64
int dbAsyncDelete(redisDb *db, robj *key) {
/* 从过期字典中删除条目不会释放键的 sds,
* 因为它与主字典共享。 */
if (dictSize(db->expires) > 0) dictDelete(db->expires,key->ptr);
/* 如果值对象只包含少量的内存分配,使用延迟释放方式
* 实际上会更慢... 所以在一定阈值以下,我们就直接
* 同步释放对象。 */
dictEntry *de = dictUnlink(db->dict,key->ptr);
if (de) {
robj *val = dictGetVal(de);
// 计算value的回收收益
size_t free_effort = lazyfreeGetFreeEffort(val);
/* 如果释放对象的工作量太大,就通过将对象添加到延迟释放列表
* 在后台进行处理。
* 注意,如果对象是共享的,现在就回收它是不可能的。这种情况
* 很少发生,但是有时 Redis 核心的某些实现部分可能会调用
* incrRefCount() 来保护对象,然后调用 dbDelete()。在这种
* 情况下,我们会继续执行并到达 dictFreeUnlinkedEntry()
* 调用,这相当于仅仅调用 decrRefCount()。 */
// 只有回收收益超过一定值,才会执行异步删除,否则还是会退化到同步删除
if (free_effort > LAZYFREE_THRESHOLD && val->refcount == 1) {
atomicIncr(lazyfree_objects,1);
bioCreateBackgroundJob(BIO_LAZY_FREE,val,NULL,NULL);
dictSetVal(db->dict,de,NULL);
}
}
/* 释放键值对,如果我们将 val 字段设置为 NULL 以便稍后
* 延迟释放,那么就只释放键。 */
if (de) {
dictFreeUnlinkedEntry(db->dict,de);
if (server.cluster_enabled) slotToKeyDel(key->ptr);
return 1;
} else {
return 0;
}
}
```
官方解释:[https://redis.io/topics/faq](https://redis.io/topics/faq)
memo:2025 年 5 月 2 日修改至此,今天[帮球友修改简历时](https://javabetter.cn/zhishixingqiu/jianli.html) 时,碰到一名同济大学的同学,让感觉自己的付出正在越来越多被更多人看到,真的很开心。

### 7.Redis 6.0 使用多线程是怎么回事?
Redis 6.0 的多线程仅用于处理网络 IO,包括网络数据的读取、写入,以及请求解析。
```
│ 单线程执行命令 │
│ ↑ ↓ │
┌─────────┐ ┌─┴────────────┴──┐
│ I/O线程1 │ ←→ │ │
├─────────┤ │ │
│ I/O线程2 │ ←→ │ 主线程 │
├─────────┤ │ │
│ I/O线程3 │ ←→ │ │
└─────────┘ └─────────────────┘
```
而命令的执行依然是单线程,这种设计被称为“IO 线程化”,能够在高负载的情况下,最大限度地提升 Redis 的响应速度。

---- 这部分面试中可以不背,方便大家理解 start ----
这一变化主要是因为随着网络带宽和服务器性能的提升,Redis 的瓶颈从 CPU 逐渐转移到了网络 IO:
- 带宽从 10Gbps 提升到 100Gbps,甚至更高。
- 请求的并发数从几千到几万,甚至几十万。
单线程在高负载场景下处理网络 IO 出现了明显的性能瓶颈,Redis 的开发团队通过研究发现,在处理大数据包时,单线程 Redis 有超过 80% 的 CPU 时间花在网络 IO 上,而实际命令执行仅占 20% 左右。

Redis 6.0 的多线程 IO 模型主要包含三个核心步骤:
- 仍然由主线程负责接收客户端的连接请求。
- 主线程将连接请求分发给多个 IO 线程进行处理,主线程负责解析和执行命令。
- 命令执行完毕后,由多个 IO 线程将结果返回给客户端。
```c
// Redis 主事件循环(简化版)
void beforeSleep(struct aeEventLoop *eventLoop) {
// 1. 主线程分派读任务给 I/O 线程
handleClientsWithPendingReadsUsingThreads();
// 2. 等待 I/O 线程完成读取
waitForIOThreads();
// 3. 主线程处理命令
processInputBuffer();
// 4. 主线程分派写任务给 I/O 线程
handleClientsWithPendingWritesUsingThreads();
}
```
Redis 6.0 默认仍然使用单线程模式,但可以通过配置文件或命令行参数启用多线程模式。
```shell
# 启用多线程模式
io-threads 4
# 启用多线程写入(Redis 6.0 默认只开启多线程读取)
io-threads-do-reads yes
```
建议将 IO 线程数设置为 CPU 核心数的一半,一般不建议超过 8 个。
经过多次测试,Redis 6.0 在处理 1-200 字节的小数据包时,性能提升 1.5-2 倍;在处理 1KB 以上的大数据包时提升约 3-5 倍。
----这部分面试中可以不背,方便大家理解 end ----
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的同学 30 腾讯音乐面试原题:redis6.0引入的多线程用作什么地方
### 8.说说 Redis 的常用命令(补充)
> 2024 年 04 月 11 日增补
一句话回答(也不用全部都背,挑三个就行):
Redis 支持多种数据结构,常用的命令也比较多,比如说操作字符串可以用 `SET/GET/INCR`,操作哈希可以用 `HSET/HGET/HGETALL`,操作列表可以用 `LPUSH/LPOP/LRANGE`,操作集合可以用 `SADD/SISMEMBER`,操作有序集合可以用 `ZADD/ZRANGE/ZINCRBY`等,通用命令有 `EXPIRE/DEL/KEYS` 等。
----这部分面试中可以不背,方便大家理解 start----
①、操作字符串的命令有:
命令| 作用| 示例
---|---|---
`SET key value`| 设置字符串键值| `SET name jack`
`GET key`| 获取字符串值| `GET name`
`INCR key`| 数值自增 1| `INCR count`
`DECR key`| 数值自减 1| `DECR stock`
`INCRBY key N`| 增加 N| `INCRBY views 10`
`APPEND key value`| 追加字符串| `APPEND log "done"`
`GETRANGE key start end`| 获取子串| `GETRANGE name 0 3`
`MSET k1 v1 k2 v2`| 批量设置多个键值| `MSET a 1 b 2`
②、操作列表的命令有:
- `LPUSH key value`:将一个值插入到列表 key 的头部。
- `RPUSH key value`:将一个值插入到列表 key 的尾部。
- `LPOP key`:移除并返回列表 key 的头元素。
- `RPOP key`:移除并返回列表 key 的尾元素。
- `LRANGE key start stop`:获取列表 key 中指定范围内的元素。
③、操作集合的命令有:
- `SADD key member`:向集合 key 添加一个元素。
- `SREM key member`:从集合 key 中移除一个元素。
- `SMEMBERS key`:返回集合 key 中的所有元素。
④、操作有序集合的命令有:
- `ZADD key score member`:向有序集合 key 添加一个成员,或更新其分数。
- `ZRANGE key start stop [WITHSCORES]`:按照索引区间返回有序集合 key 中的成员,可选 WITHSCORES 参数返回分数。
- `ZREVRANGE key start stop [WITHSCORES]`:返回有序集合 key 中,指定区间内的成员,按分数递减。
- `ZREM key member`:移除有序集合 key 中的一个或多个成员。
⑤、操作哈希的命令有:
- `HSET key field value`:向键为 key 的哈希表中设置字段 field 的值为 value。
- `HGET key field`:获取键为 key 的哈希表中字段 field 的值。
- `HGETALL key`:获取键为 key 的哈希表中所有的字段和值。
- `HDEL key field`:删除键为 key 的哈希表中的一个或多个字段。
#### 详细说说 set 命令?
SET 命令用于设置字符串的 key,支持过期时间和条件写入,常用于设置缓存、实现分布式锁、延长 Session 等场景。
```shell
SET key value [EX seconds | PX milliseconds | EXAT timestamp | PXAT timestamp-milliseconds | KEEPTTL] [NX | XX] [GET]
```
默认情况下,SET 会覆盖键已有的值。
支持多种设置过期时间的方式,比如说 EX 设置秒级过期时间,PX 设置毫秒过期时间。
支持条件写入,使其可以实现原子性操作,比如说 NX 仅在键不存在时设置值,XX 仅在键存在时设置值。

缓存实现:
```shell
SET user:profile:{userid} {JSON数据} EX 3600 # 存储用户资料,并设置1小时过期
```
实现分布式锁:
```shell
SET lock:resource_name {random_value} EX 10 NX # 获取锁,10秒后自动释放
```
存储 Session:
```shell
SET session:{sessionid} {session_data} EX 1800 # 存储用户会话,30分钟过期
```
#### sadd 命令的时间复杂度是多少?
SADD 支持一次添加多个元素,返回值为实际添加成功的元素数量,时间复杂度为 O(N)。
```shell
redis-cli SADD myset "apple" "banana" "orange"
```
#### incr命令了解吗?
INCR 是一个原子命令,可以将指定键的值加 1,如果 key 不存在,会先将其设置为 0,再执行加 1 操作。

常用于网站访问量、文章点赞数等计数器的实现;结合过期时间实现限流器;生成分布式唯一 ID;库存扣减等。
```shell
# 限制用户每分钟最多访问10次
FUNCTION limit_api_call(user_id)
current = INCR("rate:"+user_id)
IF current == 1 THEN
EXPIRE("rate:"+user_id, 60)
END
IF current > 10 THEN
RETURN false # 超出限制
ELSE
RETURN true # 允许访问
END
END
```
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的京东面经同学 1 Java 技术一面面试原题:说说 Redis 常用命令
> 2. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的农业银行面经同学 3 Java 后端面试原题:说的那么好,Redis 设置 key value 的函数是啥
> 3. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的快手面经同学 1 部门主站技术部面试原题:Redis 的 sadd 命令时间复杂度是多少?
memo:2025 年 5 月 3 日修改至此,今天[有球友发信息](https://javabetter.cn/zhishixingqiu/)说拿到了美的的软开暑期实习 offer,虽然他自己不满意,但暂时没有其他更好的,我建议他先去试一下🎉。

### 9.单线程的Redis QPS 能到多少?(补充)
> 2024 年 4 月 14 日增补
根据[官方的基准测试](https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/benchmarks/),一个普通服务器的 Redis 实例通常可以达到每秒十万左右的 QPS。

----这部分面试中可以不背,方便大家理解 start ----
Redis 的 QPS(每秒请求数)性能取决于多种因素,包括硬件配置、网络延迟、数据结构、命令类型等。
可以通过 `redis-benchmark` 命令进行基准测试:
```shell
redis-benchmark -h 127.0.0.1 -p 6379 -c 50 -n 10000
```
- `-h`:指定 Redis 服务器的地址,默认是 127.0.0.1。
- `-p`:指定 Redis 服务器的端口,默认是 6379。
- `-c`:并发连接数,即同时有多少个客户端在进行测试。
- `-n`:请求总数,即测试过程中总共要执行多少个请求。
2023 年前,我用的是一台 macOS,4 GHz 四核 Intel Core i7,32 GB 1867 MHz DDR3,测试结果如下:

可以看得出,每秒能处理超过 10 万次请求。
```
QPS = 总请求数 / 总耗时 = 10000 / 0.09 ≈ 111111 QPS
```
延迟也非常低,99% 的请求都在 0.3ms 以内完成了。
----这部分面试中可以不背,方便大家理解 end ----
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的字节跳动面经同学 1 Java 后端技术一面面试原题:单线程 Redis 的 QPS 是多少?
<MZNXQRcodeBanner />
## 持久化
### 10.🌟Redis的持久化方式有哪些?
主要有两种,RDB 和 AOF。RDB 通过创建时间点快照来实现持久化,AOF 通过记录每个写操作命令来实现持久化。

这两种方式可以单独使用,也可以同时使用。这样就可以保证 Redis 服务器在重启后不丢失数据,通过 RDB 和 AOF 文件来恢复内存中原有的数据。

#### 详细说一下 RDB?
RDB 持久化机制可以在指定的时间间隔内将 Redis 某一时刻的数据保存到磁盘上的 RDB 文件中,当 Redis 重启时,可以通过加载这个 RDB 文件来恢复数据。

RDB 持久化可以通过 save 和 bgsave 命令手动触发,也可以通过配置文件中的 save 指令自动触发。

save 命令会阻塞 Redis 进程,直到 RDB 文件创建完成。

bgsave 命令会在后台 fork 一个子进程来执行 RDB 持久化操作,主进程不会被阻塞。