小林求职记(五)上来就一连串的分布式缓存提问,我有点上头....

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 小林求职记(五)上来就一连串的分布式缓存提问,我有点上头....

 前传


小林求职记(四)不会吧不会吧,面试还真会问这些呀


在之前王哥的辅助之下,小明的简历成功被内推进到了王哥所在公司。由于一面就是王哥自己,所以简单聊聊了便过去了。接下来,二面的面试官来了。


二面面试官看上去比较年轻的消瘦,戴着一副眼镜,头发比较稀疏,看上去像是有十多年经验的样子,两人在一处安静的地方坐了下来,开始了第二轮面试。


面试官:嗯嗯,你好,请先简单自我介绍一下自己吧。


小林:嗯嗯,你好,我是XXXX,之前在XXX(此处省略200字介绍)


面试官点了点头,一副迷之微笑的表情,然后低头过了一遍简历的内容。直到看见了 “负责过电商系统的秒杀项目后端开发模块,并且对于缓存有过较为深入的研究”这么一行字眼。


面试官:我对你这上边写的秒杀项目和缓存研究有些感兴趣,想深入了解一下其中的细节点。


小林:嗯嗯,好的。


面试官:先从简单问起吧,在秒杀业务场景中,你在下单的时候是如何防止库存超卖发生的呢?


小林:嗯嗯,首先在活动开始之前,我们会进行一次商品库存的预热处理,将数据库的信息加载到redis中,然后扣除库存的时候会在redis里面进行处理,最后当库存为0的时候,会触发一个方法去触发关闭前端秒杀活动的开关。


每次当有用户提交下单请求的时候,会先将请求通过mq来进行削峰,在进行扣除库存的时候,会先更新redis里面的库存量,最后再统一更新db。


面试官:为啥不直接更新db呢?


小林:像秒杀这种典型的高并发场景,直接对db层进行写操作对数据库的访问压力实在是太大了,并发量过大容易压垮数据库。


面试官:嗯嗯,那为什么要把库存存储在redis中呢?


小林:具体有两个原因,首先第一点, redis的并发承载能力足以应付我上家公司的秒场景所需。还有一点非常重要的就是,redis是单线程模型,在做库存减1操作的时候不会出现数据竞争导致商品超卖的情况发生。


image.png


面试官似乎对小林说的这个redis单线程模型感到一些兴趣了,于是又接着深入展开了对于缓存部分的提问。


面试官:等等,既然你说了redis的并发承载能力强,但是又说是单线程模型,能解释下为什么单线程模型下redis也能有较好的性能承载能力呢?


小林在面试之前正好准备了这方面的知识点内容,于是便拿出了自己以前所学习过的知识内容点进行了讲解。


小林:redis采用了非阻塞网络IO模型,适合用于快速地操作逻辑。所谓的非阻塞网络io模型,这有点类似于java里面的nio。当有多个请求发送到服务端的时候,实际上会有一个文件事件处理器同时监听多个套接字,并且根据套接字目前执行的任务来关联不同的事件处理器。


这些不同的套接字用于给事件处理器将其分发给不同的逻辑程序处理,事件处理器只需要将它们做绑定即可。这些处理事件可能会并发地出现,但是io多路复用程序是会将所有产生的套接字都存入一个 有序且同步的队列中(单线程的核心点),最后redis会有逐一地对这个队列中的元素进行处理。


这里就是为啥单线程的原因。在一开始学习这块知识点的时候,为了更好地深入理解,我去用了nio程序来做比对。


不同的套接字事件对应的处理器也听类似的,例如说accept,read,write等事件,应对不同连接的时候处理逻辑也不同。


我当时是结合了实际业务来进行设计的,由于商品有多种,因此对于商品的库存数目采用了key-value的结构,按照商品的id作为key,库存作为value存储。对于库存的减少是采用了decr指令操作,这条指令实际上是一条原子性操作,之所以原子性操作是因为redis的单线程特性。


image.png


