Redis问题大全

一、什么是Redis?

    Redis是一种支持key-value键值对数据存储的NoSQL数据库。与传统数据库不同的是,Redis 的数据是保存在内存中的(内存数据库,支持持久化),因此读写速度非常快,被广泛应用于分布式缓存方向。

    redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。

什么是NoSQL?

NoSQL=not only sql

关系型数据库:行+列,由二维表及其之间的练习所组成的一个数据组织,同一个表下数据的结构是一样的。

非关系新数据库:不使用传统的表格关系存储数据,而是使用其他数据模型来存储数据。这些数据模型可以是键值对、文档、图形等形式。非关系型数据库通常具有高可扩展性、高性能和灵活性等优点,因此在大数据和分布式系统等领域得到了广泛应用。

Redis优缺点:

优点(主要前五个):

1、读写速度快:

1、基于内存,Redis以内存作为基础,可以提供非常快速的读写操作,通常在微妙级别完成,这使得它非常适合缓存和高性能的数据访问。

2、高效数据结构:Redis支持多种高效的数据结构,如哈希表、有序集合和位图等,这些数据结构使得Redis能够高效地处理各种数据操作。

3、单线程的时间驱动模型:Redis使用单线程的事件驱动模型来处理并发请求,这使得Redis能够高效的处理大量的并发请求。 避免了不必要的上下文切换和竞争条件,避免了多进程或多线程的切换而消耗CPU,不用考虑各种锁的问题,不存在加锁释放锁的操作以及死锁的问题。

4、异步I/O:Redis使用异步IO技术来处理网络请求,这使得Redis能够高效的处理网络请求,而不会阻塞其他操作。

5、高效的网络操作:Redis使用基于TCP协议的网络通信,同时采用了自定义的协议格式。这种自定义协议能够减少网络传输的数据量,提高网络传输效率。

2、多数据结构支持:如字符串、哈希、列表、集合、有序集合等;

3、支持数据持久化:可将数据定期保存到磁盘,证数据在重启或崩溃后不会丢失;

Redis数据持久化的方式:

1、RDB持久化:间隔一段时间,将Redis的内存数据快照保存到磁盘上,以便在重启时恢复数据。

2、AOF持久化:将Redis的写操作追加到一个日志文件中,以便在冲求时重新执行这些操作来恢复数据。

3、混合持久化:将RDB持久化和AOF持久化结合起来使用,以达到最好的数据保护效果。

4、支持多种原子操作:如递增、递减、集合操作等,这有助于在并发环境下保持数据一致性。

需要注意的是,Redis的原子性是在命令级别上保证的,而不是在事务级别上。虽然Redis支持事务,但在事务中的多个命令执行期间,其他客户端仍然可以对相同的数据进行读写操作,可能会导致数据的并发修改。

5、分布式支持:Redis支持主从复制(主机会自动将数据同步到从机,可以进行读写分离。)和集群模式,可以构建高可用性和可扩展性的系统。这使得Redis适合在分布式环境中使用。

6、数据过期和自动淘汰:Redis允许设置键的过期时间,当键到期时,Redis会自动删除他们,有助于管理缓存中的数据。

7、发布/订阅功能:Redis具有发布和订阅功能,可用于实现实时通信和消息传递,适用于构建即使通讯应用和事件驱动系统。

8、丰富的生态系统:Redis拥有丰富的客户端和工具,适用于多种编程语言,这使得在各种应用中使用Redis变得更加便捷。

缺点(主要前四个):

1、内存限制:redis数据存储在内存中,受限于物理内存的限制。如果数量超过可用内存容量,可能会导致性能下降或数据丢失。因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。

2、Redis不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分的读写请求失败,需要等待机器重新启动或者手动切换前端的IP才能恢复。

3、主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。

数据一致性:Redis使用异步主从复制,可能会导致在某些情况下数据不一致的情况。在某些用例中,需要额外的工作来确保数据的一致性。

4、Redis 较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。为避免这一问题,运维人员在系统上线时必须确保有足够的空间,这对资源造成了很大的浪费。

5、持久化性能开销:启用持久化选项会增加数据持久性,但会对性能产生一定的开销。在需要高吞吐量的情况下,可能会成为一个问题。

6、单线程模型:Redis使用单线程的事件驱动模型,这在处理大量的写操作时可能成为瓶颈。虽然Redis通过异步I/O和多路复用等技术来提高并发性能,但在极高并发写入的情况下,性能可能会受到限制。

7、缺乏复杂查询支持:Redis不支持复杂的查询操作,例如SQL中的JOIN操作,因为它不是关系数据库。这可能需要在应用层面执行额外的逻辑来满足特定查询需求。

8、无事务回滚:Redis的事务虽然支持原子性,但不支持事务回滚。一旦事务中的某个命令执行失败,后续命令仍然会继续执行,无法回滚已执行的操作。

