Redis秋招面经

  • 一、Redis是什么
    • Redis优势
    • Redis适用场景
    • Redis很快的原因
    • 单线程的原因
    • Redis为什么比MySQL快
  • 二、Redis数据类型、各种类型的底层实现及适用场景
    • 五大对象
  • 三、redis的持久化机制
    • 为什么Redis需要持久化?
    • 持久化机制
      • RDB(默认)
      • AOF
    • 持久化策略的选择
  • 四、缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级等问题
    • 缓存雪崩
    • 缓存穿透
    • 缓存预热
    • 缓存更新
    • 缓存降级
  • 五、Redis-skiplist跳表原理
  • 六、Redis过期策略、内存淘汰机制
    • Redis的淘汰策略
    • Redis的删除策略
  • 七、Redis集群方案?多机Redis部署,如何保证数据的一致性?如何处理大量请求
    • 同时有多个子系统去set一个key
    • redis集群方案
    • 多机redis部署如何保证数据一致
    • 如何处理大量请求
    • 为什么redis的操作是原子性的,如何保证
    • redis事务
    • redis实现分布式锁
    • redis线程模型

一、Redis是什么

Redis(Remote Dictionary Server)是一个基于内存的高性能kv型数据库

Redis优势

1.高性能:操作缓存就是直接操作内存,速度很快
2.高并发:直接操作缓存能够承受的请求是远远大于直接访问数据库的,所以我们可以考虑把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里而不用经过数据库。

Redis适用场景

1.数据高并发的速写2.海量数据的读写3.对拓展性要求很高的数据
配合关系型数据库做高速缓存:缓存高频次访问的数据,降低数据库io,分布式架构,做session共享
可以持久化特定数据:利用zset类型可以存储排行榜,利用list的自然时间排序存储最新n个数据

Redis很快的原因

redis很快的原因且是单线程的原因
1、完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1);
2、数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的;
3、采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
4、使用多路I/O复用模型,非阻塞IO;
5、使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;
(1)多路 I/O 复用模型
多路I/O复用模型是利用 select、poll、epoll 可以同时监察多个流的 I/O 事件的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有 I/O 事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll 是只轮询那些真正发出了事件的流),并且只依次顺序的处理就绪的流,这种做法就避免了大量的无用操作。
这里“多路”指的是多个网络连接,“复用”指的是复用同一个线程。采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络 IO 的时间消耗),且 Redis 在内存中操作数据的速度非常快,也就是说内存内的操作不会成为影响Redis性能的瓶颈,主要由以上几点造就了 Redis 具有很高的吞吐量。

单线程的原因

官方FAQ表示,因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了

Redis为什么比MySQL快

1.Redis是基于内存存储的,MySQL是基于磁盘存储的
2.Redis存储的是k-v格式的数据。时间复杂度是O(1),常数阶,而MySQL引擎的底层实现是B+Tree,时间复杂度是O(logn),对数阶。Redis会比MySQL快一点点。
3.MySQL数据存储是存储在表中,查找数据时要先对表进行全局扫描或者根据索引查找,这涉及到磁盘的查找,磁盘查找如果是按条点查找可能会快点,但是顺序查找就比较慢;而Redis不用这么麻烦,本身就是存储在内存中,会根据数据在内存的位置直接取出。
4.Redis是单线程的多路复用IO,单线程避免了线程切换的开销,而多路复用IO避免了IO等待的开销,在多核处理器下提高处理器的使用效率可以对数据进行分区,然后每个处理器处理不同的数据。

二、Redis数据类型、各种类型的底层实现及适用场景

redisObject是Redis类型系统的核心,数据库中的每个键、值,以及Redis本身处理的参数,都表示为这种数据类型。
redis Object参考资料

//redisObjecttypedef struct redisObject {// 类型属性存储的是对象的类型,也就是我们说的 string、list、hash、set、zset中的一种,// 可以使用命令 TYPE key 来查看。unsigned type:4;// 编码,记录了队形所使用的编码,即这个对象底层使用哪种数据结构实现。unsigned encoding:4;// 指向底层实现数据结构的指针void *ptr;// ...
} robj;

redis是以键值对存储数据的,所以对象又分为键对象和值对象,即存储一个key-value键值对会创建两个对象,键对象和值对象。键对象总是一个字符串对象,而值对象可以是五大对象中的任意一种。
而这五大对象的底层数据编码可以用命令OBJECT ENCODING来进行查看。