面试官:嗯嗯,既然上边你解释到了redis是单线程模型,那么在使用redis的时候需要注意些什么吗?或者说对于redis的存储有什么优化技巧可以讲解下吗?


这时候小林想起了之前王哥发给他的一份面试笔记,上边记录了很多关于redis的知识点,其中就有提及到过这一点。


小林:嗯嗯,在redis6.0之前,redis还是处于一个单线程的状态,我们就拿单线程版本的redis来说吧。


拒绝bigkey的出现场景首先key的值不宜设置地过大,尽量保证简洁明了,减少对于内存的占用。通常来说,当一个单独存储的value值大于10kb的时候就会被认为是bigkey了。

实际上在redis的内部实现中, 对于 set  key  "some string" 这样的指令而言,底层的c语言会自己构建一个称为SDS的结构体(类似java里面的类对象)进行存储。这个结构体包含了下边信息:


struct sdshdr {
int len;
int free;
char buf[];
}


内部没有直接采用c语言自带的字符串,好处有以下几点:减少原先繁琐的内存扩增问题。(会根据初始化的值,提前给出更多的空间,避免出现空间溢出问题)


通过空间预分配机制来减少内存重分配问题。(其实内存重分配是一个非常复杂的过程,需要惊动到os操作系统层面的修改, 其中涉及到了非常复杂的操作,因此sds在初始化过程中尽量帮我们把这块给优化处理) 针对bigkey而言,其实还有很多点可以注意;


1.对于hash,list,set,zset这类数据结构而言,尽量不要让其数目超过5000个。假设我们存储了一个大小为100万元素的zset数据结构到redis中,并且设计了1小时过期的机制,那么在元素到期时候触发 了删除操作,这将会对redis自身造成堵塞。


2.如何避免上述在删除过程中的堵塞情况?首先应该从根源上避免这类设计的存在,如果实际线上数据库存在这类数据信息,那么可以结合redis自身提供的机制 异步 删除机制 (需要redis4.0之后才具有)


3.bigkey是如何产生的?常见的产生bigkey场景:1)例如一些社交类产品,粉丝列表,为了减少对于db的访问,会根据注册用户的id来绑定相关的list结构存储粉丝信息,假若遇到了某些明星,大v,那么如果这个list没有做过相关的调优处理就很容易转换为一个bigkey。2)假设用list来存储用户缓存信息,当访问量增加的时候也很容易产生bigkey。3)将相关的数据存储到redis的一些复杂数据结构中(list,set相关类型)的时候,需要考虑,是否每个存储项的字段都有必要存入,如果是无关必要的字段则可以忽略掉。


4.如何对bigkey做优化处理?如果线上已经有存在这种情况的话,不建议直接暴力删除,最好是通过一些拆解手段来做平滑过滤。例如说一个list拆解为多个list1,list2,list3,如果是个map的话也是可以拆解为多个小map,另外提取元素的时候不要随意用hgetall这类占用网络带宽资源的指令。


面试官:嗯嗯,那你们之前的项目组里面会有做一些禁止命令的设置吗?防止某些不安全指令在线上环境产生造成危害。


小林:额,这个我就不是很清楚了,应该是要有的,平时没有太过注意。


面试官:嗯嗯,其实我们这边的生产环境是会精致实用keys,flushall,flushdb这类命令的,主要是通过rename机制来禁用掉它。


ps:可以借助redis内部的rename机制关闭危险指令的使用


通过修改redis.conf中的SECURITY项,在里头新增以下几行,即可实现对危险指令的禁用
rename-command FLUSHALL ""
rename-command FLUSHDB ""
rename-command CONFIG  "" 
rename-command KEYS    ""
对于命令的查询推荐通过scan来替代keys


面试官:看来你对于redis还是有些研究的啊。那么你能讲解下更深一些层次的缓存吗,例如说cpu层面的缓存管理机制?


小林此时突然脑袋一片空白,在面试之前并没有对操作系统底层的知识做过复习,一下子懵逼了。小林:这块我不是太了解。。面试官:哦,那我简单和你讲解下吧。


