Redis基础知识

Redis是什么

Redis是一个使用C语言编写的,高性能非关系型键值对(key-value)数据库。

与传统数据库不同的是Redis的数据是存放在内存中的,所以读写速度非常快,被广泛应用于缓存方向。Redis可以将数据写入磁盘中,保证了数据安全不丢失,而且Redis的操作是原子性的。

Redis的优缺点?

优点:

  1. 基于内存操作,读写速度快
  2. 支持多种数据类型,例如String、List、Hash、Set、ZSet等
  3. 支持持久化。支持RDB和AOF两种持久化机制,可以有效避免数据丢失问题。
  4. 支持事务(残血)。Redis所有单条命令都是原子性的,同时还支持几个命令合并后的原子性执行。(与传统意义上的原子性不同,Redis可以保证命令的有序执行,但没有回滚机制)
  5. 支持主从复制。主节点会自动将数据同步到从节点,可以进行读写分离提高系统性能和并发能力
  6. 单线程处理命令。Redis使用的是单线程的事件循环模型,一次只能处理一个客户端的需求,这样做的好处是简单高效,避免了多线程中死锁等问题,没有线程切换开销,延迟低;Redis采用I/O多路复用技术处理多个客户端连接。(Redis6.0引入了多线程,但并不是用来并发执行命令的,用于解决高网络负载下的瓶颈问题,用多线程处理网络I/O,但命令执行还是单线程的)

缺点:

  1. 对结构化查询的支持比较差,缺乏复杂查询支持
  2. 数据库容量受物理内存限制,成本高,不适用于用作海量数据的高性能读写
  3. Redis较难支持在线扩容,在集群容量到达上限时在线扩容会变得很复杂 
  4. Redis主从复制默认是异步的,主节点不会等待从节点确认数据同步完成,无法保证数据强一致性  

总结:Redis是一款高性能、轻量级的内存数据库,非常适用于用作缓存或需要高并发和低延迟的场景下使用,但是它的内存占用、持久化机制、单线程模型等特点在某些应用场景会带来局限性,尤其是需要大规模存储、复杂查询、强一致性或水平扩展时,并不是最佳选择。

Redis为什么这么快?

  • 基于内存。没有磁盘IO上的开销,读写速度快。
  • IO多路复用模型。Redis使用单线程来轮询描述符,将数据库的操作都转换成了事件,不在网络I/O上浪费过多时间。
  • 高效的数据结构。Redis每种数据类型底层都做了优化,目的就是为了更快的速度

Redis那么快,为什么不用他做主数据库,只用作缓存?

Redis虽然很快,但是也有一些局限性,不能替代主数据库。

  1. 事务处理:Redis只支持简单的事务处理,对复杂的事务无能为力,比如跨越多个键的事务处理
  2. 数据持久化:Redis是内存数据库,数据存在内存中(尽管可以将数据持久化到磁盘),如果服务器断电或崩溃,数据可能丢失,虽然提供了数据持久化机制,但是有些限制不绝对。
  3. 数据处理:Redis只支持一些简单的数据结构,比如字符串、列表、哈希表等。如果需要处理复杂的数据结构,比如关系型数据库中的表(多表关联登)可能不是好的选择。
  4. 数据安全:Redis没有提供像主数据库一样的安全机制(只提供了简单的密码验证),如用户认证、权限访问控制等。

Redis的线程模型

Redis的线程模型主要是单线程的,使用IO多路复用机制处理并发请求,在Redis6.0及以后,引入了多线程来处理网络I/O,主要是提升并发情况下的性能,但命令的执行依然是在单线程中进行,确保了数据操作的原子性。

单线程的模式是怎样的

Redis单线程模式是指所有的命令执行都在一个线程中完成,处理每个请求时采用IO多路复用机制。具体来说,Redis会监听多个客户端连接的读写时间,但命令的执行始终都是串行的,一个命令执行完毕在处理下一个命令。优点是避免了多线程的上下文切换和锁竞争,提高了简单场景下的性能,缺点是CPU密集型任务或大数据集时,可能无法充分利用CPU多核性能。

