redis面试详解

redis的线程模型

image
  1. Server socket 监听到客户端发的请求,会产生一个AE_REABLE的事件
  2. IO多路复用程序将AE_REABLE事件压入队列中,依次执行
  3. <font color='red'>文件事件分派器</font>从队列中取数据交给<font color='red'>链接应答处理器</font>
  4. 链接应答处理器创建与客户端的长链接socket,并将这个长链接和需要执行的命令给到<font color='red'>命令请求处理器</font>
  5. 命令请求处理器进行命令执行,执行完成之后将socket的长链接与<font color='red'>命令回复处理器</font>关联
  6. 文件事件分派器使用对应的socket关联与命令回复处理器的执行断开socket请求

线程模型的意义

  1. 使用非阻塞IO多路复用程序,所以支持高并发
  2. 使用的文件事件分派器是单线程的,所以redis是单线程的

redis的击穿(穿透)

出现原因

出现大量redis中的key不存在的请求,导致创建了太多的jdbc链接从而跳过了redis的缓存机制,给数据库带来太大的压力

解决办法

1. 增加数据规则的验证,只有符合规则的key才进行查询,防止人为恶意的进行请求
2. 数据库中查询为null的对应key值信息也进行缓存

redis的缓存雪崩

出现原因

同一时间点大量的缓存数据失效,导致透过redis直接请求数据库出现问题

解决办法

1. 均匀分配缓存的过期时间(业务逻辑角度)
2. 创建多级缓存来辅助修改(springBoot+redis+Ecache)
3. 使用分布式锁的逻辑,保证获取数据库资源有一个一定的上限,超过上限就进行等待知道锁释放

redis的主从、集群和哨兵

主从复制

image

产生的原因

1. 容错性(只有一台的情况,一旦挂掉就会影像业务逻辑)
2. 并发性(一台的并发能力必然很低,读的能力会很差)

使用主从的结果

1. 数据冗余
2. 读写分离

原理

1. 主节点负责读/写,从节点只能进行读操作
2. 可以一主多从
3. 数据同步:一般初次会讲所有主节点数据同步到从节点,后续都是补发更新的数据,并且主从节点有长链接的心跳机制

哨兵机制

image

产生的原因

在搭建主从之后,主节点只有一个,一旦发生错误之后就没有主节点信息了

原理

  1. 在主节点外层新增一套哨兵逻辑
  2. 暴露给外部的是哨兵的信息,相当于哨兵进行了一层封装
  3. 一旦主节点出现问题,哨兵机制会在剩下的从节点中通过<font color='red'>选举算法</font>选出一个节点充当主节点

哨兵原理

  1. 每个sentinel会以心跳机制请求master、slave进行PING命令,如果一个实例响应超过设置的时间.
  2. 当有足够数量的额sentinel确定master下线,就会认定下线,就会进行选举算法选举出新节点作为主节点

当使用哨兵模式之后,就不要再直接连redis的ip和端口了,而是访问哨兵的信息,由哨兵进行<font color='red'>转发</font>

redis淘汰策略

1.noeviction:禁止驱逐数据,当内存上限的时候,再添加数据会产生异常。从而保证数据不会丢失

2.allkeys-lru: 全体数据中找最近使用最少的数据进行淘汰

3.volatile-lru: 从有设置过期时间的数据中找最近使用最少的数据进行淘汰

4.allkeys-random: 全体数据随机淘汰

5.volatile-random: 设置过期时间的数据随机淘汰

6.volatile-ttl: 从设置过期时间的数据中,随机找一些数据淘汰。距离过期时间越短,淘汰优先级越高

Redis淘汰策略主要分为LRU淘汰、TTL淘汰、随机淘汰三种机制。

LRU淘汰

LRU(Least recently used,<font color="red">最近最少使用</font>)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。

在服务器配置中保存了 lru 计数器 server.lrulock,会定时(redis 定时程序 serverCorn())更新,server.lrulock 的值是根据 server.unixtime 计算出来进行排序的,然后选择最近使用时间最久的数据进行删除。另外,从 struct redisObject 中可以发现,每一个 redis 对象都会设置相应的 lru。每一次访问数据,会更新对应redisObject.lru。

在Redis中,LRU算法是一个近似算法,默认情况下,Redis会随机挑选5个键,并从中选择一个最久未使用的key进行淘汰。在配置文件中,按maxmemory-samples选项进行配置,选项配置越大,消耗时间就越长,但结构也就越精准。

TTL淘汰

也就是按照过期时间进行淘汰。距离过期时间越短,淘汰的优先级越高。

随机淘汰

