从阿里、腾讯的面试真题中总结了这11个Redis高频面试题

前言程序员

如今你们的工做生活基本已是回归正轨了,最近也是迎来了跳槽面试季,有些人已经拿到了一两个offer了。面试

v2-d195026c75b6e186f2a2f6e4d061144c_720w.png

这段时间收集了阿里、腾讯、百度、京东、美团、字节跳动等公司的Java面试题,总结了Redis系列的高频面试题:redis

一、Redis持久化机制算法

二、缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级等问题spring

三、热点数据和冷数据是什么数据库

四、Memcache与Redis的区别都有哪些?设计模式

五、单线程的redis为何这么快缓存

六、redis的数据类型,以及每种数据类型的使用场景安全

七、redis的过时策略以及内存淘汰机制服务器

八、Redis 为何是单线程的

九、Redis 常见性能问题和解决方案?

十、为何Redis的操做是原子性的,怎么保证原子性的?

十一、Redis事务

v2-195cd720ad6342520577761f85c68b7c_b.jpeg

Redis系列高频面试题解析

Redis持久化机制

Redis是一个支持持久化的内存数据库,经过持久化机制把内存中的数据同步到硬盘文件来保证数据持久化。当Redis重启后经过把硬盘文件从新加载到内存,就能达到恢复数据的目的。

实现:单首创建fork()一个子进程,将当前父进程的数据库数据复制到子进程的内存中,而后由子进程写入到临时文件中,持久化的过程结束了,再用这个临时文件替换上次的快照文件,而后子进程退出,内存释放。

RDB是Redis默认的持久化方式。按照必定的时间周期策略把内存的数据以快照的形式保存到硬盘的二

进制文件。即Snapshot快照存储,对应产生的数据文件为dump.rdb,经过配置文件中的save参数来定义快照的周期。( 快照能够是其所表示的数据的一个副本,也能够是数据的一个复制品。)

AOF:Redis会将每个收到的写命令都经过Write函数追加到文件最后,相似于MySQL的binlog。当Redis重启是会经过从新执行文件中保存的写命令来在内存中重建整个数据库的内容。

当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复。

缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级等问题

一、缓存雪崩

咱们能够简单的理解为:因为原有缓存失效,新缓存未到期间

(例如:咱们设置缓存时采用了相同的过时时间,在同一时刻出现大面积的缓存过时),全部本来应该访问缓存的请求都去查询数据库了,而对数据库CPU和内存形成巨大压力,严重的会形成数据库宕机。从而造成一系列连锁反应,形成整个系统崩溃。

解决办法:

大多数系统设计者考虑用加锁( 最多的解决方案)或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。还有一个简单方案就时讲缓存失效时间分散开。

二、缓存穿透

缓存穿透是指用户查询数据,在数据库没有,天然在缓存中也不会有。这样就致使用户查询的时候,在缓存中找不到,每次都要去数据库再查询一遍,而后返回空(至关于进行了两次无用的查询)。这样请求就绕过缓存直接查数据库,这也是常常提的缓存命中率问题。

解决办法;

最多见的则是采用布隆过滤器,将全部可能存在的数据哈希到一个足够大的bitmap中,一个必定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。

另外也有一个更为简单粗暴的方法,若是一个查询返回的数据为空(无论是数据不存在,仍是系统故障),咱们仍然把这个空结果进行缓存,但它的过时时间会很短,最长不超过五分钟。经过这个直接设置的默认值存放到缓存,这样第二次到缓冲中获取就有值了,而不会继续访问数据库,这种办法最简单粗暴。

5TB的硬盘上放满了数据,请写一个算法将这些数据进行排重。若是这些数据是一些32bit大小的数据该如何解决?若是是64bit的呢?

对于空间的利用到达了一种极致,那就是Bitmap和布隆过滤器(Bloom Filter)。

Bitmap: 典型的就是哈希表

缺点是,Bitmap对于每一个元素只能记录1bit信息,若是还想完成额外的功能,恐怕只能靠牺牲更多的空

布隆过滤器(推荐)

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

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

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