Redis的应用场景

1、计数器

可以对String进行自增自减运算,从而实现计数器功能。Redis这种内存型数据库的读写性能非常高,很适合存储频繁读写的计数量。

2、缓存

将热点数据放到内存中,设置内存的最大使用量量以及淘汰策略来保证缓存的命中率。

缓存存在的意义:1、减轻数据库压力;2、提升接口性能

缓存分为三种:

  1. 本地缓存
  2. 服务器缓存
  3. 分布式缓存

3、会话缓存

可以使用Redis来统一存储多台应用服务器的会话信息。当应用服务器不再存储用户的会话信息,也就不再具有状态,一个用户可以请求任意一个应用服务器,从而更容易实现高可用以及可伸缩性。

4、全页缓存

除基本的会话token之外,Redis还提供很简便的FPC平台。以Magento为例,Magento提供一个插件来使用Redis作为全页缓存后端。此外,对wordPress的用户来说,Pantheon有一个非常好的插件 wpredis,这个插件能帮助你以最快速度加载你曾浏览过的页面。

5、查找表

例如DNS记录就很适合使用Redis进行存储。查找表和缓存类似,也是利用Redis快速的查找特性。但是查找表的内容不能失效,而缓存的内容可以失效,因为缓存不作为可靠的数据来源。

6、消息队列(发布/订阅功能)

List是一个双向链表,可以通过lpush和rpop写入和读取消息。不过最好使用kafka、RabbitMQ等消息中间件。

发布和订阅的使用场景确实非常多。还可以作为基于发布/订阅的脚本触发器,甚至用Redis的发布/订阅功能来建立聊天系统。

7、分布式锁实现

在分布式场景下,无法使用单机环境下的锁来对多个节点上的进程同步。可以使用Redis自带的SETNX命令实现分布式锁,除此之外,还可以使用官方提供的RedLock分布式锁实现。

8、排行榜

排行榜Redis提供了列表和有序集合数据结构,合理的使用这些数据结构可以很方便的构建各种排行版系统。

9、社交网络赞/踩功能、粉丝、共同好友、推送、下拉刷新。

Redis分布式锁和Java的锁有什么区别?

1、分布式性质:

  • Redis分布式锁是用于分布式系统中的锁,允许多个应用实例在不同的服务器上协调访问共享资源或确保操作的原子性。Redis分布式锁保证一次只有一个进程或实例可以获取锁,是在分布式环境下协调多个节点之间的同步访问共享资源的方式(锁的状态)。
  • Java的锁通常是在单个应用程序内使用,用于控制并发访问共享资源,例如多线程之间的协作。Java锁不涉及跨进程或跨服务器的通信。

2、实现机制

  • Redis的分布式锁是使用Redis命令(如 SETNX 或带有 NX 选项的 SET )实现的,结合唯一的锁标识符。
  • 在Java中,可以使用各种机制实现锁,如内置的 synchronized 关键字、 ReentrantLock 或 java.util.concurrent 包提供的其他锁实现。

3、可靠性:

  • Redis分布式锁通常具有更高的可靠性,因为它可以在不同服务器之间协调,即使某个服务器崩溃,锁也不会失效。
  • Java锁的可靠性受限于单个Java应用程序的生命周期,如果应用程序崩溃或终止,锁将被释放。

Redis的消息队列和kafka的消息队列有什么区别?

1、架构层面:Redis是一个内存数据存储系统,主要关注内存数据库和缓存,消息队列知识其中的一部分功能;而kafka是一个分布式流处理平台,专门处理高吞吐量、容错性和实时数据流设计。

2、持久化:

  • Redis消息队列通常使用List实现,消息默认存储到内存中,因此在某种情况下,如果过Redis服务器奔溃或重启,消息肯会丢失。虽然Redis支持持久性选项,单持久性通常是异步的,不能保证严格意义上的持久。
  • Kafka被设计为具有强大的消息持久性,它将消息持久化到磁盘,并且消息在一段时间内一直可供订阅者使用,这意味着即使消费者无法立即处理消息时,他们仍然可以在Kafka中保留一段时间。

3、消息交付语义:

  • Redis通常使用发布、订阅模型,它提供了一对多的消息传递(最多一次消息传递语义),没有严格的消息交付语义。发布的消息会传递给所有订阅者。订阅者不能保证以相同的顺序或仅接收一次消息。
  • Kafka提供可配置的消息交付语义,包括至少一次交付和恰好一次交付的保证。让Kafka在需要精确的消息传递顺序和保证时非常有用,例如日志处理和事件溯源等应用中。

4、分布式性:

  • Redis可以部署为主从复制,但在分布式性方面不如Kafka灵活,它通常用于构建单个Redis服务器或有限数量的副本的架构。
  • Kafka是为大规模分布式部署而设计的,它支持分布式数据存储和水平扩展,能处理海量数据并支持高吞吐量。

