redis学习总结

写的很棒:(30条消息) redis学习_sahahsha的博客-CSDN博客

面试题:(30条消息) Redis面试题_sahahsha的博客-CSDN博客

(30条消息) Redis知识点总结归纳_Think_Bigger的博客-CSDN博客_redis知识总结

(30条消息) 20道经典Redis面试题_CSDN砖家的博客-CSDN博客_redis面试题  很详细

(30条消息) 110道 Redis面试题及答案 (持续更新)_架构师专栏的博客-CSDN博客_redis面试题及答案

 

 

前言:

Redis(Remote Dictionary Server) 是一个使用 C 语言编写的,开源的(BSD许可)高性能非关系型(NoSQL)的键值对数据库。有五大数据类型,包括String,List,Set,Sortedset,Hash。redis是NoSQL数据库。

NoSQL的特点

  • 不遵循SQL标准
  • 不支持ACID(原子性,一致性,持久性,隔离性)
  • 远超于SQL的性能。

 

有如下几个问题需要搞明白:

1、为什么使用redis,redis有什么优点?

2、使用redis有什么缺点?

3、单线程的redis为什么这么快?

4、redis的数据类型,以及每种数据类型的使用场景?

5、redis的过期策略以及内存淘汰机制

6、redis和数据库双写一致性问题

7、如何应对缓存穿透和缓存雪崩的问题?

8、如何解决redis的并发竞争问题

1、为什么使用redis?redis有什么优点?

主要从两个方面区考虑:性能和并发。

(一)性能

如下图所示,我们在碰到需要执行耗时特别久,且结果不频繁变动的SQL,就特别适合将运行结果放入缓存。这样,后面的请求就去缓存中读取,使得请求能够迅速响应。

用户第一次访问数据库中的某些数据。这个过程会比较慢,因为是从硬盘上读取的。将该用户访问的数据存在缓存中,这样下一次再访问这些数据的时候就可以直接从缓存中获取了。如果数据库中的对应数据改变的之后,同步改变缓存中相应的数据即可!

 

 

(二)并发

直接操作缓存能够承受的请求是远远大于直接访问数据库的。在大并发的情况下,所有的请求直接访问数据库,数据库会出现连接异常。这个时候,就需要使用redis做一个缓冲操作,让请求先访问到redis,而不是直接访问数据库。

 

2、使用reids有什么缺点

主要是四个问题:

(1)缓存和数据库双写一致性问题

(2)缓存雪崩问题

(3)缓存击穿问题

(4)缓存穿透问题

(5)缓存的并发竞争问题

 

3、单线程的redis为什么这么快

分析:这个问题其实是对redis内部机制的一个考察。其实根据博主的面试经验,很多人其实都不知道redis是单线程工作模型。所以,这个问题还是应该要复习一下的。

回答:主要一下三点:

(1)纯内存操作

(2)单线程操作,避免了频繁的上下文切换

(3)采用了非阻塞I/O多路复用机制

题外话:我们现在要仔细的说一说I/O多路复用机制,因为这个说法实在是太通俗了,通俗到一般人都不懂是什么意思。博主打一个比方:小曲在S城开了一家快递店,负责同城快送服务。小曲因为资金限制,雇佣了一批快递员,然后小曲发现资金不够了,只够买一辆车送快递。

经营方式一

客户每送来一份快递,小曲就让一个快递员盯着,然后快递员开车去送快递。慢慢的小曲就发现了这种经营方式存在下述问题

几十个快递员基本上时间都花在了抢车上了,大部分快递员都处在闲置状态,谁抢到了车,谁就能去送快递

随着快递的增多,快递员也越来越多,小曲发现快递店里越来越挤,没办法雇佣新的快递员了

快递员之间的协调很花时间

综合上述缺点,小曲痛定思痛,提出了下面的经营方式

经营方式二

小曲只雇佣一个快递员。然后呢,客户送来的快递,小曲按送达地点标注好,然后依次放在一个地方。最后,那个快递员依次的去取快递,一次拿一个,然后开着车去送快递,送好了就回来拿下一个快递。

对比

上述两种经营方式对比,是不是明显觉得第二种,效率更高,更好呢。在上述比喻中:

每个快递员——————>每个线程

每个快递——————–>每个socket(I/O流)

快递的送达地点————–>socket的不同状态

客户送快递请求————–>来自客户端的请求

小曲的经营方式————–>服务端运行的代码

一辆车———————->CPU的核数

于是我们有如下结论