在随机淘汰的场景下获取待删除的键值对,随机找hash桶再次hash指定位置的dictEntry即可。

Redis中的淘汰机制都是几近于算法实现的,主要从性能和可靠性上做平衡,所以并不是完全可靠,所以开发者们在充分了解Redis淘汰策略之后还应在平时多主动设置或更新key的expire时间,主动删除没有价值的数据,提升Redis整体性能和空间。

总结六种:

  1. 不淘汰
  2. 随机淘汰
  3. 有过期时间的随机淘汰
  4. 使用最少
  5. 有过期时间的使用最少
  6. 过期时间越短淘汰率越高

redis的八种数据结构

  • String,字符串类型
  • hash,对象类型
  • list,有序可重复类型
  • set,无需不可重复类型
  • zset,无需可重复类型
  • Geospatial,地理位置类型
  • bitmap,位图类型(key:value(1,2))
  • Hyperloglog,数学上的集合类型

队列模式:

list作为队列,rpush生产消息,lpop消费消息

redis持久化

不建议开启redis的持久化操作

RDB

原理

快照的方式保存数据,每隔一段时间有固定多少key值发生变更,将数据进行快照备份一次

由父进程fork开启一个子进程,使用子进程进行I/O操作将内存中的快照保存在硬盘上

缺点

耗时,耗性能

AOF

原理

将操作日志保存下来,做的是增量保存.在重启的时候执行所有操作一遍

缺点

文件体积大,恢复速度慢

现在一般采用两者集合的方式来进行持久化。底层对这两种方案做了融合处理

redis的事务

redis支持事务,但是整体redis集群不支持事务

1. 事务的一般执行流程

1. 开启事务`MULTI`
2. 执行操作,也就是执行你需要进行的操作set等
3. 提交事务`EXEC`,会执行一并所有的操作一起提交

2. 使用SpringBoot+redis执行事务

//1. 创建对应执行的RedisTemplate对象
@Autowired
StringRedisTemplate stringRedisTemplate
//2. 开启事务权限
stringRedisTemplate.setEnableTransationSupport(true);
//3. 开启事务
stringRedisTemplate.multi();
//4. 回滚
stringRedisTemplate.discard();
//5. 提交
stringRedisTemplate.exec();

如果开启事务,所有的命令只有在执行了exec。才会往redis中插入

redis实现分布式锁

原理

  1. redis是单线程的

如何实现

1.线程 A setnx(上锁的对象,超时时的时间戳 t1),如果返回 true,获得锁。

2.线程 B 用 get 获取 t1,与当前时间戳比较,判断是是否超时,没超时 false,若超时执行第 3 步;

3.计算新的超时时间 t2,使用 getset 命令返回 t3(该值可能其他线程已经修改过),如果

t1==t3,获得锁,如果 t1!=t3 说明锁被其他线程获取了。

4.获取锁后,处理完业务逻辑,再去判断锁是否超时,如果没超时删除锁,如果已超时,不用处理(防止删除其他线程的锁)。

核心

<font color='red'>setnx</font>关键字,将key对应的资源锁定,set成功返回1,失败返回0

对比redis与zookeeper实现分布式锁

edis实现分布式锁与Zookeeper实现分布式锁的区别

相同点

在集群的环境下,保证只允许有一个jvm进行执行

从技术上分析

Redis是nosql数据库,主要特点是缓存

Zookeeper是分布式协调工具,主要用户分布式解决方案

实现思路分析

获取锁

Zookeeper,多个客户端(jvm)会在Zookeeper上创建同一个临时节点,因为Zookeeper节点命名路径保证唯一,不允许出现重复,只要谁能创建成功就能获取这个锁

redis,多个jvm会在redis中使用setnx命令创建相同的一个key,因为redis的key保证唯一,不允许重复。只要谁先创建成功,谁就能获取锁

释放锁

Zookeeper直接关闭 临时节点session会话连接,因为临时节点生命周期session会话绑定在一起,如果session会话连接关闭的话,就会使这个临时节点被删除

然后客户端使用事件监听,监听到临时节点被删除,就释放锁

redis释放锁的时候,为了保证是锁的一致性问题,在删除锁的时候需要判断对应的value是否是对应创建的那个业务的id

死锁解决

Zookeeper使用会话有效方式解决死锁的现象

redis是对key设置有效期来解决死锁现象

性能上:

因为redis是nosql,所以redis比Zookeeper性能好

可靠性

Zookeeper更加可靠。因为redis有效求不是很好控制,可能会产生延迟

其余面试问题

7、一个字符串类型的值能存储最大容量是多少?

512M

8、为什么 Redis 需要把所有数据放到内存中?

