Redis面试问题总结

Redis是一种高性能的key-value数据库,支持多种数据类型,如string、list、set等。其持久化方式包括RDB快照和AOF日志,分别有各自的优缺点。Redis的过期策略包括定时、惰性和定期过期,防止缓存穿透和雪崩。缓存击穿可以通过布隆过滤器缓解。Redis的事务是单线程的,不支持回滚,但可以使用Lua脚本实现原子操作。Redis集群方案包括哨兵模式和RedisCluster,前者用于故障转移,后者基于槽的分片实现数据分布。分布式锁的实现和Redlock算法解决了主从模式下的锁安全问题。
摘要由CSDN通过智能技术生成

1. 什么是Redis?

Redis 是一个使用 C 语言写成的,开源的高性能key-value非关系缓存数据库。它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。Redis的数据都基于缓存的,所以很快,每秒可以处理超过 10万次读写操作,是已知性能最快的Key-Value DB。Redis也可以实现数据写入磁盘中,保证了数据的安全不丢失,而且Redis的操作是原子性的。

2. 使用redis有哪些好处?

  • (1) 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复

  • 杂度都很低

  • (2)支持丰富数据类型,支持string,list,set,sorted set,hash

  • (3) 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行

  • (4) 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除

3. Redis有哪些优缺点?

优点

  • 读写性能优异, Redis能读的速度是110000次/s,写的速度是81000次/s。

  • 支持数据持久化,支持AOF和RDB两种持久化方式。

  • 支持事务,Redis的所有操作都是原子性的,同时Redis还支持对几个操作合并后的原子性执行。

  • 数据结构丰富,除了支持string类型的value外还支持hash、set、zset、list等数据结构。

  • 支持主从复制,主机会自动将数据同步到从机,可以进行读写分离。

缺点

  • 数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。

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

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

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

4. Redis为什么这么快

  • 1、完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于HashMap,HashMap 的优势就是查找和操作的时间复杂度都是O(1);

  • 2、数据结构简单,对数据操作也简单,Redis 中的数据结构是专门进行设计的;

  • 3、采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;

  • 4、使用多路 I/O 复用模型,非阻塞 IO;

  • 5、使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了 VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;

5. Redis有哪些数据类型

Redis主要有5种数据类型,包括String,List,Set,Zset,Hash,满足大部分的使用要求

它还有三种特殊的数据结构类型

  • Geospatial

  • Hyperloglog

  • Bitmap

Geo:Redis3.2推出的,地理位置定位,用于存储地理位置信息,并对存储的信息进行操作。

HyperLogLog:用来做基数统计算法的数据结构,如统计网站的UV。

Bitmaps :用一个比特位来映射某个元素的状态,在Redis中,它的底层是基于字符串类型实现的,可以把bitmaps成作一个以比特位为单位的数组

6. Redis的应用场景

  • 计数器

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

  • 缓存

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

  • 会话缓存

可以使用 Redis 来统一存储多台应用服务器的会话信息。当应用服务器不再存储用户的会话信息,也就

不再具有状态,一个用户可以请求任意一个应用服务器,从而更容易实现高可用性以及可伸缩性。

  • 全页缓存(FPC)

除基本的会话token之外,Redis还提供很简便的FPC平台。以Magento为例,Magento提供一个插件

来使用Redis作为全页缓存后端。此外,对WordPress的用户来说,Pantheon有一个非常好的插件 wpredis,这个插件能帮助你以最快速度加载你曾浏览过的页面。

  • 查找表

例如 DNS 记录就很适合使用 Redis 进行存储。查找表和缓存类似,也是利用了 Redis 快速的查找特

性。但是查找表的内容不能失效,而缓存的内容可以失效,因为缓存不作为可靠的数据来源。

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

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

  • 分布式锁实现

在分布式场景下,无法使用单机环境下的锁来对多个节点上的进程进行同步。可以使用 Redis 自带的

SETNX 命令实现分布式锁,除此之外,还可以使用官方提供的 RedLock 分布式锁实现。

  • 其它

Set 可以实现交集、并集等操作,从而实现共同好友等功能。ZSet 可以实现有序性操作,从而实现排行榜等功能。

7.Redis 持久化

1.什么是Redis持久化?

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

Redis提供了RDB和AOF两种持久化机制

2.RDB

RDB,就是把内存数据以快照的形式保存到磁盘上。

