本文问题总结:来自于,慕课网《Java架构师成长直通车》第八周第1-8节的汇总
本文问题参考回答:来自博主自己的学习笔记和疯狂的Google现学的,由于学的不多了解有限,参考回答表述不正确的欢迎提出,共同进步。
1. 什么是Redis?
Redis是一个高性能的key-values形式存储的分布式缓存中间件
支持丰富的数据结构(五种):String、List、Set、Zset、Hash
数据对象存储在内存中,读写非常快
单线程且所有操作都是原子操作
2. Redis的全称是什么
Remote Dictionary Server (远程 字典 服务器)
3. Redis的数据类型?
String(字符串)
Hash(哈希)
List(列表)
Zset(有序集合)
Set(集合)
4. 使用Redis有哪些好处?
数据存储在内存中,读写速度快
支持丰富的数据类型
支持事务,操作都是原子操作
丰富的特性:可用于缓存,消息,按key设置过期时间
5. Redis相比Memcache有哪些好处?
Redis与Memcache相比拥有更丰富的数据类型
- Memcache只支持简单的String类型的Key-value存储,
- Redis支持五种数据类型:String、List、Hash、Set、Zset
Redis支持数据持久化,重启后可以从磁盘重新加载
Redis支持Master-slave模式的数据备份
6. Memcache和Redis的区别都有哪些?
Redis与Memcache相比拥有更丰富的数据类型
Redis支持数据持久化,Memcache不支持持久化
Redis支持Master-slave模式的数据备份,Memcache无法容灾
Redis使用单核单线程,Memcache使用多核多线程
7. Redis是单进程单线程的吗?为什么那么快那么高效?
Redis是单进程单线程的
完全基于内存,绝大多数请求是纯粹的内存操作,所以非常快速
数据结构简单,针对数据的操作也简单
单线程,避免不必要的上下文切换和竞争,不用考虑锁的问题和死锁问题
多路I/O复用模型,非阻塞IO
8. 一个字符串类型的值能存储最大容量是多少?
一个键最大能存储512M(官方给的)
9. Redis的持久化机制是什么?各自的优缺点?
RDB(redis databases)
把数据以快照的形式保存在磁盘上
是Redis默认使用的持久化机制
触发方式
- save触发:该命令会阻塞当前Redis服务器,执行save命令期间,Redis不能处理其他命令,直到RDB过程完成为止
- bgsave触发:进程执行fork操作子进程,然后由Redis会在后台异步进行快照操作,快照同时还可以响应客户端请求,阻塞只在fork创建子进程的短暂阶段
比较:
- save是同步的线程阻塞的,不会消耗额外内存,缺点是阻塞客户端
- bgsave是异步的线程非阻塞的,不阻塞客户端,缺点是fork会消耗内存
配置文件参数
- save: 如save 60 10000 表示60s内有10000个 key的值发生变化就触发保存
- stop-writes-on-bgsave-error:默认yes,RDB最后一次后台保存数据失败,Redis是否停止接收数据
- rdbcompression:默认yes,设置快照压缩存储
- rdbchecksum:默认yes ,设置开启crc64校验,影响性能
- dbfilename:快照文件名
- dir:快照文件存储路径
优势
- RDB文件紧凑,全量备份,适用于备份和容灾
- RDB在恢复大数据集是速度比AOF快
- RDB文件生成时主进程fork一个子进程进行操作,主进程不需要IO操作
劣势
- RDB持久化期间主进程发生修改子进程不可见,可能造成持久化期间数据丢失
AOF
通过追加日志的方式存储在磁盘上
当接收到写命令后将命令存储到AOF文件中
文件重写机制
- 持久化文件会变的越来越大。为了压缩aof的持久化文件。redis提供了bgrewriteaof命令。将内存中的数据以命令的方式保存到临时文件中,同时会fork出一条新进程来将文件重写。
- 重写过程不是读旧的aof,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件
AOF触发机制
- always:每次发生数据变更都执行一次记录,性能差,完整性能
- everysec:每一秒发生一次记录,宕机会丢失1s内数据
- no:从不同步
优势
- AOF可以更好的保护数据不丢失,一般AOF会每隔1秒,通过一个后台线程执行一次fsync操作,最多丢失1秒钟的数据。
- AOF日志文件没有任何磁盘寻址的开销,写入性能非常高,文件不容易破损。
- AOF日志文件即使过大的时候,出现后台重写操作,也不会影响客户端的读写。
- 非常适合做紧急恢复和少量数据恢复
劣势
- AOF文件比RDB大
- AOF写QPS会比RDB支持的写QPS低
比较
10. Redis常见性能问题和解决方案有哪些?
Master最好不要做任何持久化工作,包括内存快照和AOF日志文件,特别是不要启用内存快照做持久化。
- RDB方式如果使用save触发会长时间阻塞主服务器
- RDB方式如果使用bgsave触发会产生子进程处理备份,会占用CPU和内存,且生成子进程期间会造成主服务阻塞
- AOF方式如果不重写会导致文件过大影响Master重启恢复速度
- AOF方式如果重写在重写时会占用大量CPU和内存资源
主从之间的瓶颈在于IO瓶颈,为了主从复制的速度和连接的稳定性,Slave和Master最好在同一个局域网内。
11. Redis过期键的删除策略?
主节点过期删除策略
定时删除:在设置键的过期时间的同时,创建一个timer,让定时器在键的过期时间到达时,立即执行对键的删除操作。(主动删除)
- 对内存友好,对CPU时间不友好,短时间较多过期时会占用较大的CPU时间
定期删除:每隔一段时间就对数据库进行一次检查,删除里面的过期键。(主动删除)
- 内存和CPU都折中友好的策略,每个一段时间执行一次删除过期键操作,并通过限制操作执行的时长和频率来减少对cpu时间的影响。难在选择一个好的执行时长和执行频率
惰性删除:放任过期键不管,但是每次从键空间中获取键时,都检查取到的键是否过期,如果过期就删除,如果没过期就返回该键。(被动删除)
- 对CPU友好,对内存不友好,如果不使用就会一直占内存
从节点过期删除策略
- AOF:过期键删除后,AOF文件里追加一条del指令,AOF文件同步到从节点后执行del指令删除
- RDB:过期键被过滤
- 因为AOF同步del指令是异步的,会导致主从节点的值不一致
- 从节点不会主动扫描过期key,对于过期是被动的
12. Redis的回收策略(淘汰策略)?
noeviction: 不删除策略, 达到最大内存限制时, 如果需要更多内存, 直接返回错误信息。大多数写命令都会导致占用更多的内存(有极少数会例外, 如 DEL )。
allkeys-lru: 所有key通用; 优先删除最近最少使用(less recently used ,LRU) 的 key。
volatile-lru: 只限于设置了 expire 的部分; 优先删除最近最少使用(less recently used ,LRU) 的 key。
allkeys-random: 所有key通用; 随机删除一部分 key。
volatile-random: 只限于设置了 expire 的部分; 随机删除一部分 key。* volatile-ttl: 只限于设置了 expire 的部分; 优先删除剩余时间(time to live,TTL) 短的key。
13. 为什么Redis需要把所有数据放入内存中?
Redis 为了达到最快的读写速度将数据都读到内存中,并通过异步的方式将数据写入磁盘。所以 redis 具有快速和数据持久化的特征。
如果不将数据放在内存中,磁盘 I/O 速度为严重影响 redis 的性能。在内存越来越便宜的今天,redis 将会越来越受欢迎。
如果设置了最大使用的内存,则数据已有记录数达到内存限值后不能继续插入新值。
14. Redis的同步机制了解吗?
全量同步
将master本身的RDB文件同步给slave
- 从节点连接到主节点,发送同步(SYNC)命令
- 主节点开始执行BGSAVE,并用缓冲区记录BGSAVE之后执行的所有命令
- BGSAVE执行完成后主节点向从节点发送快照文件,发送期间继续使用缓冲区记录执行命令。从节点抛弃 所有旧数据,载入主节点发送的快照
- 主节点快照发送完毕后,开始向从节点发送缓冲区中的写命令。从节点完成对快照文件的解析后,开始接受命令请求
- 主节点发送完缓冲区数据后,每执行一条写命令都会向从节点发送相同的写命令。从节点执行完了主节点缓冲区的命令后,接受之后发送的写命令,并执行
发生时机
- redis slave首启动或者重启后,连接到master时
- redis slave进程没重启,但是掉线了,重连后不满足部分复制条件(传输偏移量不在缓冲区范围内了)
全局复制开销
- bgsave每次需要fork子进程,对内存和cup开销大
- RDB网络传输耗时
- 从节点清空数据的耗时
- 从节点加载RDB的耗时
- 如果设置了AOF会进行AOF重写,重写耗时
部分同步
当master和slave断开连接时,master会将期间所做的操作记录到复制缓存区当中(可以看成是一个队列,其大小默认1M)。
待slave重连后,slave会向master发送psync命令并传入offset和runId,并进行判断
- 如果从节点的run id和主节点的run id一致
- 如果master发现slave传输的偏移量的值,在缓存区队列范围中,
就会将从offset开始到队列结束的数据传给slave,从而达到同步,降低了使用全量复制的开销。
无磁盘化同步(测试运行阶段)
- 不依赖与硬盘性能
- 直接在内存中创建rdb,然后发送给slave
15. Pipeline(管道)有什么好处?为什么要用Pipeline?
多个指令之间没有依赖关系,可以使用 pipeline 一次性执行多个指令,减少 IO,缩减时间。
16. 是否使用过Redis集群,集群的原理是什么?
原理:分库分表,去中心化
分库分表:使用hash槽,每个节点维护部分槽及槽所映射的键值数据
去中心化:每个Master节点都有一个或多个Slave节点。集群中所有的Master节点都可以进行读写数据,不分主次
主从集群
加哨兵的主从集群
Cluster集群
17. Redis集群方案什么情况下会导致整个集群不可用?
如果集群任意master挂掉,且当前master没有slave.集群进入fail状态,也可以理解成集群的slot映射[0-16383]不完成时进入fail状态
如果集群超过半数以上master挂掉,无论是否有slave集群进入fail状态
18. Redis支持的Java客户端都有哪些?官方推荐用那个?
Redisson、Jedis、lettuce等等,官方推荐使用Redisson。
19. Jedis与Redisson对比有什么优缺点?
Jedis是Redis的Java实现的客户端,其API提供了比较全面的Redis命令的支持
Redisson实现了分布式和可扩展的Java数据结构,和Jedis相比,功能较为简单,不支持字符串操作,不支持排序、事务、管道、分区等Redis特性。Redisson的宗旨是促进使用者对Redis的关注分离,从而让使用者能够将精力更集中地放在处理业务逻辑上。
20. Redis如何设置密码和验证密码?
初始化密码 配置文件中设置: requirepass mypassword 修改完成需要重启生效
不重启修改秘密: redis 127.0.0.1:6379> config set requirepass mypassword
密码验证: redis 127.0.0.1:6379> authmy password
21. 说一说Redis哈希槽的概念?
Redis 集群中内置了 16384 个哈希槽,当需要在 Redis 集群中放置一个 key-value时,redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数,
每个 key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点。
master节点的slave节点不分配槽,只拥有读权限。
当需要增加节点时,只需要把其他节点的某些哈希槽挪到新节点就可以了;
当需要移除节点时,只需要把移除节点上的哈希槽挪到其他节点就行了;
22. Redis集群的主从复制模型是怎么的?
为了使在部分节点失败或者大部分节点无法通信的情况下集群仍然可用,所以集群使用了主从复制模型,每个节点都会有N-1个复制品.
什么是主从复制
- 一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master/leader),后者称为从节点(slave/follower);数据的复制是单向的,只能由主节点到从节点。
- 默认情况下,每台Redis服务器都是主节点;且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。
主从复制的作用
- 数据冗余:主从复制实现了数据热备份,是持久化之外的一种冗余手段
- 故障恢复:主节点出现问题可以由从节点提供服务,实现故障恢复
- 负载均衡:配合读写分离分担服务器负载(主写,从读),能大大提高并发量
- 读写分离:读写分离除了能提高负载,还能根据也无需求调整从库数量
主从复制开启方式
- 配置文件:从节点配置文件中加入slaveof <masterip> </masterip>
- 启动命令:redis-server启动命令后加上--slaveof <masterip> </masterip>
- 客户端命令:节点启动后,客户端执行slaveof <masterip> </masterip>
主从复制过程
- 从节点连接到主节点,发送同步(SYNC)命令
- 主节点开始执行BGSAVE,并用缓冲区记录BGSAVE之后执行的所有命令
- BGSAVE执行完成后主节点向从节点发送快照文件,发送期间继续使用缓冲区记录执行命令。从节点抛弃 所有旧数据,载入主节点发送的快照
- 主节点快照发送完毕后,开始向从节点发送缓冲区中的写命令。从节点完成对快照文件的解析后,开始接受命令请求
- 主节点发送完缓冲区数据后,每执行一条写命令都会向从节点发送相同的写命令。从节点执行完了主节点缓冲区的命令后,接受之后发送的写命令,并执行
部分复制和全量复制
全量复制
将master本身的RDB文件同步给slave,就是上述的主从复制过程
发生时机
- redis slave首启动或者重启后,连接到master时
- redis slave进程没重启,但是掉线了,重连后不满足部分复制条件(传输偏移量不在缓冲区范围内了)
全局复制开销
- bgsave每次需要fork子进程,对内存和cup开销大
- RDB网络传输耗时
- 从节点清空数据的耗时
- 从节点加载RDB的耗时
- 如果设置了AOF会进行AOF重写,重写耗时
部分复制
- 当master和slave断开连接时,master会将期间所做的操作记录到复制缓存区当中(可以看成是一个队列,其大小默认1M)。
- 待slave重连后,slave会向master发送psync命令并传入offset和runId,并进行判断
- 如果从节点的run id和主节点的run id一致
- 如果master发现slave传输的偏移量的值,在缓存区队列范围中,
- 就会将从offset开始到队列结束的数据传给slave,从而达到同步,降低了使用全量复制的开销。
无磁盘化复制(测试运行阶段)
- 不依赖与硬盘性能
- 直接在内存中创建rdb,然后发送给slave
23. Redis集群会有写操作丢失吗?为什么?
Redis并不能保证数据的强一致性,这意味这在实际中集群在特定的条件下可能会丢失写操作
- 主从复制模型,主服务器宕机后切换新主服务器期间客户端发送给主服务器的数据会发生写丢失
- 全量复制期间主节点宕机,缓冲区数据丢失导致写丢失
24. Redis集群之间是如何复制的?
全量复制
将master本身的RDB文件同步给slave,就是上述的主从复制过程
发生时机
- redis slave首启动或者重启后,连接到master时
- redis slave进程没重启,但是掉线了,重连后不满足部分复制条件(传输偏移量不在缓冲区范围内了)
全局复制开销
- bgsave每次需要fork子进程,对内存和cup开销大
- RDB网络传输耗时
- 从节点清空数据的耗时
- 从节点加载RDB的耗时
- 如果设置了AOF会进行AOF重写,重写耗时
部分复制
当master和slave断开连接时,master会将期间所做的操作记录到复制缓存区当中(可以看成是一个队列,其大小默认1M)。
待slave重连后,slave会向master发送psync命令并传入offset和runId,并进行判断
- 如果从节点的run id和主节点的run id一致
- 如果master发现slave传输的偏移量的值,在缓存区队列范围中,
就会将从offset开始到队列结束的数据传给slave,从而达到同步,降低了使用全量复制的开销。
无磁盘化复制(测试运行阶段)
- 不依赖与硬盘性能
- 直接在内存中创建rdb,然后发送给slave
25. Redis集群最大节点个数是多少?
最大节点个数为16384 个
Redis 集群有 16384 个哈希槽,每个 key 通过 CRC16 算法计算的结果,对 16384 取模后放到对应的编号在 0-16383 之间的哈希槽,集群的每个节点负责一部分哈希槽
26. Redis集群如何选择数据库?
Cluster集群模式下是没有多database的概念的,不能通过select选择
- Redis 集群中内置了 16384 个哈希槽,当需要在 Redis 集群中放置一个 key-value时,redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数,
每个 key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点。
27. 怎么测试Redis的连通性?
控制台测试连接: redis-cli -h host -p port -a password
连接上之后输入:ping,连通状态下会返回:PONG
28. 怎么理解Redis的事务?
事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
Redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令
Redis的事务不保证原子性:单条命令是原子性执行的,但事务不保证原子性,且没有回滚。事务中任意命令执行失败,其余的命令仍会被执行。
事务执行的几种情况
- 参考这个说的比较好https://www.cnblogs.com/DeepInThought/p/10720132.html
29. Redis事务相关的命令有哪几个?
- 参考这个说的比较好https://www.cnblogs.com/DeepInThought/p/10720132.html
MULTI、:标记一个事务块的开始
EXEC:执行所有事务块的命令
DISCARD:取消事务,放弃事务块中的所有命令
WATCH: 监视一或多个key,如果在事务执行之前,被监视的key被其他命令改动,则事务被打断 ( 类似乐观锁 )
UNWATCH: 取消watch对所有key的监控
30. Redis key的过期时间和永久有效分别怎么设置?
设置过期时间:EXPIRE key timer
设置永久有效:PERISIT KEY
31. Redis如何做内存优化?
针对于使用层面,尽可能多使用hash表,对于一个对象的存储,尽量使用hash实现而不是一个属性一个key,而是应该将一个对象所有信息存储到一张散列表
32. Redis回收进程如何工作的?
一个客户端运行了新的命令,添加了新的数据后
Redis检查内存使用情况,如果大于maxmemory的限制,根据设定好的回收策略进行回收
33. 都有哪些方法可以降低Redis的内存使用情况呢?
如果你使用的是32位的Redis实例,可以好好利用Hash,list,sorted set,set等集合类型数据,因为通常情况下很多小的Key-Value可以用更紧凑的方式存放到一起。
34. Redis的内存用完了会发生什么?
如果没有设置淘汰策略,默认使用noenviction,当内存用完后,写数据会报错,读数据不会
如果设置了淘汰策略,就会按设定的淘汰策略进行淘汰
35. 一个Redis实例最多能存放多少的keys?List、Set、Sorted Set他们最多能存放多少元素?
理论上Redis可以处理多达‘2的32次方’的keys
任何list、set、和sorted set都可以放‘2的32次方’个元素。
Redis的存储极限是系统中的可用内存值
36. MySQL里有2000w数据,Redis中只存20w数据,如何保证Redis中的数据都是热点数据?
redis内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。
为了保证都是热点,可以使用allkeys-lru淘汰策略
37. Redis最适合的场景是什么?
会话缓存:Redis提供持久化。能存储缓存数据不丢失,如用户的购物车信息
消息队列:通过list可以实现,但是通常都是使用专业的MQ
排行榜/计数器:Redis对数字的递增递减支持非常好,Set和Zset也使得操作起来非常简便
发布/订阅
分布式会话
分布式锁
38. 假设Redis里面有1亿个key,其中有10w个key是以某个固定的已知前缀开头的,如何将他们全部找出来
使用 keys 指令可以扫出指定模式的 key 列表(keys mypre*)。
redis 正在给线上的业务提供服务,那使用 keys 指令会有什么问题?
- redis 的单线程的。keys 指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。
- 可以使用 scan 指令,scan 指令可以无阻塞的提取出指定模式的 key 列表,但是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接用 keys 指令长。
39. 如果有大量的key需要设置同一时间过期,一般需要注意什么?
如果大量的key过期时间设置的过于集中,到过期的那个时间点,Redis可能会出现短暂的卡顿现象(因为redis是单线程的).严重的话可能会导致服务器雪崩,
所以一般在过期时间上加一个随机值,让过期时间尽量分散.
40. 使用过Redis做异步队列吗?怎么用的?
使用List作为队列:RPUSH生产消息,LPOP消费消息
使用List作为队列:RPUSH生产消息,BLPOP阻塞消费,阻塞直到有消息消费或者超时
发布订阅模式
41. 如何预防缓存穿透和雪崩?
缓存穿透
定义
- 般的缓存系统,都是按照 key 去缓存查询,如果不存在对应的 value,就应该去后端系统查找(比如DB)。一些恶意的请求会故意查询不存在的 key,请求量很大,就会对后端系统造成很大的压力。这就叫做缓存穿透。
如何避免:
- 对查询结果为空的情况也进行缓存,缓存时间设置短一点,或者该 key 对应的数据 insert 了之后清理缓存
- 对一定不存在的 key 进行过滤。可以把所有的可能存在的 key 放到一个大的 Bitmap 中,查询时通过该 bitmap 过滤(布隆过滤器)
缓存雪崩
定义
- 当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,如果还有大量的请求进来,就会直接打到DB上,会给后端系统带来很大压力。导致系统崩溃
如何避免
- 在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个 key 只允许一个线程查询数据和写缓存,其他线程等待。
- 做二级缓存,A1 为原始缓存,A2 为拷贝缓存,A1 失效时,可以访问 A2,A1 缓存失效时间设置为短期,A2 设置为长期
- 不同的 key,设置不同的过期时间,让缓存失效的时间点尽量均匀