5、消息处理模型

  • Redis: Redis消息队列通常用于实时消息传递,例如即时通信、通知等场景,以及简单的任务队列。它适用于需要低延迟的应用。
  • Kafka: Kafka更适用于日志处理和事件溯源等大数据场景,其中数据量较大,需要长期存储和复杂的流处理。

二、Redis5种基本数据类型

    为了满足不同的业务场景,Redis主持字符串、哈希表、列表、集合、有序集合、位图、hyperloglogs等数据类型。并且还支持事务、持久化、Lua脚本、LRU回收等功能,同时通过Redis Sentinel提供高可用,通过Redis Cluster提供自动分区。

Redis有5种基本数据类型:String、List、Set、Hash(散列)、sorted set(zset有序集合)

1.字符串(String):

    String是Redis中最基本的数据类型。可以存储任何类型的数据,比如数字、浮点数、甚至是序列化后的对象。字符串类型是二进制安全的,这意味着Redis的字符串可以包含任意数据。

应用场景

1、缓存功能:String字符串是最常用的数据类型,不仅仅是redis,各个语言都是基本类型,因此redis作为缓存,配合其他数据库作为存储层,利用redis支持高并发的特点,可以大大加快系统的读写速度、以及降低后端数据库的压力。

2、计数器:许多系统都会使用redis作为系统的实时计数器,可以快速实现计数和查询功能。而最终的数据结果可以按照特定的时间落地到数据库或其他存储介质当中进行永久保存。比如用户单位时间的请求数(简单限流可以用到)、页面单位时间的访问数....

3、会话管理:可以将用户的会话信息存储在Redis的String类型中,例如用户的登陆状态、购物车内容等,实现快速简单的会话管理。

4、分布式锁:通过在Redis中设置唯一的键值对来控制并发访问和资源竞争,保证在分布式环境下的原子操作。

5、消息队列:虽然Redis提供了专门的消息队列数据结构List,单在一些简单的场景下,String类型也可以用于实现简单的消息队列,通过在列表的两端插入和弹出元素来实现消息的发布和消费。

6、分布式缓存:String类型可以用于分布式缓存场景,通过将缓存数据存储在多个Redis节点上,提高缓存的容量和可用性。

7、存储对象:将对象序列化为字符串后存储在Redis中,可以用于一些简单的对象存储需求。

2.哈希表(Hash):

    哈希表是一个存储键值对的无序散列表。Redis的哈希类型可以存储字段和与字段关联的值。它适合存储对象。

Redis本身就是一个key-value型数据库,因此hash数据结构相当于在value中又套了一层key-value型数据。所以redis中hash数据结构特别适合存储关系型对象

应用场景

1、由于hash数据类型的key-value的特性,用来存储关系型数据库中表记录,是redis中哈希类型最常用的场景。 一条记录作为一个key-value,把每列属性值对应成field-value存储在哈希表当中,然后通过key值来区分表当中的主键。

2、经常被用来存储用户相关信息:优化用户信息的获取,不需要重复从数据库当中读取,提高系统性能。

3、实时统计:Hash类型可用于实时统计数据。例如可以存储网站用户的每日访问量、用户的积分、商品的销售统计等。字段是日期或者用户ID,而值对应的统计数据。

4、用于存储应用的配置信息:每个字段可以代表一个配置项,而字段值是配置的值。这使得应用可以轻松的查询和更新配置。

3.列表(List):

    存储有序的字符串元素列表,按照插入顺序排序,列表元素也可以重复。可以在列表的两端推入或者弹出元素,也可以根据下标进行操作,也支持指定范围元素集的读取。

    redis列表是一种比较灵活的链表数据结构(双向链表),可以充当队列或栈的角色。

应用场景

1、消息队列:redis的链表数据结构,可以轻松实现阻塞队列,可以使用左进右出的命令组成来完成队列的设计。比如:数据的生产者可以通过Lpush命令从左边插入数据,多个消费者可以使用BRpop命令阻塞的“抢夺”列表尾部的数据。

2、文章列表或者数据分页展示的应用:蔽日我们通常用的博客网站的文章列表,当用户量越来越多时,每个用户都有自己的文章列表,而且当文章多时,都需要分页展示,这时可以考虑使用redis的列表,列表不但有序同时还支持按照范围内获取元素,可以完美解决分页查询功能,大大提高查询效率。

3、最新消息排行榜:在社交媒体应用中,可以使用List来存储用户发布的最新消息,最新的消息位于列表的前面。这样可以轻松实现消息的时间线功能。

4、活动消息推送:在应用中实现实时消息推送,可以使用List存储用户的待发送消息,然后通过订阅者机制,及时将消息发送给用户。

5、实时在线用户列表:在聊天用用或者在线游戏中,可以适应List存储在线用户的聊表。当用户登陆或者注销时,更新该列表。

