Redis复习总结

之前写的博客太杂,最近想把Redis的知识点再系统的过一遍,带着自己的理解使用简短的话把一些问题总结一下,尤其是开发中和面试中的高频问题,基础知识点参考–>Redis入门Spring Cache,这篇不再赘述。

基础

简介;与Memcached的区别;为什么作为mysql缓存?

什么是redis?

Redis 是一种基于内存的数据库,读写速度极快,常用于缓存、消息队列、分布式锁等场景。Redis 提供多种数据类型支持不同业务需求。其操作具有原子性,由单线程执行,无并发竞争问题。

此外,Redis 还支持事务、持久化、Lua 脚本、多种集群模式(主从复制、哨兵、切片集群)、发布/订阅模式,以及内存淘汰和过期删除机制。


redis与与Memcached的区别

很多人选择 Redis 作为缓存,而不是 Memcached,即使 Memcached 也是基于内存的数据库。要解答这个问题,需要了解两者的区别和共同点。

共同点:1、都是基于内存的数据库,主要用作缓存;2、都有过期策略;3、性能非常高。

区别:
数据类型:Redis 支持丰富的数据类型(String、Hash、List、Set、ZSet),而 Memcached 只支持简单的 key-value 数据类型。
持久化:Redis 支持将内存数据持久化到磁盘,重启可恢复;Memcached 不支持持久化,数据存于内存中,重启或故障后数据丢失。
集群:Redis 原生支持集群模式;Memcached 没有原生集群模式,依赖于客户端实现分片写入。
功能:Redis 支持发布订阅模型、Lua脚本、事务等高级功能;Memcached 不支持这些功能。


为什么作为mysq缓存

Redis 作为 MySQL 缓存的优势主要在于「高性能」和「高并发」。

高性能:用户首次访问 MySQL 数据时会比较慢,因为需要从硬盘读取。将数据缓存在 Redis 中,这样后续访问可以直接从内存获取,速度极快。当 MySQL 数据发生变化时,需同步更新 Redis 缓存,这涉及双写一致性问题。

高并发:Redis 的 QPS 是 MySQL 的 10 倍以上。Redis 单机 QPS 能轻松突破 10万,而 MySQL 单机 QPS 很难达到 1万。因此,使用 Redis 能大幅提升请求承受能力,把部分数据缓存到 Redis 可以减少对 MySQL 的直接访问。



如何保证Redis中都是热点数据 ?

如何保证Redis中的数据都是热点数据 ?

