1.Redis 有哪些特性?
-
性能高, 读的速度是10万次/s,写的速度是8万次/s;
-
数据持久化,支持RDB 、AOF、混合持久化;
-
支持事务,通过
MULTI
和EXEC
指令包起来; -
多种数据结构类型;
-
主从复制;
-
其他特性:发布/订阅、通知、key过期等;
2.Redis 为什么这么快?
-
完全基于内存,没有磁盘IO上的开销,异步持久化除外;
-
单线程,避免多个线程切换的性能损耗;
-
非阻塞的IO多路复用机制;
-
底层的数据存储结构优化,使用原生的数据结构提升性能。
3.Redis 底层的基础数据结构有哪些?
-
字符串:没有采用C语言的传统字符串,而是自己实现的一个简单动态字符串SDS的抽象类型,并保存了长度信息。
-
链表(linkedlist):双向无环链表结构,每个链表的节点由一个listNode结构来表示,每个节点都有前置和后置节点的指针
-
字典(hashtable):保存键值对的抽象数据结构,底层使用hash表,每个字典带有两个hash表,供平时使用和rehash时使用。
-
跳跃表(skiplist):跳跃表是有序集合的底层实现之一。redis跳跃表由zskiplist和zskiplistNode组成,zskiplist用于保存跳跃表 信息(表头、表尾节点、⻓度等),zskiplistNode用于表示表跳跃节点,每个跳跃表的层高都是1- 32的随机数,在同一个跳跃表中,多个节点可以包含相同的分值,但是每个节点的成员对象必须是唯一的,节点按照分值大小排序,如果分值相同,则按照成员对象的大小排序。
-
整数集合(intset):用于保存整数值的集合抽象数据结构,不会出现重复元素,底层实现为数组。
-
压缩列表(ziplist):为节约内存而开发的顺序性数据结构,可以包含多个节点,每个节点可以保存一个字节数组或者整数值。
4.Redis 支持哪些数据类型?
五种常用数据类型:String
、Hash
、Set
、List
、SortedSet
。三种特殊的数据类型:Bitmap
、HyperLogLog
、Geospatial
,其中Bitmap 、HyperLogLog的底层都是 String 数据类型,Geospatial 底层是 Sorted Set 数据类型。
字符串对象string:int整数、embstr编码的简单动态字符串、raw简单动态字符串;
列表对象list:ziplist、linkedlist;
哈希对象hash:ziplist、hashtable;
集合对象set:intset、hashtable;
有序集合对象zset:ziplist、skiplist;
5.Redis 常用的 5 种数据结构和应用场景?
String:缓存、计数器、分布式锁等;
List:链表、队列、微博关注人时间轴列表等;
Hash:用户信息、Hash 表等;
Set:去重、赞、踩、共同好友等;
Zset:访问量排行榜、点击量排行榜等;
6.为什么采用单线程?
官方回复,CPU不会成为Redis的制约瓶颈,Redis主要受内存、网络限制。例如,在一个普通的 Linux 系统上,使用pipelining 可以每秒传递 100 万个请求,所以如果您的应用程序主要使用 O(N) 或 O(log(N)) 命令,则几乎不会使用太多 CPU,属于IO密集型系统。
7.Redis 6.0 之后又改用多线程呢?
Redis的多线程主要是处理数据的读写、协议解析。执行命令还是采用单线程顺序执行。
主要是因为redis的性能瓶颈在于网络IO而非CPU,使用多线程进行一些周边预处理,提升了IO的读写效率,从而提高了整体的吞吐量。antirez 在 RedisConf 2019 分享时提到,Redis 6 引入的多线程 IO 对性能提升至少一倍以上。
8.过期键Key 的删除策略有哪些?
有3种过期删除策略:惰性删除、定期删除、定时删除;
惰性删除:使用key时才进行检查,如果已经过期,则删除。缺点:过期的key如果没有被访问到,一直无法删除,一直占用内存,造成空间浪费;
定期删除:每隔一段时间做一次检查,删除过期的key,每次只是随机取一些key去检查;
定时删除:为每个key设置过期时间,同时创建一个定时器。一旦到期,立即执行删除。缺点:如果过期键比较多时,占用CPU较多,对服务的性能有很大影响。
9.如果Redis的内存空间不足,淘汰策略?
volatile-lru:从已设置过期时间的key中,移出最近最少使用的key进行淘汰;
allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(这个是最常用的);
volatile-ttl:从已设置过期时间的key中,移出将要过期的key;
volatile-random:从已设置过期时间的key中,随机选择key淘汰;
allkeys-random:从key中随机选择key进行淘汰;
no-eviction:禁止淘汰数据。当内存达到阈值的时候,新写入操作报错;
volatile-lfu:从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使用的数据淘汰(LFU(Least Frequently Used)算法,也就是最频繁被访问的数据将来最有可能被访问到);
allkeys-lfu:当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的key。
10.Redis 突然挂了怎么解决?
1)从系统可用性角度思考,Redis Cluster引入主备机制,当主节点挂了后,自动切换到备用节点,继续提供服务。2)Client端引入本地缓存,通过开关切换,避免Redis突然挂掉,高并发流量把数据库打挂。
11.Redis 持久化有哪些方式?
1)快照RDB。将某个时间点上的数据库状态保存到RDB文件
中,RDB文件是一个压缩的二进制文件,保存在磁盘上。当Redis崩溃时,可用于恢复数据。通过SAVE
或BGSAVE
来生成RDB文件。
SAVE:会阻塞redis进程,直到RDB文件创建完毕,在进程阻塞期间,redis不能处理任何命令请求。
BGSAVE:会fork出一个子进程,然后由子进程去负责生成RDB文件,父进程还可以继续处理命令请求,不会阻塞进程。
2)只追加文件AOF。以日志的形式记录每个写操作(非读操作)。当不同节点同步数据时,读取日志文件的内容将写指令从前到后执行一次,即可完成数据恢复。
12.Redis 常用场景
- 缓存,有句话说的好,「性能不够,缓存来凑」;
- 分布式锁,利用Redis 的 setnx;
- 分布式session;
- 计数器,通过incr命令;
- 排行榜,Redis 的 有序集合;
- 其他;
13. 说说Redis哈希槽的概念?
Redis集群引入了哈希槽的概念,Redis集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽,集群的每个节点负责一部分hash槽。
14.Redis 缓存要注意的七大经典问题?
高访问量情况下Redis缓存可能会遇到哪些问题?以及对应的解决方案。
- 缓存集中失效
- 缓存穿透
- 缓存雪崩
- 缓存热点
- 缓存大Key
- 缓存数据的一致性
- 数据并发竞争预热
15.Redis 集群方案有哪几种?
主从复制模式;
Sentinel(哨兵)模式;
Redis Cluster模式;
16.Redis 主从数据同步(主从复制)的过程?
-
1)slave启动后,向master发送sync命令;
-
2)master收到sync之后,执行bgsave保存快照,生成RDB全量文件;
-
3)master把slave的写命令记录到缓存;
-
4)bgsave执行完毕之后,发送RDB文件到slave,slave执行;
-
5)master发送缓冲区的写命令给slave,slave接收命令并执行,完成复制初始化;
-
6)此后,master每次执行一个写命令都会同步发送给slave,保持master与slave之间数据的一致性;
17.主从复制的优缺点?
优点:
master能自动将数据同步到slave,可以进行读写分离,分担master的读压力;
master、slave之间的同步是以非阻塞的方式进行的,同步期间,客户端仍然可以提交查询或更新请求;
缺点:
不具备自动容错与恢复功能,master 节点宕机后,需要手动指定新的 master;
master宕机,如果宕机前数据没有同步完,则切换IP后会存在数据不一致的问题;
难以支持在线扩容,Redis的容量受限于单机配置;
18.Sentinel(哨兵)模式的优缺点?
哨兵模式基于主从复制模式,增加了哨兵来监控与自动处理故障。
优点:
哨兵模式基于主从复制模式,所以主从复制模式有的优点,哨兵模式也有;
master 挂掉可以自动进行切换,系统可用性更高;
缺点:
Redis的容量受限于单机配置;
需要额外的资源来启动sentinel进程;
19.Redis Cluster 模式的优缺点?
实现了Redis的分布式存储,即每台节点存储不同的内容,来解决在线扩容的问题。
优点:
- 无中心架构,数据按照slot分布在多个节点;
- 集群中的每个节点都是平等的,每个节点都保存各自的数据和整个集群的状态。每个节点都和其他所有节点连接,而且这些连接保持活跃,这样就保证了我们只需要连接集群中的任意一个节点,就可以获取到其他节点的数据;
- 可线性扩展到1000多个节点,节点可动态添加或删除;
- 能够实现自动故障转移,节点之间通过
gossip协议
交换状态信息,用投票机制完成slave到master的角色转换;
缺点:
- 数据通过异步复制,不保证数据的强一致性;
- slave充当 “冷备”,不对外提供读、写服务,只作为故障转移使用;
- 批量操作限制,目前只支持具有相同slot值的key执行批量操作,对mset、mget、sunion等操作支持不友好;
- key事务操作支持有限,只支持多key在同一节点的事务操作,多key分布在不同节点时无法使用事务功能;
- 不支持多数据库空间,一台redis可以支持16个db,集群模式下只能使用一个,即
db 0
。Redis Cluster模式不建议使用pipeline和multi-keys操作,减少max redirect产生的场景。
20.Redis 如何做扩容?
为了避免数据迁移失效,通常使用一致性哈希
实现动态扩容缩容,有效减少需要迁移的Key数量。
但是Cluster 模式,采用固定Slot槽位方式(16384个),对每个key计算CRC16值,然后对16384取模,然后根据slot值找到目标机器,扩容时,我们只需要迁移一部分的slot到新节点即可。
21.Redis 的集群原理?
一个redis集群由多个节点node组成,而多个node之间通过cluster meet
命令来进行连接,组成一个集群。
数据存储通过分片的形式,整个集群分成了16384
个slot,每个节点负责一部分槽位。整个槽位的信息会同步到所有节点中。
key与slot的映射关系:
健值对 key,进行
CRC16
计算,计算出一个 16 bit 的值;将 16 bit 的值对 16384 取模,得到 0 ~ 16383 的数表示 key 对应的哈希槽;
22.Redis 如何做到高可用?
哨兵机制,具有自动故障转移、集群监控、消息通知等功能。
哨兵可以同时监视所有的主、从服务器,当某个master下线时,自动提升对应的slave为master,然后由新master对外提供服务。
23.什么是 Redis 事务?
事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。
Redis事务中没有像Mysql关系型数据库事务隔离级别的概念,不能保证原子性操作,也没有像Mysql那样执行事务失败会进行回滚操作;
24.Redis 事务执行流程?
通过MULTI
、EXEC
、WATCH
等命令来实现事务机制,事务执行过程将一系列多个命令按照顺序一次性执行,在执行期间,事务不会被中断,也不会去执行客户端的其他请求,直到所有命令执行完毕。
具体过程:
- 服务端收到客户端请求,事务以
MULTI
开始;- 如果正处于事务状态时,则会把后续命令放入队列同时返回给客户端
QUEUED
,反之则直接执行这 个命令;- 当收到客户端的
EXEC
命令时,才会将队列里的命令取出、顺序执行,执行完将当前状态从事务状态改为非事务状态;- 如果收到
DISCARD
命令,放弃执行队列中的命令,可以理解为Mysql的回滚操作,并且将当前的状态从事务状态改为非事务状态;
WATCH 监视某个key,该命令只能在MULTI命令之前执行。如果监视的key被其他客户端修改,EXEC将会放弃执行队列中的所有命令。UNWATCH 取消监视之前通过WATCH 命令监视的key。通过执行EXEC 、DISCARD 两个命令之前监视的key也会被取消监视。
25.Redis 与 Guava 、Caffeine 有什么区别?
缓存分为本地缓存和分布式缓存。
1)Caffeine、Guava,属于本地缓存,特点:
- 直接访问内存,速度快,受内存限制,无法进行大数据存储;
- 无网络通讯开销,性能更高;
- 只支持本地应用进程访问,同步更新所有节点的本地缓存数据成本较高;
- 应用进程重启,数据会丢失;
所以,本地缓存适合存储一些不易改变或者低频改变的高热点数据。
2)Redis属于分布式缓存,特点:
- 集群模式,支持大数据量存储;
- 数据集中存储,保证数据的一致性;
- 数据跨网络传输,性能低于本地缓存。但同一个机房,两台服务器之间请求跑一个来回也就需要500微秒,比起其优势,这点损耗完全可以忽略,这也是分布式缓存受欢迎的原因;
- 支持副本机制,有效的保证了高可用性。
26.如何实现一个分布式锁?
- 数据库表,性能比较差;
- 使用Lua脚本 (包含 SETNX + EXPIRE 两条指令);
- SET的扩展命令(SET key value [EX][PX] [NX|XX]);
- Redlock 框架;
- Zookeeper Curator框架提供了现成的分布式锁;
27. 使用过Redis做异步队列么,你是怎么用的?有什么缺点?
一般使用list结构作为队列,rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试。
1)如果对方追问可不可以不用sleep呢?
list还有个指令叫blpop,在没有消息的时候,它会阻塞住直到消息到来。
2)如果对方追问能不能生产一次消费多次呢?
使用pub/sub主题订阅者模式,可以实现1:N的消息队列。
3)如果对方追问pub/sub有什么缺点?
在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如rabbitmq等。
4)如果对方追问redis如何实现延时队列?
使用sortedset,拿时间戳作为score,消息内容作为key调用zadd来生产消息,消费者用zrangebyscore指令获取N秒之前的数据轮询进行处理。
28. redis和数据库双写一致性问题
一致性问题是分布式常见问题,还可以再分为最终一致性和强一致性。数据库和缓存双写,就必然会存在不一致的问题。答这个问题,先明白一个前提。就是如果对数据有强一致性要求,不能放缓存。我们所做的一切,只能保证最终一致性。另外,我们所做的方案其实从根本上来说,只能说降低不一致发生的概率,无法完全避免。因此,有强一致性要求的数据,不能放缓存。
回答: 分布式之数据库和缓存双写一致性方案解析给出了详细的分析,在这里简单的说一说。首先,采取正确更新策略,先更新数据库,再删缓存。其次,因为可能存在删除缓存失败的问题,提供一个补偿措施即可,例如利用消息队列。
29. 假如Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如果将它们全部找出来?
使用keys指令可以扫出指定模式的key列表。
如果这个redis正在给线上的业务提供服务,那使用keys指令会有什么问题?
Redis是单线程的,keys指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。这个时候可以使用scan指令,scan指令可以无阻塞的提取出指定模式的key列表,但是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接用keys指令长。