在线游戏中,通常使用以下方法来感知用户下线并刷新在线列表:

1.心跳机制:这是一种常见的方法,游戏客户端定期向服务器发送心跳包或保持连接。如果服务器在一段时间内没有接收到来自客户端的心跳包,就可以推断该客户端已下线。一旦客户端被标记为离线,服务器可以相应地更新在线用户列表。

2.断开连接检测:服务器可以检测到与客户端的连接是否断开。如果连接断开,服务器可以将客户端标记为离线,并将其从在线列表中删除。

3.客户端主动通知:在退出游戏或下线时,客户端可以主动向服务器发送一个下线通知。服务器在接收到这个通知后,将客户端标记为离线并更新在线列表。

4.超时机制:服务器可以为每个客户端维护一个时间戳,表示上次与客户端通信的时间。如果服务器发现距离上次通信已经超过了一个预定的超时时间,就可以将客户端标记为离线。

5.WebSocket关闭事件:如果游戏使用WebSocket等双向通信协议,服务器可以监听WebSocket连接的关闭事件。当客户端关闭WebSocket连接时,服务器可以立即感知用户下线。

无论使用哪种方法,一旦服务器感知到用户下线,它应该及时更新在线用户列表,以便其他在线玩家可以看到用户已下线。通常,在线用户列表是一个数据结构,如哈希表或数据库表,其中包含了每个在线用户的相关信息。服务器将用户标记为离线后,可以将其从在线用户列表中删除或进行其他处理,以反映用户的离线状态。

需要确保下线用户的处理是可靠的,以避免不必要的在线列表混乱或错误。此外,考虑到网络中断、客户端崩溃等情况,实现上线和下线的逻辑要具备容错性。

6、存储日志记录、或者历史记录:Redis的List底层是双链表,可以用LPUSH或RPUSH命令将新的历史记录添加到列表的开头或结尾,然后使用LRANGE命令来检索历史记录。可以控制列表的最大长度,以便限制历史记录的大小,以免占用过多内存。

4.集合(Set):

    存储唯一的、无序的、不重复的字符串元素集合,支持交集、并集、差集等操作。

    redis的set类型是使用哈希表结构的,因此复杂度是O(1),支持集合内的增删改查。

应用场景

1、标签:比如博客网经常给博客做的分类标签

2、共同好友功能:共同喜好,或者申引到二度好友之类的扩展应用。

3、统计网站的独立IP:利用set集合当中元素唯一性,可以快速实时统计访问网站的独立IP。

4、集合运算:Redis提供了许多Set操作,如并集、交集、差集等。这些操作可以用于处理多个Set之间的关系,例如查找两个集合的共同元素、计算两个集合的差异等。

5、 订阅和关注:Set类型可以用于实现订阅和关注功能。你可以为每个用户创建一个Set,其中存储他们所订阅或关注的内容或用户。

6. 抽奖和排行榜:Set类型可以用于实现抽奖和排行榜功能。你可以将参与抽奖或排行榜的用户存储在Set中,并使用Set操作来选择获奖者或计算排名。

5.有序集合(Sorted Set、Zset):

    有序集合和集合类似,保留元素不重复的特性,但每个成员都关联了一个分数(score),根据这个分数进行排序。

应用场景

1、排行榜:有序集合经典使用场景。例如视频网站需要对用户上传的视频做排行榜,榜单维护可能是多方面:按照时间、按照播放量、按照获得的赞数等。

2、做带权重的队列:比如普通消息score为1,重要消息score为2,工作线程可以选择按score的倒序来获取工作任务。让重要的任务优先完成。再比如将商品价格作为分数,商品名作为成员,从而创建一个按价格排序的商品列表。

3、去重和过滤:由于Sorted Set的成员是唯一的,可以使用Sorted Set来进行去重和过滤操作。例如,可以将重复的数据存储在Sorted Set中,通过成员的唯一性来去除重复数据。

String 还是Hash 存储对象数据更好呢?

  • String存储的是序列化后的对象数据,存放的是整个对象。Hash是对对象的每个字段单独存储,可以获取部分字段的信息,也可以修改或者添加部分字段,节省网络流量。如果对象中某些字段需要经常变动或者经常需要单独查询对象中的个别字段信息,Hash就非常合适。
  • String存储相对来水更加节省内存,缓存相同数量的对象数据,String消耗的空间是Hash的一半,并且存储具有多层嵌套结构的对象时也很方便。如果系统对性能的损耗非常敏感的话,String就非常合适。
  • 绝大部分使用String来存储对象。

三、Redis的持久化机制

什么是持久化?

    持久化就是把内存的数据写到磁盘中,防止服务器宕机内存数据丢失。

Redis持久化机制有:

  • RDB (默认,Redis DataBase)
  • AOF (Append Only File)

⬛RDB

