【Java校招面试】基础知识(九)——Redis


前言

本篇主要介绍Redis数据库的相关内容。

“基础知识”是本专栏的第一个部分,本篇博文是第九篇博文,如有需要,可:

  1. 点击这里,返回本专栏的索引文章
  2. 点击这里,返回上一篇《【Java校招面试】基础知识(八)——Linux服务器》

一、基础概念

01. 缓存中间件Memcache和Redis的区别
1) Memcache支持简单数据类型,Redis支持的数据类型更加丰富;
2) Memcache不支持数据持久化,Redis支持;
3) Memcache不支持主从同步,Redis支持;
4) Memcache不支持分片,Redis支持。

02. 为什么Redis能这么快?
1) 完全基于内存,绝大部分请求是纯粹的内存操作,执行效率高;
2) 数据结构简单,对数据的操作也简单;
3) 采用单线程,单线程也能处理高并发请求,需要多核时也可以开启多实例;
4) 使用多路I/O复用模型,非阻塞I/O。

03. I/O多路复用模型
1) File Descriptor: 文件描述符,一个打开的文件通过唯一的描述符进行引用,该描述符是打开文件的元数据到文件本身的映射;

2) 传统的阻塞I/O模型
在这里插入图片描述

3) Select 系统调用
在这里插入图片描述
Linux下的Selector用于同时监控多个文件描述符的可读可写状态,当某些文件可读或者可写时,Select方法就会返回可读以及可写的文件描述符个数。监听的任务交给Selector之后,程序就可以做别的事情,不用被阻塞。

4) Redis采用的I/O多路复用函数
i) Redis因地制宜,优先选择时间复杂度为O(1)的I/O多路复用函数作为底层实现,屏蔽底层的差异。
   evport——Solaris
   epoll——Linux
   kqueue——MacOS或FreeBSD
ii) 以时间复杂度为O(n)的select函数作为以上环境以外的环境中的保底函数;
iii) 基于react设计模式监听I/O事件

04. Redis供用户使用的数据类型
1) String: 最基本的数据类型,二进制安全;
2) Hash: String元素组成的字典,适合用于存储对象;
3) List: 列表,按照String元素插入顺序排序;
4) Set: String元素组成的无序集合,通过哈希表实现,不允许重复;
5) Sorted Set: 通过分数来为集合中的成员进行从小到大的排序;
6) 用于计数的HyperLogLog,用于支持存储地理位置信息的Geo等。

05. 从海量的Key里查询某一固定前缀的Key
1) KEYS pattern: 一次性返回pattern匹配到的所有key;
缺点: key的数量过大时,会使服务出现卡顿

2) SCAN cursor [MATCH pattern] [COUNT count]
  i) 基于游标的迭代器,需要基于上一次的游标延续之前的迭代过程;
  ii) 以0作为游标开始一次新的迭代,直到命令返回游标0完成本次遍历;
  iii) 不保证每次执行都返回某个给定数量的元素,支持模糊查询;
  iv) 一次返回的数量不可控,只能大概率符合count参数。

06. 如何通过Redis实现分布式锁?
1) 分布式锁需要解决的问题
  i) 互斥性: 任意时间只能有一个客户端持有锁;
  ii) 安全性: 锁只能被持有该锁的客户端删除,不能由其他客户端删除;
  iii) 死锁: 持有锁的客户端因为某些原因宕机,未能释放锁,造成其他客户端无 法获得锁;
  iv) 容错: 当部分节点宕机时,客户端仍然能获取锁和释放锁。

2) SETNX key value: 如果key不存在,创建并赋值
  i) 时间复杂度为O(1);
  ii) 返回值: 设置成功为1,失败为0。

3) 如何解决SETNX长期有效的问题?
EXPIRE key seconds: 设置key的存活时间,当key过期时,会被自动删除。

4) 一个实现分布式锁的例子

    RedisService redisService = ctx.getBean(RedisService.class);
    Long status = redisService.setnx(key,1);
    if (status == 1){
       redisService.expire(key, expire);
       //执行独占资源的业务逻辑
       doOcuppiedBusiness();
    }