单线程为什么还这么快

  1. 大部分操作都在内存中完成,并且采用了高效的数据结构
  2. 非阻塞I/O多路复用
  3. 单线程,避免上下文切换和锁竞争

Redis数据类型有哪些,以及使用场景有哪些

基本数据类型:

  1. String(字符串):Redis最基本数据类型,存储的是二进制安全的字符串,可以是文本、数字或二进制数据(最大512MB)。使用场景:缓存、计数器、分布式锁等等
  2. Hash(哈希表):是一个键值对的集合,类似于一个小型的对象。适合存储对象的字段,如用户名、年龄等。使用场景:用户信息存储、对象缓存等等
  3. List(列表):有序双向链表,可以从两端操作(如插入、删除、截取),支持LPUSH、RPUSH、LPOP、RPOP。使用场景:消息队列,最新消息流
  4. Set(集合):无序不可重复的集合,支持集合的并、交、差等操作。使用场景:点赞、共同好友(交集)、抽奖系统(去重避免重复报名)
  5. Zset(有序集合):与Set类似,但每个元素都有与之关联的score分数,可以按分数排序。使用场景:排行榜、消息排序、延迟队列

Redis如何保证数据不丢失

Redis实现了持久化机制,这个机制会把数据存储在磁盘中,这样Redis重启就可以在磁盘中恢复原有数据。

Redis共有三种数据持久化的方式:

  1. RDB快照:将某一时刻的内存数据,以二进制的方式写入磁盘;
  2. AOF日志:每执行一条写操作命令,就把该命令以追加的方式写入到一个文件里;
  3. 混合持久化方式: Redis 4.0新增的方式,集成了AOF 和RBD的优点;

RDB快照

RDB是Redis默认的持久化方案,RDB会在特定时间点生成Redis数据快照,并将其保存为一个二进制文件(dump.rdb)。Redis重启会加载dump.rdb文件恢复数据

优点:

  •    生成的 RDB 文件体积较小,便于备份。
  •   RDB 恢复速度快,适合大规模数据的备份。

缺点:

  • 因为快照是在指定的时间点进行,所以如果 Redis 崩溃,可能会丢失最近一次快照之后的数据。
  • 需要大量数据时,快照生成可能会影响 Redis 性能。

使用场景:适用于需要周期性备份、对数据一致性要求不高的场景,如缓存场景。

AOF追加

AOF 记录每次写操作(增、删、改)的日志,每当有写操作时,Redis 会将该操作的命令追加到 AOF 文件中。可以通过设置 `appendfsync` 参数控制日志写入磁盘的频率(`always`、`everysec`、`no`)。

优点:

  • 数据安全性更高,可以设置为每秒或每次写操作都将日志写入磁盘,保证最小的数据丢失
  • 可以通过重写机制对 AOF 文件进行压缩,减小文件体积。

缺点:

  •  AOF 文件体积较大,随着操作增多,文件可能变得很大。
  • 写性能相对较低,因为每次写操作都需要记录日志。

使用场景:适合对数据可靠性要求较高的场景,如需要最小化数据丢失的系统。

混合持久化方式(RDB+AOF)

Redis 4.0 引入的持久化方式,结合了 RDB 和 AOF 的优点。在触发 AOF 重写时,Redis 会将 RDB 快照和 AOF 日志结合起来存储。启动时,Redis 会优先使用混合持久化文件进行数据恢复。

优点:

  • 结合了 RDB 的快速恢复和 AOF 的数据可靠性,既能减少数据丢失,又能提升恢复效率。
  • AOF 文件体积更小,恢复时间更快。

缺点:

混合持久化可能会稍微增加复杂性,并且可能需要更多的内存。

使用场景:适用于既需要高数据安全性,又希望提高恢复速度的场景。

总结