五大对象

五大类型包含一些操作函数
String类型
是二进制安全的(二进制安全是指,在传输数据时,保证二进制数据的信息安全,也就是不被篡改、破译等,如果被攻击,能够及时检测出来。二进制安全包含了密码学的一些东西,比如加解密、签名等),最大512M
list类型
Redis列表是简单的字符串列表,从左或者从右插入。底层是双向链表,对两端的操作性能很高,通过下标查询性能很低
hash类型
键值对集合,类似于map。
set类型
类似于list的无序集合,保证列表中不会有重复元素。底层实现是一个value值为NULL的hash表
zset类型
与set类似,每个成员都会关联一个score,可以用来排序。
redis各种数据类型的底层实现

三、redis的持久化机制

为什么Redis需要持久化?

由于Redis是一种内存型数据库,即服务器在运行时,系统为其分配了一部分内存存储数据,一旦服务器挂了或宕机了,那么数据库里面的数据将会丢失,为了使服务器即使突然关机也能保存数据,必须通过持久化的方式将数据从内存保存到磁盘中。

持久化机制

RDB(默认)

在指定时间间隔内,将内存中的数据作为一个快照文件(snapshot)写入到磁盘,读取的时候也是直接读取snapshot文件到内存中
优点:RDB文件紧凑,体积小,网络传输快,适合全量复制;恢复速度比AOF快。与AOF相比,RDB最重要的优点之一是对性能的影响相对较小
缺点:RDB文件的致命缺点在于其数据快照的持久化方式决定了必然做不到实时持久化,而在数据越来越重要的今天,数据的大量丢失很多时候是无法接受的,因此AOF持久化成为主流。此外,RDB文件需要满足特定格式,兼容性差(如老版本的Redis不兼容新版本的RDB文件)

AOF

Redis会将每一个收到的写命令都通过Write函数追加到文件最后,类似于MySQL的binlog。当Redis重启是会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。

持久化策略的选择

如果Redis中的数据完全丢弃也没有关系,那么无论是单机,还是主从架构,都可以不进行任何持久化。
在单机环境下,如果可以接受十几分钟或更多的数据丢失,选择RDB对Redis的性能更加有利;如果只能接受秒级别的数据丢失,应该选择AOF。
但在多数情况下,我们都会配置主从环境,slave的存在既可以实现数据的热备,也可以进行读写分离分担Redis读请求,以及在master宕掉后继续提供服务。

四、缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级等问题

缓存雪崩

缓存雪崩我们可以简单的理解为:由于原有缓存失效,新缓存未到期间
(例如:我们设置缓存时采用了相同的过期时间,在同一时刻出现大面积的缓存过期),所有原本应该访问缓存的请求都去查询数据库了,而对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机。从而形成一系列连锁反应,造成整个系统崩溃。
解决办法:
大多数系统设计者考虑用加锁( 最多的解决方案)或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。还有一个简单方案就时讲缓存失效时间分散开。

缓存穿透

缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候,在缓存中找不到,每次都要去数据库再查询一遍,然后返回空(相当于进行了两次无用的查询)。这样请求就绕过缓存直接查数据库,这也是经常提的缓存命中率问题。
解决办法;
最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。
另外也有一个更为简单粗暴的方法,如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。通过这个直接设置的默认值存放到缓存,这样第二次到缓冲中获取就有值了,而不会继续访问数据库,这种办法最简单粗暴。

缓存预热

缓存预热指系统上线后,将相关的缓存数据直接加载到Redis中,这样就可以避免在用户请求时,先查询数据库,然后再将数据缓存的问题。
如何解决缓存预热
直接写个缓存刷新页面,系统上线时手动将缓存数据加载;
定时刷新缓存;

缓存更新

除了缓存服务器自带的缓存失效策略之外(Redis默认的有6中策略可供选择),我们还可以根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种:
(1)定时去清理过期的缓存;
(2)当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。
两者各有优劣,第一种的缺点是维护大量缓存的key是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂

缓存降级

当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。
降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。
以参考日志级别设置预案:
(1)一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
(2)警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;
(3)错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;
(4)严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。
服务降级的目的,是为了防止Redis服务故障,导致数据库跟着一起发生雪崩问题。因此,对于不重要的缓存数据,可以采取服务降级策略,例如一个比较常见的做法就是,Redis出现问题,不去数据库查询,而是直接返回默认值给用户。

五、Redis-skiplist跳表原理

