首页 > 数据库 > 数据库开发技术 > 上市公司,面试过了但测评卡住,有点懵逼
测评都能卡住了,是万万没想到的
今天分享一个上市公司社招后端Golang开发工程师的艰难历程。
走的内推渠道进入了该公司的社招面试流程,但简历筛选通过后,很快收到了HR的邮件。
以为是面试邀请邮件这么简单,但是想多了,毕竟是上市公司,流程那是一套一套的标准化。
共收到了三封邮件,第一封是视频面试邀请;一封是社招自适应能力测评,实际上是自适应cate测评,另外一封是价值观测评。
这两封邮件对应的是两个流程,一边是面试,一边是测评,测评包括:cata测评和价值观测评(没有对错)。
两个流程同步进行,任意流程不通过,则另外一个流程中断,等待确认。
而这位同学呢,对于自适应cata测评也是一无所知,后来咨询我了之后,这部分是顺利通过了。
但是另外一个价值观测评虽然没有对与错,但是第一波面试的时候却被面试官告知:cata 测评分数挺高的,但是价值观分数有点低,应该问题不大,到时候说明情况就OK了,我们继续面试。
听这位同学讲述,面试官还挺Nice的,这种情况下没有为难他,而是让他继续面试,他也认真的对待努力表现自己的经验和优势。
第二天果然面试通过了,并安排了下一轮的视频面试。
在第二波面试中,他才知道因为价值观测评这事,第一波面试官已经帮他申请走了内部特批流程,他也没放在心上,然后面试过程中也正常发挥着。
没想到,两天后,该同学跟跟内推的那位朋友资讯第二波进展情况,结果尴尬的一幕发生了,这同学也是懵逼了。
“第二轮面试也通过了,但是因为价值观测评分数这事在走内部特批流程还没下来,没办法让你继续后面流程”
说来也神奇,说好的价值观测评没有对错,只为匹配更适合的职业发展路径,但是流程竟然因为这个卡住了,估计是某些方面跟在招聘的能力确实不太匹配,毕竟到了今天这位同学还没有收到通知。
这么尴尬的局面,只能再等等了,祝好运~
我们也开始分享这位上市公司社招后端Golang开发工程师的面经。
除了深挖项目部分,其他的总体难度不大。
应用层:为特定应用程序提供数据传输服务,例如 HTTP、DNS 等协议。数据单位为报文。表示层:数据压缩、加密以及数据描述,这使得应用程序不必关心在各台主机中数据内部格式不同的问题。会话层:建立及管理会话。传输层(运输层):为进程提供通用数据传输服务。由于应用层协议很多,定义通用的传输层协议就可以支持不断增多的应用层协议。运输层包括两种协议:传输控制协议 TCP,提供面向连接、可靠的数据传输服务,数据单位为报文段;用户数据报协议 UDP,提供无连接、尽最大努力的数据传输服务,数据单位为用户数据报。TCP 主要提供完整性服务,UDP 主要提供及时性服务。网络层:为主机提供数据传输服务。而传输层协议是为主机中的进程提供数据传输服务。网络层把传输层传递下来的报文段或者用户数据报封装成分组。数据链路层:网络层针对的还是主机之间的数据传输服务,而主机之间可以有很多链路,链路层协议就是为同一链路的主机提供数据传输服务。数据链路层把网络层传下来的分组封装成帧。物理层:考虑的是怎样在传输媒体上传输数据比特流,而不是指具体的传输媒体。物理层的作用是尽可能屏蔽传输媒体和通信手段的差异,使数据链路层感觉不到这些差异。五层协议没有表示层和会话层,而是将这些功能留给应用程序开发者处理。
TCP/IP 协议:它只有四层,相当于五层协议中数据链路层和物理层合并为网络接口层。
TCP/IP 体系结构不严格遵循 OSI 分层概念,应用层可能会直接使用 IP 层或者网络接口层。
类型开关(Type Switch)是在运行时检查变量类型的最佳方式。类型开关按类型而不是值来评估变量。
每个 Switch 至少包含一个 case 用作条件语句,如果没有一个 case 为真,则执行 default。
new 创建一个该类型的实例,并且返回指向该实例的指针。new 函数是内建函数,函数定义:func new(Type) *Type
注意:
make 的作用是为 slice, map or chan 的初始化,然后返回引用。
make 函数是内建函数,函数定义:func make(Type, size IntegerType) Type
make(T, args)函数的目的和 new(T)不同,make仅仅用于创建 slice, map, channel 而且返回类型是实例。
defer 的作用
当定义 defer 语句时,跟在 defer 后面的函数会被延迟执行。直到包含该 defer 语句的函数执行完毕时,defer 后的函数才会被执行,不论包含 defer 语句的函数是通过 return 正常结束,还是由于 panic 导致的异常结束,defer 后的函数都会执行。
defer 的执行顺序
只需要在调用普通函数或方法前加上关键字 defer,就完成了 defer 所需要的语法。可以在一个函数中执行多条 defer 语句,它们的执行顺序与声明顺序相反。
defer 的常用场景
defer 语句经常被用于处理成对的操作,如打开、关闭、连接、断开连接、加锁、释放锁。 通过 defer 机制,不论函数逻辑多复杂,都能保证在任何执行路径下,资源被释放。 释放资源的 defer 应该直接跟在请求资源的语句后。
Go1.18之前 Go 中切片扩容的策略是这样的:
Go1.18开始内置函数Append对切片的扩容算法发生变化:和Go 1.17以1024作为大小分界不同,Go 1.18使用256作为threshold
MySQL中的in语句是把外表和内表作hash 连接,而exist语句是对外表作loop循环,每次loop循环再对内表进行查询。
我们可能认为exists比in语句的效率要高,这种说法其实是不准确的,要区分情景:
如果查询的两个表大小相当,那么用in和exists差别不大。
如果两个表中一个较小,一个是大表,则子查询表大的用exists,子查询表小的用in。
not in 和not exist:如果查询语句使用了not in,那么内外表都进行全表扫描,没有用到索引;而not exist的子查询依然能用到表上的索引。所以无论那个表大,用not exists都比not in要快。
客户端:最上层的服务并不是MySQL所独有的,大多数基于网络的客户端/服务器的工具或者服务都有类似的架构。比如连接处理、授权认证、安全等等。Server层:大多数MySQL的核心服务功能都在这一层,包括查询解析、分析、优化、缓存以及所有的内置函数(例如,日期、时间、数学和加密函数),所有跨存储引擎的功能都在这一层实现:存储过程、触发器、视图等。存储引擎层:第三层包含了存储引擎。存储引擎负责MySQL中数据的存储和提取。Server层通过API与存储引擎进行通信。这些接口屏蔽了不同存储引擎之间的差异,使得这些差异对上层的查询过程透明。MySQL的默认存储引擎是InnoDB,它采用的是B+树结构的索引。
B+树:只有叶子节点才会存储数据,非叶子节点只存储键值。叶子节点之间使用双向指针连接,最底层的叶子节点形成了一个双向有序链表。
在这张图里,有两个重点:
磁盘块,可以看到每个磁盘块包含几个数据项(粉色所示)和指针(黄色/灰色所示),如根节点磁盘包含数据项(左开右闭)17和35,包含指针P1、P2、P3,P1表示小于等于17的磁盘块,P2表示在大于17和小于等于35之间的磁盘块,P3表示大于35的磁盘块。真实的数据存在于叶子节点即3、4、5……、65。非叶子节点不存储真实的数据,只存储指引搜索方向的数据项,如17、35并不真实存在于数据表中。双向指针连接,最底层的叶子节点形成了一个双向有序链表,可以进行范围查询。在InnoDB存储引擎里,利用辅助索引查询,先通过辅助索引找到主键索引的键值,再通过主键值查出主键索引里面没有符合要求的数据,它比基于主键索引的查询多扫描了一棵索引树,这个过程就叫回表。
建议:在使用前最好还是先用 explain 来试试到底sql语句走不走索引,然后选择较优的sql。
先来看一个常见的缓存使用方式:读请求来了,先查下缓存,缓存有值命中,就直接返回;缓存没命中,就去查数据库,然后把数据库的值更新到缓存,再返回。
缓存穿透:指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,进而给数据库带来压力。
通俗点说,读请求访问时,缓存和数据库都没有某个值,这样就会导致每次对这个值的查询请求都会穿透到数据库,这就是缓存穿透。
缓存穿透一般都是这几种情况产生的:
业务不合理的设计,比如大多数用户都没开守护,但是你的每个请求都去缓存,查询某个userid查询有没有守护。
业务/运维/开发失误的操作,比如缓存和数据库的数据都被误删除了。
黑客非法请求攻击,比如黑客故意捏造大量非法请求,以读取不存在的业务数据。
如何避免缓存穿透呢?一般有三种方法
1.如果是非法请求,我们在API入口,对参数进行校验,过滤非法值。
2.如果查询数据库为空,我们可以给缓存设置个空值,或者默认值。但是如有有写请求进来的话,需要更新缓存哈,以保证缓存一致性,同时,最后给缓存设置适当的过期时间。(业务上比较常用,简单有效)
3.使用布隆过滤器快速判断数据是否存在。即一个查询请求过来时,先通过布隆过滤器判断值是否存在,存在才继续往下查。
布隆过滤器(BloomFilter)是一种数据结构。特点是存在性检测:
如果布隆过滤器中不存在,那么实际数据一定不存在; 如果布隆过滤器中存在,实际数据不一定存在。 相比于传统数据结构(如:List、Set、Map等)来说,它更高效,占用空间更少。缺点是它对于存在的判断是具有概率性。
缓存雪奔:指缓存中数据大批量到过期时间,而查询数据量巨大,请求都直接访问数据库,引起数据库压力过大甚至down机。
产生的原因:缓存雪奔一般是由于大量数据同时过期造成的
解决:
可通过均匀设置过期时间解决,即让过期时间相对离散一点。如采用一个较大固定值+一个较小的随机值,5小时+0到1800秒酱紫。
Redis 故障宕机也可能引起缓存雪奔。这就需要构造Redis高可用集群啦。
缓存击穿:指热点key在某个时间点过期的时候,而恰好在这个时间点对这个Key有大量的并发请求过来,从而大量的请求打到db。
缓存击穿与缓存雪崩的区别:看着有些像但其实是有区别的,缓存雪奔是指数据库收到大量的请求,造成压力过大甚至down机;缓存击穿只是大量并发请求到了DB数据库层面。可以认为击穿是缓存雪奔的一个子集吧。有些文章认为它俩区别,是区别在于击穿针对某一热点key缓存,雪奔则是很多key。
解决方案就有两种:
考察点:性能指标的理解、执行性能测试的工具与方法、性能瓶颈的判断等
响应时间(Response Time):衡量系统或服务处理请求并返回响应的速度。对于用户来说,这是直接影响体验的重要指标。响应时间应根据业务场景设定合理阈值,如对于网页加载期望在2-3秒内完成。
吞吐量(Throughput):单位时间内系统能够处理的请求数量或事务数量,如TPS(Transactions Per Second)或QPS(Queries Per Second)。吞吐量的设定应基于系统设计容量和预期用户量来确定。
TPS(Transactions Per Second):即每秒事务数,是衡量系统处理事务能力的一个关键性能指标,广泛应用于数据库、支付系统、电子商务平台等需要处理复杂业务逻辑的应用场景。TPS强调的是“事务”这一概念,每个事务可能包含一个或多个操作,完成一定的业务逻辑,而不仅仅是一个简单的查询或请求。
QPS(Queries Per Second):即每秒查询率,是衡量系统处理能力的一个重要性能指标,尤其是对于那些以处理大量查询请求为主要任务的服务,如数据库、搜索引擎、API服务器等。QPS表示服务器在一秒内可以处理的查询请求的数量。
并发用户数(Concurrency):系统同时处理的用户请求的数量,反映了系统的并发处理能力。设定时需考虑高峰时段的用户访问模式和系统承受的最大并发压力。
资源利用率(Resource Utilization):包括CPU使用率、内存使用率、磁盘I/O、网络带宽等,用于评估系统资源的使用效率和是否存在瓶颈。合理区间依硬件配置和应用需求而定,避免长时间满载导致性能下降或不稳定。
计算QPS的基本公式是:
QPS = 总请求数时间周期(秒)QPS / 时间周期(秒)总请求数
然而,实际计算中可能会根据不同的上下文和可用数据采用不同的变体,以下是几种常见的计算方式:
上述公式计算QPS。QPS=并发量平均响应时间QPS=平均响应时间并发量
这里并发量指的是系统同时处理的请求数量,平均响应时间是从请求发出到接收到响应的平均时间
当需要预估系统的峰值处理能力时,可能会使用二八原则来估算峰值时间的请求量,然后除以该时间段的秒数得到QPS。
峰值时间QPS=总PV数×80%(每天秒数×20%)峰值时间QPS=(每天秒数×20%)总PV数×80%
测试工具:这里分享我们个人可以免费、快速使用的工具
负载均衡:
缓存策略:
数据库优化:
异步处理与消息队列:
以上,觉得有用点亮【赞与在看】让我们心中充满力量、披荆斩棘、挑战自我。
推荐阅读
2024年最新,被卷到“准下岗”的9种编程语言,哪些你还在用?
关于作者:一位热爱技术,并在职场与自媒体间探索的实践者,希望通过分享个人经验和见解,帮助更多人实现自我成长和价值。
如果您对我的公众号内容感兴趣,欢迎关注我。
也可以在下方添加我微信和一支烟花AI社区技术群,一起交流技术、职场心得。
原文作者:皇子谈技术;如有侵权,请联系管理员删除。