RDB:适合定期备份,快速恢复,但可能丢失最近的数据。
AOF:数据更安全,但写性能较低,文件体积较大。
混合持久化:结合两者优点,既保证数据安全又能快速恢复。

Redis过期删除和内存淘汰

过期删除策略

Redis是可以对key设置过期时间的,每当我们对一个 key设置了过期时间时,Redis 会把该key带上过期时间存储到一个过期字典(expiresdict)中,保存了数据库中所有key 的过期时间。
当我们查询一个key时,Redis首先检查该key是否存在于过期字典中:
        如果不在,则正常读取键值;
        如果存在,则会获取该key的过期时间,然后与当前系统时间进行比对,如果比系统时间大,那就没有过期,否则判定该key已过期。

因此需要有相应的机制将已过期的键值对删除,而做这个工作的就是过期键值删除策略。

Redis提供了三种策略:定时删除、定期删除、惰性删除

定时删除(主动删除)

当设置键的过期时间时,Redis会为这个建创建一个定时器,当过期时间一到,Redis会立刻删除该键。

优点:可以保证过期的键在其到期时间立即被删除,内存能及时释放。

缺点:为每个过期键创建定时器可能对 Redis 性能造成影响,尤其是有大量过期键时,会增加 CPU 开销,对CPU最不友好。

使用场景:适合对内存非常敏感、需要确保数据准确时使用,但通常 Redis 并不采用这种策略,因为实时删除会影响性能。

定期删除(批量删除)

Redis 以固定的时间间隔(默认100ms)随机抽取部分设置了过期时间的键,检查并删除其中已经过期的键。如果发现过期键较多,则加大删除力度。

优点:通过定期扫描和删除过期键,避免了大量过期键堆积,占用内存,同时减少 CPU 开销。

缺点:由于是定期随机抽取,无法保证所有过期键都能在过期后立刻被删除,可能会有一部分过期键暂时占用内存。

使用场景:是 Redis 默认的过期删除方式,适合大部分场景,平衡了内存占用和性能。

惰性删除(被动删除)

当客户端访问某个键时,Redis 会检查该键是否过期,如果已经过期,则立即将其删除,返回 `null`。

优点:不需要额外的资源去实时检查键是否过期,只有在键被访问时才会进行检查,最大限度减少性能开销,对CPU最友好。

缺点:如果某些键从未被访问,它们会继续占用内存,直到被惰性删除触发。造成内存空间浪费,对内存不友好

使用场景:适合负载较重的场景,因为不会影响 Redis 性能,但内存释放不及时。

总结:

定时删除:内存释放及时,但增加 CPU 开销。
惰性删除:性能开销低,但内存释放不及时。
定期删除:默认策略,定期批量删除过期键,平衡了内存和性能。

Redis 实际上主要依赖惰性删除和定期删除的组合策略,确保在不过多影响性能的情况下尽可能高效地清理过期数据。

内存淘汰策略

当Redis内存满了,达到设定的内存上限(由 `maxmemory` 配置项决定)时,就会触发内存淘汰策略来决定如何释放已有数据以腾出空间。

内存淘汰策略一共有八种:

1.noeviction(不淘汰)

当内存达到上限时,不允许再写入新数据,直接返回错误。读操作依然可以正常进行。

使用场景:适用于希望严格控制内存使用,避免意外数据丢失的场景,但需要客户端处理写入失败的情况。

2.volatile-lru(对设置了过期时间的键使用 LRU 淘汰)

只对设置了过期时间的键进行淘汰,按照 LRU(Least Recently Used,最近最少使用)的算法,优先淘汰最近最少使用的键。

使用场景:适合使用 Redis 作为缓存,并且键都有过期时间时,保持最近活跃的数据。

3.allkeys-lru(对所有键使用 LRU 淘汰)

对所有的键进行淘汰,仍然按照 LRU 算法优先删除最近最少使用的键,无论该键是否设置了过期时间。

使用场景:适合需要缓存大量数据,但未设置过期时间的场景。该策略确保 Redis 内始终保留最活跃的数据。

