几率大的 Redis 面试题含答案Word格式.docx
《几率大的 Redis 面试题含答案Word格式.docx》由会员分享,可在线阅读,更多相关《几率大的 Redis 面试题含答案Word格式.docx(11页珍藏版)》请在冰豆网上搜索。
对于大量的请求怎么样处理
Redis常见性能问题和解决方案?
讲解下Redis线程模型
为什么Redis的操作是原子性的,怎么保证原子性的?
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和布隆过滤器(BloomFilter)。
Bitmap:
典型的就是哈希表
缺点是,Bitmap对于每个元素只能记录1bit信息,如果还想完成额外的功能,恐怕只能靠牺牲更多的空间、时间来完成了。
布隆过滤器(推荐)
就是引入了k(k>
1)k(k>
1)个相互独立的哈希函数,保证在给定的空间、误判率下,完成元素判重的过程。
它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。
Bloom-Filter算法的核心思想就是利用多个不同的Hash函数来解决“冲突”。
Hash存在一个冲突(碰撞)的问题,用同一个Hash得到的两个URL的值有可能相同。
为了减少冲突,我们可以多引入几个Hash,如果通过其中的一个Hash值我们得出某元素不在集合中,那么该元素肯定不在集合中。
只有在所有的Hash函数告诉我们该元素在集合中时,才能确定该元素存在于集合中。
这便是Bloom-Filter的基本思想。
Bloom-Filter一般用于在大数据量的集合中判定某元素是否存在。
缓存穿透与缓存击穿的区别
缓存击穿:
是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据。
解决方案:
在访问key之前,采用SETNX(setifnotexists)来设置另一个短期key来锁住当前key的访问,访问结束再删除该短期key。
给一个我公司处理的案例:
背景双机拿token,token在存一份到redis,保证系统在token过期时都只有一个线程去获取token;
线上环境有两台机器,故使用分布式锁实现。
三、缓存预热
缓存预热这个应该是一个比较常见的概念,相信很多小伙伴都应该可以很容易的理解,缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。
这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!
用户直接查询事先被预热的缓存数据!
解决思路:
直接写个缓存刷新页面,上线时手工操作下;
数据量不大,可以在项目启动的时候自动进行加载;
定时刷新缓存;
四、缓存更新
除了缓存服务器自带的缓存失效策略之外(Redis默认的有6中策略可供选择),我们还可以根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种:
定时去清理过期的缓存;
当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。
两者各有优劣,第一种的缺点是维护大量缓存的key是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂!
具体用哪种方案,大家可以根据自己的应用场景来权衡。
五、缓存降级
当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。
系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。
降级的最终目的是保证核心服务可用,即使是有损的。
而且有些服务是无法降级的(如加入购物车、结算)。
以参考日志级别设置预案:
一般:
比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
警告:
有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;
错误:
比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;
严重错误:
比如因为特殊原因数据错误了,此时需要紧急人工降级。
服务降级的目的,是为了防止Redis服务故障,导致数据库跟着一起发生雪崩问题。
因此,对于不重要的缓存数据,可以采取服务降级策略,例如一个比较常见的做法就是,Redis出现问题,不去数据库查询,而是直接返回默认值给用户。
热点数据,缓存才有价值
对于冷数据而言,大部分数据可能还没有再次访问到就已经被挤出内存,不仅占用内存,而且价值不大。
频繁修改的数据,看情况考虑使用缓存
对于上面两个例子,寿星列表、导航信息都存在一个特点,就是信息修改频率不高,读取通常非常高的场景。
对于热点数据,比如我们的某IM产品,生日祝福模块,当天的寿星列表,缓存以后可能读取数十万次。
再举个例子,某导航产品,我们将导航信息,缓存以后可能读取数百万次。
数据更新前至少读取两次,
缓存才有意义。
这个是最基本的策略,如果缓存还没有起作用就失效了,那就没有太大价值了。
那存不存在,修改频率很高,但是又不得不考虑缓存的场景呢?
有!
比如,这个读取接口对数据库的压力很大,但是又是热点数据,这个时候就需要考虑通过缓存手段,减少数据库的压力,比如我们的某助手产品的,点赞数,收藏数,分享数等是非常典型的热点数据,但是又不断变化,此时就需要将数据同步保存到Redis缓存,减少数据库压力。
1)、存储方式Memecache把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。
Redis有部份存在硬盘上,redis可以持久化其数据
2)、数据支持类型memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型,提供list,set,zset,hash等数据结构的存储
3)、使用底层模型不同它们之间底层实现方式以及与客户端之间通信的应用协议不一样。
Redis直接自己构建了VM机制,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。
4).value值大小不同:
Redis最大可以达到512M;
memcache只有1mb。
5)redis的速度比memcached快很多
6)Redis支持数据的备份,即master-slave模式的数据备份。
(一)纯内存操作
(二)单线程操作,避免了频繁的上下文切换
(三)采用了非阻塞I/O多路复用机制
Redis的数据类型,以及每种数据类型的使用场景
回答:
一共五种
(一)String
这个其实没啥好说的,最常规的set/get操作,value可以是String也可以是数字。
一般做一些复杂的计数功能的缓存。
(二)hash
这里value存放的是结构化的对象,比较方便的就是操作其中的某个字段。
博主在做单点登录的时候,就是用这种数据结构存储用户信息,以cookieId作为key,设置30分钟为缓存过期时间,能很好的模拟出类似session的效果。
(三)list
使用List的数据结构,可以做简单的消息队列的功能。
另外还有一个就是,可以利用lrange命令,做基于redis的分页功能,性能极佳,用户体验好。
本人还用一个场景,很合适—取行情信息。
就也是个生产者和消费者的场景。
LIST可以很好的完成排队,先进先出的原则。
(四)set
因为set堆放的是一堆不重复值的集合。
所以可以做全局去重的功能。
为什么不用JVM自带的Set进行去重?
因为我们的系统一般都是集群部署,使用JVM自带的Set,比较麻烦,难道为了一个做一个全局去重,再起一个公共服务,太麻烦了。
另外,就是利用交集、并集、差集等操作,可以计算共同喜好,全部的喜好,自己独有的喜好等功能。
(五)sortedset
sortedset多了一个权重参数score,集合中的元素能够按score进行排列。
可以做排行榜应用,取TOPN操作。
Redis内部结构
dict本质上是为了解决算法中的查找问题(Searching)是一个用于维护key和value映射关系的数据结构,与很多语言中的Map或dictionary类似。
本质上是为了解决算法中的查找问题(Searching)
sdssds就等同于char*它可以存储任意二进制数据,不能像C语言字符串那样以字符’\0’来标识字符串的结束,因此它必然有个长度字段。
skiplist(跳跃表)跳表是一种实现起来很简单,单层多指针的链表,它查找效率很高,堪