2020年抖音、腾讯、阿里、美团春招服务端开发岗位硬核面试(下)

本文详细分析了互联网大厂面试中的技术问题,涵盖Jenkins使用、MVCC在MySQL中的作用、ORDER BY实现、分布式锁的多种实现方式、Eureka和Hystrix原理、Zookeeper的角色与协议、序列化方法、DNS原理、HTTP的握手机制、算法问题(如扑克牌概率、寻找第K大元素)以及如何处理服务升级的兼容性等。此外,还讨论了Java的面试准备和职业规划。
摘要由CSDN通过智能技术生成

更多信息,请查看代与码社区

在上一篇 我们中,我们分享了几大互联网公司面试的题目,本文就来详细分析面试题答案以及复习参考和整理的面试资料,小民同学的私藏珍品🐶。
首先是面试题答案公布,在讲解时我们主要分成如下几块:语言的基础知识、中间件、操作系统、计算机网络、手写算法、开放题和项目经历。对面试题和涉及的知识点进行整理,这样更容易让各位同学理解。不会按照提问的顺序进行讲解,还请见谅。
其次是 Java 复习参考和整理的面试资料。由于内容比较多,学习有 道 非常重要,我们介绍一下其中的要点和目录,完整文件可以参见笔者提供的 pdf 资料。

面试题解析

jenkins 如何使用?

jenkins 涉及到 DevOps 相关的知识,主要用于自动化集成,持续、自动地构建/测试软件项目,监控一些定时任务。
持续集成(CI)已成为当前许多软件开发团队在整个软件开发生命周期内侧重于保证代码质量的常见做法。它是一种实践,旨在缓和和稳固软件的构建过程。
MVCC 在读方面有什么用途
MySQL的大多数事务型存储引擎实现的都不是简单的行级锁。基于提升并发性能的考虑,它们一般都同时实现了多版本并发控制(MVCC)。不同存储引擎的 MVCC 实现是不同的,典型的有乐观(optimistic)并发控制和悲观(pessimistic)并发控制。
MVCC 简单来讲就是对数据库的任何修改的提交都不会直接覆盖之前的数据,而是产生一个新的版本与老版本共存,使得读取时可以完全不加锁。这样读某一个数据时,事务可以根据隔离级别选择要读取哪个版本的数据。过程中完全不需要加锁。
可以认为 MVCC 是行级锁的一个变种,但是它在很多情况下避免了加锁操作,因此开销更低。大多数的 MVCC 都实现了非阻塞的读操作,写操作也只锁定必要的行。MVCC只能在可重复读和可提交读的隔离级别下生效。不可提交读不能使用它的原因是不能读取符合事务版本的行版本。它们总是读取最新的行版本。可序列化不能使用MVCC的原因是,它总是要锁定行。
MVCC 的实现,是通过保存数据在某个时间点的快照来实现的。也就是说,不管需要执行多长时间,每个事务看到的数据是一致的。根据事务开始的时间不同,每个事务对同一张表,同一时刻看到的数据可能是不一样的。
mysql orderby 如何实现
MySQL有两种方式可以实现ORDER BY:

通过索引扫描生成有序的结果
使用文件排序(filesort)

利用有序索引获取有序数据
取出满足过滤条件作为排序条件的字段,以及可以直接定位到行数据的行指针信息,在 Sort Buffer 中进行实际的排序操作,然后利用排好序的数据根据行指针信息返回表中取得客户端请求的其他字段的数据,再返回给客户端。
这种方式,在使用explain分析查询的时候,显示Using index。而文件排序显示Using filesort。
SQL语句中,WHERE子句和ORDER BY子句都可以使用索引:WHERE 子句使用索引避免全表扫描,ORDER BY 子句使用索引避免 filesort(用“避免”可能有些欠妥,某些场景下全表扫描、filesort 未必比走索引慢),以提高查询效率。
虽然索引能提高查询效率,但在一条 SQL 里,对于一张表的查询 一次只能使用一个索引(注:排除发生 index merge 的可能性),也就是说当 WHERE 子句与ORDER BY子句要使用的索引不一致时,MySQL 只能使用其中一个索引(B+树)。
也就是说,一个既有WHERE又有ORDER BY的SQL中,使用索引有三个可能的场景:

只用于WHERE子句 筛选出满足条件的数据
只用于ORDER BY子句 返回排序后的结果
既用于WHERE又用于ORDER BY,筛选出满足条件的数据并返回排序后的结果