4.volatile-lfu(对设置了过期时间的键使用 LFU 淘汰)

只淘汰设置了过期时间的键,采用 LFU(Least Frequently Used,最不常使用)的算法,优先淘汰使用频率最低的键。

使用场景:适合频繁访问的缓存系统,确保保留最常被使用的数据。

5.allkeys-lfu(对所有键使用 LFU 淘汰)

对所有键进行淘汰,按照 LFU 算法淘汰使用频率最低的键。

使用场景:适合需要维持高频率访问数据的场景,确保保留访问次数最多的数据。

6.volatile-random(随机淘汰过期键)

只淘汰设置了过期时间的键,随机选择其中的键进行删除。

使用场景:适合简单的缓存场景,但由于随机删除,不能确保保留最有用的数据。

7.allkeys-random(随机淘汰所有键)

对所有键进行随机淘汰,无论是否设置了过期时间。

使用场景:适用于一些不在乎具体删除哪些数据的场景,但随机删除无法保证缓存命中率。

8.volatile-ttl(根据剩余 TTL 淘汰)

只对设置了过期时间的键进行淘汰,优先淘汰剩余 TTL(Time To Live,生存时间)最短的键,即即将过期的键先被删除。

使用场景:适合需要优先删除即将过期数据的场景,确保存活时间较长的数据被保留。

总结:Redis 通过不同的内存淘汰策略,提供了灵活的机制来处理内存不足的情况。根据应用的不同需求,可以选择合适的淘汰策略来最大限度地保持数据的有效性和系统性能。

例如: 如果 Redis 主要作为缓存使用,且数据有明确的过期时间,可以选择volatile-lru或 allkeys-lru,以保留最近活跃的数据。
- 如果需要基于数据的访问频率来决定淘汰,可以选择 volatile-lfu 或 allkeys-lfu。
- 对于需要严格控制内存使用且不能丢失数据的场景,可以使用 noeviction策略,确保不会自动删除数据。

LRU和LFU的区别

Redis高可用(Redis集群)

实现Redis高可用必须从Redis的多服务节点来考虑,比如Redis的主从复制、哨兵机制、切片集群,这些机制确保了 Redis 在出现故障时能够迅速恢复服务,避免单点故障。

主从复制

Redis 通过主从复制实现数据的冗余与备份。在主从架构中,主节点(Master)负责处理写请求,从节点(Slave)负责复制主节点的数据并可以处理读请求。

作用:

  •    读写分离:可以将读请求分散到多个从节点,减轻主节点的压力,提高读性能。
  •    数据冗余:当主节点出现故障时,从节点可以被提升为主节点,继续提供服务。

缺点:

  • 仅有主节点处理写操作,主节点故障后需要手动切换到从节点,服务恢复需要人工介入。
  • 由于主从服务器之间的命令复制是异步进行的,所以无法实现强一致性

哨兵机制(Sentinel)

哨兵模式通过一组哨兵(Sentinel)节点对 Redis 主从架构进行监控自动故障转移以及通知
 监控:哨兵节点定期检查主从节点的健康状态,检测节点是否不可用。
自动故障转移:如果主节点宕机,哨兵会自动选举一个从节点作为新的主节点,并通知其余从节点更新主节点的信息。
通知机制:哨兵会通知客户端新的主节点,确保客户端能重新连接到正确的节点。

优点:

  • 无需人工介入,哨兵能够自动完成主从切换,确保系统的高可用性。
  • 可以添加多个哨兵节点来监控多个 Redis 实例,提升系统的容错能力。

缺点:

        哨兵系统本身需要至少三个或更多的节点来确保稳定性,如果哨兵节点本身故障,监控能力将受影响。

集群模式(Cluster)