什么是快照?可以这样理解,给当前时刻的数据,拍一张照片,然后保存下来。

RDB持久化,是指在指定的时间间隔内,执行指定次数的写操作,将内存中的数据集快照写入磁盘中,它是Redis默认的持久化方式。执行完操作后,在指定目录下会生成一个dump.rdb文件,Redis 重启的时候,通过加载dump.rdb文件来恢复数据。RDB触发机制主要有以下几种:

RDB 的优点

  • 适合大规模的数据恢复场景,如备份,全量复制等

RDB缺点

  • 没办法做到实时持久化/秒级持久化。

  • 新老版本存在RDB格式兼容问题

3.AOF

AOF(append only file) 持久化,采用日志的形式来记录每个写操作,追加到文件中,重启时再重新执行AOF文件中的命令来恢复数据。它主要解决数据持久化的实时性问题。默认是不开启的。

AOF的工作流程如下:

AOF的优点

  • 数据的一致性和完整性更高

AOF的缺点

  • AOF记录的内容越多,文件越大,数据恢复变慢。

4. 如何选择合适的持久化方式

一般来说, 如果想达到足以媲美PostgreSQL的数据安全性,你应该同时使用两种持久化功能。在

这种情况下,当 Redis 重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF

文件保存的数据集要比RDB文件保存的数据集要完整。

如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失,那么你可以只使用RDB持久化。

有很多用户都只使用AOF持久化,但并不推荐这种方式,因为定时生成RDB快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比AOF恢复的速度要快,除此之外,使用RDB还可以避免AOF程序的bug。

如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式。

简单的说,用RDB做全量备份,用AOF做增量备份

8. Redis的过期策略

首先我们能想到的过期策略有这么三种,

第一种是定时过期,即每个设置过期时间的key都去设置一个定时器,到点对key进行清除,这样看上去非常合理非常完美,但是会占用大量的cpu资源去处理过期数据,影响正常的响应时间和吞吐量。

第二种是惰性过期,就是每个key在请求时才去判断是否过期,过期则清除,该策略对cpu很友好,但是可能存在大量不再访问的key占用内存。

第三种策略是定期过期,即redis每隔一段时间扫描一定数量expires的key,清除其中已过期的key,算是一种折中的策略。redis采用惰性过期和定期过期相结合的策略。

我们在set key的时候,可以给它设置一个过期时间,比如expire key 60。指定这key60s后过期,60s后,redis是如何处理的嘛?我们先来介绍几种过期策略:

定时过期

每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即对key进行清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。

惰性过期

只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。

定期过期

每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。

expires字典会保存所有设置了过期时间的key的过期时间数据,其中,key是指向键空间中的某个键的指针,value是该键的毫秒精度的UNIX时间戳表示的过期时间。键空间是指该Redis集群中保存的所有键。

Redis中同时使用了惰性过期和定期过期两种过期策略。

  • 假设Redis当前存放30万个key,并且都设置了过期时间,如果你每隔100ms就去检查这全部的key,CPU负载会特别高,最后可能会挂掉。

  • 因此,redis采取的是定期过期,每隔100ms就随机抽取一定数量的key来检查和删除的。

  • 但是呢,最后可能会有很多已经过期的key没被删除。这时候,redis采用惰性删除。在你获取某个key的时候,redis会检查一下,这个key如果设置了过期时间并且已经过期了,此时就会删除。

但是呀,如果定期删除漏掉了很多过期的key,然后也没走惰性删除。就会有很多过期key积在内存内存,直接会导致内存爆的。或者有些时候,业务量大起来了,redis的key被大量使用,内存直接不够了,运维小哥哥也忘记加大内存了。难道redis直接这样挂掉?不会的!Redis用8种内存淘汰策略保护自己~

9.Redis 内存淘汰策略

首先介绍两种算法:

lru,最近最少使用,即从上次使用到现在调用的时间长短,

lfu页面置换算法,即一段时间内页面被使用的频率。