Hash存在一个冲突(碰撞)的问题,用同一个Hash获得的两个URL的值有可能相同。为了减小冲突,咱们能够多引入几个Hash,若是经过其中的一个Hash值咱们得出某元素不在集合中,那么该元素确定不在集合中。只有在全部的Hash函数告诉咱们该元素在集合中时,才能肯定该元素存在于集合中。这即是Bloom-Filter的基本思想。

Bloom-Filter通常用于在大数据量的集合中断定某元素是否存在。

三、缓存预热

缓存预热这个应该是一个比较常见的概念,相信不少小伙伴都应该能够很容易的理解,缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就能够避免在用户请求的时候,先查询数据库,而后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!

解决思路:

一、直接写个缓存刷新页面,上线时手工操做下;

二、数据量不大,能够在项目启动的时候自动进行加载;

三、定时刷新缓存;

四、缓存更新

除了缓存服务器自带的缓存失效策略以外(Redis默认的有6中策略可供选择),咱们还能够根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种:

(1)定时去清理过时的缓存;

(2)当有用户请求过来时,再判断这个请求所用到的缓存是否过时,过时的话就去底层系统获得新数据并更新缓存。

二者各有优劣,第一种的缺点是维护大量缓存的key是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂!具体用哪一种方案,你们能够根据本身的应用场景来权衡。

五、缓存降级

当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然须要保证服务仍是可用的,即便是有损服务。系统能够根据一些关键数据进行自动降级,也能够配置开关实现人工降级。

降级的最终目的是保证核心服务可用,即便是有损的。并且有些服务是没法降级的(如加入购物车、结算)。

以参考日志级别设置预案:

(1)通常:好比有些服务偶尔由于网络抖动或者服务正在上线而超时,能够自动降级;

(2)警告:有些服务在一段时间内成功率有波动(如在95~100%之间),能够自动降级或人工降级,并发送告警;

(3)错误:好比可用率低于90%,或者数据库链接池被打爆了,或者访问量忽然猛增到系统能承受的最大阀值,此时能够根据状况自动降级或者人工降级;

(4)严重错误:好比由于特殊缘由数据错误了,此时须要紧急人工降级。

服务降级的目的,是为了防止Redis服务故障,致使数据库跟着一块儿发生雪崩问题。所以,对于不重要的缓存数据,能够采起服务降级策略,例如一个比较常见的作法就是,Redis出现问题,不去数据库查询,而是直接返回默认值给用户。

热点数据和冷数据是什么

热点数据,缓存才有价值

对于冷数据而言,大部分数据可能尚未再次访问到就已经被挤出内存,不只占用内存,并且价值不场景。

对于热点数据,好比咱们的某IM产品,生日祝福模块,当天的寿星列表,缓存之后可能读取数十万次。

再举个例子,某导航产品,咱们将导航信息,缓存之后可能读取数百万次。

数据更新前至少读取两次,缓存才有意义。这个是最基本的策略,若是缓存尚未起做用就失效了,那就没有太大价值了。

那存不存在,修改频率很高,可是又不得不考虑缓存的场景呢?有!好比,这个读取接口对数据库的压力很大,可是又是热点数据,这个时候就须要考虑经过缓存手段,减小数据库的压力,好比咱们的某助手产品的,点赞数,收藏数,分享数等是很是典型的热点数据,可是又不断变化,此时就须要将数据同步保存到Redis缓存,减小数据库压力。

Memcache与Redis的区别都有哪些?

一、存储方式 Memecache把数据所有存在内存之中,断电后会挂掉,数据不能超过内存大小。 Redis有部份存在硬盘上,redis能够持久化其数据

二、数据支持类型 memcached全部的值均是简单的字符串,redis做为其替代者,支持更为丰富的数据类型 ,提供list,set,zset,hash等数据结构的存储

三、使用底层模型不一样 它们之间底层实现方式 以及与客户端之间通讯的应用协议不同。 Redis直接本身构建了VM 机制 ,由于通常的系统调用系统函数的话,会浪费必定的时间去移动和请求。