参考

六、Redis过期策略、内存淘汰机制

Redis采用的是定期删除和惰性删除策略。

Redis的淘汰策略

当Redis的内存(maxmemory参数配置)已满时,它会根据淘汰策略(maxmemory-policy参数配置)进行相应的操作。
不删除策略 no-eviction:不删除策略。Redis默认策略。达到最大内存限制时,若需要更多内存,直接返回错误信息。
最近最少使用策略(lru)
allkeys-lru:所有key通用;优先删除最近最少使用的key
volatile-lru:只限于设置了 expire 过期时间的部分;优先删除最近最少使用的key
随机策略
allkeys-random:所有key通用;随机删除一部分key。
volatile-random:只限于设置 expire 的部分;随机删除一部分key。
剩余时间短策略 volatile-ttl:只限于设置 expire 的部分;优先删除剩余时间短的key。
最不经常使用策略
volatile-lfu:只限于设置 expire 的部分;优先删除最不经常使用的key。
allkeys-lfu:所有key通用;优先删除最不经常使用的key。

Redis的删除策略

Redis是 key-value 数据库,可以设置Redis缓存的key的过期时间。Redis的过期删除策略就是指当Redis中的key过期了,Redis是如何进行处理的。
定时删除:在设置key的过期时间的同时,Redis会创建一个定时器,当key达到过期时间时,立即删除该键。
惰性删除:放任键过期不管,只有当获取键时,才检查获取的键是否过期,若过期删除该键;若没过期,就返回值。
定期删除:每隔一段时间(默认100ms),程序就对数据库进行一次检查,删除过期键。
expires字典会保存所有设置了过期时间的key的过期时间数据。key是指向键空间中的某个键的指针,value是该键的毫秒精度的UNIX时间戳表示的过期时间。
采用定期删除+惰性删除就没其他问题了么?
不是的,如果定期删除没删除key。然后你也没即时去请求key,也就是说惰性删除也没生效。这样,redis的内存会越来越高。那么就应该采用内存淘汰机制

七、Redis集群方案?多机Redis部署,如何保证数据的一致性?如何处理大量请求

同时有多个子系统去set一个key

这个时候要注意什么呢? 不推荐使用redis的事务机制。因为我们的生产环境,基本都是redis集群环境,做了数据分片操作。你一个事务中有涉及到多个key操作的时候,这多个key不一定都存储在同一个redis-server上。因此,redis的事务机制,十分鸡肋。
(1)如果对这个key操作,不要求顺序: 准备一个分布式锁,大家去抢锁,抢到锁就做set操作即可
(2)如果对这个key操作,要求顺序: 分布式锁+时间戳。 假设这会系统B先抢到锁,将key1设置为{valueB 3:05}。接下来系统A抢到锁,发现自己的valueA的时间戳早于缓存中的时间戳,那就不做set操作了。以此类推。
(3) 利用队列,将set方法变成串行访问也可以redis遇到高并发,如果保证读写key的一致性
对redis的操作都是具有原子性的,是线程安全的操作,你不用考虑并发问题,redis内部已经帮你处理好并发的问题了。

redis集群方案

1.twemproxy,它类似于一个代理方式, 使用时在本需要连接 redis 的地方改为连接 twemproxy, 它会以一个代理的身份接收请求并使用一致性 hash 算法,将请求转接到具体 redis,将结果再返回 twemproxy。
缺点: twemproxy 自身单端口实例的压力,使用一致性 hash 后,对 redis 节点数量改变时候的计算值的改变,数据无法自动移动到新的节点。

多机redis部署如何保证数据一致

主从复制,读写分离
一类是主数据库(master)一类是从数据库(slave),主数据库可以进行读写操作,当发生写操作的时候自动将数据同步到从数据库,而从数据库一般是只读的,并接收主数据库同步过来的数据,一个主数据库可以有多个从数据库,而一个从数据库只能有一个主数据库。

如何处理大量请求

redis是一个单线程程序,也就说同一时刻它只能处理一个客户端请求;
redis是通过IO多路复用(select,epoll, kqueue,依据不同的平台,采取不同的实现)来处理多个客户端请求的

为什么redis的操作是原子性的,如何保证

对于Redis而言,命令的原子性指的是:一个操作的不可以再分,操作要么执行,要么不执行。
Redis的操作之所以是原子性的,是因为Redis是单线程的。
Redis本身提供的所有API都是原子操作,Redis中的事务其实是要保证批量操作的原子性。
多个命令在并发中也是原子性的吗?
不一定