◼️工作机制:定期将对内存中的数据集进行快照,将其写入到磁盘作为RDB文件。

1、快照触发:RBD是通过生成数据快照来实现的。管理员或应用程序可以手动出发快照,也可以通过配置自动触发快照的条件,比如说经过一定数量的写操作或经过一定的时间间隔。

2、快照创建:当快照满足触发条件时,Redis通过fork一个子进程来生成数据集的快照。子进程负责处理快照的创建,而父进程继续处理客户端请求。这个快照是一个二进制文件,包含了当前Redis服务器中所有的数据。

3、后台保存:快照创建过程在后台运行,使得Redis能够继续处理读写操作而不被阻塞。Redis使用写时复制(Copy-on-Write)技术,确保后台保存进程不会干扰正在运行的操作。

4、快照写入磁盘:子进程将快照数据写入临时文件中。临时文件以追加方式创建,确保原始的RDB文件在新快照完全写入之前保持完整。

5、原子文件替换:一旦快照写入成功,Redis将重命名临时文件以替换现有的RDB文件。这种原子文件替换确保RDB文件始终处于一致状态。

6、从RDB恢复:当Redis重启时,它会将最新的RDB文件加载到内存中,将数据集恢复到快照时的状态。

◼️fork ( )

1、fork()是什么

fork()是unix和linux这种操作系统的一个api,而不是Redis的api。

2、fork()有什么用

fork()用于创建一个子进程,注意是子进程,不是子线程。fork()子进程前都会判断存在子进程了,有则忽略请求,没有才进行工作。fork( )出来的进程共享其父类的内存数据。仅仅是共享fork()出子进程的那一刻的内存数据,后期主进程修改数据对子进程不可见,同理,子进程修改的数据对主进程也不可见。比如:A进程fork( )了一个子进程B,那么A进程就称之为主进程,这时候主进程子进程所指向的内存空间是同一个,所以他们的数据一致。但是A修改了内存上的一条数据,这时候B是看不到的,A新增一条数据,删除一条数据,B都是看不到的。而且子进程B出问题了,对我主进程A完全没影响,我依然可以对外提供服务,但是主进程挂了,子进程也必须跟随一起挂。这一点有点像守护线程的概念。Redis正是巧妙的运用了fork()这个牛逼的api来完成RDB的持久化操作。

◼️Copy-on-Write 写时复制

    Redis使用fork()创建子进程,现在主进程和子进程共享了一块内存空间,怎么做到彼此更改互不影响?

原理:

    主进程fork()子进程之后,内核把主进程中所有的内存页的权限都设为read-only,然后子进程的地址空间指向主进程。这也就是共享了主进程的内存,当其中某个进程写内存时(这里肯定是主进程写,因为子进程只负责rdb文件持久化工作,不参与客户端的请求),CPU硬件检测到内存页是read-only的,于是触发页异常中断(page-fault),陷入内核的一个中断例程。中断例程中,内核就会把触发的异常的页复制一份(这里仅仅复制异常页,也就是所修改的那个数据页,而不是内存中的全部数据),于是主子进程各自持有独立的一份。

    Copy-on-Write 写时复制,创建子进程的时候不复制,这样创建进程就变快了,说白了玩的就是指针。

其实就是更改数据的之前进行copy一份更改数据的数据页出来,比如主进程收到了set k 1请求(之前k的值是2)然后这同时又有子进程在rdb持久化,那么主进程就会把 k这个key的数据页拷贝一份,并且主进程中k这个指针指向新拷贝出来的数据页地址上,然后进行更改值为1的操作,这个主进程k元素地址引用的新拷贝出来的地址,而子进程引用的内存数据k还是修改之前的。

    在 Redis 服务中,子进程只会读取共享内存中的数据,它并不会执行任何写操作,只有主进程会在写入时才会触发这一机制,而对于大多数的 Redis 服务或者数据库,写请求往往都是远小于读请求的,所以使用fork()加上写时拷贝这一机制能够带来非常好的性能,也让BGSAVE这一操作的实现变得很简单。

全量复制浪费时间空间资源。

◼️RBD优点:

1、快速恢复:RDB文件包含完整数据集,Redis服务器重启时,数据可以快速加载到内存中。 速度比AOF快,因为RDB采用了更紧凑的二进制格式,允许更快的数据加载。

2、RDB文件是二进制格式,紧凑而高效,能加快备份和恢复的速度,减少存储需求。

3、适用于冷数据的备份:RDB适用于数据相对静止、不经常变化的情况,因为他的持久化是点对点,不适用于实时数据的同步备份。

4、性能开销小:RDB快照通常是在子进程中生成的,因此对Redis主进程的性能影响较小。

◼️RDB缺点:

1、可能丢失数据:因为RDB快照是定期执行,如果发生两快照之间崩溃,可能会丢失数据。最后一个快照之后的数据更改将不会被捕获在RDB文件中。