1、经营方式一就是传统的并发模型,每个I/O流(快递)都有一个新的线程(快递员)管理。

2、经营方式二就是I/O多路复用。只有单个线程(一个快递员),通过跟踪每个I/O流的状态(每个快递的送达地点),来管理多个I/O流。

下面类比到真实的redis线程模型,如图所示

 

 参照上图,简单来说,就是。我们的redis-client在操作的时候,会产生具有不同事件类型的socket。在服务端,有一段I/0多路复用程序,将其置入队列之中。然后,文件事件分派器,依次去队列中取,转发到不同的事件处理器中。

按我的总结I/O多路复用是,一个IO管理器,来管理多个IO,这个管理器可以把已经就绪的IO起来,然后根据不同IO的事件分派给不同的操作函数去处理(reactor)。

 

4、redis的数据类型,以及每种数据类型的使用场景

分析:是不是觉得这个问题很基础,其实我也这么觉得。然而根据面试经验发现,至少百分八十的人答不上这个问题。建议,在项目中用到后,再类比记忆,体会更深,不要硬记。基本上,一个合格的程序员,五种类型都会用到。

一共有五种

(1)String

最常规的set/get操作,value可以是String也可以是数字。一般做一些复杂的计数功能的缓存。

(2)hash

这里value存放的是结构化的对象,比较方便的就是操作其中的某个字段。博主在做单点登录的时候,就是用这种数据结构存储用户信息,以cookieId作为key,设置30分钟为缓存过期时间,能很好的模拟出类似session的效果。

(3)list

使用List的数据结构,可以做简单的消息队列的功能。另外还有一个就是,可以利用lrange命令,做基于redis的分页功能,性能极佳,用户体验好。本人还用一个场景,很合适---取行情信息。就也是个生产者和消费者的场景。LIST可以很好的完成排队,先进先出的原则。

(4)set

(5)zset

sorted set多了一个权重参数score,集合中的元素能够按score进行排列。可以做排行榜应用,取TOP N操作。

 

5、redis的过期策略以及内存淘汰机制

分析:这个问题其实相当重要,到底redis有没用到家,这个问题就可以看出来。比如你redis只能存5G数据,可是你写了10G,那会删5G的数据。怎么删的,这个问题思考过么?还有,你的数据已经设置了过期时间,但是时间到了,内存占用率还是比较高,有思考过原因么?

回答:redis采用的是定期删除+惰性删除策略

(1)为什么不用定时删除策略?

定时删除,用一个定时器来负责监视key,过期则自动删除。虽然内存及时释放,但是十分消耗CPU资源。在大并发请求下,CPU要将时间应用在处理请求,而不是删除key,因此没有采用这一策略。

(2)定期删除和惰性删除是如何工作的呢?

定期删除,redis默认每个100ms检查,是否有过期的key,有过期key则删除。需要说明的是,redis不是每个100ms将所有的key检查一次,而是随机抽取进行检查(如果每隔100ms,全部key进行检查,redis岂不是卡死)。因此,如果只采用定期删除策略,会导致很多key到时间没有删除。

于是,惰性删除派上用场。也就是说在你获取某个key的时候,redis会检查一下,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除。

(3)采用定期删除和惰性删除就没其他问题了吗?

不是的,如果定期删除没删除key。然后你也没即时去请求key,也就是说惰性删除也没生效。这样,redis的内存会越来越高。那么就应该采用内存淘汰机制。

在redis.conf种有一行配置

# maxmemory-policy volatile-lru

该配置就是配内存淘汰策略的(什么,你没配过?好好反省一下自己)

1)noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。应该没人用吧。

2)allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。推荐使用,目前项目在用这种。

3)allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。应该也没人用吧,你不删最少使用Key,去随机删。

4)allkeys-lfu,针对所有key使用lfu算法进行淘汰。lfu算法:最不频繁使用,会淘汰掉使用频率最低的数据,如果存在相同使用频率的数据,则使用时间间隔,将最久未使用的数据淘汰。

5)volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。这种情况一般是把redis既当缓存,又做持久化存储的时候才用。不推荐

6)volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。依然不推荐

7)volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。不推荐

8)volatile-lfu,针对设置了过期时间的key,使用lfu算法进行淘汰。

ps:如果没有设置 expire 的key, 不满足先决条件(prerequisites); 那么 volatile-lru, volatile-random 和 volatile-ttl 策略的行为, 和 noeviction(不删除) 基本上一致。

6、redis和数据库双写一致性问题