redis事务

Redis事务功能是通过MULTI、EXEC、DISCARD和WATCH 四个原语实现的
Redis会将一个事务中的所有命令序列化,然后按顺序执行。
1.redis 不支持回滚“Redis 在事务失败时不进行回滚,而是继续执行余下的命令”, 所以 Redis 的内部可以保持简单且快速。
2.如果在一个事务中的命令出现错误,那么所有的命令都不会执行;
3.如果在一个事务中出现运行错误,那么正确的命令会被执行。
注:redis的discard只是结束本次事务,正确命令造成的影响仍然存在.

1)MULTI命令用于开启一个事务,它总是返回OK。 MULTI执行之后,客户端可以继续向服务器发送任意多条命令,这些命令不会立即被执行,而是被放到一个队列中,当EXEC命令被调用时,所有队列中的命令才会被执行。
2)EXEC:执行所有事务块内的命令。返回事务块内所有命令的返回值,按命令执行的先后顺序排列。 当操作被打断时,返回空值 nil 。
3)通过调用DISCARD,客户端可以清空事务队列,并放弃执行事务, 并且客户端会从事务状态中退出。
4)WATCH 命令可以为 Redis 事务提供 check-and-set (CAS)行为。 可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行,监控一直持续到EXEC命令。

redis实现分布式锁

Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多客户端对Redis的连接并不存在竞争关系Redis中可以使用SETNX命令实现分布式锁。
将 key 的值设为 value ,当且仅当 key 不存在。 若给定的 key 已经存在,则 SETNX 不做任何动作
解锁:使用 del key 命令就能释放锁
解决死锁:
1)通过Redis中expire()给锁设定最大持有时间,如果超过,则Redis来帮我们释放锁。
2) 使用 setnx key “当前系统时间+锁持有的时间”和getset key “当前系统时间+锁持有的时间”组合的命令就可以实现。

redis线程模型

文件事件处理器包括分别是套接字、 I/O 多路复用程序、 文件事件分派器(dispatcher)、 以及事件处理器。使用 I/O 多路复用程序来同时监听多个套接字, 并根据套接字目前执行的任务来为套接字关联不同的事件处理器。当被监听的套接字准备好执行连接应答(accept)、读取(read)、写入(write)、关闭(close)等操作时, 与操作相对应的文件事件就会产生, 这时文件事件处理器就会调用套接字之前关联好的事件处理器来处理这些事件。
I/O 多路复用程序负责监听多个套接字, 并向文件事件分派器传送那些产生了事件的套接字。
工作原理:
1)I/O 多路复用程序负责监听多个套接字, 并向文件事件分派器传送那些产生了事件的套接字。
尽管多个文件事件可能会并发地出现, 但 I/O 多路复用程序总是会将所有产生事件的套接字都入队到一个队列里面, 然后通过这个队列, 以有序(sequentially)、同步(synchronously)、每次一个套接字的方式向文件事件分派器传送套接字: 当上一个套接字产生的事件被处理完毕之后(该套接字为事件所关联的事件处理器执行完毕), I/O 多路复用程序才会继续向文件事件分派器传送下一个套接字。如果一个套接字又可读又可写的话, 那么服务器将先读套接字, 后写套接字.

