技术架构面试
2024-06-03 20:31:32 1 举报
AI智能生成
在技术架构面试中,应聘者需要展示他们对技术架构的深刻理解和实践经验。面试可能涉及各种核心内容,如系统设计、可扩展性、性能优化、安全性和可靠性。应聘者需要证明他们能够根据业务需求选择合适的技术栈,并设计高效、可扩展的解决方案。此外,面试还可能评估应聘者的沟通和团队协作能力,因为这些都是成功实施技术架构的关键因素。面试官可能会要求应聘者介绍他们过去的项目经验,以及他们在项目中扮演的角色。通过这种全方位的评估,公司可以找到具备深厚技术功底和丰富实践经验的技术架构师。
作者其他创作
大纲/内容
1.需要具备的三个认知
对架构设计的认知
架构设计一定要立足于点、连接成线,扩散成面
为什么要做架构拆分
为什么要做系统解耦
为什么要职责单一
为什么要关注开发效率
架构拆分其实是管理在上技术上提效的一种手段
对分析问题的认知
业务方:关注点在系统能力
管理者:关注点在人效管理
技术人员:系统设计原则
针对系统现阶段业务发展带来的主要矛盾提出,才会更有价值且被认可
如果研发效率不能匹配业务发展的速度,并且单靠加人不能解决问题,此阶段系统架构的核心原则就不能随意定义为要保证高可用和高性能
软件复杂性来源于2点
本质复杂性和偶然复杂性
架构最重要的是要解决本质复杂性
这包括人的复杂性和业务的复杂性
对能力边界的认知
2.如何用架构师视角讲解技术方案
谈复杂来源
复杂度评估
功能复杂度
要解决业务发展带来的系统耦合、开发效率缓慢问题
非功能复杂度
高性能
高可用
扩展性
安全性
低成本
谈解决方案
明确了系统主要复杂度后,就有了明确的方案设计目标
至少2-3种备选方案
谈评估标准
评估标准
功能性复杂度
非功能性复杂度
系统无单点
可水平扩展原则
可降级原则
限流
降级
熔断
不要陷入某个纯粹的技术点优劣之争,那样很难有结果,越大的项目越明显
作为技术人,考虑问题的方式要比具体的选型结果更为重要
谈技术实现
要讲出技术的实现原理,不要浮于表面的框架组合
3.CAP有关的分布式理论如何考察?
CAP理论描述了在出现网络分区的情况下,要在C和A之间做取舍,所以会影响站在调用端的视角看系统是不可用的
CAP理论要用于指导在系统设计时需要衡量的因素,而非进行绝对的选择
子主题
技术认知
解题思路
4.亿级商品存储下,如何深度回答分布式系统的原理性问题
如何设计一个支持海量商品存储的高扩展性架构?
分布式数据存储
数据分片
数据复制
数据副本
数据容错
数据一致性
协议算法
在做分库分表时,基于Hash和一致性Hash的数据分片是如何实现的?
Hash分片缺点
扩展性不足
基于虚拟节点的一致性Hash
将存储节点和数据都映射到一个首尾相连你的哈希环上
存储节点一般可以根据ip地址进行hash计算
数据的存储位置是从数据映射在环上的位置开始,依照顺时针方向所找到的第一个存储节点
优点: 数据可以均匀底分配到各节点,并发写入性能表现也不错
缺点:虽然提高了稳定性,但通过Hash分片的方式很难针对热点商品做单独的架构设计
在电商大促期间,如何对热点商品数据做存储策略?
Range范围分片
子主题
要达到这种灵活性,更好的方式是基于分片元数据,元数据包括数据分片信息、数据量、读写QPS、分片副本的监控状态
如何保证分片元数据的可用性和数据一致性?
给分片元数据做集群服务,通过ETCD存储数据分片信息
每个数据存储实例节点定时向元数据服务集群同步心跳和分片信息
当调用端的请求来的时候,元数据服务节点只需要做好高可用和缓存即可
子主题
强一致性和最终一致性的数据共识算法是如何实现的?
ETCD的共识算法是什么?
还有哪些常用的共识算法?
子主题
5.海量并发场景下,如何解决分布式事务的一致性问题?
介绍目前主流实现分布式系统事务一致性的方案
回答出可实现的方案和关键知识点
分布式事务
2PC
准备阶段:协调者询问事务的所有参与者是否准备好提交,如果已经准备好提交回复 Prepared,否则回复 Non-Prepared。
执行阶段
协调者如果在准备阶段收到所有事务参与者回复的 Prepared 消息,就会首先在本地持久化事务状态为 Commit,然后向所有参与者发送 Commit 指令,所有参与者立即执行提交操作;否则,任意一个参与者回复了 Non-Prepared 消息,或任意一个参与者超时未回复,协调者都会将自己的事务状态持久化为“Abort”之后,向所有参与者发送 Abort 指令,参与者立即执行回滚操作。
缺点
单点问题
参与者等待协调者指令时无法做超时处理。一旦协调者宕机,所有参与者都会受到影响。如果协调者一直没有恢复,没有正常发送 Commit 或者 Rollback 的指令,那所有参与者都必须一直等待。
性能问题
两段提交过程中,所有参与者相当于被绑定成为一个统一调度的整体,期间要经过两次远程服务调用、三次数据持久化(准备阶段写重做日志,协调者做状态持久化,提交阶段在日志写入 Commit Record),整个过程将持续到参与者集群中最慢的那一个处理操作结束为止。这就决定了两段式提交的性能通常都比较差。
一致性风险
如果协调者在发出准备指令后,根据各个参与者发回的信息确定事务状态是可以提交的,协调者就会先持久化事务状态,并提交自己的事务。如果这时候网络忽然断开了,无法再通过网络向所有参与者发出 Commit 指令的话,就会导致部分数据(协调者的)已提交,但部分数据(参与者的)既未提交也没办法回滚,导致数据不一致。
子主题
3PC
三段式提交把原本的两段式提交的准备阶段再细分为两个阶段,分别称为 CanCommit、PreCommit,把提交阶段改为 DoCommit 阶段。其中,新增的 CanCommit 是一个询问阶段,协调者让每个参与的数据库根据自身状态,评估该事务是否有可能顺利完成。
将准备阶段一分为二的理由是,这个阶段是重负载的操作,一旦协调者发出开始准备的消息,每个参与者都将马上开始写重做日志,这时候涉及的数据资源都会被锁住。如果此时某一个参与者无法完成提交,相当于所有的参与者都做了一轮无用功
所以,增加一轮询问阶段,如果都得到了正面的响应,那事务能够成功提交的把握就比较大了,也意味着因某个参与者提交时发生崩溃而导致全部回滚的风险相对变小了。
因此,在事务需要回滚的场景中,三段式的性能通常要比两段式好很多,但在事务能够正常提交的场景中,两段式和三段式提交的性能都很差,三段式因为多了一次询问,性能还要更差一些。
同样地,也是因为询问阶段使得事务失败回滚的概率变小了,所以在三段式提交中,如果协调者在 PreCommit 阶段开始之后发生了宕机,参与者没有能等到 DoCommit 的消息的话,默认的操作策略将是提交事务而不是回滚事务或者持续等待。你看,这就相当于避免了协调者的单点问题。
缺点: 三段式提交对单点问题和回滚时的性能问题有所改善,但是对一致性风险问题并未有任何改进,甚至是增加了面临的一致性风险。为什么这么说呢?我们看一个例子。比如,进入 PreCommit 阶段之后,协调者发出的指令不是 Ack 而是 Abort,而此时因为网络问题,有部分参与者直至超时都没能收到协调者的 Abort 指令的话,这些参与者将会错误地提交事务,这就产生了不同参与者之间数据不一致的问题。
TCC
Try: 尝试执行阶段,完成所有业务可执行性的检查(保障一致性),并且预留好事务需要用到的所有业务资源(保障隔离性)。
Confirm:确认执行阶段,不进行任何业务检查,直接使用 Try 阶段准备的资源来完成业务处理。注意,Confirm 阶段可能会重复执行,因此需要满足幂等性。
Cancel:取消执行阶段,释放 Try 阶段预留的业务资源。注意,Cancel 阶段也可能会重复执行,因此也需要满足幂等性。
TCC 其实有点类似于 2PC 的准备阶段和提交阶段,但 TCC 是位于用户代码层面,而不是在基础设施层面,这就为它的实现带来了较高的灵活性,我们可以根据需要设计资源锁定的粒度。
缺点: TCC 最主要的限制是它的业务侵入性很强,但并不是指由此给开发编码带来的工作量,而是指它所要求的技术可控性上的约束。
Seata
Saga把大事务拆分成若干个子事务,T1,T2,…,Ti,…,Tn
每一个子事务都对应一个补偿动作,C1,C2,…,Ci,…,Cn
如果 Ti 事务提交失败,整个事务要回滚,通过补偿动作 Ci,…,C2,C1 依次恢复 T1,T2,…,Ti 事务造成的影响,回到初始状态。
Saga 模式适用于业务流程长,业务流程多且需要保证事务最终一致性的业务系统。
优点:一阶段提交本地数据库事务,无锁,高性能;补偿服务易于理解,易于实现。
缺点:Sage无法保证隔离性,需要额外加锁保证。
子主题
基于 MQ 的可靠消息投递方案
MQ 自动应答机制导致的消息丢失
要采取编程的方式手动发送应答
高并发场景下的消息积压导致消息丢失
定时任务扫描的方式,将未完成的消息重新投递来进行消息补偿。这是基于消息队列实现分布式事务的关键,是一种双向消息确认的机制。
基于MQ的可靠消息投递方案
子主题
如何落地
6.分布式系统中与锁有关的面试题
案例背景
分布式锁可能出现哪些问题
可用性问题:无论何时都要保证锁服务的可用性
死锁问题:客户端一定可以获取到锁,即使锁住某个已的客户端在释放锁之前崩溃或者网络不可达
脑裂问题:集群同步时产生的数据不一致,导致新的进程有可能拿到锁,但之前的进程以为自己还有锁,那么就出现两个进程拿到同一把锁的问题
基于MySQL实现分布式锁
悲观锁
select for update锁住这行,然后将查询和插入的SQL在同一个事务中提交
可能出现死锁
乐观锁
select amount, old_ver from order where order_id = ?
update order set ver = old_ver+1, amount = yyy where order_id = ? and ver = old_ver
基于分布式缓存Redis实现分布式锁
加锁
释放锁
Redis实现分布式锁优点
性能高效
实现方便
出现某个线程操作完成 setnx 之后,还没有来得及设置过期时间,线程就挂掉了,就会导致当前线程设置 key 一直存在,后续的线程无法获取锁,最终造成死锁的问题,所以要选型 Redis 2.6.12 后的版本或通过 Lua 脚本执行加锁和设置超时时间(Redis 允许将 Lua 脚本传到 Redis 服务器中执行, 脚本中可以调用多条 Redis 命令,并且 Redis 保证脚本的原子性)。
避免单点
因为 Redis 是跨集群部署的,自然就避免了单点故障
如何合理得设置超时时间
子主题
基于续约的方式设置超时时间
先给锁设置一个超时时间,然后启动一个守护线程,让守护线程在一段时间后,重新设置这个锁的超时时间。实现方式就是:写一个守护线程,然后去判断锁的情况,当锁快失效的时候,再次进行续约加锁,当主线程执行完成后,销毁续约锁即可。
如何解决集群情况下分布式锁的可靠性
RedLock
Redis 集群节点实现分布式锁会存在高可用的问题
由于 Redis 集群数据同步到各个节点时是异步的,如果在 Redis 主节点获取到锁后,在没有同步到其他节点时,Redis 主节点宕机了,此时新的 Redis 主节点依然可以获取锁,所以多个应用服务就可以同时获取到锁。
7.如何在面试中展示造轮子的能力
一次完整的RPC流程
子主题
BIO
子主题
子主题
子主题
NIO
子主题
子主题
子主题
8.如何回答消息队列的丢失、重复与积压问题
MQ作用
用MQ消息队列,可以隔离系统上下游环境变化带来的不稳定隐私
遇到秒杀等流量突增的场景,通过MQ还可以实现流量的削峰填谷作用,可以根据下有的处理能力自动调节流量
引入MQ带来的问题
会影响系统之间数据传输的一致性
会使消息端处理能力不足从而导致消息积压
如何保证消息不丢失
如何知道有消息丢失?
如何进行消息检测
具体怎么落地
全局唯一ID
哪些环节可能丢消息?如何确保消息不丢失?
怎么解决消息被重复消费的问题
怎么解决消息积压问题
这里有一个考点需要特别注意, 那就是在扩容消费者的实例数的同时,必须同步扩容主题 Topic 的分区数量,确保消费者的实例数和分区数相等。如果消费者的实例数超过了分区数,由于分区是单线程消费,所以这样的扩容就没有效果。
比如在 Kafka 中,一个 Topic 可以配置多个 Partition(分区),数据会被写入到多个分区中,但在消费的时候,Kafka 约定一个分区只能被一个消费者消费,Topic 的分区数量决定了消费的能力,所以,可以通过增加分区来提高消费者的处理能力。
9.MySQL索引原理及优化
如何优化这段SQL语句?
建立一个status和create_time的组合索引
MySQL索引底层使用的是什么数据结构和算法?
存储引擎
InnoDB 引擎
MyISAM引擎
Memoey引擎
索引类型
B+Tree索引
Hash索引
Full-Test索引
为什么MySQL InnoDB 选择B+Tree当默认的索引数据结构?
B+ Tree索引
主键索引查询:通过主键索引中的B+Tree树查询对应的叶子节点,然后回去整行数据
辅助索引: 先检索辅助索引中的B+Tree的商品编码,找到对应的叶子节点,获取主键值,然后通过主键索引中的B+Tree树查询对应的叶子节点,然后获取整行数据,这个过程叫回表
B+Tree 相比其他常见索引结构的优势在哪?
B+Tree相对B树的优势
子主题
B+Tree相对二叉树的优势
子主题
B+Tree相对Hash表的优势
如何通过执行计划查看索引的使用情况
子主题
有哪些情况会导致索引失效
子主题
平时有哪些常见的优化索引的方法
前缀索引优化
覆盖索引优化
联合索引优化
10.MySQL事务隔离级别和锁机制
Mysql事务隔离级别
脏读-读未提交
假设有 A 和 B 两个事务,在并发情况下,事务 A 先开始读取商品数据表中的数据,然后再执行更新操作,如果此时事务 A 还没有提交更新操作,但恰好事务 B 开始,然后也需要读取商品数据,此时事务 B 查询得到的是刚才事务 A 更新后的数据。
如果接下来事务 A 触发了回滚,那么事务 B 刚才读到的数据就是过时的数据,这种现象就是脏读。
不可重复读
事务 A 先读取一条数据,然后执行逻辑的过程中,事务 B 更新了这条数据,事务 A 再读取时,发现数据不匹配,这个现象就是“不可重复读”。
幻读
事务 A 读了一次商品表,得到最后的 ID 是 3,事务 B 也同样读了一次,得到最后 ID 也是 3。接下来事务 A 先插入了一行,然后读了一下最新的 ID 是 4,刚好是前面 ID 3 加上 1,然后事务 B 也插入了一行,接着读了一下最新的 ID 发现是 5,而不是 3 加 1。
这时,你发现在使用 ID 做判断或做关键数据时,就会出现问题,这种现象就像是让事务 B 产生了幻觉一样,读取到了一个意想不到的数据,所以叫幻读。当然,不仅仅是新增,删除、修改数据也会发生类似的情况。
避免数据库死锁
持有并等待:我们可以一次性申请所有的资源,这样就不存在等待了。
不可剥夺:占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源,这样不可剥夺这个条件就破坏掉了。
循环等待:可以靠按序申请资源来预防,也就是所谓的资源有序分配原则,让资源的申请和使用有线性顺序,申请的时候可以先申请资源序号小的,再申请资源序号大的,这样的线性化操作就自然就不存在循环了
数据库锁
行锁也解决不了幻读
间隙锁解决幻读
11.MySQL如何优化数据查询方案
互联网大部分系统是读多写少,读写请求的数量级可能达几个数量级
MySQL主从复制的原理
MySQL的主从复制依赖于binlog,也就是记录MySQL所有变化并以二进制的方式保存到磁盘上
复制的过程就是将binlog中的数据从主库传输到从库上
主从复制的3个阶段
阶段1
阶段2
阶段3
主从复制具体步骤
大促流量大时了,是不是主要多增加几个从库,就能抗住大促的并发读请求了
不是!
MySQL主从复制有哪些模型?
同步复制
事务线程要等所有的从库的复制成功响应
异步复制
事务线程完全不等侯从库的复制成功响应
半同步复制
MySQL5.7之后增加的一种复制方式,介入两者之间,事务线程不等侯所有从库的复制成功响应,主要一部分复制响应成功就行
如何在代码中区分主库和从库的访问?
MySQL数据复制的抽象能力
子主题
以 Raft 协议为例,其内部是通过日志复制同步的方式来实现共识的,例如在领导者选举成功后,它就会开始接收客户端的请求,此时每一个客户端请求都将被解析成一条指令日志,然后并行地向其他节点发起通知,要求其他节点复制这个日志条目,并最终在各个节点中回放日志,实现共识。
子主题
如果客户端将要执行的命令发送给集群中的一台服务器,那么这台服务器就会以日志的方式记录这条命令,然后将命令发送给集群内其他的服务,并记录在其他服务器的日志文件中,注意,只要保证各个服务器上的日志是相同的,并且各服务器都能以相同的顺序执行相同的命令的话,那么集群中的每个节点的执行结果也都会是一样的。
这种数据共识的机制叫复制状态机,目的是通过日志复制和回放的方式实现集群内所有节点的一致性
其实 MySQL 中的主从复制,通过 binlog 操作日志来实现从主库到从库的数据复制的,就是应用了这种复制状态机的机制。所以这种方式不是 MySQL 特有的。
基本所有的存储系统和数据库都用这样一套方法来解决数据复制和数据恢复问题
12.MySQL如何优化数据存储方案?
分库
单库的性能无法满足高并发的要求,就把并发请求分到多个数据库实例上
分表
当数据量过大,造成事务执行缓慢时
垂直拆分
把不同的业务数据进行隔离
垂直拆分可以把不同的业务数据进行隔离,让系统和数据更为“纯粹”,更有助于架构上的扩展。但它依然不能解决某一个业务的数据大量膨胀的问题,一旦系统中的某一个业务库的数据量剧增,比如商品系统接入了一个大客户的供应链,对于商品数据的存储需求量暴增,在这个时候,就要把数据拆分到多个数据库和数据表中,也就是对数据做水平拆分。
水平拆分
把单一表数据按照规则拆分到多个数据库和数据表
Hash分片
范围分片
垂直水平拆分
如何解决数据查询问题
13.掌握Redis哪些原理?
Redis属于单线程还是多线程
Redis只有单线程吗?
Redis是单线程,主要指Redis网络I/0线程,Redis的持久化、集群同步等操作,则是另外的线程完成的
Redis采用单线程为什么还这么快?
Redis大部分操作是在内存中完成的
单线程模型避免了多线程的竞争
Redis采用I/0多路复用机制处理大量的客户端的socket请求
Redis4.0 之前采用单线程快是上面的原因
Redis4.0版本之后,Redis添加了多线程的支持,但这时候的多线程主要体现在大数据的异步删除功能上,例如 unlink key、flushdb async、flushall async 等。
Redis6.0之后,新增多线程I/0的异步读写并发功能,虽然 Redis 一直是单线程模型,但是在 Redis 6.0 版本之后,也采用了多个 I/O 线程来处理网络请求,这是因为随着网络硬件的性能提升,Redis 的性能瓶颈有时会出现在网络 I/O 的处理上,所以为了提高网络请求处理的并行度,Redis 6.0 对于网络请求采用多线程来处理。但是对于读写命令,Redis 仍然使用单线程来处理。
Redis是如何实现数据不丢失的?
为了保证数据不丢失,要把内存中的数据存储到磁盘上,以免缓存服务器重启之后,还能从磁盘中恢复原有的数据,这个过程就是Redis的持久化
AOF日志:记录所有的操作命令,并以文本的形式追加到文件中
RDB快照:将某一时间的内存数据,以二进制的形式写入磁盘
混合持久化方式:Redis4.0新增混合持久化方式,集成了RDB和AOF的优点
Redis如何实现服务可用性?
Redis哨兵模式
Redis主从复制
这是 Redis 高可用服务的最基础的保证,实现方案就是将从前的一台 Redis 服务器,同步数据到多台从 Redis 服务器上,即一主多从的模式,这样我们就可以对 Redis 做读写分离了,来承载更多的并发操作,这里和 MySQL 的主从复制原理上是一样的。
Redis集群模式
14. 如何回答缓存穿透、雪崩等问题?
缓存穿透问题
什么是缓存穿透
缓存穿透指的是每次查询个别 key 时,key 在缓存系统不命中,此时应用系统就会从数据库中查询,如果数据库中存在这条数据,则获取数据并更新缓存系统。但如果数据库中也没有这条数据,这个时候就无法更新缓存,就会造成一个问题:查询缓存中不存在的数据时,每次都要查询数据库。
解决方案
布隆过滤器来解决缓存穿透
布隆过滤器判断一个 key 存在,那么这个 key 可能不存在,布隆过滤器判断一个 key 不存在,那么这个 key 肯定不存在。用布隆过滤器来解决缓存穿透的过程:1. 数据存入缓存前先存入布隆过滤器;2. 数据查询请求到来时,先用布隆过滤器判断当前 key 是否不存在,不存在的话直接返回 null
缓存并发问题
什么是缓存并发
解决方案
解决方案
缓存雪崩问题
什么是缓存雪崩
缓存雪崩解决方案
如何设计一个动态缓存热点数据的架构
15. 如何证明你的系统是高可用的?
高级工程师和架构师的区别,不在于掌握多少技术,而在于你所能驾驭的系统边界
负责一个功能
负责一个系统模块
负责一个系统
负责多个系统
如何评估系统是高可用的?
如何监控系统高可用?
基础设施监控报警
监控报警策略
系统应用监控报警
存储服务监控报警
16.回答系统容错、降级等高可用问题
熔断设计的原理
如何设计一个熔断器
降级设计的原理
读操作降级和写操作降级
小结
17.如何应对千万级流量
高性能设计中的“术”
前端优化
减少请求次数
增加缓存控制
前端研发同学经常会设置 HTML 的缓存控制头部(Cache-Control 头),这样浏览器在请求同一个文件时,只访问本地保存的资源副本,从而加速文件的访问速度。
减少图像的请求次数
你可能经常会发现,大部分网站会将所有用到的多张图片拼成一张,这样多张图片只需要下载一次,然后再通过 CSS 中的 background-image 和 background-position 来定位目标位置选择显示哪一张图片
减少脚本的请求次数
通用的做法就是 CSS 压缩和 JavaScript 压缩,将多个文件压缩成一个,目的是减少传输文件的大小,而更重要的是减少请求数。
页面静态化
边缘计算
边缘计算,被很多人提及,原因是大数据处理的实时性越来越高,由集中式的服务系统提供实时性的计算能力捉襟见肘,所以很多大厂开始将计算能力放到距离用户最近的 CDN 节点中,这就要求原有的 CDN 节点不只作为静态资源文件的缓存,而是要提供可以定制化的计算能力。
后端优化
基础设施优化
网络优化
架构优化
高性能中的“术”
18. 互联网面试中需要掌握的知识体系
存储
计算
输入输出
控制器
19. 程序员的道、术、势
送给老人的四条建议
思考
表达
你要有良好的表达能力,无论是和同事还是领导之间的沟通,这里的沟通并不是指会说话,而是会表达、敢表达、表达准确。
惊艳
事事有回应,件件有着落,回应和着落都要超出预期
认知
和一面解释:架构就是系统设计,比我在做 A 项目时,考虑到问题 X,我的解决办法是 Y。
和二面解释:架构就是业务发展中将系统变得有序,比如架构设计就是合理的组织系统、模块、组件,让它们更加有序,为的是让系统有能力快速响应(需求/用户/市场)变化。
和部门总监解释:架构即管理
0 条评论
下一页