Redis 集群模式通过分片(Sharding)自动故障恢复实现了高可用性和扩展性。数据被分布在多个主节点上,每个主节点可以有多个从节点,集群中的节点通过槽位(Slot)进行数据分布管理。
分片存储:Redis 集群将整个数据集按哈希槽划分为 16384 个槽位,每个主节点负责处理部分槽位的数据,确保数据分散在多个节点上。
主从结构:每个主节点可以有多个从节点进行数据复制,主节点故障时,从节点会自动接管该主节点的工作。
自动故障转移:如果某个主节点故障,集群会自动将其从节点提升为新的主节点,并继续提供服务。

优点:

  • 线性扩展:集群模式可以轻松扩展,通过添加节点可以增加集群的容量和吞吐量。
  • 无单点故障:即使某个节点出现问题,集群中的其他节点仍能继续提供服务。
  • 高可用和分布式存储:数据自动分片并在多个节点上存储和备份,保证了高可用性

缺点:

        实现较为复杂,尤其是需要确保数据分布均衡以及避免跨节点访问带来的性能影响。

Redis 高可用的实现流程

1. 主从复制:配置多个从节点以分散读请求,并准备好进行主从切换。
2. 哨兵监控:使用哨兵机制进行节点监控和自动故障转移,减少手动维护的工作量。
3. 集群模式:当数据量较大或高并发需求时,采用集群模式来实现水平扩展,并确保在多个主节点间分配数据,提高系统容灾能力。

总结:

  • 主从复制:通过数据冗余保证数据的可用性和容灾性。
  • 哨兵模式:提供自动故障检测和转移,减少人工干预。
  • 集群模式:提供数据分片和多主多从结构,确保系统在高负载下的高可用性和可扩展性。

Redis哨兵机制和集群模式区别

在集群模式下,Redis 通过自身的机制来实现自动故障检测和故障转移,而不需要额外的哨兵系统来监控和管理节点。
Redis 集群内置故障转移机制:Redis 集群中的每个节点都会定期与其他节点通信(通过心跳和消息交换)以检测彼此的状态。如果某个主节点失效,集群中的其他节点会通过投票机制自动选择其从节点来接管工作,这一过程无需哨兵介入。

去中心化架构:Redis 集群是一个去中心化的架构,所有节点之间相互通信,没有像哨兵模式中那样的中心化监控角色。每个节点都会了解集群中其他节点的状态,并能根据投票结果来进行故障转移。

对比哨兵模式和集群模式:

哨兵模式:用于监控主从架构中的主节点健康状态,自动完成故障转移,但数据仍然存储在单个主节点上,不具备水平扩展能力。
  
集群模式:自带故障检测和恢复机制,支持数据分片存储,具备水平扩展和容错能力,但每个节点都具备管理和协同能力,不需要哨兵来辅助管理。

 总结:
        在 Redis 集群模式中,哨兵并不适用。集群模式自带自动故障检测和恢复机制,通过去中心化的设计来管理节点状态和故障转移。

Redis缓存设计

Redis 缓存设计涉及多个关键点,涵盖了缓存的使用策略数据结构选择持久化淘汰策略等方面,目的是确保缓存的高效性、数据一致性、以及系统的高可用性。

1.缓存使用场景

数据缓存:将经常访问但变化不频繁的数据存储在 Redis 中,如用户信息、配置参数等。
会话缓存:保存用户会话信息,类似于分布式 Session 存储,帮助提高请求的响应速度。
排行榜、计数器:用来快速计算和存储热点数据,如访问量统计、点赞数等。
队列/消息队列:通过 Redis 的 List 或 Pub/Sub 机制,可以实现任务队列或消息通知系统。

2. 缓存模式

实际开发中,Redis 和 MySQL的更新策略用的是Cache Aside,另外两种策略应用不了。

常见的缓存设计模式有两种:
Cache Aside(旁路缓存):应用在读取数据时,先从缓存中查找数据,若未命中,则从数据库中读取并更新到缓存。写入数据时,先更新数据库,再删除或更新缓存。()
优点:缓存与数据库解耦,灵活性高。
缺点:需要额外处理缓存和数据库的一致性问题。
  
Write Through(写透缓存):所有数据写入先写到缓存,再同步到数据库中。
优点:对缓存进行统一管理,保证数据一致性。
缺点:写操作性能受限,写入时延增加。