1.一般redis会给key设置过期时间,可以设置在过期之前如果被访问了,就给key加上过期时间,类似看门狗(redission)机制的思想,具体加多少可以根据实际业务场景来决定,这样的话访问的次数越多,过期时间就会越长,留在redis里的自然就是热点数据(过期key是怎么删除的下面有介绍

如果定期删除漏掉了很多过期key没有及时查询也没有走惰性删除,就会走内存淘汰机制了。

2.可以使用 allkeys-lru (挑选最近最少使用的数据淘汰)淘汰策略(下面内存淘汰有介绍),核心思想是“如果数据过去被访问多次,那么将来被访问的频率也更高”,那留下来的都是经常访问的热点数据。不使用 LRU 算法的原因是,其无法解决缓存污染问题,大量一次性读取数据会长时间占用内存。

3.对于一些可以预测的热点数据,比如秒杀库存、热门产品等,这些访问量比较大的就可以预先加载到redis里。

4.定期监控redis的使用情况,比如说命中率或者内存使用情况,然后根据实际情况去调整和配置淘汰策略,确保数据有效管理和内存高效利用



线程模型?为什么单线程还那么快?I/O多路复用(epoll)?

Redis的网络(线程)模型是怎样的?

Redis 6.0之前,使用的是单Reactor单线程模式。核心在于所有操作都在单个进程中完成,避免了进程间通信和多进程竞争的复杂性,因此实现相对简单。

单Reactor单线程模式存在两个主要缺点:

1.无法充分利用多核CPU性能:因为只有一个线程在执行,CPU的多核优势无法得到充分发挥。
2.处理延迟问题:当一个Handler对象正在处理业务时,其他连接的事件无法得到及时处理。如果某个任务耗时较长,会导致整体响应时间延迟。
因此,单Reactor单线程模式不适用于CPU密集型的场景,只适合业务处理非常快速的场景。Redis主要通过C语言实现,在6.0之前因为操作主要在内存中完成,处理速度很快,所以性能瓶颈不在CPU上。随着网络硬件性能的提升,网络I/O处理有时会成为瓶颈。

Redis 6.0之后,Redis引入了多线程模式。通过多线程处理网络I/O,可以提高并行度和网络处理性能。然而,Redis在命令执行上仍然采用单线程模式,也就不会存在线程安全问题。这种设计在增强网络I/O处理性能的同时,保持了命令执行的一致性和简洁性,使得Redis依然能够高效地运行。

总结:Redis的网络模型在6.0版本之前是单Reactor单线程模式,在6.0版本后改为多线程处理网络I/O,但命令执行仍用单线程。

Redis 6.0之前实际上并不是单线程的。只是主要工作(网络I/O和命令执行)一直使用单线程模型,其实启动时,Redis 会启动后台线程(BIO)以处理耗时任务:
在Redis 2.6版本,启动了2个后台线程,分别处理文件关闭和AOF刷盘任务,例如,命令 unlink key、flushdb async、flushall async 会交给后台线程处理,避免主线程卡顿。因此,删除大key时应使用 unlink 而非 del 命令,以免阻塞主线程。
在Redis 4.0版本后,新增一个用于异步释放内存的线程,即lazyfree线程。
Redis为「关闭文件、AOF刷盘、释放内存」等任务创建单独线程,因为这些操作耗时较长,如果在主线程处理会导致阻塞,影响后续请求的处理。后台线程相当于一个消费者,不停轮询任务队列,任务完成就执行相应操作。


Redis是单线程的,但是为什么还那么快?

Redis 实际上并不是单线程的。只是主要工作(网络I/O和命令执行)一直使用单线程模型,但在Redis 6.0版本后,引入了多I/O线程来处理网络请求,是因为随着硬件性能提升,Redis的瓶颈有时出现在网络I/O处理上。
其实启动时,Redis 会启动后台线程(BIO)以处理耗时任务:
在Redis 2.6版本,启动了2个后台线程,分别处理文件关闭和AOF刷盘任务,例如,命令 unlink key、flushdb async、flushall async 会交给后台线程处理,避免主线程卡顿。因此,删除大key时应使用 unlink 而非 del 命令,以免阻塞主线程。
在Redis 4.0版本后,新增一个用于异步释放内存的线程,即lazyfree线程。
Redis为「关闭文件、AOF刷盘、释放内存」等任务创建单独线程,因为这些操作耗时较长,如果在主线程处理会导致阻塞,影响后续请求的处理。后台线程相当于一个消费者,不停轮询任务队列,任务完成就执行相应操作。

Redis 之所以采用单线程模型,并且能够保持极高的性能,主要有以下几个原因:

  • 内存操作和高效数据结构:Redis 的大部分操作都在内存中完成,速度极快,;采用了高效的数据结构,进一步提升了操作效率;瓶颈通常在于机器的内存或网络带宽,而非 CPU。因此,使用单线程并不会遇到性能瓶颈。
  • 避免多线程开销:单线程模型避免了多线程之间的资源竞争,省去了线程切换带来的时间开销。任务执行也是单线程的,也不存在线程安全问题。这样也消除了死锁等多线程编程中的潜在问题,提高了系统的稳定性和性能。
  • I/O 多路复用机制:Redis 采用 I/O 多路复用机制来处理大量的客户端 Socket 请求。I/O 多路复用是一种允许一个线程处理多个 IO 流的技术,常见的实现方式包括select和epoll。例如:bgsave 和 bgrewriteaof 都是在后台执行操作,不影响主线程的正常使用,不会产生阻塞

解释一下I/O多路复用模型?

I/O多路复用是指利用单个线程来同时监听多个Socket ,并在某个Socket可读、可写时得到通知,从而避免无效的等待,充分利用CPU资源。目前的I/O多路复用都是采用的epoll模式实现,它会在通知用户进程Socket就绪的同时,把已就绪的Socket写入用户空间,不需要挨个遍历Socket来判断是否就绪,提升了性能。
其中Redis的网络模型就是使用I/O多路复用结合事件的处理器来应对多个Socket请求,比如,提供了连接应答处理器、命令回复处理器,命令请求处理器;
在Redis6.0之后,为了提升更好的性能,在命令回复处理器使用了多线程来处理回复事件,在命令请求处理器中,将命令的转换使用了多线程,增加命令转换速度,在命令执行的时候,依然是单线程

最基础的 TCP Socket 编程使用阻塞 I/O 模型,只能一对一通信。为支持多客户端,传统方法是使用多进程/线程模型,每个客户端连接分配一个进程/线程。当请求过多时,调度和内存开销巨大,成为瓶颈。
I/O 多路复用解决上述问题,有三种 API:select、poll、epoll。
select 和 poll:
1.本质没有区别,都是使用线性结构存储进程关注的 Socket 集合。只不过poll 不再使用 BitsMap 存储关注的文件描述符,而是改用动态数组并以链表形式组织。这打破了 select 的文件描述符个数限制,但仍受系统文件描述符限制。
2.需要将关注的 Socket 集合通过系统调用从用户态拷贝到内核态,由内核检测事件。
3.当事件生成,内核要遍历集合集才能找到对应 Socket 并设置状态为可读/可写,再拷贝回用户态,用户态继续遍历处理。
3.缺陷在于客户端多时,遍历和拷贝开销大,难以应对 C10K 问题(C 是 Client 单词首字母缩写,C10K 就是单机同时处理 1 万个请求的问题。)


epoll:
使用红黑树管理待检测 Socket,增删改 O(logn),这能减少内核和用户空间的大量数据拷贝和内存分配。
事件驱动机制,内核通过链表记录就绪事件,仅传递有事件发生的 Socket 集合,提高效率。
支持边缘触发和水平触发,边缘触发效率更高,而 select/poll 仅支持水平触发。

什么是边缘触发、水平触发?
Epoll 支持两种事件触发模式:边缘触发(ET,Edge-triggered)和水平触发(LT,Level-triggered)。虽然这两个词听上去有些抽象,但其实很容易理解。
边缘触发(ET):当监控的 Socket 有可读事件时,服务器只会从 epoll_wait 中苏醒一次,无论你是否调用 read 函数读取数据。因此,你需要保证每次都尽可能读完所有数据。
水平触发(LT):当监控的 Socket 有可读事件时,服务器会不断从 epoll_wait 中苏醒,直到内核缓冲区的数据被读完,以确保你知道还有数据需要处理。
类比
边缘触发:想象你的快递到了一个快递箱,快递箱只能发一次短信通知,即使你没有去取,它也不会再提醒你。即只要数据未读完,就会持续通知。
水平触发:快递箱会不停地发短信,直到你把快递取走为止。即只在事件第一次发生时通知一次,之后不会再重复通知。


用水平触发时,当内核通知文件描述符可读写后,你可以检测它的状态,并决定是否继续操作,没有必要一次性读写完数据。而用边缘触发时,事件只会通知一次,所以你必须尽可能多地读写数据,否则可能错过处理机会。
边缘触发一般和非阻塞 I/O搭配使用。因为如果你使用边缘触发模式,当 I/O 事件发生时系统只会通知你一次,我们不知道具体能读写多少数据。因此,在收到通知后要尽可能多地读写数据,以免错失机会。这通常需要对文件描述符进行循环读写操作。如果文件描述符是阻塞的,当没有数据可读写时,进程会卡在读写操作上,程序就无法继续运行。因此,边缘触发模式通常与非阻塞 I/O 结合使用,这样程序可以一直尝试读写操作,直到系统调用(例如 read 和 write)因为没有数据而返回 EAGAIN 或 EWOULDBLOCK 错误。


一般来说,边缘触发效率更高,因为它减少了 epoll_wait 的调用次数,从而减少了系统调用的开销。传统的 select/poll 只有水平触发模式,而 epoll 默认是水平触发,但可以设置为边缘触发。



除了做缓存还能拿来做什么?

Redis 是非关系型的基于内存存储的键值对数据库,它的主要作用就是用来缓存数据,来提高系统的性能;同时 Redis中,还提供了多种数据类型(比如:String / Hash / List / Set / ZSet / Geo / BitMap等),正是基于这些丰富的数据类型和它的单线程等特性,也赋予了 Redis 除缓存数据外的多种能力。

1.分布式锁。我们可以使用 Redis 自带的 SETNX 命令来实现分布式锁,当然,生产场景中我们还是更推荐使用 Redisson 框架来帮我们实现分布式锁的功能。
2.分布式ID。利用 Redis 原子性的自增命令,可以考虑作为应用程序的分布式ID来使用。如果获取分布式ID 比较频繁,我们可以每次请求设置一个合适的步长,比如 2000(一次取2000个连续的ID),然后缓存在本地。
3. 分布式Session。我们可以使用Redis 中提供的 String 数据类型或者 Hash 数据类型来保存 Session 数据,从而实现分布式环境下 Session 会话的同步。
4. 分布式限流。比如我们可以基于 Redis 的 SETNX命令可以实现计数器算法限流;基于 Redis 的 ZSet 数据结构可以实现滑动窗口算法限流;基于 Redis 的 List 数据结构可以实现令牌桶算法限流;当然,我们还可以基于Redis +Lua 脚本的方式实现分布式限流。限流算法简介
5. 消息队列。如果是中小型项目,业务量不是很大,对于数据丢失不敏感,且项目中已经使用了中间件 Redis,同时又需要消息中间件功能的情况下,可以考虑使用 Redis 的Stream 来实现消息队列的功能。但是如果并发量很高,资源又足够支持的情况下,还是强烈建议使用更专业的消息中间件,比如 RocketMQ、Kafka 等。
6. 抽奖。Redis 中提供的 Set 数据类型,可以很轻松的实现模拟抽奖的功能。存储某活动中中奖的用户名 ,Set 类型因为有去重功能,可以保证同一个用户不会中奖两次。
7. 地理位置应用。Redis 中提供的GEO 数据类型,可以很轻松的实现附近的人等查询功能。
8. 海量数据统计。Redis 中提供的 BitMap 的数据类型,可以用来很方便的做海量数据的统计。还记得腾讯三面中被问到的那道经典的面试题吗:限制1个G的内存,40亿的QQ号如何实现快速去重!这道面试题就可以使用 BitMap 来解決!
9.排行榜。Redis 中提供的 ZSet 数据类型,可以很轻松的实现排行榜等查询功能,比如经典的 TOP 10问题。
10. 关注模型。Redis 中提供的 Set 数据类型,可以很轻松的实现共同关注、我关注的人也关注他、我可能认识的人等关注模型的查询功能。(Set 类型支持交集运算,set的查找也是O(1),所以效率高,总的时间复杂度会更接近于O(min(M, N)), 而不是O(MN))。



Redis是AP还是CP的?

CAP 理论是针对分布式环境而言的,其中C代表一致性(Consistency)࿰

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值