基础
什么是redis
Redis 是一种基于键值对的 NoSQL 数据库,支持多种数据结构(如字符串、哈希、列表、集合、有序集合等)。它将所有数据存储在内存中,因此读写性能极高。Redis 还支持持久化(将内存数据保存到硬盘)、键过期、发布订阅等功能。
Redis是用来干什么的?
- 缓存:降低数据库压力,提高响应速度。
- 计数器:记录浏览量、点赞量等。
- 排行榜:利用有序集合实现。
- 社交网络:处理点赞、粉丝、共同好友等功能。
- 消息队列:通过发布订阅和阻塞队列实现。
- 分布式锁:利用 Redis 实现分布式环境下的锁机制。
Redis 有哪些数据结构?
- String:基础数据结构,支持缓存、计数、共享 Session 等。
- Hash:嵌套的键值对结构,适合缓存用户信息、对象等。
- List:有序字符串列表,用于消息队列、文章列表等。
- Set:无序且不重复的字符串集合,用于标签、共同关注等。
- Sorted Set:有序集合,通过权重排序,适合用户点赞统计、排行榜等
它还有三种特殊的数据结构类型
- Geospatial
- Hyperloglog
- Bitmap
Redis 的三种特殊数据类型
- Geo:Redis3.2推出的,地理位置定位,用于存储地理位置信息,并对存储的信息进行操作。
- HyperLogLog:用来做基数统计算法的数据结构,如统计网站的UV。
- Bitmaps :用一个比特位来映射某个元素的状态,在Redis中,它的底层是基于字符串类型实现的,可以把bitmaps成作一个以比特位为单位的数组
Redis为什么快?
- 数据存储在内存中,读写性能极高。
- 单线程模型避免了线程切换和竞态条件的消耗。
- 基于非阻塞的 I/O 多路复用机制。
- 使用 C 语言实现,优化了数据结构。
什么是I/O多路复用
I/O 多路复用是一种让单个线程同时处理多个 socket 连接的技术。Redis 使用 epoll
(Linux 系统)来高效地监听多个 socket 的事件,避免了大量无用操作,提升了性能。
6. Redis 为什么早期选择单线程?
官方解释是因为 Redis 的瓶颈通常是内存或网络限制,而非 CPU。单线程模型简单高效,避免了多线程带来的复杂性。Redis 4.0 之后引入了后台线程处理部分耗时操作(如清理数据、释放连接等)。
7. Redis 6.0 使用多线程是怎么回事?
Redis 6.0 引入了多线程处理数据的读写和协议解析,但命令执行仍然是单线程的。这种设计是为了提升网络 I/O 的效率,从而整体提高 Redis 的性能。
基于内存存储实现
我们都知道内存读写是比在磁盘快很多的,Redis基于内存存储实现的数据库,相对于数据存在磁盘的MySQL数据库,省去磁盘I/O的消耗。
高效的数据结构
Mysql索引为了提高效率,选择了B+树的数据结构。其实合理的数据结构,就是可以让你的应用/程序更快。先看下Redis的数据结构&内部编码图:
SDS简单动态字符串
- 字符串长度处理:Redis获取字符串长度,时间复杂度为O(1),而C语言中,需要从头开始遍历,复杂度为O(n);
- 空间预分配:字符串修改越频繁的话,内存分配越频繁,就会消耗性能,而SDS修改和空间扩充,会额外分配未使用的空间,减少性能损耗。
- 惰性空间释放:SDS 缩短时,不是回收多余的内存空间,而是free记录下多余的空间,后续有变更,直接使用free中记录的空间,减少分配。
- 二进制安全:Redis可以存储一些二进制数据,在C语言中字符串遇到'\0'会结束,而 SDS中标志字符串结束的是len属性。
持久化
8. Redis 的持久化方式有哪些?有什么区别?
- RDB:将内存中的数据生成快照保存到硬盘。优点是文件小、恢复速度快,适合备份和容灾。缺点是实时性低。
- AOF:以日志的形式记录每次写操作,重启时重放日志恢复数据。优点是实时性好,适合数据安全性要求高的场景。缺点是文件大、恢复速度慢。
9. RDB 和 AOF 各自有什么优缺点?
- RDB 优点:文件紧凑、恢复速度快、容灾性好。
- RDB 缺点:实时性低、存在兼容性问题。
- AOF 优点:实时性好、支持数据一致性。
- AOF 缺点:文件大、恢复速度慢。
10. RDB 和 AOF 如何选择?
- 如果需要高数据安全性,建议同时使用 RDB 和 AOF,Redis 重启时会优先加载 AOF 文件。
- 如果可以接受几分钟的数据丢失,可以选择仅使用 RDB。
- 如果需要频繁备份,建议使用 RDB。
11. Redis 的数据恢复?
- 从 RDB 或 AOF 文件恢复数据。恢复流程:
- 如果启用了 AOF,优先加载 AOF 文件。
- 如果 AOF 未启用或文件不存在,则加载 RDB 文件。
- 如果文件损坏,Redis 启动失败并打印错误信息。
12. Redis 4.0 的混合持久化是什么?
Redis 4.0 引入了混合持久化,将 RDB 文件的内容和增量 AOF 日志存储在一起。重启时先加载 RDB 文件,再重放增量 AOF 日志,大幅提升了恢复效率。
13. Redis 主从复制了解吗?
主从复制是将主节点(master)的数据复制到从节点(slave)。主节点负责写操作,从节点负责读操作,实现数据冗余和负载均衡。
14. 主从复制的作用?
- 数据冗余:实现数据的热备份。
- 故障恢复:主节点故障时,从节点可以快速接管。
- 负载均衡:通过读写分离分担服务器负载。
15. 主从复制的常见拓扑结构?
- 一主一从:简单复制结构。
- 一主多从:适合读多写少的场景。
- 树状主从:通过引入中间层降低主节点的同步负担。
16. 主从复制的原理?
- 主节点将数据全量复制给从节点。
- 主节点持续将写命令发送给从节点,保证数据一致性。
17. 主从数据同步的方式?
- 全量复制:初次复制时,主节点将所有数据发送给从节点。
- 部分复制:网络中断后恢复,主节点发送丢失的命令数据。
18. 主从复制存在的问题?
- 主节点故障时需要手动切换。
- 写能力和存储能力受限于单机。
20. Sentinel 的实现原理?
- 哨兵节点通过定时任务监控主从节点的健康状态。
- 当主节点故障时,哨兵会自动将一个从节点晋升为主节点。
- 客户端通过哨兵获取当前主节点的地址。
21. 新的主节点是怎样被挑选出来的?
- 过滤不健康的从节点。
- 选择优先级最高的从节点。
- 如果优先级相同,选择复制偏移量最大的从节点。
- 如果仍无法决定,选择运行 ID 最小的从节点。
22. Redis 集群了解吗?
Redis 集群通过数据分区和自动故障转移实现高可用和分布式存储。集群将数据分散到多个节点,突破单机内存限制,同时支持主从复制和故障转移。
23. 集群中数据如何分区?
- 节点取余分区:通过哈希值取余确定数据存储节点。
- 一致性哈希分区:通过虚拟环减少节点增删的影响。
- 虚拟槽分区:Redis 使用 16384 个槽作为数据管理单位,槽与节点解耦,增删节点影响小。
24. Redis 集群的原理?
- 数据分区在集群创建时完成。
- 节点通过 Gossip 协议通信,形成多节点集群。
- 故障转移通过主从节点的协作完成。
缓存设计
什么是缓存击穿、缓存穿透、缓存雪崩?
- 缓存击穿:高并发访问某个过期的 key,导致所有请求直接打到数据库。
-
- 解决方案:加锁更新或异步刷新过期时间。
- 缓存穿透:查询缓存和数据库中都不存在的数据,导致每次请求都直接访问数据库。
-
- 解决方案: 1.缓存空值
- 检查缓存:用户请求数据时,首先检查缓存中是否存在该数据。
- 缓存未命中:如果缓存中没有数据,接着查询数据库。
- 数据库查询结果:如果数据库中也没有数据,那么创建一个空对象,并将其存储在缓存中,同时设置一个较短的过期时间。
- 返回结果:无论是从缓存还是数据库中获取到数据,都将结果返回给用户。
- 更新策略:在数据被添加到数据库后,需要更新缓存中的空对象,以确保数据的一致性。
26. 如何保证缓存和数据库数据的一致性?
- 选择合适的缓存更新策略(如先更新数据库再删除缓存)。
- 引入消息队列保证缓存和数据库操作的顺序性。
- 设置合理的过期时间兜底。
27. 如何保证本地缓存和分布式缓存的一致性?
- 使用 Redis 的发布订阅机制通知所有节点删除本地缓存。
- 引入专业消息队列(如 RocketMQ)保证消息可靠性。
- 设置较短的过期时间兜底。
28. 如何处理热 key?
- 监控热 key 的访问频率。
- 将热 key 打散到不同的服务器。
- 引入二级缓存,提前加载热 key 数据到内存。
29. 缓存预热怎么做?
- 上线时手动刷新缓存。
- 项目启动时自动加载数据。
- 定时任务刷新缓存。
30. 热点 key 重建的问题和解决?
- 使用互斥锁(mutex key)确保只有一个线程重建缓存。
- 永不过期:逻辑过期由单独线程处理。
7.3 计数器应用
各大网站、APP应用经常需要计数器的功能,如短视频的播放数、电商网站的浏览数。这些播放数、浏览数一般要求实时的,每一次播放和浏览都要做加1的操作,如果并发量很大对于传统关系型数据的性能是一种挑战。Redis天然支持计数功能而且计数的性能也非常好,可以说是计数器系统的重要选择。
7.4 共享Session
如果一个分布式Web服务将用户的Session信息保存在各自服务器,用户刷新一次可能就需要重新登录了,这样显然有问题。实际上,可以使用Redis将用户的Session进行集中管理,每次用户更新或者查询登录信息都直接从Redis中集中获取。
7.5 分布式锁
几乎每个互联网公司中都使用了分布式部署,分布式服务下,就会遇到对同一个资源的并发访问的技术难题,如秒杀、下单减库存等场景。
- 用synchronize或者reentrantlock本地锁肯定是不行的。
- 如果是并发量不大话,使用数据库的悲观锁、乐观锁来实现没啥问题。
- 但是在并发量高的场合中,利用数据库锁来控制资源的并发访问,会影响数据库的性能。
- 实际上,可以用Redis的setnx来实现分布式的锁。
7.6 社交网络
赞/踩、粉丝、共同好友/喜好、推送、下拉刷新等是社交网站的必备功能,由于社交网站访问量通常比较大,而且传统的关系型数据不太适保存 这种类型的数据,Redis提供的数据结构可以相对比较容易地实现这些功能。
7.7 消息队列
消息队列是大型网站必用中间件,如ActiveMQ、RabbitMQ、Kafka等流行的消息队列中间件,主要用于业务解耦、流量削峰及异步处理实时性低的业务。Redis提供了发布/订阅及阻塞队列功能,能实现一个简单的消息队列系统。另外,这个不能和专业的消息中间件相比。
7.8 位操作
用于数据量上亿的场景下,例如几亿用户系统的签到,去重登录次数统计,某用户是否在线状态等等。腾讯10亿用户,要几个毫秒内查询到某个用户是否在线,能怎么做?千万别说给每个用户建立一个key,然后挨个记(你可以算一下需要的内存会很恐怖,而且这种类似的需求很多。这里要用到位操作——使用setbit、getbit、bitcount命令。原理是:redis内构建一个足够长的数组,每个数组元素只能是0和1两个值,然后这个数组的下标index用来表示用户id(必须是数字哈),那么很显然,这个几亿长的大数组就能通过下标和元素值(0和1)来构建一个记忆系统。
https://zhuanlan.zhihu.com/p/1889695197102864281
https://zhuanlan.zhihu.com/p/427496556