四、 value 值大小不一样:Redis 最大能够达到 1gb;memcache 只有 1mb。

五、redis的速度比memcached快不少

六、Redis支持数据的备份,即master-slave模式的数据备份。

单线程的redis为何这么快

一、纯内存操做

二、单线程操做,避免了频繁的上下文切换

三、采用了非阻塞I/O多路复用机制

v2-b2c0bae12db537d55a114d8ce200076f_720w.png

redis的数据类型,以及每种数据类型的使用场景

回答:一共五种

一、String

这个其实没啥好说的,最常规的set/get操做,value能够是String也能够是数字。通常作一些复杂的计数功能的缓存。

二、hash

这里value存放的是结构化的对象,比较方便的就是操做其中的某个字段。博主在作单点登陆的时候,就是用这种数据结构存储用户信息,以cookieId做为key,设置30分钟为缓存过时时间,能很好的模拟出相似session的效果。

三、list

使用List的数据结构,能够作简单的消息队列的功能。另外还有一个就是,能够利用lrange命令,作基于redis的分页功能,性能极佳,用户体验好。本人还用一个场景,很合适—取行情信息。就也是个生产者和消费者的场景。LIST能够很好的完成排队,先进先出的原则。

四、set

由于set堆放的是一堆不重复值的集合。因此能够作全局去重的功能。为何不用JVM自带的Set进行去重?由于咱们的系统通常都是集群部署,使用JVM自带的Set,比较麻烦,难道为了一个作一个全局去重,再起一个公共服务,太麻烦了。

另外,就是利用交集、并集、差集等操做,能够计算共同喜爱,所有的喜爱,本身独有的喜爱等功能。

五、sorted set

redis的过时策略以及内存淘汰机制

redis采用的是按期删除+惰性删除策略。

为何不用定时删除策略?

定时删除,用一个定时器来负责监视key,过时则自动删除。虽然内存及时释放,可是十分消耗CPU资源。

在大并发请求下,CPU要将时间应用在处理请求,而不是删除key,所以没有采用这一策略.

按期删除+惰性删除是如何工做的呢?

按期删除,redis默认每一个100ms检查,是否有过时的key,有过时key则删除。须要说明的是,redis不是每一个100ms将全部的key检查一次,而是随机抽取进行检查(若是每隔100ms,所有key进行检查,redis岂不是卡死)。所以,若是只采用按期删除策略,会致使不少key到时间没有删除。

因而,惰性删除派上用场。也就是说在你获取某个key的时候,redis会检查一下,这个key若是设置了过时时间那么是否过时了?若是过时了此时就会删除。

采用按期删除+惰性删除就没其余问题了么?

不是的,若是按期删除没删除key。而后你也没即时去请求key,也就是说惰性删除也没生效。这样,redis的内存会愈来愈高。那么就应该采用内存淘汰机制。

在redis.conf中有一行配置

maxmemory-policy volatile-lru

该配置就是配内存淘汰策略的(什么,你没配过?好好检讨一下本身)

volatile-lru:从已设置过时时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰

volatile-ttl:从已设置过时时间的数据集(server.db[i].expires)中挑选将要过时的数据淘汰

volatile-random:从已设置过时时间的数据集(server.db[i].expires)中任意选择数据淘汰

allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰

allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰

no-enviction(驱逐):禁止驱逐数据,新写入操做会报错

ps:若是没有设置 expire 的key, 不知足先决条件(prerequisites); 那么 volatile-lru, volatile-random 和volatile-ttl 策略的行为, 和 noeviction(不删除) 基本上一致。

Redis 为何是单线程的

官方FAQ表示,由于Redis是基于内存的操做,CPU不是Redis的瓶颈,Redis的瓶颈最有多是机器内存的大小或者网络带宽。既然单线程容易实现,并且CPU不会成为瓶颈,那就瓜熟蒂落地采用单线程的方案了(毕竟采用多线程会有不少麻烦!)Redis利用队列技术将并发访问变为串行访问

一、绝大部分请求是纯粹的内存操做(很是快速)