当内存满了的时候,redis有8种内存淘汰策略:1针对设置了过期时间的key,使用lru,使用lfu算法各一种,针对所有key,使用lru、lfu算法各一种,对已设置过期的key和所有key使用随机淘汰策略,第七种是设置了过期时间的key,过期时间早的先淘汰,第八种是写入新数据时报错,也是默认的策略。

  • volatile-lru:当内存不足以容纳新写入数据时,从设置了过期时间的key中使用LRU(最近最少使用)算法进行淘汰;

  • allkeys-lru:当内存不足以容纳新写入数据时,从所有key中使用LRU(最近最少使用)算法进行淘汰。

  • volatile-lfu:4.0版本新增,当内存不足以容纳新写入数据时,在过期的key中,使用LFU算法进行删除key。

  • allkeys-lfu:4.0版本新增,当内存不足以容纳新写入数据时,从所有key中使用LFU算法进行淘汰;

  • volatile-random:当内存不足以容纳新写入数据时,从设置了过期时间的key中,随机淘汰数据;。

  • allkeys-random:当内存不足以容纳新写入数据时,从所有key中随机淘汰数据。

  • volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的key中,根据过期时间进行淘汰,越早过期的优先被淘汰;

  • noeviction:默认策略,当内存不足以容纳新写入数据时,新写入操作会报错。

10.什么是缓存击穿、缓存穿透、缓存雪崩?

1. 缓存穿透问题

先来看一个常见的缓存使用方式:读请求来了,先查下缓存,缓存有值命中,就直接返回;缓存没命中,就去查数据库,然后把数据库的值更新到缓存,再返回。

缓存穿透:指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,进而给数据库带来压力。

通俗点说,读请求访问时,缓存和数据库都没有某个值,这样就会导致每次对这个值的查询请求都会穿透到数据库,这就是缓存穿透。

缓存穿透一般都是这几种情况产生的:

  • 业务不合理的设计,比如大多数用户都没开守护,但是你的每个请求都去缓存,查询某个userid查询有没有守护。

  • 业务/运维/开发失误的操作,比如缓存和数据库的数据都被误删除了。

  • 黑客非法请求攻击,比如黑客故意捏造大量非法请求,以读取不存在的业务数据。

如何避免缓存穿透呢? 一般有三种方法。

  • 1.如果是非法请求,我们在API入口,对参数进行校验,过滤非法值。

  • 2.如果查询数据库为空,我们可以给缓存设置个空值,或者默认值。但是如有有写请求进来的话,需要更新缓存哈,以保证缓存一致性,同时,最后给缓存设置适当的过期时间。(业务上比较常用,简单有效)

  • 3.使用布隆过滤器快速判断数据是否存在。即一个查询请求过来时,先通过布隆过滤器判断值是否存在,存在才继续往下查。

2.缓存雪崩问题

缓存雪崩: 指缓存中数据大批量到过期时间,而查询数据量巨大,请求都直接访问数据库,引起数据库压力过大甚至down机。

  • 缓存雪崩一般是由于大量数据同时过期造成的,对于这个原因,可通过均匀设置过期时间解决,即让过期时间相对离散一点。如采用一个较大固定值+一个较小的随机值,5小时+0到1800秒酱紫。

  • Redis 故障宕机也可能引起缓存雪崩。这就需要构造Redis高可用集群啦。

  • 服务降级保证服务的可用性

  • 缓存不过期

  • 缓存重建:同步重建或者异步重建,同步重建即请求的key缓存没有时,加锁,请求数据库,回写入缓存,异步重建则是定时重新刷入缓存。

3. 缓存击穿问题

缓存击穿: 指热点key在某个时间点过期的时候,而恰好在这个时间点对这个Key有大量的并发请求过来,从而大量的请求打到db。

缓存击穿看着有点像,其实它两区别是,缓存雪奔是指数据库压力过大甚至down机,缓存击穿只是大量并发请求到了DB数据库层面。可以认为击穿是缓存雪奔的一个子集吧。有些文章认为它俩区别,是区别在于击穿针对某一热点key缓存,雪奔则是很多key。

解决方案就有两种:

  • 1.使用互斥锁方案。缓存失效时,不是立即去加载db数据,而是先使用某些带成功返回的原子操作命令,如(Redis的setnx)去操作,成功的时候,再去加载db数据库数据和设置缓存。否则就去重试获取缓存。

  • 2. “永不过期”,是指没有设置过期时间,但是热点数据快要过期时,异步线程去更新和设置过期时间。

11.布隆过滤器是什么?有什么作用?

布隆过滤器判断结果是不存在的话就一定不存在

就是引入了k(k>1)k(k>1)个相互独立的哈希函数,保证在给定的空间、误判率下,完成元素判重的过程。

它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。

Bloom-Filter算法的核心思想就是利用多个不同的Hash函数来解决“冲突”。