Redis秋招面经(自用)相关推荐

  1. 秋招面试总结:Java+并发+Spring+MySQL+分布式+Redis+算法+JVM等

    轰轰烈烈的秋招季已经开始,有人欢喜有人愁,不管你是喜是忧,学习总归是好的.这不,小编特地整理了一下秋招面试知识点+答案(JAVA岗),一起分享出来给大家看看~ 需要完整版的 知识点+答案 的朋友可以  ...

  2. 秋招JAVA面试总结:Java+并发+Spring+MySQL+分布式+Redis+算法+JVM等

    轰轰烈烈的"金九银十"秋招季已经落幕,有人欢喜有人愁,不管你是喜是忧,学习总归是好的.这不,小编特地整理了一下秋招面试知识点(JAVA岗),一起分享出来给大家看看~ 第一部分 Ja ...

  3. 秋招面试总结:Java+并发+Spring+MySQL+分布式+Redis+算法+JVM等,太香了~

    轰轰烈烈的"金九银十"秋招季已经落幕,有人欢喜有人愁,不管你是喜是忧,学习总归是好的.这不,小编特地整理了一下秋招面试知识点+答案(JAVA岗),一起分享出来给大家看看~ 第一部分 ...

  4. 经验 | 秋招总结(拼多多,腾讯,百度,字节)

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达本文转自|计算机视觉联盟 offer情况 秋招运气好拿到了几个off ...

  5. 字节跳动秋招超6000人,渣本双非的出路都被谁堵死了?

    有消息称,字节跳动将持续大规模招聘,今年全年校招岗位需求将超过1.2万人.在Boss上,字节系正在招聘的岗位数量也超过了1万个. 并且几乎隔三差五,能看到某某高管跳槽字节跳动.腾讯"一线&q ...

  6. 清华通信本硕巨佬秋招经验总结,收割互联网大厂后端 SP/SSP offer,太强了!

    作者 | 程序员峰哥 来源 | 程序员峰哥 学习群里的清华学霸,去年这时候只有 Java 基础,后面帮他制定了学习路线开始学习,并参加了春季实习,这次秋招收获颇丰,基本都是顶级大厂的 A+/A++ O ...

  7. **Java有哪些悲观锁的实现_阿里秋招Java研发工程师岗:来自校友的面试还原(已拿Offer)...

    前言 本篇题材来自我的校友投稿,他在最近的秋招校招中拿到了蚂蚁金服的实习生Offer,整体思路和面试题目由面试本人--小林提供(译名) 由于作者面试过程中高度紧张,本文中只列出了自己还记得的部分题目. ...

  8. leetcode 滴滴_一个菜逼程序媛的求职历程(秋招已拿阿里、网易、滴滴等校招offer)...

    跟我一个学校,一个学院,隔壁专业的学妹,春招拿到阿里暑期实习 offer,秋招拿到了阿里.网易.滴滴等大厂 offer,真的很厉害!看看她的秋招之路,对于马上参加暑期实习.秋招绝对是有帮助的,还有一些 ...

  9. 拾牙的2021年秋招总结(大概会有帮助?)

    目录 秋招面试经历 秋招面经参考 基础部分 面经 常见问题 对秋招一些经验 最后收获 后续安排 秋招面试经历 时间 公司 岗位 面试轮次 是否完成 2021年7月2日 07:00 禾赛 嵌入式软件工程 ...

最新文章

  1. google chrome 谷歌浏览器 快捷键说明
  2. 两个SEO技巧让你的网站排名靠前
  3. ef mysql code first_关于ef+codefirst+mysql(入门)
  4. ElasticSearch启动报错at least one of [discovery.seed_hosts, discovery.seed_provid---ElasticSearch工作笔记032
  5. js中的正则表达式(2)
  6. MySQL学习笔记——第1章 数据库和MySQL
  7. JavaScript 必会的知识点
  8. 高等数学张宇18讲 第十讲 多元函数微分学
  9. 怎么使用biopython_使用biopython解析Fasta文件描述
  10. android上查看浏览器内核版本号,各种浏览器的userAgent及如何使用JS来检测游览器类型,或android是什么版本号...
  11. NVIDIA vid2vid论文复现
  12. 教育网看高清世界杯方法
  13. Win10系统 IE11浏览器调用F12开发人员工具,打开后底部显示空白
  14. 安徽省2016“京胜杯”程序设计大赛_C_箭无虚发
  15. 磁盘存储DiskStore
  16. 虚拟机怎么架设dns服务器,虚拟机centos7 DNS服务器搭建
  17. UART、I2C、USB、SPI、CAN、Jtag、PCI/PCIE协议汇总
  18. 计算机高级 论文怎么考,干货丨如何在一个月内通过高级软考证
  19. angularjs的SEO问题解决方案
  20. 质量体系建设——环境治理

热门文章

  1. 俄罗斯研究人员表示可访问全球所有小米宠物喂食器
  2. 接口请求时加载loading动画
  3. Ceres Power和潍柴动力敲定战略协作及合资协议
  4. 计算机基础上机考试系统,《计算机基础》课程上机考试系统的设计与实现
  5. Windows10 设置文件显示扩展名后缀
  6. java 类 抽象类 接口_Java抽象类和接口
  7. PS2020中文版软件下载和安装说明
  8. 为什么要压缩模型,而不是直接训练一个小的CNN?
  9. IDEA设置类注释和方法注释的详细操作步骤
  10. 子网掩码必须是相邻的是什么意思_子网划分与子网掩码