缺点: 原子性得不到满足,若第二句执行完宕机,仍然不能达到目标

5) 改进的分布式锁方案
SET key value [EX seconds] [PX milliseconds] [NX|XX]
  i) EX seconds: 设置键的过期时间为seconds秒;
  ii) PX milliseconds: 设置键的过期时间为milliseconds毫秒;
  iii) NX: 只在键不存在时,才对键进行设置操作;
  iv) XX: 只在键存在时,才对键进行设置操作。
  v) SET成功时返回OK,否则返回nil。

    RedisService redisService = ctx.getBean(RedisService.class);
    String result = redisService.set(key,1, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expire);
    if ("OK".equals(result)){
       //执行独占资源的业务逻辑
       doOcuppiedBusiness();
    }

07. 有大量的key同时过期时需要注意什么?
1) 问题: 由于集中过期,清除大量的key比较耗时,会出现卡顿现象。
2) 解决方案: 在设置key的过期时间时,加上一定的随机值,是过期时间分散开来。

08. 如何使用Redis做异步队列?
使用List作为队列,RPUSH生产消息,LPOP消费消息

缺点: LPOP不会等待队列里有值才消费

改进措施:
1) 在业务逻辑中引入Sleep机制调用LPOP重试;

2) BLPOP key timeout: 阻塞直到队列有消息或者超时。
缺点: 只能供一个消费者消费

3) pub/sub: 主题/订阅者模式
  i) 发送者(pub)发送消息,订阅者(sub)接收消息;
  ii) 订阅者可以订阅任意数量的频道;
在这里插入图片描述
  iii) 消息的发布是无状态的,无法保证可达。


二、Redis持久化

01. Redis如何做持久化?
1) RDB(快照)持久化:
保存某个时间点的全量数据快照;
  i) SAVE: 阻塞Redis进程,直到RDB文件被创建完成;
  ii) BGSAVE: Fork出一个子进程来创建,不阻塞Redis进程。
RDB文件会在Redis启动时自动被载入。

缺点:
  i) 内存数据的全量同步,数据量大会由于I/O而严重影响性能;
  ii) 可能会因为Redis崩溃而丢失从当前至最近一次快照期间的数据。

操作方法:
在redis.conf文件中配置,参数:
  i) save interval count,表示interval秒内有count次写入操作就保存快照
  例如:
  save 900 1
  save 300 10
  save 60 10000
配置多条是因为不同时段负载不同,为了性能和数据安全的需要

  ii) stop-writes-on-bgsave-error yes/no,表示在备份线程出错时是否继续写 入数据。为了保证持久化的数据的一致性.
  iii) rdbcompression yes/no,表示是否压缩后再保存RDB文件。最好设置为 no,因为Redis本来就是CPU密集型的数据库,再进行压缩会增加CPU负担。

2) AOF(Append-Only-File)持久化:
保存写状态
  i) 记录除了查询以外的所有变更数据库状态的指令;
  ii) 以append的形式追加保存到AOF文件中(增量)。

操作方法:
在redis.conf文件中配置(默认关闭),参数:
  i) appendonly yes/no,表示是否开始AOF持久化;
  ii) appendfilename “filename.aof”,指定AOF文件名;
  iii) appendfsync always/everysec/no,表示同步的机制,每次写入就同步/每 秒同步一次/决策交由系统决定,最好设置为everysec,速度快,安全性也好。

02. 自动化触发RDB持久化的方式
1) 根据redis.conf配置里的SAVE m n定时触发(用的是BGSAVE);
2) 主从复制时,主节点自动触发;
3) 执行Debug Reload;
4) 执行Shutdown且没有开启AOF持久化。

03. BGSAVE的原理
在这里插入图片描述
Linux下Fork进程时用到的Copy-On-Write机制

如果有多个调用者同时要求相同的资源(如内存或磁盘上的数据),他们会共同 获取相同的指针指向相同的资源,直到某个调用者试图修改资源的内容时,系统才会 真正复制一份专用的副本给该调用者,而其他调用者所见到的最初的资源仍然保持不 变。