Hash存在一个冲突(碰撞)的问题,用同一个Hash得到的两个URL的值有可能相同。为了减少冲突,我们可以多引入几个Hash,如果通过其中的一个Hash值我们得出某元素不在集合中,那么该元素肯定不在集合中。只有在所有的Hash函数告诉我们该元素在集合中时,才能确定该元素存在于集合中。这便是Bloom-Filter的基本思想。

Bloom-Filter一般用于在大数据量的集合中判定某元素是否存在。

直观的说,bloom算法类似一个hash set,用来判断某个元素(key)是否在某个集合中。

和一般的hash set不同的是,这个算法无需存储key的值,对于每个key,只需要k个比特位,每个存储一个标志,用来判断key是否在集合中。

算法:

1. 首先需要k个hash函数,每个函数可以把key散列成为1个整数

2. 初始化时,需要一个长度为n比特的数组,每个比特位初始化为0

3. 某个key加入集合时,用k个hash函数计算出k个散列值,并把数组中对应的比特位置为1

4. 判断某个key是否在集合时,用k个hash函数计算出k个散列值,并查询数组中对应的比特位,如果所有的比特位都是1,认为在集合中。

优点:不需要存储key,节省空间

缺点:

1. 算法判断key在集合中时,有一定的概率key其实不在集合中

2. 无法删除

布隆过滤器主要用于判断某个元素是否在某个集合中。它是一个很长的二进制向量,也就是010101这种,它应用了hash映射原理,hash函数可以将任意输入数据转换成特定大小的输出数据,hash函数的特点是,hash值不同,输入一定不同,hash值相同,输入可能相同,也可能不同。布隆过滤器正是使用了这一点,它利用k个hash映射函数,将变量映射到k个点,这k个点的二进制值设为1,查询某个变量是否存在时,我们只需要同样的k个映射函数,找到对应的k个点,判断点上的数据是否全是1,如果都是1则可能存在,只要有一个0,则一定不存在。优点是快,内存占用小,缺点是存在一定的误判,不能删除元素,因为不能删除,占用的内存也会越来越多。如何设置布隆过滤器呢,redis服务的一个插件。在redis中使用布隆过滤器防止缓存击穿,应用的是布隆过滤器判断结果是不存在的话就一定不存在的原理,如果布隆过滤器判断不存在,则直接返回不存在,判断存在的,再执行查询。虽然有一定误判,不能完全解决缓存穿透问题,但是能缓解。

12.什么是热Key问题,如何解决热key问题

什么是热Key呢?在Redis中,我们把访问频率高的key,称为热点key。

如果某一热点key的请求到服务器主机时,由于请求量特别大,可能会导致主机资源不足,甚至宕机,从而影响正常的服务。

热点Key是怎么产生的呢?主要原因有两个:

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

  • 请求分片集中,超过单Redi服务器的性能,比如固定名称key,Hash落入同一台服务器,瞬间访问量极大,超过机器瓶颈,产生热点Key问题。

那么在日常开发中,如何识别到热点key呢

  • 凭经验判断哪些是热Key;

  • 客户端统计上报;

  • 服务代理层上报

如何解决热key问题?

  • Redis集群扩容:增加分片副本,均衡读流量;

  • 将热key分散到不同的服务器中;

  • 使用二级缓存,即JVM本地缓存,减少Redis的读请求。

13. Redis事务的概念

Redis 事务的本质是通过MULTI、EXEC、WATCH等一组命令的集合。事务支持一次执行多个命

令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,

其他客户端提交的命令请求不会插入到事务执行命令序列中。

总结说:redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。

1. Redis事务的三个阶段

1. 事务开始 MULTI

2. 命令入队

3. 事务执行 EXEC

事务执行过程中,如果服务端收到有EXEC、DISCARD、WATCH、MULTI之外的请求,将会把请求放入队列中排队

2. Redis事务支持隔离性吗

Redis 是单进程程序,并且它保证在执行事务时,不会对事务进行中断,事务可以运行直到执行完

所有事务队列中的命令为止。因此,Redis 的事务是总是带有隔离性的

3. Redis事务保证原子性吗,支持回滚吗

Redis中,单条命令是原子性执行的,但事务不保证原子性,且没有回滚。事务中任意命令执行失败,其余的命令仍会被执行。

4. Redis事务其他实现

