面向面试知识-Redis

面向面试知识-Redis

什么是Redis

运行于内存的基于key-value的非关系型数据库。

一款开源的内存数据结构存储,用作数据库、缓存、消息代理等。(可以基于Redis实现分布式锁、以及消息队列)
发布订阅??

对数据类型的操作都是原子性的,因为执行命令由单线程负责,不存在并发竞争的问题。
除此之外,Redis还支持:

Redis与Memcached的区别?

Redis和Memcached的共同点:

  1. 都是基于内存的数据库,性能都很高,一般可以用做缓存;
  2. 都有过期策略。

Redis和Memcached的区别:

  • Redis支持的数据类型更加丰富(String、Hash、Set、List、Zset);而Memcached只支持最简单的key-value数据类型;
  • Redis支持数据的持久化,如何持久化??,可以将内存中的数据保持在磁盘中,重启时可以再次加载使用;而Memcached没有持久化功能;
  • Redis原生支持集群;
  • Redis支持订阅模型、Lua脚本、事务等功能,而Memcached不支持。

为什么使用Redis作为MySQL的缓存

主要是因为高性能和高并发

  1. Redis具备高性能
    运行在内存的key-value缓存,读取速度快。
    在这里插入图片描述
    但是需要一定的方法解决Redis和MySQL数据库数据一致性的问题。
  2. Redis具备高并发
    单台设备的Redis的QPS(Query Per Second),是MySQL的10倍,Redis单机的QPS能轻松破10w,而MySQL单机的QPS很难破1W。
    因此可以考虑把数据库的部分数据转移到缓存中,这样会直接到缓存而不用经过数据库。

Redis的数据类型

类型类型:存储的值:结构的读写能力;

String字符串;缓存对象、常规计数、分布式锁、共享session信息等;
Hash哈希;缓存对象、购物车等??
List列表;消息队列(有两个问题①生产者需要自行实现全局唯一;②不能以消费组形式消费数据等;);
Set集合;聚合计算(并集、交集、差集)场景,比如点赞、共同关注、 抽奖活动,为嘛?
Zset有序集合;排序场景,比如排行榜、电话和姓名排序等。

Redis常见数据类型:
在这里插入图片描述
数据类型:存储的值:结构的读写能力
在这里插入图片描述
Redis各数据类型的数据结构实现:
在这里插入图片描述

  1. String类型内部实现:
    主要是SDS,Simple Dynamic String,简单动态字符串。
  • SDS不仅可以保存文本数据,还可以保存二进制数据。
  • SDS获取字符串长度的时间复杂度为O(1)。
  • Redis的SDS API是安全的,拼接字符串不会造成缓冲区溢出。
    主要保存对象,以key-value的方式存储;(分布式锁、常规计数、共享session等)
  1. List类型内部实现:
    LIst保存有序的对象,(如,消息队列,但是需要生产者实现全局唯一的id);以key-filed-value的形式存储;
    底层数据结构是由双向链表或压缩列表实现的:
  • if 列表元素个数小于512,每个item的值都小于64字节,Redis会使用List类型的底层;
  • else 使用双向链表;
    在Redis3.2之后,List数据类型的底层DS只由quickList实现。
  1. Hash类型内部实现:
    Hash类型保存缓存对象、购物车等??
    压缩列表或者哈希表
  • if 列表元素个数小于512,且所有值小于64字节,用压缩列表;
  • else 使用Hash表;
    Redis 7.0之后只由quickList实现
  1. Set类型内部实现:
    聚合计算,并集、交集、差集场景
    主要保存无重复的对象;
    Hash表或者整数集合实现
  • if 集合中的元素都是整数,且元素个数小于512,使用整数集合;
  • else 使用哈希表;
  1. Zset类型内部实现
    有序的对象,比如排行榜、电话或者姓名排序之类的;
    压缩列表或者跳表:
  • if 有序集合的元素个数小于128个,且所有元素的值小于64个字节时,使用压缩列表;
  • else 使用跳表;
    Redis7.0之后使用listpack实现。

扩展数据类型

  1. Bitmap:位图,保存0/1形式的数据,保存签到、判断用户登录
  2. hyperloglog:查询模糊数据,比如视频播放量等类似数据;
  3. geo:记录地理位置信息,比如外卖或者打车需要用到地址信息;

Redis的线程模型

Redis是单线程吗?