分析:一致性问题是分布式常见问题,还可以再分为最终一致性和强一致性。数据库和缓存双写,就必然会存在不一致的问题。答这个问题,先明白一个前提。就是如果对数据有强一致性要求,不能放缓存。我们所做的一切,只能保证最终一致性。另外,我们所做的方案其实从根本上来说,只能说降低不一致发生的概率,无法完全避免。因此,有强一致性要求的数据,不能放缓存。
首先,采取正确更新策略,先更新数据库,再删缓存。其次,因为可能存在删除缓存失败的问题,提供一个补偿措施即可,例如利用消息队列。

 

7、如何应对缓存穿透和缓存雪崩问题

分析:这两个问题,说句实在话,一般中小型传统软件企业,很难碰到这个问题。如果有大并发的项目,流量有几百万左右。这两个问题一定要深刻考虑。

缓存穿透,即黑客故意去请求缓存中不存在的数据,导致所有的请求都怼到数据库上,从而数据库连接异常。

解决方案:

(一)利用互斥锁,缓存失效的时候,先去获得锁,得到锁了,再去请求数据库。没得到锁,则休眠一段时间重试

(二)采用异步更新策略,无论key是否取到值,都直接返回。value值中维护一个缓存失效时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。需要做缓存预热(项目启动前,先加载缓存)操作。

(三)提供一个能迅速判断请求是否有效的拦截机制,比如,利用布隆过滤器,内部维护一系列合法有效的key。迅速判断出,请求所携带的Key是否合法有效。如果不合法,则直接返回。

缓存雪崩,即缓存同一时间大面积的失效,这个时候又来了一波请求,结果请求都怼到数据库上,从而导致数据库连接异常。

解决方案:

(一)给缓存的失效时间,加上一个随机值,避免集体失效。

(二)使用互斥锁,但是该方案吞吐量明显下降了。

(三)双缓存。我们有两个缓存,缓存A和缓存B。缓存A的失效时间为20分钟,缓存B不设失效时间。自己做缓存预热操作。然后细分以下几个小点

  • I 从缓存A读数据库,有则直接返回

  • II A没有数据,直接从B读数据,直接返回,并且异步启动一个更新线程。

  • III 更新线程同时更新缓存A和缓存B。

8、如何解决redis的并发竞争key问题

分析:这个问题大致就是,同时有多个子系统去set一个key。这个时候要注意什么呢?大家思考过么。需要说明一下,博主提前百度了一下,发现答案基本都是推荐用redis事务机制。博主不推荐使用redis的事务机制。因为我们的生产环境,基本都是redis集群环境,做了数据分片操作。你一个事务中有涉及到多个key操作的时候,这多个key不一定都存储在同一个redis-server上。因此,redis的事务机制,十分鸡肋。

回答:如下所示

(1)如果对这个key操作,不要求顺序

这种情况下,准备一个分布式锁,大家去抢锁,抢到锁就做set操作即可,比较简单。

(2)如果对这个key操作,要求顺序

假设有一个key1,系统A需要将key1设置为valueA,系统B需要将key1设置为valueB,系统C需要将key1设置为valueC.

期望按照key1的value值按照 valueA–>valueB–>valueC的顺序变化。这种时候我们在数据写入数据库的时候,需要保存一个时间戳。假设时间戳如下:

系统A key 1 {valueA  3:00}

系统B key 1 {valueB  3:05}

系统C key 1 {valueC  3:10}

那么,假设这会系统B先抢到锁,将key1设置为{valueB 3:05}。接下来系统A抢到锁,发现自己的valueA的时间戳早于缓存中的时间戳,那就不做set操作了。以此类推。

其他方法,比如利用队列,将set方法变成串行访问也可以。总之,灵活变通。

 

补充:

9、持久化机制

redis的数据是存储在内存中,如果机器宕机,那么数据就会全部丢失,因此需要有持久化机制来保证数据不会因为宕机而丢失。redis为我们提供了两种持久化方案,一种是基于快照(RDB),另外一种是基于AOF日志。

(1)RDB (Redis Database)

将指定的时间间隔内将内存中的数据集快照写入磁盘,这个过程就是RDB。

恢复时是将快照文件直接读到内存中。

1)备份是如何执行的?哪三种触发方式

a、执行save命令;b、执行bgsave命令;c、在redis.config种配置自动化。

a)save触发