Write Back(写回缓存):数据先写入缓存,定期异步地将缓存中的数据刷新到数据库中。
优点:写性能提升明显,尤其适合高频写入场景。
缺点:若缓存丢失,可能导致数据不一致或丢失。

3. 数据结构选择

参照数据类型的应用场景

4.缓存过期策略

参照上文缓存过期策略

5.缓存淘汰策略

上文

6.缓存预热

在系统启动或重启时,将常用的、热点的数据提前加载到缓存中,避免启动初期的缓存穿透问题,提升系统的响应速度。

可以通过定期批量加载数据或通过初始化时的定制查询实现。

7.缓存更新策略

缓存数据可能因为数据库更新而过期或不再准确,缓存更新策略主要包括:
主动更新:每次数据库更新时,立即同步更新缓存中的数据,或将缓存中的旧数据删除。
定时更新:定期批量刷新缓存中的数据,保证数据的时效性和准确性。

延迟更新:等到缓存过期或下一次访问时,才重新从数据库加载到缓存

8.缓存穿透、缓存击穿和缓存雪崩

缓存穿透

缓存穿透是指请求的数据既不在缓存中,也不存在于数据库中,导致每次请求都绕过缓存,直接打到数据库。常见的情况是用户不断请求不存在的数据,例如通过恶意构造大量无效请求,导致数据库压力陡增。

解决方法:

布隆过滤器:使用布隆过滤器拦截不存在的请求。布隆过滤器可以有效判断某个请求是否一定不存在,防止无效请求直接打到数据库。
缓存空结果:对于数据库中不存在的数据,将其结果(如 null 或空对象)也缓存起来,设置一个较短的过期时间,防止反复请求。
参数校验:对用户的请求参数进行合法性校验,过滤掉明显的非法请求。

缓存击穿

缓存击穿是指某个热点数据的缓存失效,而这个数据恰好是高并发请求的目标。当该缓存失效的瞬间,大量请求同时涌向数据库,导致数据库负载过重。

解决方法:

互斥锁:在缓存失效时,对数据库查询加锁,防止大量线程同时查询数据库。只有第一个线程能够访问数据库,其他线程等待缓存更新完毕后再读取缓存。
缓存预热:在缓存到期之前,主动更新缓存。可以通过后台定时任务或在即将到期前提前更新缓存。
延长热点数据的缓存时间:对于热点数据,设置一个较长的过期时间,或者在过期前主动刷新缓存。

缓存雪崩

缓存雪崩是指在某一时刻,缓存中大量数据同时过期失效,或者缓存服务器宕机,导致原本应被缓存拦截的请求大量涌向数据库,给数据库带来极大压力,可能导致系统崩溃。

解决方法:

设置缓存过期时间的随机性:在设置缓存过期时间时,增加一个随机因子,避免大量缓存同时失效。
多级缓存:可以在本地或其他层次增加缓存,减轻缓存服务器宕机时的压力。
缓存数据的异步更新:对于重要的缓存数据,可以通过后台任务定期异步更新缓存,避免某一时刻集中失效。
限流和降级:在缓存失效时,对数据库的请求进行限流,避免数据库被压垮。同时可以针对部分非关键服务进行降级处理,缓解压力。
缓存高可用:使用主从复制、分片等手段,保证缓存服务的高可用性,避免缓存服务器的单点故障。

9.持久化设计

虽然 Redis 缓存通常不追求强一致性,但如果对数据安全性有要求,可以开启持久化(如 RDB 或 AOF)以确保在故障重启后仍能恢复数据。此外,集群或主从复制也是保障缓存高可用的关键设计

总结

        Redis 缓存设计涉及多个方面,从缓存使用策略、数据结构选择到过期和淘汰机制,再到缓存的一致性和高可用性设计,最终目标是通过 Redis 提高系统性能、降低数据库压力,同时确保缓存数据的时效性和准确性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值