在处理一系列请求时,即接受客户端请求,解析请求,进行数据读写等操作,返回数据给客服端的过程,是单线程;
但是Redis程序并不是单线程的,Redis在启动的时候,会启动后台线程(BIO)。

  • Redis在2.6版本加了两个后台线程,分别处理关闭文件、AOF刷盘两个任务;
  • Redis 4.0之后,新增了一个后台线程,用来异步释放Redis内存,也就是lazyfree线程。
    详细内容见Redis 常见面试题
    之所以 Redis 为「关闭文件、AOF 刷盘、释放内存」这些任务创建单独的线程来处理,是因为这些任务的操作都是很耗时的,如果把这些任务都放在主线程来处理,那么 Redis 主线程就很容易发生阻塞,这样就无法处理后续的请求了。
    在这里插入图片描述
    此图生产者就是一个负责管理任务的后台线程,生产者把耗时任务丢到队列中,消费者不停轮询这个队列,拿出任务就去执行相应的方法即可。
    文件关闭、AOF刷盘、释放内存三个任务的任务队列:
  • BIO_CLOSE_FILE,关闭文件队列:当队列有任务后,后台线程会调用close(fd),将文件关闭;
  • BIO_AOF_FSYNC,AOF写队列:当AOF日志配置为everysec选项后,主线程会把AOF写日志操作封装为一个任务,也放进队列中。当发现队列有任务后,后台线程会调用fsync(fd),将AOF文件刷盘;
  • BIO_LAZY_FREE,lazy fredd任务队列:当队列有任务后,后台线程会free(obj)释放对象/free(dict)删除数据库所有对象/free(skiplist)释放跳表对象;
    在这里插入图片描述

为什么Redis采用单线程都能这么快?

  1. 运行在内存;
  2. 非多线程,避免了多线程竞争,免去了线程切换等的资源消耗;
  3. 采用I/O多路复用处理大量的客户端Socket请求;

使用单线程,可维护性搞,多线程模型虽然在某些方面表现优异,但是引入了程序执行顺序的不确定性,带来了并发读写的一系列问题,增加了系统复杂度、同时可能存在线程切换、甚至加锁解锁、死锁造成的性能损耗。
CPU并不是限制Redis性能表现的瓶颈所在,更多时候是受到内存大小以及I/O的限制。
Redis在6.0之后引入了多线程,但是主要面向I/O,采用了多个I/O线程来处理网络请求,因为随着网络硬件的性能提升,Redis的性能瓶颈有时会出现在网络I/O的处理上。
哪里是性能瓶颈,就把哪里设置为多线程

高版本Redis线程概况

Redis6.0之后,Redis默认创建6个线程:

  • Redis-server:Redis 主线程,主要负责执行命令。
  • BIO_CLOSE_FILE、BIO_AOF_FSYNC、BIO_LAZY_FREE:三个后台线程,分别负责文件关闭、AOF刷盘、释放内存;
  • io_thd_1、io_thd_2、io_thd_3,三个I/O多进程,分担Redis网络I/O的压力;
关于IO的补充

BIO:
在这里插入图片描述

Redis持久化

Redis如何实现数据不丢失(持久化的原因)

Redis运行在内存,当重启或者崩溃,内存中的数据会丢失,因此需要将数据保存至磁盘实现数据的持久化,在Redis重启后重新恢复保存在磁盘中的数据,避免数据丢失。
三种持久化方式:

  • AOF日志:每执行一条写操作,就将该命令以追加的方式写入一个特定文件中。
  • RDB快照:将某一时刻的内存数据,以二进制的方式写入磁盘。
  • 混合持久化方式:集成了AOF和RDB的优点。

AOF日志是如何实现的?

AOF,Append only File(追加文件)
记录写操作命令到一个文件中,然后Redis重启时,读取文件记录的命令,并逐一执行命令,进行数据恢复。
在这里插入图片描述
为什么先执行命令再保存到文件?

  • 避免额外的检查开销:防止保存完执行过程发现语法错误等;
  • 不会阻塞写命令的执行:;
    可能的风险:
  • 数据可能会丢失:两个过程,如果写文件过程中,服务器宕机,那么数据就会有丢失的风险;
  • 可能阻塞其他操作:因为AOF日志是需要主线程执行的;
AOF的写回策略有哪些?(需要回看)

执行写操作命令,首先放入AOF缓冲区,然后I/O系统调用,从用户态转到核心态,将命令写入内核缓冲区,最后由内核执行写入磁盘的操作。
在这里插入图片描述
内核缓冲区的数据何时写入硬盘?

  • Always,
  • Everysec,
  • No,Redis不管写入时间,由系统内核决定写回时间。
    在这里插入图片描述
    在这里插入图片描述

