Java高频面试题---Redis

hwjres / 2024-03-02 / 原文

Redis

一、Redis的使用场景

① 缓存  ② 分布式锁  ③限流  ④购物车  ⑤Token存储  ⑥点赞关注  ⑦短信验证码存储  ⑧分布式Session  ⑨发布订阅  ⑩排行榜

1、缓存

  热点数据(经常查询,但不修改和删除)首选redis,性能高。

 2、分布式锁

注:锁,即在多线程环境下,对共享资源的访问造成线程安全问题,通过锁的机制来实现资源访问互斥;比如Java语言有线程锁:synchronize / Lock 等。

 分布式锁:在分布式环境下,所有应集群部署,锁(互斥)的范围发生了改变;

分布式锁是一种用于分布式系统中实现同步访问共享资源的机制。在分布式系统中,多个节点需要协调合作完成某项任务时,为避免资源竞争和数据不一致问题,可以使用分布式锁来确保在某一时刻只有一个节点可以访问共享资源。

 redis分布式锁实现:① Jedis   ② Lttuce   ③ RedisTemplete   ④ Redisson   

一。命令实现:setnx(获取锁) ,expire(设置锁的过期时间),del(删除锁,释放锁)

1、获取锁:setnx  key value

2、设置锁的过期时间:expire  key  30

3、执行业务代码

4、删除锁:del  key

二、Redisson实现

1、获取锁:redisson.getLock("lock");

       lock.lock();

2、执行业务代码

3、释放锁:lock.unlock();

Redisson实现的分布式锁是可重入的吗?  

注:可重入?   即可重复获取,它是指线程T获取到锁A之后,线程T再次去获取锁A是可以获取到的,Java中的synchronized、ReentrantLock都是可重入锁。

Redisson实现的分布式锁是可重入锁

redis实现分布式锁需要注意的问题?

1、不是原子操作

因为redis获取锁和设置锁的过期时间不是原子操作,在极端情况下就可以会出现问题,如果在获取锁后,redis宕机了,这个时候锁是没有过期时间的,会导致这个锁一直无法释放的情况。所以需要将获取锁和设置锁的过期时间在一个命令下执行,保证原子性。

2、没有释放锁

如果在执行业务代码后,服务突然宕机了,这个时候锁是还没有释放的,所以在获取锁的时候一定要设置过期时间,这样就算服务宕机了,我们没有手动释放锁,也会因为我们设置了锁的过期时间而自动释放锁,如果没有设置过期时间,会导致其他线程一直获取不到锁。

3、释放了锁。但业务还没有执行完

因为redis实现的分布式锁默认过期时间是30秒,但如果业务代码执行完要35秒,但是锁已经释放了,剩下的5秒内如果有线程进来获取锁并执行业务代码,这时就会有两个线程来执行业务代码,这样就会导致数据不一致,比如在减库存的时候会出现超卖的情况。所以锁的过期时间要让续期,redis已经提供了,是锁的过期时间的1/3。

4、释放了别人的锁

当前有两个线程,线程A和线程B,如果设置锁的过期时间是30s,但执行业务代码要35s,并且没有设置锁的过期时间的续期,此时线程A在执行,但锁已经释放了,然后线程B进来执行,因为线程A的锁已经过期了,此时线程A释放的锁是线程B的,这样就产生了释放了别人的锁的情况。在实现分布式锁时,要求自己的锁要自己释放,不允许释放别人的锁。线程可以在获取锁的时候,生成一个唯一的id,然后把这个id设置到key的值里面去,当要释放锁的时候,去判断当前锁是不是当前线程设置的。

5、大量请求竞争锁失败

① 重试,让锁自旋,重试3~5次,每一次获取失败间隔50ms,如果超过5次,就返回获取锁失败。

② 让业务执行尽可能短

③ 限流处理

6、多节点Redis主从复制的问题

 假设当前有两个线程,线程A和线程B,并且当前redis是哨兵模式的主从复制,一主二从,当线程A去主服务器去获取key,并且将key同步到两个从节点,但是还没有复制,这个主服务器就宕机了,然后从服务器升级成主服务器,这个时候线程B就开始去这个新的主服务器去获取key,这样两个线程都获取到了锁,都会区执行业务代码,这样数据会有安全问题。可以使用redis提供的红锁来解决。