Redis 为了达到最快的读写速度将数据都读到内存中,并通过异步的方式将数据写入磁盘。

所以 redis 具有快速和数据持久化的特征,如果不将数据放在内存中,磁盘 I/O 速度为严重影响 redis 的性能。

在内存越来越便宜的今天,redis 将会越来越受欢迎, 如果设置了最大使用的内存,则数据已有记录数达到内存限值后不能继续插入新值。

11、MySQL 里有 2000w 数据,redis 中只存 20w 的数据,如何保证 redis 中的数据都是热点数据?

使用的淘汰策略可以是LRU策略,可以实现淘汰最近最少使用的数据淘汰掉

22、Redis 中的管道有什么用?

一次请求/响应服务器能实现处理新的请求即使旧的请求还未被响应,这样就可以将多个命令发送到服务器,而不用等待回复,最后在一个步骤中读取该答复。

这就是管道(pipelining),是一种几十年来广泛使用的技术。例如许多 POP3 协议已经实现支持这个功能,大大加快了从服务器下载新邮件的过程。

25、Redis key 的过期时间和永久有效分别怎么设置?

EXPIRE 和 PERSIST 命令

26、Redis 如何做内存优化?

尽可能使用散列表(hashes),散列表(是说散列表里面存储的数少)使用的内存非常小,所以你应该尽可能的将你的数据模型抽象到一个散列表里面。

比如你的 web 系统中有一个用户对象,不要为这个用户的名称,姓氏,邮箱,密码设置单独的 key,而是应该把这个用户的所有信息存储到一张散列表里面。

27、Redis 回收进程如何工作的?

一个客户端运行了新的命令,添加了新的数据。Redi 检查内存使用情况,如果大于 maxmemory 的限制, 则根据设定好的策略进行回收。一个新的命令被执行,等等。

所以我们不断地穿越内存限制的边界,通过不断达到边界然后不断地回收回到边界以下。

如果一个命令的结果导致大量内存被使用(例如很大的集合的交集保存到一个新的键),不用多久内存限制就会被这个内存使用量超越。

29.锁互斥机制

那么在这个时候,如果客户端 2 来尝试加锁,执行了同样的一段 lua 脚本,会咋样呢?很简单,第一个 if 判断会执行“exists myLock”,发现 myLock 这个锁 key 已经存在了。接着第二个 if 判断,判断一下,myLock 锁 key 的 hash 数据结构中,是否包含客户端 2 的 ID,但是明显不是的,因为那里包含的是客户端 1 的 ID。

所以,客户端 2 会获取到 pttl myLock 返回的一个数字,这个数字代表了 myLock 这个锁 key的剩余生存时间。比如还剩 15000 毫秒的生存时间。此时客户端 2 会进入一个 while 循环,不停的尝试加锁。

30.watch dog 自动延期机制

客户端 1 加锁的锁 key 默认生存时间才 30 秒,如果超过了 30 秒,客户端 1 还想一直持有这把锁,怎么办呢?

简单!只要客户端 1 一旦加锁成功,就会启动一个 watch dog 看门狗,他是一个后台线程,会每隔 10 秒检查一下,如果客户端 1 还持有锁 key,那么就会不断的延长锁 key 的生存时间。

缓存与数据库不一致怎么办

假设采用的主存分离,读写分离的数据库,

如果一个线程 A 先删除缓存数据,然后将数据写入到主库当中,这个时候,主库和从库同步没有完成,线程 B 从缓存当中读取数据失败,从从库当中读取到旧数据,然后更新至缓存,这个时候,缓存当中的就是旧的数据。

发生上述不一致的原因在于,主从库数据不一致问题,加入了缓存之后,主从不一致的时间被拉长了

处理思路:在从库有数据更新之后,将缓存当中的数据也同时进行更新,即当从库发生了数据更新之后,向缓存发出删除,淘汰这段时间写入的旧数据。

主从数据库不一致如何解决场景描述,对于主从库,读写分离,如果主从库更新同步有时差,就会导致主从库数据的不一致

1、忽略这个数据不一致,在数据一致性要求不高的业务下,未必需要时时一致性

2、强制读主库,使用一个高可用的主库,数据库读写都在主库,添加一个缓存,提升数据读取的性能。

3、选择性读主库,添加一个缓存,用来记录必须读主库的数据,将哪个库,哪个表,哪个主键,作为缓存的 key,设置缓存失效的时间为主从库同步的时间,如果缓存当中有这个数据,直接读取主库,如果缓存当中没有这个主键,就到对应的从库中读取。

46.Redis 如何实现延时队列

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容