面试之redis专题

一、为什么用redis?

1)、性能,对于执行耗时特别久,且结果不频繁变动的sql,就特别适合放到缓存里,这样,后面的请求从缓存中获取数据,使得请求响应快。

2)、并发,大并发情况下,所有请求都去请求数据库,数据库就会出现异常。用了缓存,这样就可以减少数据库的压力。请求先去缓存中获取数据,没有的话再去查询数据库。

二、redis是什么?

完全开源免费的,用C语言编写的,是一个高性能的K/V分布式内存数据库,基于内存运行并支持持久化的NoSql数据库。

与同类产品相比,具有如下优点:

1)支持数据的持久化,可以将内存中的数据保存到磁盘中,重启的时候可以再次加载进行使用

2)不仅仅支持key-value类型的数据,同时还支持list、集合、有序集合、哈希等

3)支持数据的备份,即主从模式的数据备份

三、redis为什么快?

1)纯内存操作

2)单线程操作,避免了线程的上下文的切换耗时

3)采用了非阻塞I/O多路复用机制

举例:只有单个线程(一个快递员),通过跟踪每个I/O流(快递)的状态(每个快递的送达地点),来管理多个I/O流。

四、数据类型及使用场景

1)String,最常规的get/set操作,value可以是字符串和数字。一般做一些复杂的计数功能,还有短信验证码验证功能等。

2)hash这里value存放的是结构化的对象,比较方便的就是操作其中的某个字段。博主在做单点登录的时候,就

是用这种数据结构存储用户信息,以cookieId作为key,设置30分钟为缓存过期时间,能很好的模拟出类似session

的效果。

3) list 使用List的数据结构,可以做简单的消息队列的功能。另外还有一个就是,可以利用lrange命令,做基于

redis的分页功能,性能极佳,用户体验好。(

4) set 因为set堆放的是一堆不重复值的集合。所以可以做全局去重的功能。为什么不用JVM自带的Set进行去重?

因为我们的系统一般都是集群部署,使用JVM自带的Set,比较麻烦,难道为了一个做一个全局去重,再起一个公共

服务,太麻烦了。 另外,就是利用交集、并集、差集等操作,可以计算共同喜好,全部的喜好,自己独有的**喜

好等功能**。

5) sorted set多了一个权重参数score,集合中的元素能够按score进行排列。可以做排行榜应用,取TOP N操作。

五、过期策略以及内存淘汰机制

分析:这个问题其实相当重要,到底redis有没用到家,这个问题就可以看出来。比如你redis只能存5G数据,可是你

写了10G,那会删5G的数据。怎么删的,这个问题思考过么?

还有,你的数据已经设置了过期时间,但是时间到了,内存占用率还是比较高,有思考过原因么?

回答: redis采用的是定期删除+惰性删除策略。

为什么不用定时删除策略? 定时删除,用一个定时器来负责监视key,过期则自动删除。虽然内存及时释放,但是十分消耗CPU资源。在大并发请求下,CPU要将时间应用在处理请求,而不是删除key,因此没有采用这一策略.

定期删除+惰性删除是如何工作的呢? 定期删除,redis默认每个100ms检查,是否有过期的key,有过期key则删除。需要说明的是,redis不是每个100ms将所有的key检查一次,而是随机抽取进行检查(如果每隔100ms,全部key进行检查,redis岂不是卡死)。因此,如果只采用定期删除策略,会导致很多key到时间没有删除。 于是,惰性删除派上用场。也就是说在你获取某个key的时候,redis会检查一下,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除。

采用定期删除+惰性删除就没其他问题了么? 不是的,如果定期删除没删除key。然后你也没即时去请求key,

也就是说惰性删除也没生效。这样,redis的内存会越来越高。那么就应该采用内存淘汰机制。 在redis.conf中有一行配置,该配置就是配内存淘汰策略的(什么,你没配过?好好反省一下自己)

1)noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。应该没人用吧。

2)allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。推荐使用,目前项目在用这种。

3)allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。应该也没人用吧,你不删最少使用Key,去随机删。

4)volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。这种情况一般是把redis既当缓存,又做持久化存储的时候才用。不推荐

5)volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。依然不推荐

6)volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。不推荐 ps:如果没有设置 expire 的key, 不满足先决条件(prerequisites); 那么 volatile-lru, volatile-random 和 volatile-ttl 策略的行为, 和 noeviction(不删除) 基本上一致

六、渐进式ReHash

reHash的过程:

1) 首先创建一个比现有哈希表更大的新哈希表(expand)

2) 然后将旧哈希表的所有元素都迁移到新哈希表去(rehash)

渐增式rehash和平摊操作

集中式的 rehash 会引起大量的计算工作,很可能会导致服务器宕机。

渐增式 rehash将 rehash 操作平摊到dictAddRaw 、dictGetRandomKey 、dictFind 、dictGenericDelete这些函数里面,每当上面这些函数被执行的时候, _dictRehashStep 函数就会执行,将 1 个元素从 0 号哈希表 rehash 到 1 号哈希表,这样就避免了集中式的 rehash 。

注:渐进式rehash的好处在于它采取分为而治的方式,将rehash键值对的计算均摊到每个字典增删改查操作,避

免了集中式rehash的庞大计算量。

七、缓存穿透

概念访问一个不存在的key,缓存不起作用,请求会穿透到DB,流量大时DB会挂掉。

解决方案:

采用布隆过滤器,使用一个足够大的bitmap,用于存储可能访问的key,不存在的key直接被过滤;

访问key未在DB查询到值,也将空值写进缓存,但可以设置较短过期时间。