立即停止RedisServer对外的服务,开始将内存中的数据保存到硬盘指定的位置。(Redis是单线程程序,这个线程要同时负责多个客户端套接字的并发读写操作和内存结构的逻辑读写。而save命令会阻塞当前的redis服务器,在执行该命令期间,redis无法处理其他的命令,知道整个RDB过程完成为止,当这条指令执行完毕,将RDB文件保存下来后,才能继续去相应请求。这种方式用于新机器上数据的备份还好,如果用在生产上,那么简直是灾难,数据量过于庞大,阻塞时间过长。)

 

 

默认保存在:命令执行所在的文件目录

b) bgsave

为了不阻塞线上的业务,那么Redis就必须一边持久化,一边相应客户端的请求。所以在执行bgsave时可以通过fork一个子进程,然后后通过这个子进程来处理接下来所有的保存工作,父进程就可以继续相应请求而无需去关心I/O操作。

 

 

单独建一个子进程(fork)进行持久化,这个进程他是怎么工作的
先将数据写入到一个临时文件中,待持久化过程结束了,再将这个临时文件替换上次持久化的文件。有个缺点就是最后一次持久化的数据可能会丢失。这个工程用到一个技术“写时复制技术”。具体来说,数据要进行持久化,要生成一个文件,文件默认名为dump.rdb,fork进程创建临时文件同步redis数据,再把临时区域的内容覆盖到dump.rdb文件中

c)redis.config配置

上述两种方式都需要我们在客户端中去执行save或者bgsave命令,在生产情况下我们更多地需要是自动化的触发机制,那么Redis就提供了这种机制,我们可以在redis.config中队持久化进行配置。

 

 像上述这样在redis.config中进行配置,如save 900 1 是指在 900 秒内,如果有一个或一个以上的修改操作,那么就自动进行一次自动化备份;save 300 10同样意味着在 300 秒内如果有十次或以上的修改操作,那么就进行数据备份,依次类推。

如果你不想进行数据持久化,只希望数据只在数据库运行时存在于内存中,那么你可以通过 save ""禁止掉数据持久化。

这里再介绍几个在配置文件中与 RDB 持久化相关的系数:

stop-writes-on-bgsave-error:默认值为yes,即当最后一次 RDB 持久化保存文件失败后,拒绝接收数据。这样做的好处是可以让用户意识到数据并没有被成功地持久化,避免后续更严重的业务问题的发生;

rdbcompression:默认值为yes,即代表将存储到磁盘中的快照进行压缩处理;

rdbchecksum:默认值为yes,在快照存储完成后,我们还可以通过CRC64算法来对数据进行校验,这会提升一定的性能消耗;

dbfilename:默认值为dump.rdb,即将快照存储命名为dump.rdb;

dir:设置快照的存储路径。

 

COW机制(copy on write)

先前提到了Redis为了不阻塞线上业务,所以需要一边持久化一边响应客户端的请求,因此fork出一个子进程来处理这些保存工作。那么具体这个fork出来的子进程是如何做到使得Redis可以一边做持久化操作,一边做响应工作呢?这就涉及到COW (Copy On Write)机制,那我们具体讲解以下这个COW机制。

Redis在持久化的时候会去调用glibc的函数fork出一个子进程,快照持久化完成交由子进程来处理,父进程继续响应客户端的请求。而在子进程刚刚产生时,它其实使用的是父进程中的代码段和数据段。所以fork之后,kernel会将父进程中所有的内存页的权限都设置为read-only,然后子进程的地址空间指向父进程的地址空间。当父进程写内存时,CPU硬件检测到内存页是read-only的,就会触发页异常中断(page-fault),陷入 kernel 的一个中断例程。中断例程中,kernel就会把触发的异常的页复制一份,于是父子进程各自持有独立的一份。而此时子进程相应的数据还是没有发生变化,依旧是进程产生时那一瞬间的数据,故而子进程可以安心地遍历数据,进行序列化写入磁盘了。

随着父进程修改操作的持续进行,越来越多的共享页面将会被分离出来,内存就会持续增长,但是也不会超过原有数据内存的两倍大小(Redis实例里的冷数据占的比例往往是比较高的,所以很少出现所有页面都被分离的情况)。

COW机制的好处很明显:首先可以减少分配和复制时带来的瞬时延迟,还可以减少不必要的资源分配。但是缺点也很明显:如果父进程接收到大量的写操作,那么将会产生大量的分页错误(页异常中断page-fault)。

 

RDB的优劣

优势:

1)RDB是一个非常紧凑的文件(二进制数据),它保存了Redis在某个时间点上的数据集。这种文件适用于进行备份:比如说可以在最近的24小时内,每小时备份一次RDB文件,并且在每个月的每一天,也备份一个 RDB 文件。 这样的话,即使遇上问题,也可以随时将数据集还原到不同的版本;