二、采用单线程,避免了没必要要的上下文切换和竞争条件

三、非阻塞IO优势:

(1)速度快,由于数据存在内存中,相似于HashMap,HashMap的优点就是查找和操做的时间复杂度都是O(1)

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

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

(4)丰富的特性:可用于缓存,消息,按key设置过时时间,过时后将会自动删除如何解决redis的并发竞争key问题同时有多个子系统去set一个key。这个时候要注意什么呢? 不推荐使用redis的事务机制。由于咱们的生产环境,基本都是redis集群环境,作了数据分片操做。你一个事务中有涉及到多个key操做的时候,这多个key不必定都存储在同一个redis-server上。所以,redis的事务机制,十分难搞。

(1)若是对这个key操做,不要求顺序: 准备一个分布式锁,你们去抢锁,抢到锁就作set操做便可做了。以此类推。

(2) 利用队列,将set方法变成串行访问也能够redis遇到高并发,若是保证读写key的一致性对redis的操做都是具备原子性的,是线程安全的操做,你不用考虑并发问题,redis内部已经帮你处理好并发的问题了。

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

一、Master 最好不要作任何持久化工做,如 RDB 内存快照和 AOF 日志文件

二、 若是数据比较重要,某个 Slave 开启 AOF 备份数据,策略设置为每秒同步一次

三、为了主从复制的速度和链接的稳定性, Master 和 Slave 最好在同一个局域网内

四、尽可能避免在压力很大的主库上增长从库

五、主从复制不要用图状结构,用单向链表结构更为稳定,即: Master <- Slave1 <- Slave2 <-Slave3…

为何Redis的操做是原子性的,怎么保证原子性的?

对于Redis而言,命令的原子性指的是:一个操做的不能够再分,操做要么执行,要么不执行。

Redis的操做之因此是原子性的,是由于Redis是单线程的。

Redis自己提供的全部API都是原子操做,Redis中的事务实际上是要保证批量操做的原子性。

多个命令在并发中也是原子性的吗?

不必定, 将get和set改为单命令操做,incr 。使用Redis的事务,或者使用Redis+Lua==的方式实现.

Redis事务

Redis事务功能是经过MULTI、EXEC、DISCARD和WATCH 四个原语实现的

Redis会将一个事务中的全部命令序列化,而后按顺序执行。

一、redis 不支持回滚“Redis 在事务失败时不进行回滚,而是继续执行余下的命令”, 因此 Redis 的内部能够保持简单且快速。

二、若是在一个事务中的命令出现错误,那么全部的命令都不会执行;

三、若是在一个事务中出现运行错误,那么正确的命令会被执行。

(1)MULTI命令用于开启一个事务,它老是返回OK。 MULTI执行以后,客户端能够继续向服务器发送任意多条命令,这些命令不会当即被执行,而是被放到一个队列中,当EXEC命令被调用时,全部队列中的命令才会被执行。

(2)EXEC:执行全部事务块内的命令。返回事务块内全部命令的返回值,按命令执行的前后顺序排列。当操做被打断时,返回空值 nil 。

(3)经过调用DISCARD,客户端能够清空事务队列,并放弃执行事务, 而且客户端会从事务状态中退出。

(4)WATCH 命令能够为 Redis 事务提供 check-and-set (CAS)行为。 能够监控一个或多个键,一旦其中有一个键被修改(或删除),以后的事务就不会执行,监控一直持续到EXEC命令。

欢迎关注公种浩:程序员追风,回复66  ,领取一份300页pdf文档的Java核心知识点总结!

v2-66e3ac816240d5e37736cc7394c9bf75_720w.jpeg


这些资料的内容都是面试时面试官必问的知识点,篇章包括了不少知识点,其中包括了有基础知识、Java集合、JVM、多线程并发、spring原理、微服务、Netty 与RPC 、Kafka、日记、设计模式、Java算法、数据库、Zookeeper、分布式缓存、数据结构等等。

最后

欢迎你们一块儿交流,喜欢文章记得关注我点个赞哟,感谢支持!

相关文章
相关标签/搜索