文件排序(filesort)
filesort仅仅是排序而已,是否会放入磁盘看情况而定。filesort是否会使用磁盘取决于它操作的数据量大小。总结来说就是,filesort按排序方式来划分 分为两种:

数据量小时,在内存中快排
数据量大时,在内存中分块快排,再在磁盘上将各个块做归并

数据量大的情况下涉及到磁盘 io,所以效率会低一些。根据回表查询的次数,filesort又可以分为两种方式:

回表读取两次数据(two-pass):两次传输排序
回表读取一次数据(single-pass):单次传输排序

单次传输排序的弊端在于会将所有涉及到的列都放入排序缓冲区,排序缓冲区一次能放下的tuples更少了,进行归并排序的概率增大。列数据量越大,需要的归并路数更多,增加了额外的I/O开销。所以列数据量太大时,单次传输排序的效率可能还不如两次传输排序。
两次传输排序会进行两次回表操作:第一次回表用于在WHERE子句中筛选出满足条件的rowid以及rowid对应的ORDER BY的列值;第二次回表发生在ORDER BY子句对指定列进行排序之后,通过rowid回表查出SELECT子句需要的字段信息。
mysql 索引 orderby 之后的字段要不要加进去
对于order by字段加入索引本身这个问题,如果最终的结果集是以order by字段为条件筛选的,将order by字段加入索引,并放在索引中正确的位置,会有明显的性能提升。
eureka源码
Eureka 是 Spring Cloud 推荐的服务注册与发现组件,包括Eureka Server 和 Eureka Client:

Eureka Server提供服务注册服务,各个节点启动后,会在Eureka Server中注册,这样Server中的服务注册表中将会存储所有可用的服务节点的信息;

Eureka Client是一个Java客户端,用于简化与Eureka Server交互,客户端同时具备一个内置的、使用轮询负载均衡算法的负载均衡器;

在应用启动后,将会向Eureka Server发送心跳(默认周期30s),如果Eureka Server在多个心跳周期没有收到某个节点的心跳,Eureka Server会从服务注册表中把这个服务节点删除(默认为90s);

Eureka Server之间通过复制的方式完成数据的同步;

Eureka Client具有缓冲机制,如果Eureka Server全部宕机的情况,客户端依然可以利用缓存的信息消费其他服务API;

Eureka region可以理解为地理上的分区,没有具体大小的限制;

Eureka zone可以理解为region内具体的机房信息;

使用Jersey框架实现自身的Restful HTTP接口,peer之间同步与服务注册通过HTTP协议实现,定时任务(发送心跳、定时清理过期服务、节点同步等)通过JDK自带的Timer实现,内存缓存实现Google的guava实现;

当服务注册中心Server检测服务提供者宕机时,在服务中心将服务置为DOWN状态,并将该服务向其他订阅者发布,订阅者更新本地缓存信息;

当Eureka Server节点在短时间内丢失过多的客户端时,这个节点会进入自我保护模式,不再注销任何服务;

Hystrix源码
分布式系统环境下,服务间类似依赖非常常见,一个业务调用通常依赖多个基础服务。如下图,对于同步调用,当库存服务不可用时,商品服务请求线程被阻塞,当有大批量请求调用库存服务时,最终可能导致整个商品服务资源耗尽,无法继续对外提供服务。并且这种不可用可能沿请求调用链向上传递,这种现象被称为雪崩效应。
Hystrix 中文含义是豪猪,因其背上长满棘刺,从而拥有了自我保护的能力。
Hystrix设计目标

对来自依赖的延迟和故障进行防护和控制——这些依赖通常都是通过网络访问的
阻止故障的连锁反应
快速失败并迅速恢复
回退并优雅降级
提供近实时的监控与告警

Hystrix遵循的设计原则

防止任何单独的依赖耗尽资源(线程)
过载立即切断并快速失败,防止排队
尽可能提供回退以保护用户免受故障
使用隔离技术(例如隔板,泳道和断路器模式)来限制任何一个依赖的影响
通过近实时的指标,监控和告警,确保故障被及时发现
通过动态修改配置属性,确保故障及时恢复
防止整个依赖客户端执行失败,而不仅仅是网络通信

分布式锁实现方式?
分布式锁在微服务架构中很常用,主要有几下实现方式:
基于数据库做分布式锁
基于表主键唯一做分布式锁
思路:利用主键唯一的特性,如果有多个请求同时提交到数据库,数据库只会保证只有一个操作可以成功,那么就可以认为操作成功的线程获取到了该方法的锁。当方法执行完毕之后,通过删除该行数据就可释放锁。