解决:红锁会向所有的redis节点加锁,但这种方法性能很低,也很复杂,如果要在redis集群环境下使用分布式锁,建议使用zookeeper来实现分布式锁。redis是保证AP,zookeeper是保证CP。

7、锁的性能问题

分段锁。

8、锁的可重入性

Redisson实现的分布式锁就具有可重入性。

3、Token存储

在现在的前后端分离的开发模式下,redis存储token很常见。

用户在前端登录成功后会生成token,并将token写入redis,并且前端也会将token进行保存,后续用户需要进行访问其他接口时,会携带token请求,在后端中经过过滤器,拦截器等校验,最后会判断当前token是否和登录成功时存入redis的token进行比较,一致则将结果返回到前端。

4、短信验证码存储

用户在前端点击发送验证码,在后台生成验证码之后,将验证码存入redis,并且将验证码发送给用户,用户输入验证码后发起请求,将用户输入的验证码和在redis里的验证码进行比对,一致则成功。

5、计数器

 6、全局唯一id

 

 7、排行榜

使用redis的Zset数据结构

放入一些数据

 

 将这些数据按score从高到低排序

 增加分数

 8、限流

redis可以配合lua脚本来对数据进行限流

 9、购物车

可以使用redis的hash结构来存储用户的购物车数据

 10、点赞关注

点赞可以使用redis的hash结构,关注可以使用redis的set结构。

 

二、缓存穿透,缓存击穿,缓存雪崩

1、缓存穿透

缓存穿透是由于请求一个不存在的数据而导致的;如果数据库或者redis被一直这样恶意请求一个不存在的数据,会造成数据库的访问压力过大,导致数据库响应过慢,严重甚至会导致数据库宕机。

 解决:

① 缓存空值。在缓存中,之所以会发生穿透,就是因为没有将那些不存在的值的key缓存下来,从而导致每次查询请求都要请求到数据库。所以就可以为这样key对应的值设置为null并放到缓存当中,这样再出现查询这个key的时候,直接返回null即可

② 布隆过滤器(BloomFilter)。布隆过滤器用于检索一个元素是否在一个集合中。它是采用一个很长的二进制数组,通过一系列的hash函数来确定该数据是否存在。

布隆过滤器是一种比较巧妙的概率性数据结构,它可以告诉你数据一定不存在或者可能存在,相比Map,Set,List等传统的数据结构它占用内存少、结构更高效。

布隆过滤器添加元素时,会使用多个hash函数对元素进行哈希,然后用数组的长度取余数,算出一个索引位置值,再把数组的位置值设置为1,这样就完成了元素添加

向布隆过滤器查询元素时,和添加元素一样,也会对元素进行多次hash计算出数组的位置,然后查看数组的位置值是否都为1,只要有一个位置值为0,就表示布隆过滤器不存在该元素。如果这几个位置都为1,则表示元素可能存在(并不是一定存在)

③布隆过滤器为什么存在误判?

 具体实现:Guava,Hutool,Redisson,手动实现

redisson

 2、缓存击穿

缓存击穿是指当某一个key的缓存过期时,大并发量的请求同时访问此key,瞬间击穿缓存服务器直接访问数据库,让数据库处于负载的情况。

解决:

① 异步定时更新

比如某一个热点数据的过期时间是1小时,那么每59分钟,通过定时任务去更新这个热点key,并重新设置其过期时间

② 互斥锁

当redis中根据key获得的value值为空时,先锁上,然后从数据库加载,加载完毕,释放锁。若其他线程也在请求该key时,发现获取锁失败,则先阻塞。

3、缓存雪崩

缓存雪崩是指当大量缓存同时过期或者缓存服务宕机,所有的请求都直接访问数据库,造成数据库高负载,影响性能,甚至数据库宕机。

解决:

① 不同的过期时间

为了避免大量的缓存在同一时间过期,可以把不同的key过期时间设置成不同的,并且通过定时刷新的方式更新过期时间。

② 集群

在缓存雪崩问防治上,一个比较典型的技术就是采用集群方式部署,使用集群可以避免服务单点故障

 

三、Redis是AP还是CP的?

AP

Redis的设计目标是高性能、高可扩展性和高可用性,Redis的一致性模型是最终一致性,即在某个时间点读取的数据可能并不是最新的,但最终会达到一致的状态。

Redis没办法保证强一致性的主要原因是,因为它的分布式设计采用的是异步复制,这导致在节点之间存在数据同步延迟和不一致的可能性。