AOF文件过大时,会出发AOF重写机制。,如果AOF文件大小超过阈值,即去除冗余操作,精简文件内容。
在这里插入图片描述
重写AOF的过程?
bgrewriteaof子进程进行重写;
好处:

  • 子进程进行AOF重写,主进程可以继续处理命令请求,从而避免阻塞主进程;
  • 子进程而不是子线程,是为了避免线程的共享的内存数据被加锁。当不同线程需要修改共享内存中的数据时,需要加锁保证数据安全,这样会降低系统性能。对于创建父子进程的原因:子进程以只读形式共享同一内存空间,当父子进程修改共享内存中的数据时,就发生写时复制,于是父子进程就有了独立的数据副本,不用加锁。

触发重写机制后,主线程就会创建重写AOF子进程,父子进程共享物理内存,重写子进程对这块内存进行只读,重写AOF子进程会读取数据库中的所有数据,并逐一把内存数据的键值对转换成一条命令,再将命令记录到重写日志(新的AOF文件)。
==在重写过程中,主进程依然可以正常处理命令。==为了解决主进程在AOF重写过程中发起修改key-value的命令,Redis设置了AOF重写缓冲区,该缓冲区在AOF重写子进程创建时就开始使用;
在重写AOF期间,当Redis执行写命令之后,会同时将命令写入【AOF缓冲区】和A【OF重写缓冲区】
在这里插入图片描述
即,bgrewriteaof子进程执行AOF重写期间,主线程需要完成以下三个工作:

  1. 执行客户端发来的命令;
  2. 将执行后的写命令保存至【AOF缓冲区】;
  3. 将执行后的写命令保存至【AOF重写缓冲区】。
    当子进程完成AOF重写工作之后(扫描数据库中的所有数据,逐一把内存数据的键值对转换为一条命令,再将命令记录到重写日志)后,像主进程发一条信号,信号是进程间异步通信的方式。
    主进程收到信号后,调用信号处理函数,主要做以下工作:
  • 将AOF重写缓冲区中的内容追加到新的AOF文件中,使得新旧两个AOF文件所保存的数据库状态一致;
  • 新的AOF文件进行更名,覆盖现有的AOF文件。
    信号函数执行完之后,主进程就可以继续像往常一样处理命令了。

AOF保存的不是实际的数据,而是数据库操作命令,所以使用AOF方法做故障恢复时,需要全量把日志都执行一遍,如果AOF文件内容较多,势必Redis恢复慢。
RDB,快照,记录某一瞬间的内存数据,记录的是实际数据,因此RDB在恢复时,直接将RDB文件读入内存即可,不需要像AOF那样还需要额外执行命令。

RDB是如何实现的?

RDB,Redis DataBase,内存快照。

RDB做快照时会阻塞线程吗?

RDB有两种实现方式,分别是save和bgsave:

  • save可能会阻塞主线程,是主线程创建RDB文件,如果写入时间过长,会阻塞主线程;
  • bgsave是创建子线程,不会阻塞主线程;会创建子线程生成RDB文件,避免主线程的阻塞。
RDB执行快照时,数据可以修改吗?

答案是可以,执行bgsave时,Redis可以继续处理操作命令,关键技术是写时复制技术(Copy-On-Write,COW)
在执行bgsave时候,会通过fork()创建子进程,子进程和父进程共享内存空间。如果在bgsave过程中,主进程修改了数据,被修改的数据会复制副本,以供子进程使用。

上两部分见参考:Redis 如何实现数据不丢失?
RDB的优缺点是:数据恢复速度快,但是快照的时机把握是一个比较难的问题;
AOF的优缺点是:丢失数据少,但是恢复慢;

  1. 创建RDB文件
    a)内存中全量数据的快照,因此如果数据过多或者过于频繁,会比较耗时。但是如果频率太低,服务器故障时,丢失的数据会很多。因此时机把握非常重要。
  2. 载入RDB文件

混合持久化

发生在AOF日志重写过程。
使用混合持久化的AOF文件,前半部分是RDB格式的全量数据,后半部分是AOF格式的增量数据。
在这里插入图片描述
优点:

  • 前半部分是RDB数据,所以加载速度快;
  • 后半部分是AOF数据,加载完RDB数据,再加载AOF,所以数据的完整性较好,丢失的数据少。
    缺点:
  • AOF文件中加入RDB文件,可读性变差;
  • 兼容性差,不能用于Redis4.0之前的版本;

Redis集群

主从复制

在这里插入图片描述
主从服务器的命令复制是异步进行的。无法实现强一致性,数据不一致在所难免。

哨兵模式