2)RDB 非常适用于灾难恢复(disaster recovery):它只有一个文件,并且内容都非常紧凑,可以(在加密后)将它传送到别的数据中心;

3)RDB 可以最大化 Redis 的性能:父进程在保存 RDB 文件时唯一要做的就是 fork 出一个子进程,然后这个子进程就会处理接下来的所有保存工作,父进程无须执行任何磁盘 I/O 操作;

4)RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。

劣势:

1)如果业务上需要尽量避免在服务器故障时丢失数据,那么 RDB 并不适合。 虽然 Redis 允许在设置不同的保存点(save point)来控制保存 RDB 文件的频率, 但是, 由于 RDB 文件需要保存整个数据集的状态, 所以这个过程并不快,可能会至少 5 分钟才能完成一次 RDB 文件保存。 在这种情况下, 一旦发生故障停机, 就可能会丢失好几分钟的数据。

2)每次保存 RDB 的时候,Redis 都要 fork() 出一个子进程,并由子进程来进行实际的持久化工作。 在数据集比较庞大时, fork() 可能会非常耗时,造成服务器在某某毫秒内停止处理客户端; 如果数据集非常巨大,并且 CPU 时间非常紧张的话,那么这种停止时间甚至可能会长达整整一秒。 虽然 AOF 重写也需要进行 fork() ,但无论 AOF 重写的执行间隔有多长,数据的耐久性都不会有任何损失。

 

(2)AOF持久化(默认不开启)

当两种方式同时开启时,数据恢复redis会优先选择AOF恢复。RDB持久化时全量备份,比较耗时,所以Redis提供了一种更为高效对的AOF持久化方案。

AOF(Append Only-File)是以日志的形式来记录每个写操作(增量保存),将redis执行过的所有写指令都记下来。AOF只记录对内存进行修改的指令记录。

在服务器重新启动时,Redis就会利用AOF日志中记录的这些操作重新构建原始数据集。

 

 Redis会在收到客户端修改指令后,进行参数修改、逻辑处理,如果没有问题,就立即将该指令文本存储到 AOF 日志中,也就是说,先执行指令才将日志存盘。这点不同于 leveldb、hbase等存储引擎,它们都是先存储日志再做逻辑处理。

1)AOF的触发配置

AOF也有不同的触发方案,这里简要描述一下三种触发方案:

a、always:每次发生数据修改就会立即记录到磁盘文件中,这种方案的完整性好但是IO开销很大,性能较差;

b、everysec:在每一秒中进行同步,速度有所提升。但是如果在一秒内宕机的话可能失去这一秒内的数据;

c、no:默认配置,即不适用AOF持久化方案

 2)AOF重写机制

随着Redis的运行,AOF的日志会越来越长,如果实例宕机重启,那么重放整个AOF将会变得十分耗时,而在日志记录中,又有很多无意义的记录,比如我现在将一个数据 incr 一千次,那么就不需要去记录这1000次修改,只需要记录最后的值即可。所以就需要进行 AOF 重写。

Redis 提供了bgrewriteaof指令用于对AOF日志进行重写,该指令运行时会开辟一个子进程对内存进行遍历,然后将其转换为一系列的 Redis 的操作指令,再序列化到一个日志文件中。完成后再替换原有的AOF文件,至此完成。

同样的也可以在redis.config中对重写机制的触发进行配置:通过将no-appendfsync-on-rewrite设置为yes,开启重写机制;auto-aof-rewrite-percentage 100意为比上次重写后文件大小增长了100%再次触发重写;

auto-aof-rewrite-min-size 64mb意为当文件至少要达到64mb才会触发制动重写。

 

 重写也是会耗费资源的,所以当磁盘空间足够的时候,这里可以将 64mb 调整更大写,降低重写的频率,达到优化效果。

3)AOF的优劣

优点:

a、AOF 持久化的默认策略为每秒钟 fsync 一次,在这种配置下,Redis 仍然可以保持良好的性能,并且就算发生故障停机,也最多也只会丢失掉一秒钟内的数据;

b、AOF 文件是一个只进行追加操作的日志文件(append only log), 因此对 AOF 文件的写入不需要进行 seek , 即使日志因为某些原因而包含了未写入完整的命令(比如写入时磁盘已满,写入中途停机,等等), redis-check-aof 工具也可以轻易地修复这种问题。

