Redis面试必问
# 1.Redis和Memcache有什么区别?如何选用?
Redis:
- 单线程模式处理请求(非阻塞异步处理机制,内存IO),避免上下文切换
- 支持多种数据格式,String,hash,list,set,sorted Set
- 主从同步机制,持久化
Memcache:
- 多线程异步IO
- 内存存储数据,支持key-value数据结构,不支持持久化,主从同步
- 可设置失效期,延迟生效
- 当容量满会对缓存中的数据进行剔除,对过期的Key进行清理,还会 通过Lru策略对数据进行剔除
- Key不能超过250个字节,value不能超过1m字节,最大生效时间30天
# 2.Redis数据结构
- String:SDS实现(类似java的ArrayList)
- List:ziplist压缩列表和linkedlist双向链表,3.2版本增加qquicklist,一个双向无环链表
- hash:ziplist和hashtable两种实现,hash表中所有的key和value长度都小于64字节,并且键值数量小于512采用压缩表来节省空间,超过采用hashtable
- Set:内部实现可以是inset或者hashtable
- sorted set:实现可以是ziplist或者是skiplist,元素数量小于128并且所有元素长度小于64使用ziplist,否则转为skiplist
# 3.Redis持久化方式
- RDB:将Redis在内存数据库的记录定时dump到磁盘上
- AOF:将Redis的操作日志以追加的方式写入到文件
两种方式的区别
RDB: AOF:
# 4.Redis的过期机制,Redis有哪些淘汰策略
redis的过期策略:每隔100ms随机抽一些key检查,定期删除+惰性删除
# 定时删除
含义: 在设置key的过期时间的同时,为该key创建一个定时器,让定时器在key的过期时间来临时,对key进行删除 优点:保证内存被尽快释放
缺点: 若过期key很多,删除这些key会占用很多的CPU时间,在CPU时间紧张的情况下,CPU不能把所有的时间用来做要紧的事儿,还需要去花时间删除这些key 定时器的创建耗时,若为每一个设置过期时间的key创建一个定时器(将会有大量的定时器产生),性能影响严重 没人用
# 惰性删除
含义: key过期的时候不删除,每次从数据库获取key的时候去检查是否过期,若过期,则删除,返回null。
优点: 删除操作只发生在从数据库取出key的时候发生,而且只删除当前key,所以对CPU时间的占用是比较少的,而且此时的删除是已经到了非做不可的地步(如果此时还不删除的话,我们就会获取到了已经过期的key了)
缺点: 若大量的key在超出超时时间后,很久一段时间内,都没有被获取过,那么可能发生内存泄露(无用的垃圾占用了大量的内存)
# 定期删除
含义: 每隔一段时间执行一次删除过期key操作
优点: 通过限制删除操作的时长和频率,来减少删除操作对CPU时间的占用--处理"定时删除"的缺点 定期删除过期key--处理"惰性删除"的缺点
缺点: 在内存友好方面,不如"定时删除" 在CPU时间友好方面,不如"惰性删除"
难点 合理设置删除操作的执行时长(每次删除执行多长时间)和执行频率(每隔多长时间做一次删除)(这个要根据服务器运行情况来定了)
# 大量过期的数据未删除,则采用内存淘汰机制
内存淘汰机制:
- no-eviction: 当内存不足时,新写入数据报错
- allkeys-lru: 当内存不足容纳新数据时,移除最近做少使用的key(更多选用)
- allkeys-random:当内存不足容纳新数据时,随机移除某一个key
- volatile-lru:当内存不足容纳新数据时,设置了过期时间的key中,移除最近最少使用的key
- volatile-random:当内存不足容纳新数据时,设置了过期时间的key中,随机移除某个key
- volatile-ttl:当内存不足容纳新数据时,设置了过期时间的key中,有更早过期key优先移除
# ※ 什么时候会执行内存淘汰策略,内存占用率过高的标准是什么?
redis.conf配置文件中的 maxmemory 属性限定了 Redis 最大内存使用量,当占用内存大于maxmemory的配置值时会执行内存淘汰策略。
# ※ 内存淘汰策略的配置
内存淘汰机制由redis.conf配置文件中的maxmemory-policy属性设置,没有配置时默认为no-eviction模式。
# ※ 淘汰策略的执行过程
- 1.客户端执行一条命令,导致Redis需要增加数据(比如set key value);
- 2.Redis会检查内存使用情况,如果内存使用超过 maxmemory,就会按照配置的置换策略maxmemory-policy删除一些key;
- 3.再执行新的数据的set操作;
# 5淘汰策略算法
- FIFO:先进先出
- LRU:最近最少使用,关注数据最近访问的时间
- LFU:最近最少使用,关注数据最近访问的次数
# 6如何保证Redis的高并发和高可用
主从架构,一主多从,一般来说,很多项目其实就足够了,单主用来写入数据,单机几万QPS,多从用来查询数据,多个从实例可以提供每秒10万的QPS。
# 7 如何使用Redis实现延迟队列?如何使用Redis实现分布式锁?
# 业务场景:
- 订单一直处于未支付状态,如何及时关闭订单
- 如何定期检查退款状态,订单是否已经退款成功
# Redis实现延迟队列:
使用zset,list实现
详情参考:
# 分布式锁
- 加锁:通过Redis的setnx或者Hsetnx命令加锁,设置过期时间,存储线程id和重入计数
- 解锁:判断重入计数大于1则-1,等于1则删除key释放锁
- timout:超时自动释放,防止死锁
详情参考:
# 8什么是缓存雪崩,缓存穿透,缓存击穿,如何解决?
# 缓存穿透
高并发下查询key不存在数据,导致直接查数据库
- 针对查询结果为空的也进行缓存,造成太多空值,占用空间
- 布隆过滤器,先去布隆过滤器查询key是否存在,不存在直接返回
# 缓存击穿
设置了过期时间的key,某个时间点过期,大量线程访问该数据
- 使用分布式锁控制访问线程
- 不设置超时时间,造成写一致性问题,可使用延迟双删
# 缓存雪崩
重启或大量的key失效
- key的失效期分散开,不同的key设置不同的有效期
- 二级缓存,本地和redis
- 高可用,允许脏读,失效仍能查到数据但不是最新的
# 9什么是大key,热key,会造成什么问题?
# 大key:存储的值非常大
问题: 占用很多内存,主动删除或过期删除时阻塞时间过长
优化:
- String减少长度,List,Set减少成员数量
- 针对String,大key存储于其他数据库,Mondb或者缓存到CDN
- hash,set,对元素进行拆分存储
- 删除不使用delete(阻塞),使用lazydelte(非阻塞)
# 热key:热点数据
问题: 缓存击穿
优化:
- 热点数据直接加载到本地缓存,不要求强一致性
- Redis主节点备份热key数据
- 对热Key限流熔断,每秒最多请求缓存集群不超过400次
# 10 缓存和数据库中的数据不一致造成什么问题,如何解决?数据并发竞争,会造成什么问题?
最终一致性: 延时双删,更新数据库时删除缓存,读取时在设置缓存,2s后在删除一次。
数据并发竞争: 分布式锁+时间戳
# 11单线程的Redis为什么这么快?
内存,集群,最大内存+淘汰策略,无锁,无线程调度切换,多路复用