也就是说,当某个节点上的数据发生改变时,redis会将这个修改操作发送给其他节点进行同步,但是由于网络传输的延迟等原因,这些操作不一定会立即被其他节点收到和执行,这就可能导致节点之间存在数据不一致的情况。

除此之外,redis的一致性还受到节点故障的影响。当一个节点宕机时,这个节点上的数据可能无法同步到其他节点,这就可能导致数据在节点之间的不一致。虽然redis通过主从复制和哨兵等机制可以提高系统的可用性和容错性,但是这些机制并不能完全解决数据一致性问题。

四、Redis的集群模式?

redis有三种主要的集群模式,用于分布式环境中实现高可用和数据复制。

1、主从复制

主从复制是Redis最简单的集群模式。这个模式主要是为了解决单点故障的问题,所以将数据复制到多个副本中,这样即便有一台服务器出现故障,其他服务器依然可以继续提供服务。

主从模式中,包括一个主节点和一个或者多个从节点。主节点负责处理所有的写操作和读操作,而从节点则负责主节点的数据,并且只能处理读操作。当主节点发生故障时,可以将一个从节点升级为主节点,实现故障转移。

 优点:简单易用,适用于读多写少的场景。它提供了数据备份的功能,并且可以有很好的扩展性,只要增加更多的节点,就能让整个集群的读的能力不断提升。

缺点:不具备故障自动转移的能力,没有办法做容错和恢复。主节点和从节点的宕机都会导致客户端部分读写请求失败,需要人工介入让节点恢复或者手动切换一台从节点变成主节点服务器才可以。并且在主节点宕机时,如果数据没有及时复制到从节点,也会导致数据不一致。

 2、哨兵模式

哨兵模式是为了解决主从模式无法自动容错及恢复的问题。

哨兵模式在主从复制的基础上加入了哨兵节点。哨兵节点是一种特殊的redis节点,用于监控主节点和从节点的状态。当主节点发生故障时,哨兵节点可以自动进行故障转移,选择一个合适的从节点升级为主节点,并通知其他从节点和应用程序进行更新。每个redis实例都可以作为哨兵节点,通常需要部署多个哨兵节点,以确保故障转移的可靠性。

 哨兵节点会定期向所有主节点和从节点发送PING命令,如果在指定的时间内未收到PONG响应,哨兵节点会将该节点标记为主观下线。如果一个主节点被多数哨兵节点标记为主观下线,那么它将被标记为客观下线

当主节点被标记客观下线时,哨兵节点会触发故障转移过程。他会从所有的健康的从节点中选举一个新的主节点,并将所有从节点切换到新的主节点,实现自动故障转移。同时,哨兵节点会更新所有客户端的配置,指向新的主节点。

哨兵节点通过发布订阅功能来通知客户端有关主节点状态变化的消息。客户端收到消息后,会更新配置,将新的主节点信息应用于连接池,从而使客户端可以继续与新的主节点进行交互。

优点:为整个集群系统提供了一种故障转移和恢复的能力。

3、Cluster模式

Redis  Cluster是Redis中推荐的分布式集群解决方案。它将数据自动分片到多个节点上,每个节点负责一部分数据。

 Redis  Cluster 采用主从复制来提高可用性。每个分片都有一个主节点和多个从节点。主节点负责写操作,而从节点复制主节点的数据并处理读请求。

Redis  Cluster能够自动检测节点的故障。当一个节点失去连接或者不可达时,Redis  Cluster会尝试将该节点标记为不可用,并从可用的从节点中提升一个新的主节点。

优点:

① 适用于大规模应用的解决方案,它提供了更好的横向扩展和容错能力。它自动管理数据分片和故障转移,减少了运维的负担。

② 数据分片存储在不同的节点上,每个节点都可以单独对外提供读写服务。不存在单点故障的问题。

 

五、Redis的数据分片

Redis的数据分片是一种将一个reids数据集分割成多个部分,分别存储在不同的redis节点上的技术。它可用用于将一个单独的redis数据库扩展到多个物理机器上,从而提高redis集群的性能和可扩展性。

实现方式(Cluster):

在Redis的Cluster集群模式中,使用哈希槽(hash  slot)的方式来进行数据分片,将整个数据集划分为多个槽,每个槽分配给一个节点。客户端访问数据时,先计算出数据对应的槽,然后直接连接到该槽所在的节点进行操作。