c、Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写: 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。 整个重写操作是绝对安全的,因为 Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。 而一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。

d、AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。 导出(export) AOF 文件也非常简单: 举个例子, 如果你不小心执行了 FLUSHALL 命令, 但只要 AOF 文件未被重写, 那么只要停止服务器, 移除 AOF 文件末尾的 FLUSHALL 命令, 并重启 Redis , 就可以将数据集恢复到 FLUSHALL 执行之前的状态。

缺点:

a、对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。

b、根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB 。 在一般情况下, 每秒 fsync 的性能依然非常高, 而关闭 fsync 可以让 AOF 的速度和 RDB 一样快, 即使在高负荷之下也是如此。 不过在处理巨大的写入载入时,RDB 可以提供更有保证的最大延迟时间(latency)。

 

4)混合持久化

重启 Redis 时,如果使用 RDB 来恢复内存状态,会丢失大量数据。而如果只使用 AOF 日志重放,那么效率又太过于低下。Redis 4.0 提供了混合持久化方案,将 RDB 文件的内容和增量的 AOF 日志文件存在一起。这里的 AOF 日志不再是全量的日志,而是自 RDB 持久化开始到持久化结束这段时间发生的增量 AOF 日志,通常这部分日志很小。

 

 于是在 Redis 重启的时候,可以先加载 RDB 的内容,然后再重放增量 AOF 日志,就可以完全替代之前的 AOF 全量重放,重启效率因此得到大幅提升。

 

补充:

1、什么是热key?

在Redis中,我们把访问频率高的Key,称为热Key。比如突然又几十万的请求去访问redis中某个特定的Key,那么这样会造成redis服务器短时间流量过于集中,很可能导致redis的服务器宕机。那么接下来对这个Key的请求,都会直接请求到我们的后端数据库中,数据库性能本来就不高,这样就可能直接压垮数据库,进而导致后端服务不可用。

2、热key产生的原因?

  • 用户消费的数据远大于生产的数据,如商品秒杀、热点新闻、热点评论等读多写少的场景。

双十一秒杀商品,短时间内某个爆款商品可能被点击/购买上百万次,或者某条爆炸性新闻等被大量浏览,此时会造成一个较大的请求Redis量,这种情况下就会造成热点Key问题。

  • 请求分片集中,超过单台Redis服务器的性能极限。

在服务端读数据进行访问时,往往会对数据进行分片切分,例如采用固定Hash分片,hash落入同一台redis服务器,如果瞬间访问量过大,超过机器瓶颈时,就会导致热点 Key 问题的产生。

3. 热key的危害?

  • 缓存击穿,压垮redis服务器,导致大量请求直接发往后端服务,并且DB本身性能较弱,很可能进一步导致后端服务雪崩。

4. 如何识别热key?

  • 凭借个人经验,结合业务场景,判断哪些是热Key。

比如,双十一大促的时候,苹果手机正在秒杀,那么我们可以判断苹果手机这个sku就是热Key。

  • 使用redis之前,在客户端写程序统计上报。

修改我们的业务代码,在操作redis之前,加入Key使用次数的统计逻辑,定时把收集到的数据上报到统一的服务进行聚合计算,这样我们就可以找到那些热点Key。缺点就是对我们的业务代码有一定的侵入性。

  • 服务代理层上报。

这个要看具体公司redis集群架构是怎么样的,如果是在redis前面有一个代理层,那么我们可以在代理层进行收集上报,也是可以找到热点Key。如下这种架构:

 

 这种方式确定也很明显, 每个公司的架构不一样,并非都有Proxy代理这一层。

  • 使用redis自带的命令

例如monitor、redis-cli加上--hotkeys选项等,不过这种方式执行起来很慢,可能会降低redis的处理请求的性能,慎用。

monitor命令:可以实时抓取出redis服务器接收到的命令,然后写代码统计出热Key,也有现成的分析工具可以使用。

  • redis节点抓包分析

自己写程序监听端口,解析数据,进行分析。

5. 如何解决热key问题?

  • Redis集群扩容:增加分片副本,分摊客户端发过来的读请求;
  • 使用二级缓存,即JVM本地缓存,减少Redis的读请求。

例如使用Caffeine+redis 实现二级缓存,先从本地缓存中取,取不到再去redis中去取。当然也可以使用其它框架,如ehcache、甚至一个HashMap都可以。

 

 

————————————————
版权声明:本文为CSDN博主「每天都要进步一点点」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Weixiaohuai/article/details/125310401

posted @   放弃吧  阅读(62)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示