在这里插入图片描述
主从复制模式有一个问题,当主节点宕机时,需要手动恢复。为了自动恢复,Redis增加了哨兵模式,因为哨兵模式可以监控主从服务器,且提供主从节点故障转移功能

切片集群模式

当Redis缓存数据量大到一台服务器无法缓存时,就需要使用Redis切片集群
使用哈希槽的方案,

Redis的内存淘汰策略

Redis会把key和过期时间一起保存到【过期字典】中,即【过期字典】中保存了数据库中所有key的过期时间。当在Redis查询key时,先查询【过期字典】:

  • 如果不在,就正常访问
  • 如果存在,获取系统当前时间进行比对,如果过期,执行相应的删除策略。

Redis使用的过期删除测试是惰性删除+定期删除

  • 惰性删除:只有被访问时,才会检查过期时间,若过期则删除。问题:如果一直不被访问,就一直无法删除。在这里插入图片描述

  • 定期删除:每过一段时间,随机从数据库中取出一定数量的key值进行检查,删除过期的key。定期的时间不好确定,太频繁会CPU不友好,太少又会占用内存很多无法释放。在这里插入图片描述- redis的定期删除:如上图。
    缺点是无法准确的设定删除执行时长和频率。

持久化时,对过期键的处理

RDB:
  1. 在写入时过期,就不保存到RDB中;
  2. 在读入时过期,要分情况:
    a) 如果是主节点:过期key不会放进内存;
    b) 如果是从节点:不管是否过期,均复制进内存。当主从服务器在进行数据同步时,从服务器的数据会被清空。所以一般来说,过期键对载入RDB文件的从服务器不会造成影响。
AOF:
  1. 写入阶段:如果过期,先保留在AOF文件中,但是如果过期,会是添加一句del语句删除对应的数据。
  2. 重写阶段:在重写时,会检查是否过期,如果过期就不保存到重写后的AOF文件中,因此对AOF重写没有任何影响。

以上是内存删除。
下面是内存淘汰

当内存满了,就需要内存淘汰

  1. 从设置了过期时间的数据中淘汰:
    a) 随机选择:
    b) LRU,Least Recently Used(默认淘汰策略):最久未使用;

LRU可以用链表实现,在末尾保存最少使用的元素。
Redis使用近似LRU算法,用额外的字段记录最近使用的时间。
c) LFU, Least Frequently Used:最近最少使用;

  1. 针对所有数据淘汰:
    a) 随机选择:
    b) LRU(默认淘汰策略):最久未使用;
    c) LFU:最近最少使用;

Redis如何保证数据一致性

Redis如何解决数据一致性问题?

1. 先更新数据库,再更新缓存
2. 先更新缓存,再更新数据库
这两个方法都由于并发问题,不推荐;
3. 先删除缓存,再更新数据库
4. 先更新数据库,再删除缓存
5. 延迟双删:先删除缓存,然后更新数据库,最后删除缓存。

如何解决缓存击穿、穿透、雪崩等问题?

缓存击穿

查询的热点缓存过期,导致高并发的访问数据库;
①设置热点数据不过期,或者在热点数据快过期时更新过期时间;
②设置互斥锁(Redis使用setNX来设置状态位,表示锁状态),避免高并发。
在这里插入图片描述

缓存穿透

查询在缓存和数据库都不命中。
①可以在缓存为特定数据设置空值。
②布隆过滤器。

  1. 布隆过滤器使用场景:
    a) Redis中防止缓存穿透的问题;
  2. 布隆过滤器的实现原理和方式:基于哈希函数和位数组实现
    a) 需要存放二进制元素的BitSet,初始值全为0、多样的哈希计算函数
    b) 添加时:元素通过K个哈希函数计算哈希地址,同时访问K个点,将对应位置设置为1;
    c) 查询时:元素通过K个哈希函数找到相应位置的BitSet;如果全为1,表示元素很可能存在,如果有任何一个0,则一定不存在;
  3. 如何提高布隆过滤器的准确性
    a) 增加更多的哈希函数,选择具有良好分布特性和低碰撞率的哈希种子和函数;
  4. 哈希函数有?
    a) MD5哈希算法;
    b) SHA哈希算法:
    c) HMAC哈希算法等;
  5. 有哪些类型的布隆过滤器实现?
    a) 谷歌开源的Guava中自带的布隆过滤器、Redis中的布隆过滤器;

缓存雪崩

大量热点缓存同时过期,导致大量的查询无法命中缓存,全去数据库查询了,因此增加了数据库负担;
解决方法,布隆过滤器(原理是?)
更正:想办法解决同时大量缓存过期。
①可以设置随机的过期时间;
②可以设置热点缓存不过期;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值