cpu内部的其实经常会需要用到内存中的数据做运算和读写操作,但是cpu的计算性能和内存的计算性能差距非常巨大,针对这类密集型计算的物件,后续人类发明了“缓存”的概念。早期的时候人们只发明了一级缓存,后来又增加了L2,L3级别的缓存。


将缓存分为了L1,L2,L3,其速度值大小为L3<L2<L1,当cpu需要获取数据的时候会先从自己的寄存器中提取数据,然后再从L1中查询,L1都能查询的缓存数据若没有命中,则会返回到L2查询,如果L2也查询不到就会追溯到L3查询,通常情况下L3中能够命中80%的数据信息。L3如果没有命中数据则会到内存里面查询。


为什么要分这么多级的缓存?因为不同级别的缓存速度差异都巨大,运算越快的缓存制作难度越高,成本也越高。


为什么不把java程序存储到cpu的多级缓存中呢?别逗了,cpu的多级缓存是数据内核层面的东西,java存储的数据是属于jvm虚拟机层面的玩意,两者根本不在一个层面上,而且cpu的多级缓存存储空间相对于内存而言也非常小。


在面对这么多级的缓存数据中,如何保证查询数据的正确和有效呢?于是便有了一个叫做MESI的缓存一致性协议。



image.png


听了面试官的简单介绍后,小林似乎发现自己还是有部分知识体系存在不足之处,连上露出了尴尬而不失礼貌的微笑。


image.png


面试官: 好吧,那我们继续。你可以讲解下之前自己在秒杀项目中使用到的缓存设计方案吗?例如说一些机器的部署方面?


小林:emmmm,让我思考一下先(脑海中疯狂回忆以前学过的笔记内容点和工作接触点) (一两分钟后....) 嗯呢,我思绪准备好了。之前的项目中采用的是sentinel架构来进行设计redis存储。


sentinel一共部署了三台机器,一台作为主机,另外两台作为从机器,每台机器上边都设立了一个哨兵的角色,当主节点出现异常的时候会有从节点来顶替。


以前在预发布环境曾经遇到过一个问题,当时是做容灾模拟演练,当日模拟中将redis主节点和从节点之间的网络做了截断操作,导致从节点的机器一直没有和主节点的机器进行网络通信,于是此时便从从机器中选定了一台机器作为主节点,在主从做切换期间redis服务曾经出现过异常中断服务的情况。


image.png


面试官:嗯嗯,那么请你讲解下redis主从切换过程中可能会遇到哪些生产的异常问题呢?


小林:嗯嗯,我大概知道那么几个常见的场景吧。例如说在一些结合redis用的分布式锁,在这一时刻可能会失效,假设说秒杀活动的高峰期,主节点挂了,那么分布式锁就会失效,可能会引发后续一连串可怕的事情发生,因此对于接口的压测和限流是非常重要的。


emm,还有的话例如说一些知识在redis中存储并没有实际落入db做持久化的数据也会丢失,假设一些购物车中存放的数据,可能会在主从切换中的那段时间里面突然发现 "加入购物车" 失效了!


面试官:嗯嗯,那么你对于主从切换中的选举原理了解吗?可以简单介绍下吗?


小林:emmm,这块并不是特别了解。


ps:关于redis的sentinel架构采用到的raft选举算法考点 https://blog.csdn.net/sanwenyublog/article/details/53385616


面试官:好吧。那你在做redis设计的时候主要的目的还是为了防止请求进入到db层面,在这方面还有哪些细节点也需要注意到的吗?


小林 : 需要注意一下缓存的过期时间,假设某些热点数据是同时存入到redis的话,那么它们的过期时间最好是能够做成随机值,防止出现时间到达后缓存大面积失效,导致缓存击穿的情况大规模发生。


面试官:嗯嗯,关于缓存的模块大概就先问到这里吧。你还有什么需要问我吗?


看来这次面试似乎小林在面试官前的表现已经达到了入职的技术要求,距离成功上岸似乎还差一步之遥的感觉。


小林:嗯嗯,请问我还有机会吗?