基于Lua脚本,Redis可以保证脚本内的命令一次性、按顺序地执行,其同时也不提供事务运行错误的回滚,执行过程中如果部分命令运行错误,剩下的命令还是会继续运行完* 基于中间标记变量,通过另外的标记变量来标识事务是否执行完成,读取数据时先读取该标记变量判断是否事务执行完成。但这样会需要额外写代码实现,比较繁琐

14.Redis集群方案

1. 哨兵模式

简单来说,哨兵模式就三个作用:

  • 发送命令,等待Redis服务器(包括主服务器和从服务器)返回监控其运行状态;

  • 哨兵监测到主节点宕机,会自动将从节点切换成主节点,然后通过发布订阅模式通知其他的从节点,修改配置文件,让它们切换主机;

  • 哨兵之间还会相互监控,从而达到高可用。

故障切换的过程是怎样的呢

假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行 failover 过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象成为主观下线。当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行 failover 操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为客观下线。这样对于客户端而言,一切都是透明的。

哨兵的工作模式如下:

  1. 每个Sentinel以每秒钟一次的频率向它所知的Master,Slave以及其他Sentinel实例发送一个 PING命令。

  1. 如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 则这个实例会被 Sentinel标记为主观下线。

  1. 如果一个Master被标记为主观下线,则正在监视这个Master的所有 Sentinel 要以每秒一次的频率确认Master的确进入了主观下线状态。

  1. 当有足够数量的 Sentinel(大于等于配置文件指定的值)在指定的时间范围内确认Master的确进入了主观下线状态, 则Master会被标记为客观下线。

  1. 在一般情况下, 每个 Sentinel 会以每10秒一次的频率向它已知的所有Master,Slave发送 INFO 命令。

  1. 当Master被 Sentinel 标记为客观下线时,Sentinel 向下线的 Master 的所有 Slave 发送 INFO 命令的频率会从 10 秒一次改为每秒一次

  1. 若没有足够数量的 Sentinel同意Master已经下线, Master的客观下线状态就会被移除;若Master 重新向 Sentinel 的 PING 命令返回有效回复, Master 的主观下线状态就会被移除。

2.官方Redis Cluster 方案(服务端路由查询)

简介

Redis Cluster是一种服务端Sharding技术,3.0版本开始正式提供。Redis Cluster并没有使用一致性hash,而是采用slot(槽)的概念,一共分成16384个槽。将请求发送到任意节点,接收到请求的节点会将查询请求发送到正确的节点上执行

方案说明

1. 通过哈希的方式,将数据分片,每个节点均分存储一定哈希槽(哈希值)区间的数据,默认分配了16384 个槽位

2. 每份数据分片会存储在多个互为主从的多节点上

3. 数据写入先写主节点,再同步到从节点(支持配置为阻塞同步)

4. 同一分片多个节点间的数据不保持一致性

5. 读取数据时,当客户端操作的key没有分配在该节点上时,redis会返回转向指令,指向正确的节点

6. 扩容时时需要需要把旧节点的数据迁移一部分到新节点

在 redis cluster 架构下,每个 redis 要放开两个端口号,比如一个是 6379,另外一个就是 加1w的端口号,比如 16379。

16379 端口号是用来进行节点间通信的,也就是 cluster bus 的东西,cluster bus 的通信,用来进行故障检测、配置更新、故障转移授权。cluster bus 用了另外一种二进制的协议,gossip 协议,用于节点间进行高效的数据交换,占用更少的网络带宽和处理时间。

节点间的内部通信机制

基本通信原理

集群元数据的维护有两种方式:集中式、Gossip 协议。redis cluster 节点间采用 gossip 协议进行通信。

分布式寻址算法

hash 算法(大量缓存重建)

一致性 hash 算法(自动缓存迁移)+ 虚拟节点(自动负载均衡)

redis cluster 的 hash slot 算法

优点

无中心架构,支持动态扩容,对业务透明

具备Sentinel的监控和自动Failover(故障转移)能力

客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可

高性能,客户端直连redis服务,免去了proxy代理的损耗

缺点

运维也很复杂,数据迁移需要人工干预

只能使用0号数据库

不支持批量操作(pipeline管道操作)

分布式逻辑和存储模块耦合等

3.codis

codis是什么?

豌豆荚开源,是redis的一种分布式的集群解决方案

包含的组件都有哪些?

dashboard、codis-proxy、fe、codis-server(zookeeper为前提)

每个组件作用?