2、不适合实时应用

3、大数据集恢复慢

4、无法提供秒级RPO:由于RDB的定期生成快照机制,无法提供秒级的RPO,即数据丢失可能超过秒级。

5、RDB每次fork出子进程来执行RDB快照生成文件时,如果文件特别大,可能会导致客户端提供服务暂停数毫秒或者几秒

⬛AOF

    当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复。

工作机制:以日志的形式来记录每个写操作,将每次对数据修改的命令保存到指定文件中。Redis重启时读取此文件,重新执行这些命令恢复数据。

1、追加记录:每当有写操作(如SET、DEL等)发生时,Redis不仅会将数据更新到内存中,还会将这个写操作以追加(append)的方式记录到AOF文件中。这确保了每个写操作都被持久化。

2. 异步写入:默认情况下,Redis使用后台进程异步将AOF日志写入磁盘。这个进程确保Redis可以继续处理客户端请求,而不会被磁盘I/O阻塞。

3. 文件重写:随着时间的推移,AOF日志文件可能会变得很大。为了防止日志文件过大并优化磁盘空间的使用,Redis提供了重写AOF文件的选项。这个过程通过重新播放现有AOF日志文件中的写操作来创建一个新的AOF文件。

4. 从AOF恢复:当Redis重新启动时,它会重新播放AOF日志文件中的写操作,以重建数据集。这个过程确保数据集恢复到重新启动之前的状态。

5. 安全机制:为了确保数据完整性,Redis提供了fsync和appendfsync等选项,用于控制AOF日志与磁盘同步的频率。这些选项在数据安全性和性能考虑之间进行平衡。

◼️AOF优点

1、数据完整性:AOF持久的记录每个写操作,因此在灾难发生时,Redis可以通过重放AOF来恢复,最大程度的减少了数据丢失的可能。

2、通过 append 模式写文件,即使中途服务器宕机,可以通过 redis-check-aof 工具解决数据一致性问题。

3、可读性:AOF以文本格式记录,是可读的,并且包含写操作的时间,还为调试和分析提供了审计跟踪。

4、AOF持久化非常适合复制场景:AOF日志文件可用于将数据更改复制到其他Redis实例,确保多个节点之间的数据一致性。有助于实现高可用性和容错性。

◼️AOF缺点

1、对于同一份文件AOF文件比RDB数据快照要大。因为包含所有写操作的历史记录;

2、AOF开启后支持写的QPS会比RDB支持的写的QPS低,因为AOF一般会配置成每秒fsync操作,每秒的fsync操作还是很高的;

3、数据恢复比较慢,不适合做冷备。

4、潜在的数据损坏:如果AOF文件损坏或者存在写入错误,可能会导致数据恢复的问题。因此,需要定期对AOF文件进行备份和检查。

RDB和AOF的选择

    Redis是一个将数据存储到内存的数据库,在发生断电或奔溃时,内存的数据会丢失,虽然Redis提供了两种持久化机制,但他们都是在兼顾性能的前提下降低数据丢失的风险,发生极端情况也只能是尽可能减少损失。

    且Redis里没有内置的安全机制,例如身份验证和加密。如果过需要对数据进行安全保护,可以通过配置防火墙、使用SSL/TLS等方式来增强Redis的安全性。

    在整个项目的架构体系中,Redis大部分情况是扮演“二级缓存”角色。二级缓存通常保存的数据类型:经常要查询但很少被修改的数据、不是非常重要允许出现偶尔的并发问题、不会被其他应用程序修改的数据。

    在架构中,一级缓存通常是指位于应用程序内部的内存缓存。它是直接与应用程序交互的缓存层,用于存储经常被访问的数据,以提供快速的读取和响应时间。

    一级缓存通常是基于内存的数据结构,例如使用Java中的HashMap或ConcurrentHashMap来存储数据。它位于应用程序的内部,与应用程序共享同一个进程和内存空间。

    相比之下,二级缓存(如Redis)是一个独立的缓存服务器,位于应用程序和持久化存储(如数据库)之间。它可以在多个应用程序实例之间共享数据,并提供更高的容量和可扩展性。

    一级缓存的优点是它与应用程序紧密集成,访问速度非常快,适用于存储频繁访问的热点数据。由于一级缓存通常是应用程序特定的,因此可以根据应用程序的需求进行定制和优化。

    然而,一级缓存的容量有限,无法处理大量数据。而二级缓存(如Redis)则提供了更大的存储容量和分布式能力,适用于处理更复杂的缓存需求。因此,在架构中通常将Redis作为二级缓存来补充和扩展一级缓存的功能。

那么RDB和AOF到底怎么选择?

    官方推荐两个都用:如果对数据不敏感,可以选单独用RDB;不建议单独用AOF,因为可能出现Bug;如果只是 做纯内存缓存,可以都不用