面试官:你先回家等下通知吧,我们这边和hr商量一下再做决定。


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
2月前
|
存储 缓存 NoSQL
数据的存储--Redis缓存存储(一)
数据的存储--Redis缓存存储(一)
107 1
|
7天前
|
存储 缓存 NoSQL
解决Redis缓存数据类型丢失问题
解决Redis缓存数据类型丢失问题
131 85
|
2月前
|
存储 缓存 NoSQL
数据的存储--Redis缓存存储(二)
数据的存储--Redis缓存存储(二)
53 2
数据的存储--Redis缓存存储(二)
|
2月前
|
消息中间件 缓存 NoSQL
Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。
【10月更文挑战第4天】Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。随着数据增长,有时需要将 Redis 数据导出以进行分析、备份或迁移。本文详细介绍几种导出方法:1)使用 Redis 命令与重定向;2)利用 Redis 的 RDB 和 AOF 持久化功能;3)借助第三方工具如 `redis-dump`。每种方法均附有示例代码,帮助你轻松完成数据导出任务。无论数据量大小,总有一款适合你。
84 6
|
2月前
|
缓存 NoSQL 关系型数据库
redis和缓存及相关问题和解决办法 什么是缓存预热、缓存穿透、缓存雪崩、缓存击穿
本文深入探讨了Redis缓存的相关知识,包括缓存的概念、使用场景、可能出现的问题(缓存预热、缓存穿透、缓存雪崩、缓存击穿)及其解决方案。
232 0
redis和缓存及相关问题和解决办法 什么是缓存预热、缓存穿透、缓存雪崩、缓存击穿
|
5天前
|
缓存 监控 NoSQL
Redis经典问题:缓存穿透
本文详细探讨了分布式系统和缓存应用中的经典问题——缓存穿透。缓存穿透是指用户请求的数据在缓存和数据库中都不存在,导致大量请求直接落到数据库上,可能引发数据库崩溃或性能下降。文章介绍了几种有效的解决方案,包括接口层增加校验、缓存空值、使用布隆过滤器、优化数据库查询以及加强监控报警机制。通过这些方法,可以有效缓解缓存穿透对系统的影响,提升系统的稳定性和性能。
|
1月前
|
缓存 NoSQL 关系型数据库
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
本文详解缓存雪崩、缓存穿透、缓存并发及缓存预热等问题,提供高可用解决方案,帮助你在大厂面试和实际工作中应对这些常见并发场景。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
|
1月前
|
存储 缓存 NoSQL
【赵渝强老师】基于Redis的旁路缓存架构
本文介绍了引入缓存后的系统架构,通过缓存可以提升访问性能、降低网络拥堵、减轻服务负载和增强可扩展性。文中提供了相关图片和视频讲解,并讨论了数据库读写分离、分库分表等方法来减轻数据库压力。同时,文章也指出了缓存可能带来的复杂度增加、成本提高和数据一致性问题。
【赵渝强老师】基于Redis的旁路缓存架构
|
1月前
|
缓存 NoSQL Redis
Redis 缓存使用的实践
《Redis缓存最佳实践指南》涵盖缓存更新策略、缓存击穿防护、大key处理和性能优化。包括Cache Aside Pattern、Write Through、分布式锁、大key拆分和批量操作等技术,帮助你在项目中高效使用Redis缓存。
280 22
|
1月前
|
缓存 NoSQL PHP
Redis作为PHP缓存解决方案的优势、实现方式及注意事项。Redis凭借其高性能、丰富的数据结构、数据持久化和分布式支持等特点,在提升应用响应速度和处理能力方面表现突出
本文深入探讨了Redis作为PHP缓存解决方案的优势、实现方式及注意事项。Redis凭借其高性能、丰富的数据结构、数据持久化和分布式支持等特点,在提升应用响应速度和处理能力方面表现突出。文章还介绍了Redis在页面缓存、数据缓存和会话缓存等应用场景中的使用,并强调了缓存数据一致性、过期时间设置、容量控制和安全问题的重要性。
43 5

相关实验场景

更多