❤ 作者主页:欢迎来到我的技术博客😎
❀ 个人介绍:大家好,本人热衷于Java后端开发,欢迎来交流学习哦!( ̄▽ ̄)~*
🍊 如果文章对您有帮助,记得关注、点赞、收藏、评论⭐️⭐️⭐️
📣 您的支持将是我创作的动力,让我们一起加油进步吧!!!🎉🎉
文章目录
- 1、谈谈你对 Redis 的了解?
- 2、Redis 一般都有哪些使用场景?
- 3、Redis 有哪些常见的功能?
- 4、Redis 支持的数据类型有哪些?
- 5、Redis 为什么这么快?
- 6、什么是缓存穿透?怎么解决?
- 7、什么是缓存雪崩?该如何解决?
- 8、什么是缓存击穿?该如何解决?
- 9、Redis 持久化有几种方式?
- 10、Redis 内存淘汰策略有哪些?
- 11、Redis常见性能问题和解决方案?
- 12、我们知道通过expire来设置key 的过期时间,那么对过期的数据怎么处理呢?
- 13、Redis 如何做内存优化?
- 14、Redis事务及其相关面试题?
- 15、Redis是单线程的,如何提高多核CPU的利用率?
- 16、为什么要做Redis分区?
- 17、你知道有哪些Redis分区实现方案?
- 18、分布式Redis是前期做还是后期规模上来了再做好?为什么?
- 19、如何选择合适的持久化方式?
- 20、Redis key的过期时间和永久有效分别怎么设置?
- 21、双写一致性方案一:先删除缓存,后更新数据库
- 22、双写一致性方案二:先更新数据库,后删除缓存
- 23、什么是缓存预热?
- 24、什么是缓存降级?
1、谈谈你对 Redis 的了解?
Redis(全称:Remote Dictionary Server 远程字典服务)是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API。
2、Redis 一般都有哪些使用场景?
Redis适合的场景
- 缓存:减轻 MySQL 的查询压力,提升系统性能;
- 排行榜:利用 Redis 的 SortSet(有序集合)实现;
- 计数器/限速器:利用 Redis 中原子性的自增操作,我们可以统计类似用户点赞数、用户访问数等。这类操作如果用 MySQL,频繁的读写会带来相当大的压力;限速器比较典型的使用场景是限制某个用户访问某个 API 的频率,常用的有抢购时,防止用户疯狂点击带来不必要的压力;
- 好友关系:利用集合的一些命令,比如求交集、并集、差集等。可以方便解决一些共同好友、共同爱好之类的功能;
- 消息队列:除了 Redis 自身的发布/订阅模式,我们也可以利用 List 来实现一个队列机制,比如:到货通知、邮件发送之类的需求,不需要高可靠,但是会带来非常大的 DB 压力,完全可以用 List 来完成异步解耦;
- Session 共享:Session 是保存在服务器的文件中,如果是集群服务,同一个用户过来可能落在不同机器上,这就会导致用户频繁登陆;采用 Redis 保存 Session 后,无论用户落在那台机器上都能够获取到对应的 Session 信息。
Redis不适合的场景
数据量太大、数据访问频率非常低的业务都不适合使用Redis,数据太大增加成本,访问频率太低,保存在内存中纯属浪费资源。
3、Redis 有哪些常见的功能?
- 数据缓存功能;
- 分布式锁的功能;
- 支持数据持久化;
- 支持事务;
- 支持消息队列。
4、Redis 支持的数据类型有哪些?
Redis 有五种基本数据结构:
- String(字符串)
- Hash(哈希)
- List(列表)
- set(集合)
- zset(有序集合)
Redis 有三种特殊数据结构:
- Geospatial
- BitMap
- Hyperloglog
5、Redis 为什么这么快?
- 完全基于内存,绝大部分请求是纯粹的内存操作,非常快速;
- 数据结构简单,对数据操作也简单;
- 采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
- 使用多路 I/O复用模型,非阻塞IO。
6、什么是缓存穿透?怎么解决?
缓存穿透:缓存穿透是指客户端请求的数据在缓存和数据库中都不存在,这样缓存永远不会生效,这样请求都会打到数据库。
解决方案:
- 缓存空对象: 如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。
- 优点:实现简单,维护方便
- 缺点:
- 额外的内存消耗
- 可能造成短期的不一致
- 布隆过滤器: 将所有可能存在的数据哈希存到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存储系统的查询压力。
- 优点:内存占用较少,没有多余key
- 缺点:
- 实现复杂
- 存在误判可能
7、什么是缓存雪崩?该如何解决?
缓存雪崩: 缓存雪崩是指在同一时段大量的缓存key同时生效或者Redis宕机,导致大量请求到达数据库,带来的巨大压力。
解决方案:
- 给不同的Key的TTL添加随机值
- 利用Redis集群提高服务的可用性
- 给缓存业务添加降级限流策略
- 给业务添加多级缓存
8、什么是缓存击穿?该如何解决?
缓存击穿: 缓存击穿问题也叫热点Key问题,就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击。
解决方案:
- 互斥锁
- 逻辑过期
9、Redis 持久化有几种方式?
持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。Redis提供了两种持久化方式:RDB(默认) 和 AOF。
RDB
RDB全称Redis Database Backup file(Redis数据备份文件),也被叫做Redis数据快照。简单来说就是把内存中的所有数据都记录到磁盘中。当Redis实例故障重启后,从磁盘读取快照文件,恢复数据。快照文件称为RDB文件,默认是保存在当前运行目录。
AOF
AOF全称为Append Only File(追加文件)。Redis处理的每一个写命令都会记录在AOF文件,可以看做是命令日志文件。
RDB和AOF的区别:
- AOF 文件比 RDB 更新频率高,优先使用 AOF 还原数据;
- AOF 比 RDB 更安全也更大;
- RDB 性能比 AOF 好;
- 如果两个都配了优先加载 AOF。
10、Redis 内存淘汰策略有哪些?
- volatile-lru:从已设置过期时间的数据集中挑选最近最少使用的数据淘汰;
- volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰;
- volatile-random:从已设置过期时间的数据集中任意选择数据淘汰;
- allkeys-lru:从数据集中挑选最近最少使用的数据淘汰;
- allkeys-random:从数据集(server. db[i]. dict)中任意选择数据淘汰;
- no-enviction(驱逐):禁止驱逐数据。
11、Redis常见性能问题和解决方案?
- Master 最好不要做任何持久化工作,如 RDB 内存快照和 AOF 日志文件。如果数据比较重要,某个 Slave 开启 AOF 备份数据,策略设置为每秒同步一次;
- 为了主从复制的速度和连接的稳定性, Master 和 Slave 最好在同一个局域网内;
- 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3…
12、我们知道通过expire来设置key 的过期时间,那么对过期的数据怎么处理呢?
除了缓存服务器自带的缓存失效策略之外(Redis默认的有6种策略可供选择),我们还可以根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种:
- 定期去处理过期的缓存;
- 当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。
两者各有优劣,第一种的缺点是维护大量缓存的key是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂!具体用哪种方案,大家可以根据自己的应用场景来权衡。
13、Redis 如何做内存优化?
-
控制key的数量 :当使用Redis存储大量数据时,通常会存在大量键,过多的键同样会消耗大量内存。Redis本质是一个数据结构服务器,它为我们提供多种数据结构,如hash,list,set,zset 等结构。使用Redis时不要进入一个误区,大量使用get/set这样的API,把Redis当成Memcached使用。对于存储相同的数据内容利用Redis的数据结构降低外层键的数量,也可以节省大量内存。
-
缩短键值对象,降低Redis内存使用最直接的方式就是缩减键(key)和值(value)的长度。
- key长度:如在设计键时,在完整描述业务情况下,键值越短越好。
- value长度:值对象缩减比较复杂,常见需求是把业务对象序列化成二进制数组放入Redis。首先应该在业务上精简业务对象,去掉不必要的属性避免存储无效数据。其次在序列化工具选择上,应该选择更高效的序列化工具来降低字节数组大小。
-
编码优化: Redis对外提供了string,list,hash,set,zet等类型,但是Redis内部针对不同类型存在编码的概念,所谓编码就是具体使用哪种底层数据结构来实现。编码不同将直接影响数据的内存占用和读写效率。
14、Redis事务及其相关面试题?
什么是事务?
事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
事务是一个原子操作: 事务中的命令要么全部被执行,要么全部都不执行。
Redis事务的概念
Redis 事务的本质是通过MULTI、EXEC、WATCH等一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。
总结说:redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。
Redis事务的三个阶段
- 事务开始MULTI
- 命令入队
- 事务执行EXEC
事务执行过程中,如果服务端收到有EXEC、DISCARD、WATCH、MULTI之外的请求,将会把请求放入队列中排队。
Redis的事务总是具有ACID中的一致性和隔离性,其他特性是不支持的。当服务器运行在AOF持久化模式下,并且appendfsync选项的值为always时,事务也具有耐久性。
Redis事务支持隔离性吗
Redis 是单进程程序,并且它保证在执行事务时,不会对事务进行中断,事务可以运行直到执行完所有事务队列中的命令为止。因此,Redis 的事务是总是带有隔离性的。
Redis事务保证原子性吗,支持回滚吗
Redis中,单条命令是原子性执行的,但事务不保证原子性,且没有回滚。事务中任意命令执行失败,其余的命令任会被执行。
15、Redis是单线程的,如何提高多核CPU的利用率?
可以在同一个服务器部署多个Redis的实例,并把他们当作不同的服务器来使用,在某些时候,无论如何一个服务器是不够的,所以,如果你想使用多个CPU,可以考虑一个分片(shard)。
16、为什么要做Redis分区?
分区可以让Redis管理更大的内存,Redis将可以使用所有机器的内存。如果没有分区,你最多只能使用一台机器的内存。分区使Redis的计算能力通过简单地增加计算机得到成倍提升,Redis的网络带宽也会随着计算机和网卡的增加而成倍增长。
17、你知道有哪些Redis分区实现方案?
- 客户端分区就是在客户端就已经决定数据会被存储到哪个redis节点或者从哪个redis节点读取。大多数客户端已经实现了客户端分区。
- 代理分区 意味着客户端将请求发送给代理,然后代理决定去哪个节点写数据或者读数据。代理根据分区规则决定请求哪些Redis实例,然后根据Redis的响应结果返回给客户端。redis和memcached的一种代理实现就是Twemproxy。
- 查询路由(Query routing) 的意思是客户端随机地请求任意一个redis实例,然后由Redis将请求转发给正确的Redis节点。Redis Cluster实现了一种混合形式的查询路由,但并不是直接将请求从一个redis节点转发到另一个redis节点,而是在客户端的帮助下直接redirected到正确的redis节点。
18、分布式Redis是前期做还是后期规模上来了再做好?为什么?
既然Redis是如此的轻量(单实例只使用1M内存),为防止以后的扩容,最好的办法就是一开始就启动较多实例。即便你只有一台服务器,你也可以一开始就让Redis以分布式的方式运行,使用分区,在同一台服务器上启动多个实例。
一开始就多设置几个Redis实例,例如32或者64个实例,对大多数用户来说这操作起来可能比较麻烦,但是从长久来看做这点牺牲是值得的。
这样的话,当你的数据不断增长,需要更多的Redis服务器时,你需要做的就是仅仅将Redis实例从一台服务迁移到另外一台服务器而已(而不用考虑重新分区的问题)。一旦你添加了另一台服务器,你需要将你一半的Redis实例从第一台机器迁移到第二台机器。
19、如何选择合适的持久化方式?
1、如果是数据不那么敏感,且可以从其他地方重新生成补回的,那么可以关闭持久化。
2、如果是数据比较重要,不想再从其他地方获取,且可以承受数分钟的数据丢失,比如缓存等,那么可以只使用RDB。
3、如果是用做内存数据库,要使用Redis的持久化,建议是RDB和AOF都开启,或者定期执行bgsave做快照备份,RDB方式更适合做数据的备份,AOF可以保证数据的不丢失。
补充:Redis4.0 对于持久化机制的优化
Redis4.0相对与3.X版本其中一个比较大的变化是4.0添加了新的混合持久化方式。
简单的说:新的AOF文件前半段是RDB格式的全量数据后半段是AOF格式的增量数据,如下图:
优点 :混合持久化结合了RDB持久化 和 AOF 持久化的优点, 由于绝大部分都是RDB格式,加载速度快,同时结合AOF,增量的数据以AOF方式保存了,数据更少的丢失。
缺点 :兼容性差,一旦开启了混合持久化,在4.0之前版本都不识别该aof文件,同时由于前部分是RDB格式,阅读性较差。
20、Redis key的过期时间和永久有效分别怎么设置?
通过expire或pexpire命令,客户端可以以秒或毫秒的精度为数据库中的某个键设置生存时间。
与expire和pexpire命令类似,客户端可以通过expireat和pexpireat命令,以秒或毫秒精度给数据库中的某个键设置过期时间,可以理解为:让某个键在某个时间点过期。
21、双写一致性方案一:先删除缓存,后更新数据库
该方案也会出问题,此时来了两个请求,请求 A(更新操作) 和请求 B(查询操作)
- 请求A进行写操作,删除缓存;
- 请求B查询发现缓存不存在;
- 请求B去数据库查询得到旧值;
- 请求B将旧值写入缓存;
- 请求A将新值写入数据库。
上述情况就会导致不一致的情形出现。而且,如果不采用给缓存设置过期时间策略,该数据永远都是脏数据。
最简单的解决办法就是 延时双删。
伪代码如下:
public void write(String key,Object data){
Redis.delKey(key);
db.updateData(data);
Thread.sleep(1000);
Redis.delKey(key);
}
转化为中文描述就是:
- 先淘汰缓存;
- 再写数据库(这两步和原来一样)
- 休眠1秒,再次淘汰缓存,可以将1秒内所造成的缓存脏数据再次删除。确保读请求结束,写请求可以删除读请求造成的缓存脏数据。自行评估自己的项目的读数据业务逻辑的耗时,写数据的休眠时间则在读数据业务逻辑的耗时基础上,加几百ms即可。
22、双写一致性方案二:先更新数据库,后删除缓存
这一种情况也会出现问题,比如更新数据库成功了,但是在删除缓存的阶段出错了没有删除成功,那么此时再读取缓存的时候每次都是错误的数据了。
此时解决方案就是利用消息队列进行删除的补偿。具体的业务逻辑用语言描述如下:
- 请求A先对数据库进行更新操作;
- 在对Redis进行删除操作的时候发现报错,删除失败;
- 此时将Redis的key作为消息体发送到消息队列中;
- 系统接收到消息队列发送的消息后再次对Redis进行删除操作。
23、什么是缓存预热?
缓存预热是指系统上线后,提前将相关的缓存数据加载到缓存系统。避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题,用户直接查询事先被预热的缓存数据。
如果不进行预热,那么Redis初始状态数据为空,系统上线初期,对于高并发的流量,都会访问到数据库中, 对数据库造成流量的压力。
缓存预热解决方案:
- 数据量不大的时候,工程启动的时候进行加载缓存动作;
- 数据量大的时候,设置一个定时任务脚本,进行缓存的刷新;
- 数据量太大的时候,优先保证热点数据进行提前加载到缓存。
24、什么是缓存降级?
缓存降级是指缓存失效或缓存服务器挂掉的情况下,不去访问数据库,直接返回默认数据或访问服务的内存数据。降级一般是有损的操作,所以尽量减少降级对于业务的影响程度。
在进行降级之前要对系统进行梳理,看看系统是不是可以丢卒保帅;从而梳理出哪些必须誓死保护,哪些可降级;比如可以参考日志级别设置预案:
一般 :比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
警告 :有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;
错误 :比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;
严重错误 :比如因为特殊原因数据错误了,此时需要紧急人工降级。
非常感谢您阅读到这里,如果这篇文章对您有帮助,希望能留下您的点赞👍 关注💖 分享👥 留言💬thanks!!!