用AOF来保证数据不丢失,作为恢复数据的第一选择;用RDB来做不同程度的冷备,在AOF文件都丢失或损坏不可用的时候,可以使用RDB进行快速的数据恢复。

Redis持久化数据和缓存怎么做扩容?

  • 如果Redis被当做缓存使用,使用一致性哈希实现动态扩容缩容。
  • 如果Redis被当做一个持久化存储使用,必须使用固定的keys-to-nodes映射关系,节点的数量一旦确定不能变化。否则的话(即 Redis节点需要动态变化的情况),必须使用可以在运行时进行数据再平衡的一套系统,而当前只有Redis集群可以做到这样。

四、Redis过期键删除策

如果一个键过期了那它什么时候被删除? ---三种策略

1、定时删除

    每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即删除。该策略可以立即清除过期数据,对内存很友好,但会占用CPU资源去处理过期的数据,无论CPU此时负载多高都会占用,从而影响缓存的响应时间和吞吐量,对性能有一定损耗。

---->总结:用处理器的性能换取存储空间,牺牲CPU处理时长保障内存能最大化的释放掉。(时间换空间)

2、惰性删除

    数据达到过期时间时不做处理,等下次访问改数据时,再判断该key是否过期,如果没过期则返回数据,如果发现已经过期则删除数据,返回不存在。 这个策略节约CPU性能,发现必须删除的时候才删除,但查询不到的数据会永远存在,内存长期占用。

---->总结:延时执行,用存储空间换处理器性能。(空间换时间)

3、定期过期

    结合以上两种方案

    周期性轮询redis数据库中的时效性数据,采取随机抽取的策略,利用过期数据占比的方式控制删除频度。

---->CPU性能占用设置有峰值,检测频度可自定义设置。

---->内存压力不是很大,长期占用内存的冷数据会持续被清理。

---->总结:周期性抽查存储空间(随机抽查,重点抽查)

五、Redis事务

1、什么是事务

我的理解,事务是一个原子操作:要么全做,要么全不做,要么全部执行,要么全部回滚。在数据库和分布式系统中,事务用于确保数据的一致性和完整性。

2、Redis的事务

    Redis事务是一组命令的集合,将多个命令进行打包,然后这些命令会被顺序的添加到队列中,并且按顺序的执行这些命令。

--「Redis事务中没有像Mysql关系型数据库事务隔离级别的概念,不能保证原子性操作,也没有像Mysql那样执行事务失败会进行回滚操作,而是继续执行剩余操作」。

    Redis 不支持事务回滚,因为支持回滚会对 Redis 的简单性和性能产生重大影响。因为事务回滚本身是一种复杂的功能,这和 Redis 追求的简单高效的设计主旨不符合。

    但是 Redis 提供了 DISCARD 命令,不过这个命令只能用来主动放弃事务执行,把暂存的命令队列清空,起不到回滚的效果。

一组命令中两种错误的不同处理:

--「如果在一个事务中的命令出现错误(语法,编译时错误),那么所有的命令都不会执行」

--「如果在一个事务中出现运行错误(代码逻辑错误),那么正确的命令会被执行,这点不保证事务原子性」。

Redis支持事物隔离吗?

    Redis是单进程程序,并且保证在执行事务时,不会对事务进行中断,事务可以运行直到执行完所有事物队列中的命令为止。因此,Redis的事务是总是带有隔离性的。

Redis中执行事务的流程只需要简单的下面三个步骤:

  1. 开始事务(MULTI)
  2. 命令入队
  3. 执行事务(EXEC)、撤销事务(DISCARD )

主要命令

在Redis中事务的实现主要是通过如下的命令实现的:

命令

功能描述

MULTI

「事务开始的命令」,执行该命令后,后面执行的对Redis数据类型的「操作命令都会顺序的放进队列中」,等待执行EXEC命令后队列中的命令才会被执行

DISCARD

「放弃执行队列中的命令」,放弃执行事务,「并且将当前的状态从事务状态改为非事务状态」。客户端清空事务队列并退出事务状态。

EXEC

执行该命令后「表示顺序执行队列中的命令」,执行完后并将结果显示在客户端,「将当前状态从事务状态改为非事务状态」,当操作被打断时,返回空值 nil。若是执行该命令之前有key被执行WATCH命令并且又被其它客户端修改,那么就会放弃该事务,在客户端显示报错信息,若是没有修改就会执行队列中的所有命令。

WATCH key

表示指定监视某个key,「该命令只能在MULTI命令之前执行」,如果监视的key被其他客户端修改,之后的事务就不会执行,「EXEC将会放弃执行队列中的所有命令」

UNWATCH

「取消监视之前通过WATCH 命令监视的key」,通过执行EXEC、 DISCARD 两个命令之前监视的key也会被取消监视

