1.Redis为什么这么快
1.Redis基于内存,内存访问速度比磁盘快
2.Redis基于Reactor模式设计开发一套高效的事件处理模型,主要时单线程事件循环和IO多路复用
3.Redis内置了多种优化过后的数据类型/结构实现,性能非常高
4.Redis通信协议实现简单且解析高效
2.Redis的优势
1.访问速度快:传统数据库的数据保存在磁盘,而Redis基于内存
2.高并发:一般像MySQL这类数据库的QPS大概在4k左右(8g4核),而Redis缓存之后很容易到达5+
3.功能全面:Redis除了能做缓存,还可以做为消息队列,分布式锁,限流,延时队列等场景
3.Redis应用
3.1 Redis实现分布式锁
主要是通过其原子操作如SET
命令的NX
和PX
选项来确保锁的安全性,并通过Lua脚本来保证解锁的原子性,从而有效避免分布式环境下的资源竞争问题。
3.2 Redis实现消息队列
通过一些数据结构和操作支持,可以被用来实现简单的消息队列系统。Redis实现消息队列的方式主要有几种,包括使用列表(Lists)、发布/订阅(Pub/Sub)模式,以及更复杂的Stream数据结构(Redis 5.0及以上版本引入)。
3.2.1 使用Lists
Redis的列表是一个简单的字符串列表,按照插入顺序排序。你可以使用LPUSH
命令将消息插入到列表的头部,使用RPUSH
插入到尾部。消费者可以使用LPOP
命令从列表的头部移除并获取元素,或者使用BRPOP
(或BLPOP
)以阻塞模式等待列表中有元素可消费。
3.2.2 发布/订阅
发布/订阅模式允许消息发送者(publisher)发送消息到频道(channel),而消息接收者(subscriber)订阅一个或多个频道并接收消息。
3.2.3 Stream
Streams是Redis 5.0引入的一种新的数据结构,用于支持消息队列和发布/订阅系统。Streams提供了消息持久化、消费者组、消息确认等高级功能。
3.3 Redis实现搜索引擎
Redis 是可以实现全文搜索引擎功能的,需要借助 RediSearch ,这是一个基于 Redis 的搜索引擎模块。
RediSearch 支持中文分词、聚合统计、停用词、同义词、拼写检查、标签查询、向量相似度查询、多关键词搜索、分页搜索等功能,算是一个功能比较完善的全文搜索引擎了。
3.4 Redis实现延时队列
基于 Redis 实现延时任务的功能无非就下面两种方案:
1.Redis 过期事件监听
2.Redisson 内置的延时队列
Redis 过期事件监听的存在时效性较差、丢消息、多服务实例下消息重复消费等问题,不被推荐使用
Redisson 内置的延时队列具备下面这些优势:
减少了丢消息的可能:DelayedQueue 中的消息会被持久化,即使 Redis 宕机了,根据持久化机制,也只可能丢失一点消息,影响不大。当然了,你也可以使用扫描数据库的方法作为补偿机制。
消息不存在重复消费问题:每个客户端都是从同一个目标队列中获取任务的,不存在重复消费的问题。
4.Redis数据类型
5种基础数据类型:List、Set、String、Hash、Zset
3种特殊数据类型:HyperLogLog(基数统计)、Bitmap(位图)、Geispatial(地理位置)
5.Redis持久化
5.1 RDB持久化
5.1.1 概念
Redis可以通过创建快照来获得存储在内存里面的数据在某个时间点上的副本。Redis创建快照后,可以对快照进行备份,可以将快照复制到其他服务器从而创建有相同数据的服务器副本,还可以将快照留在原地以便重启服务器的时候使用
5.1.2 RDB创建快照会阻塞主线程吗
1.使用save命令创建快照,同步保存操作,会阻塞Redis主线程
2.使用bgsave命令创建快照,fork出一个子进程,子进程执行,不会阻塞Redis主线程,默认选项
5.2 AOF持久化
5.2.1 概念
开启 AOF 持久化后每执行一条会更改 Redis 中的数据的命令,Redis 就会将该命令写入到 AOF 缓冲区 server.aof_buf
中,然后再写入到 AOF 文件中(此时还在系统内核缓存区未同步到磁盘),最后再根据持久化方式( fsync
策略)的配置来决定何时将系统内核缓存区的数据同步到硬盘中的。
只有同步到磁盘中才算持久化保存了,否则依然存在数据丢失的风险,比如说:系统内核缓存区的数据还未同步,磁盘机器就宕机了,那这部分数据就算丢失了。
5.2.2 AOF工作流程
1、命令追加(append):所有的写命令会追加到 AOF 缓冲区中。
2、文件写入(write) :将 AOF 缓冲区的数据写入到 AOF 文件中。这一步需要调用write
函数(系统调用),write
将数据写入到了系统内核缓冲区之后直接返回了(延迟写)。注意!!!此时并没有同步到磁盘。
3、文件同步(fsync) :AOF 缓冲区根据对应的持久化方式( fsync
策略)向硬盘做同步操作。这一步需要调用 fsync
函数(系统调用), fsync
针对单个文件操作,对其进行强制硬盘同步,fsync
将阻塞直到写入磁盘完成后返回,保证了数据持久化。
4、文件重写(rewrite):随着 AOF 文件越来越大,需要定期对 AOF 文件进行重写,达到压缩的目的。
5、重启加载(load):当 Redis 重启时,可以加载 AOF 文件进行数据恢复。
6.Redis线程模型
6.1 Redis单线程模型
Redis 基于 Reactor 模式设计开发了一套高效的事件处理模型 (Netty 的线程模型也基于 Reactor 模式,Reactor 模式不愧是高性能 IO 的基石),这套事件处理模型对应的是 Redis 中的文件事件处理器(file event handler)。由于文件事件处理器(file event handler)是单线程方式运行的,所以我们一般都说 Redis 是单线程模型。
6.2 Redis多线程模型
Redis的多线程模型并不是将所有操作都并行化,而是将网络IO的读写操作分离出来,使用多线程进行处理,而命令的执行仍然保持单线程。这样做的好处是可以充分利用多核CPU的优势,提高网络IO的吞吐量,同时保持命令执行的原子性和顺序性。
7. Redis内存管理
7.1 Redis给缓存数据设置过期时间
内存是有限且珍贵的,如果不对缓存数据设置过期时间,那内存占用就会一直增长,最终可能会导致 OOM 问题。通过设置合理的过期时间,Redis 会自动删除暂时不需要的数据,为新的缓存数据腾出空间。还有为了业务实现,比如登录验证码一分钟过期
7.2 Redis过期key删除策略
惰性删除:只会在取出/查询 key 的时候才对数据进行过期检查。这种方式对 CPU 最友好,但是可能会造成太多过期 key 没有被删除。
定期删除:周期性地随机从设置了过期时间的 key 中抽查一批,然后逐个检查这些 key 是否过期,过期就删除 key。相比于惰性删除,定期删除对内存更友好,对 CPU 不太友好。
延迟队列:把设置过期时间的 key 放到一个延迟队列里,到期之后就删除 key。这种方式可以保证每个过期 key 都能被删除,但维护延迟队列太麻烦,队列本身也要占用资源。
定时删除:每个设置了过期时间的 key 都会在设置的时间到达时立即被删除。这种方法可以确保内存中不会有过期的键,但是它对 CPU 的压力最大,因为它需要为每个键都设置一个定时器。
7.3 大量key集中过期如何处理
1.尽量避免 key 集中过期,在设置键的过期时间时尽量随机一点。
2.对过期的 key 开启 lazyfree 机制(修改 redis.conf
中的 lazyfree-lazy-expire
参数即可),这样会在后台异步删除过期的 key,不会阻塞主线程的运行。
7.4 Reids内存淘汰策略
Redis 提供了8 种内存淘汰策略:
- volatile-lru(least recently used) :从已设置过期时间的数据集(
server.db[i].expires
)中挑选最近最少使用的数据淘汰。 - volatile-ttl :从已设置过期时间的数据集(
server.db[i].expires
)中挑选将要过期的数据淘汰。 - volatile-random :从已设置过期时间的数据集(
server.db[i].expires
)中任意选择数据淘汰。 - allkeys-lru(least recently used) :从数据集(
server.db[i].dict
)中移除最近最少使用的数据淘汰。 - allkeys-random :从数据集(
server.db[i].dict
)中任意选择数据淘汰。 - no-eviction(默认内存淘汰策略):禁止驱逐数据,当内存不足以容纳新写入数据时,新写入操作会报错。
- volatile-lfu(least frequently used) :从已设置过期时间的数据集(
server.db[i].expires
)中挑选最不经常使用的数据淘汰。 - allkeys-lfu(least frequently used) :从数据集(
server.db[i].dict
)中移除最不经常使用的数据淘汰。
8.Reids性能优化
8.1. 缩短键值对的存储长度
- 原因:键值对的长度与性能成反比。较长的键值对会增加存储、传输和处理的开销,降低整体性能。
- 优化措施:在保证数据完整性的前提下,尽量缩短键值对的长度。对于较大的数据,考虑进行序列化和压缩后再存储。
8.2. 使用Lazy Free(延迟删除)特性
- 功能介绍:Lazy Free是Redis 4.0引入的一个功能,用于异步延时释放键值对,以减少删除操作对主线程的阻塞。
- 优化场景:建议在删除大键值对或在高并发场景下开启Lazy Free,以提高系统性能。
8.3. 设置键值的过期时间
- 原因:设置合理的键值过期时间可以避免键值对的无限期堆积,减少内存占用,并防止频繁触发内存淘汰策略。
- 优化措施:根据业务需求设置键值对的过期时间,让Redis自动清理过期数据。
8.4. 禁用长耗时的查询命令
- 原因:Redis是单线程模型,长耗时的查询命令会阻塞主线程,影响其他命令的执行。
- 优化措施 :
- 禁止使用如
KEYS
等可能导致长时间阻塞的命令。 - 使用
SCAN
命令进行分批、游标式的遍历,以减少对Redis的影响。 - 将排序、并集、交集等复杂操作放在客户端执行。
- 禁止使用如
8.5. 使用Slowlog优化耗时命令
- 功能介绍:Slowlog是Redis的一个功能,用于记录并查询最耗时的命令。
- 优化措施 :通过配置Slowlog的相关参数(如
slowlog-log-slower-than
和slowlog-max-len
),监控并优化耗时命令,提高Redis的运行速度。
8.6. 使用Pipeline批量操作数据
- 功能介绍:Pipeline是客户端提供的一种批处理技术,允许一次发送多个命令并一次性返回结果,从而减少网络往返次数,提高性能。
- 优化措施:在需要执行多个相关命令时,使用Pipeline进行批处理。
8.7. 避免大量数据同时失效
- 原因:大量数据同时失效会导致Redis在短时间内进行大量的过期扫描和删除操作,影响性能。
- 优化措施:为过期时间添加随机数,避免大量数据在同一时刻失效。
8.8. 客户端使用优化
- 优化措施 :
- 使用连接池减少网络连接的创建和销毁开销。
- 合理使用Redis的数据类型和结构,以优化内存使用和查询性能。
8.9. 限制Redis内存大小
- 原因:合理的内存限制可以避免Redis因内存不足而频繁进行内存淘汰,影响性能。
- 优化措施 :在Redis配置文件中设置
maxmemory
参数,限制Redis使用的最大内存量。
8.10. 使用物理机而非虚拟机安装Redis服务
- 原因:物理机通常比虚拟机具有更好的性能和稳定性。
- 优化措施:在条件允许的情况下,优先考虑在物理机上部署Redis服务。
8.11. 检查数据持久化策略
- 原因:数据持久化是Redis高可用性的重要保障,但不当的持久化策略会影响性能。
- 优化措施:根据业务需求和数据重要性选择合适的持久化策略(如RDB快照或AOF日志),并合理配置相关参数。
8.12. 使用分布式架构增加读写速度
- 优化措施:通过主从复制、分片等技术实现Redis的分布式部署,以提高系统的读写速度和可用性。
8.13. 监控和性能调优
- 重要性:监控是保证Redis稳定性和性能的关键环节。
- 优化措施 :
- 使用Redis自带的INFO命令或第三方监控工具(如Prometheus、Grafana等)实时监控Redis的运行状态。
- 根据监控数据及时调整Redis配置和优化策略。
9.Redis生产问题
9.1 缓存击穿
9.1.1 概念
缓存击穿是因为请求的key是热点数据,存在数据库中,但不存在缓存中(通常是因为key过期),导致大量请求到达数据库,导致数据库瞬间压力过大,导致宕机 。
9.1.2 解决方法
- 永不过期(不推荐):设置热点数据永不过期或者过期时间比较长。
- 提前预热(推荐):针对热点数据提前预热,将其存入缓存中并设置合理的过期时间比如秒杀场景下的数据在秒杀结束之前不过期。
- 加锁(看情况):在缓存失效后,通过设置互斥锁确保只有一个请求去查询数据库并更新缓存
9.2 缓存穿透
9.2.1 概念
缓存穿透是大量key不合理,即不存在缓存,也不存在数据库,导致这些请求根本不经过缓存,直接到达数据库,导致数据库压力过大。
9.2.1 解决方法
- 缓存无效key:如果缓存和数据库都查不到某个 key 的数据就写一个到 Redis 中去并设置过期时间。
- 布隆过滤器:布隆过滤器是一个非常神奇的数据结构,通过它我们可以非常方便地判断一个给定数据是否存在于海量数据中。
- 接口限流:根据用户或者 IP 对接口进行限流,对于异常频繁的访问行为,还可以采取黑名单机制,例如将异常 IP 列入黑名单。
9.3缓存雪崩
9.3.1 概念
缓存雪崩是因为大量的缓存同时失效,导致大量的请求到达数据库,导致数据库压力过大,发送原因有两点,一点是大量缓存同时过期,二是Redis服务宕机
9.3.2 解决方法
1.针对大量缓存过期
设置随机失效时间(可选):为缓存设置随机的失效时间,例如在固定过期时间的基础上加上一个随机值,这样可以避免大量缓存同时到期,从而减少缓存雪崩的风险。
提前预热(推荐):针对热点数据提前预热,将其存入缓存中并设置合理的过期时间比如秒杀场景下的数据在秒杀结束之前不过期。
持久缓存策略(看情况):虽然一般不推荐设置缓存永不过期,但对于某些关键性和变化不频繁的数据,可以考虑这种策略
2.针对服务器宕机
Redis 集群:采用 Redis 集群,避免单机出现问题整个缓存服务都没办法使用。Redis Cluster 和 Redis Sentinel 是两种最常用的 Redis 集群实现方案。
多级缓存:设置多级缓存,例如本地缓存+Redis 缓存的二级缓存组合,当 Redis 缓存出现问题时,还可以从本地缓存中获取到部分数据。