八、缓存雪崩

大量的key设置了相同的过期时间,导致在缓存在同一时刻全部失效,造成瞬时DB请求量大、压力骤增,引起雪

崩。

解决方案

可以给缓存设置过期时间时加上一个随机值时间,使得每个key的过期时间分布开来,不会集中在同一时刻失

效;

采用限流算法,限制流量;

采用分布式锁,加锁访问

九、缓存击穿

key对应的数据存在,但在redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。

缓存击穿解决方案

key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时候,需要考虑一个问题:缓存被“击穿”的问题。

使用互斥锁(mutex key)

业界比较常用的做法,是使用mutex(互斥锁)。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。

十、持久化

redis提供了两种持久化的方式,分别是RDB(Redis DataBase)和AOF(Append Only File)

RDB,全量方式,简而言之,就是在不同的时间点,将redis存储的数据生成快照并存储到磁盘等介质上;

AOF,增量方式,就是将redis执行过的所有写指令记录下来,在下次redis重新启动时,只要把这些写指令从前到后再重复执行一遍,就可以实现数据恢复了。

其实RDB和AOF两种方式也可以同时使用,在这种情况下,如果redis重启的话,则会优先采用AOF方式来进行数据恢复,这是因为AOF方式的数据恢复完整度更高。

如果你没有数据持久化的需求,也完全可以关闭RDB和AOF方式,这样的话,redis将变成一个纯内存数据库,就像memcache一样。

Redis持久化机制:RDB和AOF

RDB持久化:定时任务,BGSAVE命令 fork一个子进程生成RDB文件(二进制)

AOF持久化:根据配置将写命令存储至日志文件中,顺序写&&异步刷盘(子线程),重写AOF文件也是需要 fork 子进程。Redis4.0之后支持混合持久化,用什么持久化机制看业务场景

十一、手写一个LRU算法

 使用LinkedHashMap来实现, LinkedHashMap底层就是用的HashMap+双链表实现的。而且本身已经实现了按照访问顺序的存储。此外,LinkedHashMap中本身就实现了1个方法,用于判断是否需要移除最不常读的数,方法默认是直接返回false。所以需要重新改方法。当缓存满后就移除最不常用的数。

package thread4;

import java.util.LinkedHashMap;

/**

* 利用LinkedHashMap实现简单的缓存, 必须实现removeEldestEntry方法,

*/

public class LRULinkedHashMap<K,V> extends LinkedHashMap<K,V> {

//最多能缓存多少数据

private final int maxCapacity;

private static final float DEFAULT_LOAD_FACTOR = 0.75f;

public LRULinkedHashMap(int maxCapacity) {

//设置1个hash的初始大小 accessOrder为true意思是让linkedHashMap按照顺序

//访问来进行排序,最近访问的放在头,最老访问的放在尾

super(maxCapacity, DEFAULT_LOAD_FACTOR, true);

this.maxCapacity = maxCapacity;

}

@Override

protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) {

//当map中的数据量大于指定的缓存个数的时候,就自动删除最老的数据

return size() > maxCapacity;

}

public static void main(String args[]){

LRULinkedHashMap lruLinkedHashMap = new LRULinkedHashMap(5);

for (int i =0;i<10;i++) {

lruLinkedHashMap.put(i,i);

}

System.out.println(lruLinkedHashMap);

}

}

十二、为啥要用redis

1、缓存是如何实现高性能的

频繁查询,查询时间长,且改动少的数据,放入缓存。第一次查询慢,后面的查询都走缓存,时间就很快了。

2、缓存是如何实现高并发的

数据库连接资源是有限的,很宝贵。交易高峰期,假如每秒4000个请求,如果都走数据库,那么数据库就可能宕机。因为数据库建议并发请求不超过2000/s。如果走缓存,那么就是属于内存操作了,内存可支持4000/s,4万/s都没有问题。

十三、redis和memcached的区别

   1、Redis支持更多的数据类型。String、hash、list、set、zset。而memcached只支持String。

   2、Redis支持持久化。可以通过aof或者rdb进行数据恢复。memcached不支持。

   3、Redis支持原生的集群模式redis cluster

十四、为什么Redis单线程还能支持高并发

       1、非阻塞IO多路复用

       2、纯内存操作

        3、单线程避免了多线程频繁的上下文切换。

说明:上下文切换:CPU通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务。但是,在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以再次加载这个任务的状态,从任务保存到再加载的过程就是一次上下文切换

十五、内存淘汰策略

内存淘汰策略的(什么,你没配过?好好反省一下自己)

1)noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。应该没人用吧。

2)allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。推荐使用,目前项目在用这种。

3)allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。应该也没人用吧,你不删最少使用Key,去随机删。

4)volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。这种情况一般是把redis既当缓存,又做持久化存储的时候才用。不推荐

5)volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。依然不推荐

6)volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。不推荐 ps:如果没有设置 expire 的key, 不满足先决条件(prerequisites); 那么 volatile-lru, volatile-random 和 volatile-ttl 策略的行为, 和 noeviction(不删除) 基本上一致

十六、Redis 集群会有写操作丢失吗?为什么?

Redis 并不能保证数据的强一致性,这意味这在实际中集群在特定的条件下可能会丢失写操作。

十七、Redis 集群之间是如何复制的?

异步复制

十八、Redis 集群最大节点个数是多少?

16384 个

十九、一个字符串类型的值能存储最大容量是多少?

512M

二十、MySQL 里有 2000w 数据,redis 中只存 20w 的数据,如何保证 redis 中的数据都是热点数据?

redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。选择内存不够时,移除最近最少使用策略。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值