六、内存淘汰策略(逐出算法)

    当 Redis 内存使用达到最大内存限制时,如果继续进行写入操作会导致 Redis 服务崩溃。因此,为了保证 Redis 服务的稳定性,Redis 在内存使用达到最大限制时采取一系列措施,如内存淘汰、警告等。

内存淘汰策略分类

🔹放弃数据驱逐

  1. no-eviction (4.0默认策略)

不驱逐策略,直接返回OOM错误信息。这种方式可能导致内存使用过多而导致Redis 崩溃。

🔹检测全库数据

  1. allkeys-lru

从所有的键中选取最近最少使用的数据淘汰。这种方式通常可以保留热点数据,但是可能会出现内存碎片,导致内存浪费。

  1. allkeys-lfu

从所有的键中选取访问频率最少的数据进行淘汰。这种方式适用于处理访问分布相对均匀的数据。

  1. allkeys-random

任意选择数据淘汰。

🔹检测易失数据

  1. volatile-lru (建议使用)

只从设定了过期时间(ttl)的键中选取最近最少使用的数据淘汰。这种方式适用于缓存等使用场景。

  1. volatile-lfu

只从设定了过期时间(ttl)的键中选取访问频率最少的数据进行淘汰。

  1. volatile-ttl

只从设定了过期时间(ttl)的键中选取即将过期的数据进行淘汰。这种方式适用于缓存等使用场景。

  1. volatile-random

只从设定了过期时间(ttl)的键中任意选择数据淘汰。

使用INFO命令输出监控信息。查询缓存hit和miss的次数,根据业务需求调优Redis配置

注意:Redis的内存淘汰策略的选取并不会影响过期的key的处理。内存淘汰策略用于处理内存不足时的需要申请额外空间的数据;过期策略用于处理过期的缓存数据

七、缓存穿透、击穿、雪崩

1、缓存穿透

  缓存穿透是指:key对应的数据在数据源中并不存在,每次针对此key的请求从缓存获取不到,请求就会打到数据库,这样的请求过多会增加数据库压力甚至压垮数据库。 要是有人利用不存在的key频繁攻击我们的应用,这就是漏洞。

---->解决:

1、接口层增加校验,如用户权限校验、id做基础校验,例如id<0直接拦截。

2、缓存空值,当客户端请求到redis,未命中缓存,数据库中也没有,可以将缓存的key-value写为key-null。缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击。

  • 优点:实现简单,维护方便;
  • 缺点:当客户端请求大量数据都是不存在的,则redis缓存大量的null数据,消耗内存。

3、布隆过滤器,布隆过滤器是redis提供的一种数据结构,它可以在海量数据中判断某个键在缓存中存不存在,拦截那些已知不存在于缓存中的请求。 它能将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。

  • 优点:内存占用少,没有多余key;
  • 缺点:准确率并不是百分百,有1%左右的误判率,对于海量数据几乎可以忽略。

2、缓存击穿

  key对应的数据存在,但在redis中过期,这时有大量并发请求过来,就会直接打到数据库,导致数据库压力瞬间增大,可能压垮数据库。(热点数据过期,例如考试报名、秒杀场景的秒杀商品数据,热门文章,在这些并发高的节骨眼缓存过期)

---->解决:

1、非常高频的热点数据不设置过期时间; 并开启定时任务。定时查看缓存有没有被删除(误删),如果不存在则更新缓存。

2、加互斥锁,当线程1查询缓存未命中时,会获取一个互斥锁,然后查询数据库并重建缓存数据。在此期间,如果线程2查询缓存也未命中,并且没有获取到互斥锁,线程2会休眠并重试,直到线程1写入缓存并释放锁,线程2才能获取互斥锁并命中缓存。

  • 存在的问题:互斥等待时间,如果1000个线程同时访问,则只有1个获取成功,其他999个都是在等待,性能会下降

3、接口限流与熔断,降级。重要的接口一定要做好限流策略,防止用户恶意刷接口,同时要降级准备,当接口中的某些服务不可用时候,进行熔断,失败快速返回机制。

0

3、缓存雪崩

  缓存中大批量的数据过期,导致所有请求都打到数据库。 例如缓存服务宕机、缓存服务器重启、大量缓存集中在某一个时间段失效。

---->解决:

1、缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。

2、设置热点数据永远不过期。

3、如果缓存数据库是分布式部署,将热点数据均匀分布在不同的缓存数据库中。

4、多级缓存:设置多级缓存,第一级缓存失效的基础上,访问二级缓存,每一级缓存的失效时间都不同。

5、服务熔断:当缓存服务器宕机或超时响应时,为了防止整个系统出现雪崩,暂时停止业务服务访问缓存系统。

6、服务降级:当出现大量缓存失效,而且处在高并发高负荷的情况下,在业务系统内部暂时舍弃对一些非核心的接口和数据的请求,而直接返回一个提前准备好的 fallback(退路)错误处理信息。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值