dashboard:可以添加或删除redis和proxy节点,添加删除group等

codis-proxy:处理客户端请求的

fe:简单的管理控制界面

codis-server:Codis 项目维护的一个 Redis 分支,基于 2.8.21 开发,加入了 slot 的支持和原子的数据迁移指令(较官方的解释)

codis优缺点?

codis的优点:

1.支持在线数据迁移(slot合并),有简单的管理和监控界面

2.因为是分布式集群,数据存储和代理节点都高可用

3.自动进行数据均匀分配到每个组

4.存储容量比较大,因为最大支持1024个redis实例

codis的缺点:

1.国内开源,活跃度不高,但非常好用

2.代理节点proxy只有一个的时候,codis-server的性能可能会下降

3.codis自带的redis与原版的redis可能会不兼容

为什么会分组?

为了添加主从(交叉布置),保证不会因为节点宕机而丢数据,这样即使宕机,数据在另一台上也是完整的,可以通过数据迁移恢复

15.使用过Redis分布式锁嘛?有哪些注意点呢?

  • 命令setnx + expire分开写

  • setnx + value值是过期时间

  • set的扩展命令(set ex px nx)

  • set ex px nx + 校验唯一随机值,再删除

16. 使用过Redisson嘛?说说它的原理

redisson框架是为了解决锁过期释放,业务没处理完的问题。它启动了一个看门狗线程,是一个后台守护线程,每隔一段时间就会去检查一下,如果线程还有锁就会延长线程的时间。

分布式锁可能存在锁过期释放,业务没执行完的问题。有些小伙伴认为,稍微把锁过期时间设置长一些就可以啦。其实我们设想一下,是否可以给获得锁的线程,开启一个定时守护线程,每隔一段时间检查锁是否还存在,存在则对锁的过期时间延长,防止锁过期提前释放。

当前开源框架Redisson就解决了这个分布式锁问题。

只要线程一加锁成功,就会启动一个watch dog看门狗,它是一个后台线程,会每隔10秒检查一下,如果线程1还持有锁,那么就会不断的延长锁key的生存时间。因此,Redisson就是使用Redisson解决了锁过期释放,业务没执行完问题。

17. 什么是Redlock算法

redlock算法是为了解决主从模式下,线程从主节点拿到锁后,此时没有同步到从节点,主节点挂掉了,从节点被选为主节点,此时另一个线程又从新的主节点拿到锁的问题。算法的原理很简单,就是一个主节点容易挂,那么就搞多个主节点,请求锁的时候半数以上获取成功则认为加锁成功,否则就是加锁失败。

如果线程一在Redis的master节点上拿到了锁,但是加锁的key还没同步到slave节点。恰好这时,master节点发生故障,一个slave节点就会升级为master节点。线程二就可以获取同个key的锁啦,但线程一也已经拿到锁了,锁的安全性就没了。

为了解决这个问题,Redis作者 antirez提出一种高级的分布式锁算法:Redlock。Redlock核心思想是这样的:

搞多个Redis master部署,以保证它们不会同时宕掉。并且这些master节点是完全相互独立的,相互之间不存在数据同步。同时,需要确保在这多个master实例上,是与在Redis单实例,使用相同方法来获取和释放锁。

我们假设当前有5个Redis master节点,在5台服务器上面运行这些Redis实例。

RedLock的实现步骤:如下

  • 1.获取当前时间,以毫秒为单位。

  • 2.按顺序向5个master节点请求加锁。客户端设置网络连接和响应超时时间,并且超时时间要小于锁的失效时间。(假设锁自动失效时间为10秒,则超时时间一般在5-50毫秒之间,我们就假设超时时间是50ms吧)。如果超时,跳过该master节点,尽快去尝试下一个master节点。

  • 3.客户端使用当前时间减去开始获取锁时间(即步骤1记录的时间),得到获取锁使用的时间。当且仅当超过一半(N/2+1,这里是5/2+1=3个节点)的Redis master节点都获得锁,并且使用的时间小于锁失效时间时,锁才算获取成功。(如上图,10s> 30ms+40ms+50ms+4m0s+50ms)

  • 如果取到了锁,key的真正有效时间就变啦,需要减去获取锁所使用的时间。

  • 如果获取锁失败(没有在至少N/2+1个master实例取到锁,有或者获取锁时间已经超过了有效时间),客户端要在所有的master节点上解锁(即便有些master节点根本就没有加锁成功,也需要解锁,以防止有些漏网之鱼)。

