美文网首页
redis面试题

redis面试题

作者: 快去学习不然怎么去看aimer | 来源:发表于2019-12-23 21:19 被阅读0次

    redis是C语言编写的,key-value数据库,包含的数据类型:自符串、列表、集合、有序集合、hash。
    数据是在内存中操作的,定期flush到硬盘上保存,因此,他是现在以知的最快的key-value DB。单个key的数据最大为1G。memcached最大为1M.

    redis与memcached的区别:

    • Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储。memcache支持简单的数据类型,String。
    • redis支持主从复制,master-salve的模式
    • reids支持数据持久化,memcached只能将数据存在内存中
    • redis的速度比memcached块
    • Memcached是多线程,非阻塞IO复用的网络模型;Redis使用单线程的IO复用模型

    使用缓存的优点:

    • 提高性能。查询速度比查询mysql速度块
    • 提高并发能力。缓存缓解了部分请求

    redis的优点

    • 速度块
    • 支持丰富的数据类型
    • 支持事务
    • 可用于缓存,可以设置消息过期时间

    redis命中率

    命中:可以通过缓存取的所需的数据
    为名中:无法通过缓存取的所需的数据,需要再次查询数据库或执行其他操作

    info 
    keyspace_hits:0                #命中
    keyspace_misses:0          #未命中
    

    命中率:keyspace_hits/(keyspace_misses+keyspace_hits)
    一般情况下,一个缓存失效机制,和过期时间设计良好的系统命中率可以达到95%

    最大空间:缓存中可以存放的最大元素的数量,一旦缓存中元素数量超过这个值就会触发缓存启动清空策率

    清空策率:

    • FIFO(先进先出)最先进入缓存的数据在缓存空间不够的情况下(超出最大元素限制)会被优先被清除掉,以腾出新的空间接受新的数据。策略算法主要比较缓存元素的创建时间。在数据实效性要求场景下可选择该类策略,优先保障最新数据可用。
    • LFU(最少使用政策):无论是否过期,根据元素的被使用次数判断,清除使用次数较少的元素释放空间
    • LRU(最近最少使用)根据元素最后一次被使用的时间戳,清除最远使用时间戳的元素释放空间

    redis缓存失效移除政策
    redis-key的三种过期政策:

    • 惰性删除:当读/写一个已经过期的key时,会触发惰性删除策略,直接删除掉这个过期key,这是被动的!
    • 定期删除:由于惰性删除策略无法保证冷数据被及时删掉,所以 redis 会定期主动淘汰一批已过期的key。
    • 主动删除:当前已用内存超过maxmemory限定时,触发主动清理策略,该策略由启动参数的配置决定,可配置数据淘汰策略

    影响缓存命中率的几个因素:
    1、业务场景和业务需求:缓存适合“读多写少”的业务场景,反之,使用缓存的意义其实并不大,命中率会很低。互联网应用的大多数业务场景下都是很适合使用缓存的。

    2、缓存的设计(粒度和策略):通常情况下,缓存的粒度越小,命中率会越高。
    例如:当缓存单个对象的时候(例如:单个用户信息),只有当该对象对应的数据发生变化时,我们才需要更新缓存或者让移除缓存。而当缓存一个集合的时候(例如:所有用户数据),其中任何一个对象对应的数据发生变化时,都需要更新或移除缓存。
    此外,缓存的更新/过期策略也直接影响到缓存的命中率。当数据发生变化时,直接更新缓存的值会比移除缓存(或者让缓存过期)的命中率更高,当然,系统复杂度也会更高。

    3、缓存容量和基础设施
    缓存的容量有限,则容易引起缓存失效和被淘汰(目前多数的缓存框架或中间件都采用了LRU算法)。同时,缓存的技术选型也是至关重要的,比如采用应用内置的本地缓存就比较容易出现单机瓶颈,而采用分布式缓存则毕竟容易扩展。所以需要做好系统容量规划,并考虑是否可扩展。通常,两种也可以结合使用,不过实现复杂度也会相应提升,具体看并发量。

    4、其他因素
    当缓存节点发生故障时,需要避免缓存失效并最大程度降低影响,这种特殊情况也是架构师需要考虑的。业内比较典型的做法就是通过一致性Hash算法,或者通过节点冗余的方式。

    提高缓存命中率的方法:
    通常来讲,在相同缓存时间和key的情况下,并发越高,缓存的收益会越高,即便缓存时间很短。
    需要在业务需求,缓存粒度,缓存策略,技术选型等各个方面去通盘考虑并做权衡。尽可能的聚焦在高频访问且时效性要求不高的热点业务上,通过缓存预加载(预热)、增加存储容量、调整缓存粒度、更新缓存等手段来提高命中率。

    对于时效性很高(或缓存空间有限),内容跨度很大(或访问很随机),并且访问量不高的应用来说缓存命中率可能长期很低,可能预热后的缓存还没来得被访问就已经过期了。

    redis缓存雪崩

    • 由于redis的宕机,导致全部请求都请求数据库

    解决方法:
    1.使用redis集群或使用redis的哨兵模式(宕机前)
    2.使用本地缓存(ehcache)+限流(hystrix),尽量避免我们的数据库被干掉(宕机时)
    3.使用数据持久化(RDB<快照>或AOF<保存命令>)

    • redis设置的缓存清除策略导致,设置了相同的清空时间,导致缓存失效

    解决方法:在缓存的时候给过期时间加上一个随机值,尽量避免在同一时间缓存失效

    redis缓存穿透

    由于每次请求一个一定不存在的key,导致redis不命中,因此每次请求都去数据库查询,增加数据库的压力,严重会导致数据库的崩溃

    解决方法:
    1.使用布隆过滤器,将不合法的请求过滤掉,使之无法到达数据库层
    2.将这个空值设置到缓存中,当遇到这个值时就返回设定值,当然,应该给该值设定一个短的过期时间

    MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据?
    当缓存达到大小达到一定值时,就会使用淘汰策略

    Redis支持的Java客户端都有哪些?官方推荐用哪个?
    redisson、jedis 官方推荐redisson

    redis与redisson的关系
    Redisson是一个高级的分布式协调Redis客服端

    jedis与redisson的区别
    jedis是java实现的redis客户端,功能齐全。而redisson的宗旨是使用者对redis的分离,使其更专著于业务代码

    redis的主从复制是异步复制

    redis事务
    redis事务是连续的指令集,当执行事务时,redis不会停止,直到事务执行完成
    事务的原子性:要么事务全部执行、要么事务全不执行
    若要进行复杂的操作,可以使用lua脚本

    查看redis的信息命令:info

    redis是单线程的,如何高效的使用CPU
    在一台机器上启动多个redis实例或者使用数据分片

    redis常见的性能问题及解决方法
    1.master最好不要作数据持久化
    2.在一个slave节点进行数据持久化,使用AOF,每秒一次

    1. 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3...
      这样遇到单点问题只是slave1变为master,其他不变

    redis集群:

    • cluster
    • codis


      image.png

      Codis 是一个代理中间件,用的是 GO 语言开发的,因为Codis是一个无状态的,所以可以增加多个Codis来提升QPS,同时也可以起着容灾的作用

    RDB的缺点:

    • 不能保持高的数据可用性,若在持久化之前数据丢失,就无法备份
    • 使用fork子进程完成的,在集群规模较大时,容易影响服务器性能

    AOF的缺点:

    • 恢复速度比RDB慢
    • AOF运行效率低于RDB

    Redis性能调优

    简单的redis优化

    • 尽量使用短的key,因为在redis中,key也是会占先当大的存储空间的
    • 不要使用keys * ,这条命令是阻塞的。当执行这条命令时,该实例不会再处理其他请求。使用SCAN代替(SCAN,用于迭代元素,每次返回少量的元素)
    • 设置key的有效时间
    • 选择合适的回收策略

    尽管Redis是一个非常快速的内存数据存储媒介,也并不代表Redis不会产生性能问题。
    前文中提到过,Redis采用单线程模型,所有的命令都是由一个线程串行执行的,所以当某个命令执行耗时较长时,会拖慢其后的所有命令,这使得Redis对每个任务的执行效率更加敏感。

    针对Redis的性能优化,主要从下面几个层面入手:

    • 最初的也是最重要的,确保没有让Redis执行耗时长的命令
    • 使用pipelining将连续执行的命令组合执行
    • 操作系统的Transparent huge pages功能必须关闭:
    echo never > /sys/kernel/mm/transparent_hugepage/enabled
    
    
    • 如果在虚拟机中运行Redis,可能天然就有虚拟机环境带来的固有延迟。可以通过./redis-cli --intrinsic-latency 100命令查看固有延迟。同时如果对Redis的性能有较高要求的话,应尽可能在物理机上直接部署Redis。
    • 检查数据持久化策略
    • 考虑引入读写分离机制

    长耗时命令

    Redis绝大多数读写命令的时间复杂度都在O(1)到O(N)之间,在文本和官方文档中均对每个命令的时间复杂度有说明。

    通常来说,O(1)的命令是安全的,O(N)命令在使用时需要注意,如果N的数量级不可预知,则应避免使用。例如对一个field数未知的Hash数据执行HGETALL/HKEYS/HVALS命令,通常来说这些命令执行的很快,但如果这个Hash中的field数量极多,耗时就会成倍增长。
    又如使用SUNION对两个Set执行Union操作,或使用SORT对List/Set执行排序操作等时,都应该严加注意。

    避免在使用这些O(N)命令时发生问题主要有几个办法:

    • 不要把List当做列表使用,仅当做队列来使用
    • 通过机制严格控制Hash、Set、Sorted Set的大小
    • 可能的话,将排序、并集、交集等操作放在客户端执行
    • 绝对禁止使用KEYS命令
    • 避免一次性遍历集合类型的所有成员,而应使用SCAN类的命令进行分批的,游标式的遍历

    Redis提供了SCAN命令,可以对Redis中存储的所有key进行游标式的遍历,避免使用KEYS命令带来的性能问题。同时还有SSCAN/HSCAN/ZSCAN等命令,分别用于对Set/Hash/Sorted Set中的元素进行游标式遍历。SCAN类命令的使用请参考官方文档:https://redis.io/commands/scan

    Redis提供了Slow Log功能,可以自动记录耗时较长的命令。相关的配置参数有两个:

    slowlog-log-slower-than xxxms  #执行时间慢于xxx毫秒的命令计入Slow Log
    slowlog-max-len xxx  #Slow Log的长度,即最大纪录多少条Slow Log
    
    

    使用SLOWLOG GET [number]命令,可以输出最近进入Slow Log的number条命令。
    使用SLOWLOG RESET命令,可以重置Slow Log

    网络引发的延迟

    • 尽可能使用长连接或连接池,避免频繁创建销毁连接
    • 客户端进行的批量数据操作,应使用Pipeline特性在一次交互中完成。具体请参照本文的Pipelining章节

    数据持久化引发的延迟

    Redis的数据持久化工作本身就会带来延迟,需要根据数据的安全级别和性能要求制定合理的持久化策略:

    • AOF + fsync always的设置虽然能够绝对确保数据安全,但每个操作都会触发一次fsync,会对Redis的性能有比较明显的影响
    • AOF + fsync every second是比较好的折中方案,每秒fsync一次
    • AOF + fsync never会提供AOF持久化方案下的最优性能
    • 使用RDB持久化通常会提供比使用AOF更高的性能,但需要注意RDB的策略配置
    • 每一次RDB快照和AOF Rewrite都需要Redis主进程进行fork操作。fork操作本身可能会产生较高的耗时,与CPU和Redis占用的内存大小有关。根据具体的情况合理配置RDB快照和AOF Rewrite时机,避免过于频繁的fork带来的延迟

    Redis在fork子进程时需要将内存分页表拷贝至子进程,以占用了24GB内存的Redis实例为例,共需要拷贝24GB / 4kB * 8 = 48MB的数据。在使用单Xeon 2.27Ghz的物理机上,这一fork操作耗时216ms。

    可以通过INFO命令返回的latest_fork_usec字段查看上一次fork操作的耗时(微秒)

    Swap引发的延迟

    当Linux将Redis所用的内存分页移至swap空间时,将会阻塞Redis进程,导致Redis出现不正常的延迟。Swap通常在物理内存不足或一些进程在进行大量I/O操作时发生,应尽可能避免上述两种情况的出现。

    /proc/<pid>/smaps文件中会保存进程的swap记录,通过查看这个文件,能够判断Redis的延迟是否由Swap产生。如果这个文件中记录了较大的Swap size,则说明延迟很有可能是Swap造成的。

    数据淘汰引发的延迟

    当同一秒内有大量key过期时,也会引发Redis的延迟。在使用时应尽量将key的失效时间错开。

    引入读写分离机制

    Redis的主从复制能力可以实现一主多从的多节点架构,在这一架构下,主节点接收所有写请求,并将数据同步给多个从节点。
    在这一基础上,我们可以让从节点提供对实时性要求不高的读请求服务,以减小主节点的压力。
    尤其是针对一些使用了长耗时命令的统计类任务,完全可以指定在一个或多个从节点上执行,避免这些长耗时命令影响其他请求的响应。

    相关文章

      网友评论

          本文标题:redis面试题

          本文链接:https://www.haomeiwen.com/subject/agqznctx.html