04. 日志重写解决AOF文件不断增大的问题
1) 调用fork(),创建一个子进程;
2) 子进程把当前数据库的键值写入一个临时文件里,不依赖原来的AOF文件;
3) 主进程持续将新的变动同时写到内存和原来的AOF里;
4) 主进程获取子进程重写AOF完成的信号,往新AOF同步增量动态;
5) 使用新的AOF文件替换掉旧的AOF文件。

05. RDB和AOF共存情况下的回复流程
Redis在启动时,先判断判断AOF文件是否存在,如果存在就直接用AOF文件恢复数据库,否则判断RDB文件是否存在,如果存在则使用RDB文件恢复数据库,否则启动失败。

06. RDB和AOF的优点及缺点
1) RDB
优点: 全量数据快照,文件小,恢复快;
缺点: 无法保存最近一次快照之后的数据。

2) AOF
优点: 可读性强,适合保存增量数据,数据不易丢失;
缺点: 文件体积大,恢复时间长。

07. RDB-AOF混合持久化方式
BGSAVE做镜像全量持久化,AOF做增量持久化。

08. 使用Pipeline的好处
1) Pipeline和Linux的管道类似;

2) Redis基于请求/响应模型,单个请求处理需要一一应答, Pipeline批量执行命令,节省多次I/O往返的时间;

3) 有顺序以来的指令应该分批发送。


三、Redis分布式存储

01. Redis的同步机制
1) 主从同步原理
在这里插入图片描述

2) 全量同步
  i) Slave发送sync命令道Master;
  ii) Master启动一个后台进程,将Redis中的数据快照保存到文件中;
  iii) Master将保存数据快照期间接收到的写命令混存起来;
  iv) Master完成写文件操作后,将文件发送给Slave;
  v) Slave使用新的AOF文件替换掉旧的AOF文件;
  vi) Master将这期间收集的增量写命令发送给Slave;

3) 增量同步
  i) Master接收到用户的操作指令,判断是否需要传播到Slave;
  ii) 将操作记录追加到AOF文件;
  iii) 将操作传播到其他Slave:①对齐主从库;②往响应Slave缓存写入数据;
  iv) 将缓存中的数据发送给Slave。

4) 主从模式的弊端
不具备高可用性,当Master崩溃后,Redis将不能对外提供写入操作。

02. Redis Sentinel
哨兵: 解决主从同步中Master宕机后的主从切换问题

1) 监控: 检查主从服务器是否运行正常;
2) 提醒: 通过API向管理员或者其他应用程序发送故障通知;
3) 自动故障迁移: 主从切换。

03. 流言协议Gossip
1) 每个节点都随机地与对方通信,最终所有节点的状态达成一致;
2) 种子节点定期随机向其他节点发送节点列表以及需要传播的消息;
3) 不保证信息一定会传递给所有节点,但最终会趋于一致。

04. 如何从海量的数据里快速找到所需?
1) 分片: 按照某种规则划分数据,分散存储在多个节点上。例如哈希算法,缺点是常规的按照哈希划分无法实现节点的动态增减;

2) 一致性哈希算法
2 32 2^{32} 232取模,将哈希值空间组织成一个圆环
在这里插入图片描述

3) 若Node C宕机(删减节点)
在这里插入图片描述

节点A、B将不受影响,原先节点C的数据将会被移动到D

4) 若新增Node X(增加节点)
在这里插入图片描述

节点A、B、D不受影响,C的部分数据将会被移动到X

5) 哈希环的数据倾斜问题
在这里插入图片描述

节点较少时,容易造成数据倾斜,多数数据存在某一台服务器上

6) 虚拟节点——解决数据倾斜问题
在这里插入图片描述

为每个节点计算多个哈希值,让所有的虚拟节点均匀分布在哈希环上,数据定位时只需要多加一步虚拟节点到实际节点的映射


后记

Redis相关的知识还有快表跳跃表等,在后续的实战面经我们会涉及到,这里常考的知识点可以用作速查和补缺。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IMplementist

你的鼓励,是我继续写文章的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值