Java面试题——Redis

一、Redis简介

  简单来说Redis(Remote Dictionary Server(远程数据服务))就是一个数据库,不过与传统的数据库不同的是Redis的数据是存在内存中的,所以读写速度非常快,因此Redis被广泛应用于缓存方向。另外Redis也经常用来做分布式锁。Redis提供了多种数据类型来支持不同的业务场景。除此之外,Redis支持事务(部分支持事务,但是不保证强一致性)、持久化、LUA脚本、LRU驱动事件、多种集群方案等。

二、Redis面试题

 1、Redis的特点

  • 支持多种数据结构,如 string(字符串)、 list(双向链表)、dict(hash表)、set(集合)、zset(排序set)、hyperloglog(基数估算)
  • 支持持久化操作,可以进行aof及rdb数据持久化到磁盘,从而进行数据备份或数据恢复等操作,较好的防止数据丢失的手段。
  • 单进程请求,所有命令串行执行,并发情况下不需要考虑数据一致性问题。
  • 支持通过Replication进行数据复制,通过master-slave机制,可以实时进行数据的同步复制,支持多级复制和增量复制, master-slave机制是Redis进行HA的重要手段。(HA :redis的高可用HA)

 2、Redis持久化机制 

  Redis是一个支持持久化的内存数据库,通过持久化机制把内存中的数据同步到硬盘文件来保证数据持久化。当Redis重启后通过把硬盘文件重新加载到内存,就能达到恢复数据的目的。
  
  实现:单独创建fork()一个子进程,将当前父进程的数据库数据复制到子进程的内存中,然后由子进程写入到临时文件中,持久化的过程结束了,再用这个临时文件替换上次的快照文件,然后子进程退出,内存释放
  
  RDB:是Redis默认的持久化方式。按照一定的时间周期策略把内存的数据以快照的形式保存到硬盘的二进制文件。即Snapshot快照存储,对应产生的数据文件为dump.rdb,通过配置文件中的save参数来定义快照的周期。( 快照可以是其所表示的数据的一个副本,也可以是数据的一个复制品。)
  AOF:Redis会将每一个收到的写命令都通过Write函数追加到文件最后,类似于MySQL的binlog。当Redis重启是会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复
 

 3、热点数据和冷数据是什么? 

  热点数据,缓存才有价值
  对于冷数据而言,大部分数据可能还没有再次访问到就已经被挤出内存,不仅占用内存,而且价值不大。频繁修改的数据,看情况考虑使用缓存对于上面两个例子,寿星列表、导航信息都存在一个特点,就是信息修改频率不高,读取通常非常高的场景。
  对于热点数据,比如我们的某IM产品,生日祝福模块,当天的寿星列表,缓存以后可能读取数十万次。再举个例子,某导航产品,我们将导航信息,缓存以后可能读取数百万次。数据更新前至少读取两次,缓存才有意义。这个是最基本的策略,如果缓存还没有起作用就失效了,那就没有太大价值了。
  那存不存在,修改频率很高,但是又不得不考虑缓存的场景呢?有!比如,这个读取接口对数据库的压力很大,但是又是热点数据,这个时候就需要考虑通过缓存手段,减少数据库的压力,比如我们的某助手产品的,点赞数,收藏数,分享数等是非常典型的热点数据,但是又不断变化,此时就需要将数据同步保存到Redis缓存,减少数据库压力
  

 4、单线程的Redis为什么那么快? 

  1. 纯内存操作
  2. 单线程操作,避免了频繁的上下文切换
  3. 采用了非阻塞I/O多路复用机制  

 5、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
    sorted set多了一个权重参数score,集合中的元素能够按score进行排列。可以做排行榜应用,取TOP N操作

 6、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(不删除)基本一致

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

  1. Master最好不要做任何的持久化工作,如RDB内存快照和AOF日志文件
  2. 如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次
  3. 为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内
  4. 尽量避免在压力很大的主库上添加从库
  5. 主从复制不要用图状结构,用单向链表结构更为稳定,即Master<-Slave1<-Slave2...

 8、为什么Redis的操作是原子性的,怎么保证原子性的?

  对于Redis而言,命令的原子性指的是:一个操作的不可以再分,操作要么执行,要么不执行、

  Redis的操作之所以是原子性的,是因为Redis是单线程的

  Redis本身提供的所有API都是原子操作,Redis中的事务其实是要保证批量操作的原子性。

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

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

 9、Redis 的持久化机制是什么?各自的优缺点?

  Redis 提供两种持久化机制 RDB 和 AOF 机制:
  1、RDBRedis DataBase)持久化方式: 是指用数据集快照的方式半持久化模式)记录 redis 数据库的所有键值对,在某个时间点将数据写入一个临时文件,持久化结束后,用这个临时文件替换上次持久化的文件,达到数据恢复。
  优点:
  1、只有一个文件 dump.rdb,方便持久化。
  2、容灾性好,一个文件可以保存到安全的磁盘。
  3、性能最大化,fork 子进程来完成写操作,让主进程继续处理命令,所以是 IO最大化。使用单独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 redis的高性能) 4.相对于数据集大时,比 AOF的启动效率更高。
  缺点:
  1、数据安全性低。RDB 是间隔一段时间进行持久化,如果持久化之间 redis 发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候)
  2、AOFAppend-only fifile)持久化方式: 是指所有的命令行记录以 redis 命令请求协议的格式完全持久化存储)保存为 aof 文件。
  优点:
  1、数据安全,aof 持久化可以配置 appendfsync 属性,有 always,每进行一次命令操作就记录到 aof文件中一次。
  2、通过 append 模式写文件,即使中途服务器宕机,可以通过 redis-check-aof工具解决数据一致性问题。
  3、AOF 机制的 rewrite 模式。AOF 文件没被 rewrite 之前(文件过大时会对命令进行合并重写),可以删除其中的某些命令(比如误操作的 flflushall))
  缺点:
  1、AOF 文件比 RDB 文件大,且恢复速度慢。
  2、数据集大的时候,比 rdb 启动效率低。

 10、Jedis与Redission对比有什么特点?

  Jedis是Redis的Java实现的客户端,其API提供了比较全面的Redis支持;Redission实现了分布式和可扩展的Java数据结构,和Jedis相比,功能较为简单,不支持字符串操作、不支持排序、事务、管道、分区等Redis特性。Redission的宗旨是促进使用者对Redis的关注分离,从而让使用者能够将精力更集中的放在处理业务逻辑上。

 11、怎么测试Redis的连通性?

  使用ping命令

 12、Redis 如何做内存优化?

  尽可能使用散列表(hashes),散列表(是说散列表里面存储的数少)使用的内存非常小,所以你应该尽可能的将你的数据模型抽象到一个散列表里面。比如你的 web 系统中有一个用户对象,不要为这个用户的名称,姓氏,邮箱,密码设置单独的 key,而是应该把这个用户的所有信息存储到一张散列表里面.

 13、Redis 的内存用完了会发生什么?

  如果达到设置的上限,Redis的写命令会返回错误信息(但是读命令还可以正常返回)或者你可以将Redis当缓存来使用配置淘汰机制,当Redis达到内存上限时会冲刷掉旧的内容

 14、Redis最适合的场景?

  • 会话缓存(Session Cache)
    最常用的一种使用 Redis 的情景是会话缓存(session cache)。用 Redis 缓存会话比其他存储(如
    Memcached)的优势在于:Redis 提供持久化。当维护一个不是严格要求一致性的缓存时,如果用户
    的购物车信息全部丢失,大部分人都会不高兴的,现在,他们还会这样吗? 幸运的是,随着 Redis 这些
    年的改进,很容
    易找到怎么恰当的使用 Redis 来缓存会话的文档。甚至广为人知的商业平台Magento 也提供 Redis 的
    插件。
  • 全页缓存(FPC)
    除基本的会话 token 之外,Redis 还提供很简便的 FPC 平台。回到一致性问题,即使重启了 Redis 实
    例,因为有磁盘的持久化,用户也不会看到页面加载速度的下降,这是一个极大改进,类似 PHP 本地
    FPC。 再次以 agento 为例,Magento提供一个插件来使用 Redis 作为全页缓存后端。 此外,对
    WordPress 的用户来说,Pantheon 有一个非常好的插件 wp-redis,这个插件能帮助你以最快速度加
    载你曾浏览过的页面。
  • 队列
    Reids 在内存存储引擎领域的一大优点是提供 list 和 set 操作,这使得 Redis能作为一个很好的消息队
    列平台来使用。Redis 作为队列使用的操作,就类似于本地程序语言(如 Python)对 list 的 push/pop
    操作。 如果你快速的在 Google中搜索“Redis queues”,你马上就能找到大量的开源项目,这些项目的
    目的就是利用 Redis 创建非常好的后端工具,以满足各种队列需求。例如,Celery 有一个后台就是使用
    Redis 作为 broker,你可以从这里去查看。
  • 排行榜/计数器
    Redis 在内存中对数字进行递增或递减的操作实现的非常好。集合(Set)和有序集合(Sorted Set)也
    使得我们在执行这些操作的时候变的非常简单,Redis 只是正好提供了这两种数据结构。所以,我们要
    从排序集合中获取到排名最靠前的 10个用户–我们称之为“user_scores”,我们只需要像下面一样执行即
    可: 当然,这是假定你是根据你用户的分数做递增的排序。如果你想返回用户及用户的分数,你需要这
    样执行: ZRANGE user_scores 0 10 WITHSCORES Agora Games 就是一个很好的例子,用 Ruby 实现
    的,它的排行榜就是使用 Redis 来存储数据的,
    你可以在这里看到。
  • 发布/订阅

    最后(但肯定不是最不重要的)是 Redis 的发布/订阅功能。发布/订阅的使用场景确实非常多。我已看

    见人们在社交网络连接中使用,还可作为基于发布/订阅的脚本触发器,甚至用 Redis 的发布/订阅功能

    来建立聊天系统!

15、

 

 
 
 
  

 

 

    

   

 

    

    

posted @ 2021-07-19 11:51  潜跃  阅读(255)  评论(0编辑  收藏  举报