单点数据库导致强依赖,可以通过多数据库主从切换
通过定时器删除超时数据避免死锁
通过自旋CAS的方式插入实现阻塞
可重入可以通过检查对应的记录是否存在实现
公平锁可以通过等待线程表的方式实现
在大并发的情况下,通过主键冲突防重容易导致锁表,尽量在程序中生产主键进行防重

基于表字段版本号做分布式锁
基于mysql的mvcc机制,只有版本号一致才能进行对应的修改,修改后版本号加1,通过CAS的方式进行修改。
基于数据库排他锁做分布锁
通过事务和 for update 语句实现,数据库会在该事务下给数据库增加排他锁。在 InnoDB 引擎加锁的时候,只有通过索引进行检索的时候才会使用行级锁,否则使用表级锁。
基于Redis做分布锁
基于Redis的SETNX()、EXPIRE()方法做分布式锁
setnx 方法的语义为 SET if Not Exists,其主要有两个参数,setnx(key, value)。该方法是原子的,如果 key 不存在,则设置当前的 key 成功,返回1;如果当前 key 已经存在,则设置当前 key 失败,返回 0。
expire 方法设置过期时间,setnx 命令不能设置 key 的超时时间,只能通过 expire() 来对 key 设置。
如果在 setnx 执行成功后,在 expire 命令执行成功,执行的线程出现宕机的现象,就可能出现死锁现象。
基于 Redis 的 SETNX()、GET()、GETSET() 方法做分布式锁

getset方法有两个参数 getset(key,newValue)。该方法是原子的,对 key 设置 newValue 这个值,并且返回 key 原来的旧值。假设 key 原来是不存在的,那么多次执行这个命令,会出现下边的效果:getset(key, “value1”) 返回 null 此时 key 的值会被设置为 value1;getset(key, “value2”) 返回 value1 此时 key 的值会被设置为 value2。
使用步骤:

setnx(lockkey,当前时间+过期超时时间),如果返回 1,则获取锁成功;如果返回0则没有获取到锁,转向2。
get(lockkey) 获取值 oldExpireTime,并将这个 value 值与当前的系统时间进行比较,如果小于当前系统时间,则认为这个锁已经超时,可以允许别的请求重新获取,转向 3。

计算 newExpireTime = 当前时间 + 过期超时时间,然后 getset(lockkey, newExpireTime) 会返回当前 lockkey 的值 currentExpireTime。
判断 currentExpireTime 与 oldExpireTime 是否相等,如果相等,说明当前 getset 设置成功,获取到了锁。如果不相等,说明这个锁又被别的请求获取走了,那么当前请求可以直接返回失败,或者继续重试。
在获取到锁之后,当前线程可以开始自己的业务处理,当处理完毕后,比较自己的处理时间和对于锁设置的超时时间,如果小于锁设置的超时时间,则直接执行 delete 释放锁;如果大于锁设置的超时时间,则不需要再锁进行处理。
基于 REDLOCK 做分布式锁
Redlock 是 Redis 的作者开发的集群模式的 Redis 分布式锁,它基于 N 个完全独立的 Redis 节点.
客户端获取当前时间,以毫秒为单位。客户端尝试获取N个节点的锁(每个节点获取锁的方式和前面说的缓存锁一样),N 个节点以相同的 key 和 value 获取锁。客户端需要设置接口访问超时,接口超时时间需要远远小于锁超时时间,比如锁自动释放的时间是 10s,那么接口超时大概设置5-50ms。这样可以在有redis节点宕机后,访问该节点时能尽快超时,而减小锁的正常使用。
客户端计算在获得锁的时候花费了多少时间,方法是用当前时间减去在步骤一获取的时间,只有客户端获得了超过3个节点的锁,而且获取锁的时间小于锁的超时时间,客户端才获得了分布式锁。
客户端获取的锁的时间为设置的锁超时时间减去步骤三计算出的获取锁花费时间。
如果客户端获取锁失败了,客户端会依次删除所有的锁。
基于 REDISSON 做分布式锁
Redisson 是 redis 官方的分布式锁组件。在Redisson中,如果如果线程获取锁成功,Redisson会在后台起开一个定时任务watchdog,定时任务会定时检查调用renewExpirationAsync(threadId)对锁进行续约。定时调度的时间差为internalLockLeaseTime/3,即10秒&#x

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值