简化下步骤就是:

  • 按顺序向5个master节点请求加锁

  • 根据设置的超时时间来判断,是不是要跳过该master节点。

  • 如果大于等于三个节点加锁成功,并且使用的时间小于锁的有效期,即可认定加锁成功啦。

  • 如果获取锁失败,解锁!

18. MySQL与Redis 如何保证双写一致性

  • 缓存延时双删

  • 删除缓存重试机制

  • 读取biglog异步删除缓存

1 延时双删?

什么是延时双删呢?流程图如下:

  1. 先删除缓存

  1. 再更新数据库

  1. 休眠一会(比如1秒),再次删除缓存。

这个休眠一会,一般多久呢?都是1秒?

这个休眠时间 = 读业务逻辑数据的耗时 + 几百毫秒。为了确保读请求结束,写请求可以删除读请求可能带来的缓存脏数据。

这种方案还算可以,只有休眠那一会(比如就那1秒),可能有脏数据,一般业务也会接受的。但是如果第二次删除缓存失败呢?缓存和数据库的数据还是可能不一致,对吧?给Key设置一个自然的expire过期时间,让它自动过期怎样?那业务要接受过期时间内,数据的不一致咯?还是有其他更佳方案呢?

2 删除缓存重试机制

因为延时双删可能会存在第二步的删除缓存失败,导致的数据不一致问题。可以使用这个方案优化:删除失败就多删除几次呀,保证删除缓存成功就可以了呀~ 所以可以引入删除缓存重试机制

  1. 写请求更新数据库

  1. 缓存因为某些原因,删除失败

  1. 把删除失败的key放到消息队列

  1. 消费消息队列的消息,获取要删除的key

  1. 重试删除缓存操作

3 读取biglog异步删除缓存

重试删除缓存机制还可以吧,就是会造成好多业务代码入侵。其实,还可以这样优化:通过数据库的binlog来异步淘汰key。

以mysql为例吧

  • 可以使用阿里的canal将binlog日志采集发送到MQ队列里面

  • 然后通过ACK机制确认处理这条更新消息,删除缓存,保证数据缓存一致性

19. 为什么Redis 6.0 之后改多线程呢?

  • Redis6.0之前,Redis在处理客户端的请求时,包括读socket、解析、执行、写socket等都由一个顺序串行的主线程处理,这就是所谓的“单线程”。

  • Redis6.0之前为什么一直不使用多线程?使用Redis时,几乎不存在CPU成为瓶颈的情况, Redis主要受限于内存和网络。例如在一个普通的Linux系统上,Redis通过使用pipelining每秒可以处理100万个请求,所以如果应用程序主要使用O(N)或O(log(N))的命令,它几乎不会占用太多CPU。

redis使用多线程并非是完全摒弃单线程,redis还是使用单线程模型来处理客户端的请求,只是使用多线程来处理数据的读写和协议解析,执行命令还是使用单线程。

这样做的目的是因为redis的性能瓶颈在于网络IO而非CPU,使用多线程能提升IO读写的效率,从而整体提高redis的性能。

20.Redis如何实现延时队列

使用sortedset,使用时间戳做score, 消息内容作为key,调用zadd来生产消息,消费者使用zrangbyscore获取n秒之前的数据做轮询处理。

21.Redis常见性能问题和解决方案?

1. Master最好不要做任何持久化工作,包括内存快照和AOF日志文件,特别是不要启用内存快照做持久化。

2. 如果数据比较关键,某个Slave开启AOF备份数据,策略为每秒同步一次。

3. 为了主从复制的速度和连接的稳定性,Slave和Master最好在同一个局域网内。

4. 尽量避免在压力较大的主库上增加从库

5. Master调用BGREWRITEAOF重写AOF文件,AOF在重写的时候会占大量的CPU和内存资源,导致服务load过高,出现短暂服务暂停现象。

6. 为了Master的稳定性,主从复制不要用图状结构,用单向链表结构更稳定,即主从关系为:

Master<–Slave1<–Slave2<–Slave3…,这样的结构也方便解决单点故障问题,实现Slave对Master的替换,也即,如果Master挂了,可以立马启用Slave1做Master,其他不变。

22.Redis如何做大量数据插入?

Redis2.6开始redis-cli支持一种新的被称之为pipe mode的新模式用于执行大量数据插入工作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值