1. 列举三个非冯·诺依曼计算结构
非冯结构是指不遵循传统冯·诺依曼体系的计算架构,包括:
-
数据流结构(Dataflow Architecture):指令执行取决于数据的可用性而不是程序计数器。
-
神经网络结构(Neural Network Architecture):模拟生物神经元连接,用于人工智能。
-
量子计算结构(Quantum Computing Architecture):利用量子比特和量子叠加原理进行计算。
2. 简述同步通信
同步通信指通信双方在数据传输过程中必须同时处于活动状态,例如:
-
发送方发出数据后等待接收方响应;
-
接收方只有在发送方传输时才能接收;
-
常见于阻塞式消息传递,如
MPI_Send
与MPI_Recv
。
3. 简述云计算中的基础服务模型
云计算有三种基本服务模型:
模型 | 名称 | 功能 |
---|---|---|
IaaS | Infrastructure as a Service | 提供虚拟机、存储、网络等基础资源(如AWS EC2、阿里云ECS) |
PaaS | Platform as a Service | 提供应用开发运行平台(如Google App Engine、Microsoft Azure) |
SaaS | Software as a Service | 提供在线应用服务(如Gmail、Office 365) |
4. 简述Flynn分类法
Flynn分类法根据指令流和数据流的数量将计算机系统分为四类:
分类 | 含义 | 示例 |
---|---|---|
SISD | 单指令流单数据流 | 传统单核处理器 |
SIMD | 单指令流多数据流 | GPU |
MISD | 多指令流单数据流 | 稀有,用于容错系统 |
MIMD | 多指令流多数据流 | 多核多线程系统、分布式系统 |
5. 举例说明MPI中标签的作用
**MPI标签(Tag)**用于区分不同类型的消息,防止通信混乱。
假设进程 P 向进程 Q 发送两类消息:
// 进程 P
MPI_Send(&data1, count, MPI_INT, dest=Q, tag=100, comm);
MPI_Send(&data2, count, MPI_INT, dest=Q, tag=200, comm);
// 进程 Q
MPI_Recv(&recv1, count, MPI_INT, source=P, tag=100, comm, status);
MPI_Recv(&recv2, count, MPI_INT, source=P, tag=200, comm, status);
📌 作用:标签 100
和 200
确保数据 data1
和 data2
不会混淆。
6. 简述虚拟机和容器的异同
比较项 | 虚拟机(VM) | 容器(Container) |
---|---|---|
启动速度 | 慢(分钟级) | 快(秒级) |
占用资源 | 多(完整OS) | 少(共享宿主OS) |
隔离性 | 强(硬件层) | 相对弱(内核级) |
应用场景 | 多系统环境 | 微服务、快速部署 |
虚拟化层 | Hypervisor | Docker Engine / Containerd |
7. 简述云计算的三种存储方式
-
对象存储(Object Storage)
-
面向非结构化数据,如图片、音频、备份。
-
如 Amazon S3、阿里云 OSS。
-
-
块存储(Block Storage)
-
类似硬盘,适合数据库、虚拟机挂载。
-
如 Amazon EBS、阿里云云盘。
-
-
文件存储(File Storage)
-
提供标准文件系统访问,适合共享访问。
-
如 Amazon EFS、阿里云 NAS。
-
8. 简述什么是 Cache 一致性问题
Cache一致性问题是指在多处理器系统中,每个CPU都有自己的缓存,如果多个缓存中同一变量副本不一致,可能导致程序结果错误。
📌 示例:
-
CPU1 缓存了变量
X=1
,CPU2 更新为X=2
。 -
如果CPU1未刷新,就会读取过时数据。
🔄 常用解决方案:
-
MESI 协议(修改-独占-共享-无效)
-
总线监听(Bus Snooping)
9. 任务并行 vs 数据并行
类型 | 含义 | 示例 |
---|---|---|
任务并行 | 各处理单元执行不同任务,操作不同数据 | 多线程同时执行输入、处理、输出 |
数据并行 | 各处理单元执行相同操作,处理不同数据 | 多核同时对不同数组片段求平方 |
10. 负载均衡 / 通信 / 同步
-
负载均衡:将任务在各节点上平均分配,避免某些过载、某些空闲。
-
通信:节点间交换数据,例如发送接收消息(如 MPI)。
-
同步:进程/线程间协调执行时序,如 barrier、锁等。
11. 并发 / 并行 / 分布式计算对比
特性 | 并发计算 | 并行计算 | 分布式计算 |
---|---|---|---|
核心思想 | 多任务交替 | 多任务同时 | 多节点协作 |
硬件需求 | 单/多核 | 多核/多处理器 | 多主机网络 |
通信 | 同步/共享内存 | 同步/共享内存 | 网络通信 |
示例 | 单核多线程 | 多核计算矩阵 | 云平台运行搜索引擎 |
12. 冯诺依曼瓶颈及解决
-
问题:处理器与内存间数据传输速率差距导致带宽瓶颈。
-
解决方法:
-
多级缓存
-
指令预取、乱序执行
-
使用哈佛结构分开指令/数据总线
-
13. 缓存映射的三种方式
类型 | 映射策略 | 特点 |
---|---|---|
直接映射 | 每块内存映射到唯一缓存位置 | 实现简单,冲突多 |
全相联映射 | 任意内存块可映射至任何缓存行 | 命中率高,硬件复杂 |
组相联映射 | 缓存分组,每块映射至某组内任一行 | 折中方案,常用 |
14. 分布式 vs 共享式存储系统
类型 | 特点 | 示例 |
---|---|---|
共享式 | 所有节点访问同一存储 | 多核服务器共享内存 |
分布式 | 存储分布在多个节点上 | HDFS、Ceph 等 |
15. 阿达姆定律(Amdahl’s Law)
并行化程序的加速比受限于其串行部分。
公式:
S=1(1−P)+PNS = \frac{1}{(1 - P) + \frac{P}{N}}S=(1−P)+NP1
其中:
-
SSS:加速比
-
PPP:可并行部分比例
-
NNN:处理器数量
16. Foster 并行化方法步骤
-
分解(Decomposition)
-
分配(Assignment)
-
通信(Communication)
-
合成/调度(Agglomeration & Mapping)
17. 缓存 / 一致性问题 / 伪共享
-
缓存:位于CPU与内存之间的高速缓冲。
-
缓存一致性问题:多核对同一变量副本操作,导致数据不一致。
-
伪共享:不同线程使用同一缓存行内不同变量,仍引起无效失效。
18. 矩阵向量乘三种实现对比
实现 | 特点 |
---|---|
行划分 | 每线程处理矩阵某几行 |
列划分 | 每线程处理输入向量的某几列 |
块划分 | 分块组合行列划分,适合大规模计算 |
19. parallel for
中静态 vs 动态线程分配
分配方式 | 含义 | 适用情况 |
---|---|---|
静态 | 预先划分任务段 | 均匀负载 |
动态 | 空闲线程动态领取任务 | 不均匀任务(load imbalance) |
20. Flynn 分类法四类
类型 | 特征 | 示例 |
---|---|---|
SISD | 单指令单数据 | 传统CPU |
SIMD | 单指令多数据 | GPU |
MISD | 多指令单数据 | 容错系统(少见) |
MIMD | 多指令多数据 | 多核/集群系统 |
21. 流水线 vs 多发射
-
流水线:将指令划分为阶段(如取指/译码/执行),指令重叠执行。
-
多发射(超标量):同周期多指令发射执行,提升吞吐率。
21. 忙等待 / 互斥量 / 信号量 / 临界区
-
忙等待:循环检测资源,无睡眠。
-
互斥量:互斥访问资源(lock/unlock)。
-
信号量:支持计数、资源控制(P/V操作)。
-
临界区:程序中访问共享资源的代码段。
22. 点对点通信 vs 集合通信
类型 | 含义 | 示例 |
---|---|---|
点对点通信 | 单对单通信 | MPI_Send / MPI_Recv |
集合通信 | 多个进程参与,如广播、规约 | MPI_Bcast / MPI_Reduce 等 |
23. 事务的4个特性
特性 | 全称 | 描述 |
---|---|---|
A | Atomicity(原子性) | 事务中的操作要么全部执行,要么全部不执行,不能只执行一部分。 |
C | Consistency(一致性) | 执行前后数据库必须保持一致,符合数据完整性约束。 |
I | Isolation(隔离性) | 多个事务并发执行时,彼此之间不会互相干扰。 |
D | Durability(持久性) | 一旦事务提交,其结果就永久保存在数据库中,即使宕机也不会丢失。 |
🔁 举个实际场景总结一下:
假设你从支付宝给朋友转账:
**原子性:**转账过程要么钱成功转出并到账,要么完全失败,不能只扣钱不收钱。
**一致性:**总金额在转账前后保持不变。
**隔离性:**并发转账不会影响彼此账户的最终状态。
**持久性:**转账成功后,即使系统宕机,钱也不会“消失”或“回滚”
24.ACID 一致性和CAP一致性
ACID 一致性 —— 数据库事务的一致性
✅ 定义:
事务执行前后,数据库必须从一个一致的状态变为另一个一致的状态,满足所有数据完整性约束(如主键唯一、外键、触发器、检查约束等)。
✅ 核心目标:
保证事务内部的逻辑正确性,防止数据出现非法状态
25.CAP 一致性 —— 分布式系统中的一致性
📌 所属领域:分布式系统(如微服务、分布式数据库)中的 CAP 定理
✅ CAP 定理三要素:
C(Consistency)一致性:对分布式系统各个节点对外的表现是一致的
A(Availability)可用性:每个请求都能收到响应(不一定是最新数据)。
P(Partition tolerance)分区容错性:系统能容忍网络分区(节点通信失败)而继续运行。
✅ CAP 中的一致性含义:
指强一致性(Strong Consistency),即:在网络正常或出现分区的情况下,所有用户访问到的数据是最新的、完全一致的。
✅ 举例说明:
有两个节点 A 和 B,当你向 A 写入了数据,立刻再从 B 读取时也必须返回这条最新数据,否则就是不一致。
26.数据冒险是什么
✅ 正确理解 Read After Write (RAW):
它是数据冒险(Data Hazard) 中最常见的一种,准确地说:
Read After Write:指一个指令试图读取某个寄存器(或内存位置),但这个寄存器正在被前一条尚未完成写入的指令所更新。
🔍 举个例子说明:
1. ADD R1, R2, R3 ; R1 = R2 + R3
2. SUB R4, R1, R5 ; R4 = R1 - R5
-
第 1 条指令正在把计算结果写入寄存器 R1
-
第 2 条指令马上就要读取 R1,但是 R1 还没写好!
-
这就造成了 Read After Write(RAW) Hazard
❗为什么叫 Read After Write?
因为第二条指令要 Read(读),但它依赖的值 需要在前面的指令 Write(写)之后才正确,因此叫:
Read(第二条) after Write(第一条)
⚠️ 为什么不能翻成“读后写”?
“读后写”会给人一种先读再写的错觉,好像程序顺序是:
读 -> 写
但实际是指:
第一条:写
第二条:读
→ 第二条读太早 → 冒险!
所以准确的理解应该是“写之后才可读”,而不是“读了之后再写”。
✅ 更准确的翻译:
-
Read After Write:应翻译为 “写后读”
-
表达了这个依赖关系:必须先写完,才能读
📚 延伸:数据冒险的三种基本类型
类型 | 简称 | 示例 | 描述 |
---|---|---|---|
Read After Write | RAW | 读用到前面尚未写好的值 | ✅ 最常见 |
Write After Read | WAR | 写被排在读之前 | 异常情况 |
Write After Write | WAW | 两个写竞争 | 通常影响结果一致性 |
27. 简述一下raft 是啥,写出领导者选举的伪代码
领导者选举(Leader Election)相关算法
最常见的是:
算法 | 关键特点 | 场景 |
---|---|---|
Bully Algorithm | 编号高者优先,向高编号发起选举,收到回应则放弃 | 同步网络,编号唯一 |
Ring Algorithm | 按环传递选举消息,编号最大者当选 | 节点构成环形结构 |
Raft Election | 基于任期(term)、多数投票,心跳维持领导地位 | 实现简单、一致性强 |
以 Raft 为例(常考):
✅ Leader Election 流程图(简化版)
+--------------------+
| Follower |
| (Timeout reached) |
+---------+----------+
|
v
+--------------------+
| Candidate |
| - Increment term |
| - Vote for self |
| - Send RequestVote|
+---------+----------+
|
+---------+----------+
| Majority votes? |
| Yes No |
v v
+---------------+ +-------------+
| Leader | | Back to |
| (Send heart- | | Follower |
| beats) | +-------------+
+---------------+
✅ C语言实现:模拟 Raft RequestVote
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
// ---------- 定义结构体 ----------
typedef struct {
int term; // 候选人当前任期
int candidateId; // 候选人 ID
int lastLogIndex; // 候选人最后一条日志索引
int lastLogTerm; // 候选人最后一条日志任期
} RequestVoteRequest;
typedef struct {
int term; // 当前节点任期
bool voteGranted; // 是否投票
} RequestVoteResponse;
typedef struct {
int id;
int currentTerm;
int votedFor; // -1 表示尚未投票
int lastLogIndex;
int lastLogTerm;
} RaftNode;
// ---------- 判断日志是否更新 ----------
// 若 a 日志比 b 更新,返回 true
bool isLogUpToDate(int candidateTerm, int candidateIndex, int localTerm, int localIndex) {
if (candidateTerm > localTerm) return true;
if (candidateTerm == localTerm && candidateIndex >= localIndex) return true;
return false;
}
// ---------- 模拟处理 RequestVote 请求 ----------
RequestVoteResponse handleRequestVote(RaftNode *node, RequestVoteRequest req) {
RequestVoteResponse resp;
resp.term = node->currentTerm;
resp.voteGranted = false;
if (req.term < node->currentTerm) {
// 候选人任期太旧,拒绝投票
return resp;
}
// 若请求 term 更大,更新当前节点任期,并重置投票
if (req.term > node->currentTerm) {
node->currentTerm = req.term;
node->votedFor = -1;
}
// 如果还没投票或者之前就投给这个 candidate
// 并且候选人日志不落后
if ((node->votedFor == -1 || node->votedFor == req.candidateId) &&
isLogUpToDate(req.lastLogTerm, req.lastLogIndex, node->lastLogTerm, node->lastLogIndex)) {
node->votedFor = req.candidateId;
resp.voteGranted = true;
}
resp.term = node->currentTerm;
return resp;
}
// ---------- 测试主函数 ----------
int main() {
// 模拟 follower 节点
RaftNode follower = {
.id = 1,
.currentTerm = 3,
.votedFor = -1,
.lastLogIndex = 5,
.lastLogTerm = 3
};
// 模拟候选人发送投票请求
RequestVoteRequest request = {
.term = 4,
.candidateId = 2,
.lastLogIndex = 5,
.lastLogTerm = 3
};
// 处理请求
RequestVoteResponse response = handleRequestVote(&follower, request);
// 输出结果
printf("Follower Term: %d\n", follower.currentTerm);
printf("Vote Granted to Candidate %d: %s\n", request.candidateId,
response.voteGranted ? "YES" : "NO");
return 0;
}
🔹28. “电梯调度:事件优先 vs 时间优先”
这个问题其实很经典,常用于考察事件驱动 vs 时间驱动调度模型的理解。
✅ 电梯控制问题中的两种调度策略:
模型 | 描述 |
---|---|
事件优先模型 | 电梯状态变化由外部事件触发(如按钮被按)。适用于低负载、响应快的场景。 |
时间优先模型 | 定期轮询电梯状态进行调度(如每秒检查一次状态)。适用于状态稳定但响应延迟。 |
🧠 通常参考点:
实时性要求高 → 事件优先
系统结构简单、可预测 → 时间优先
✅ 二、电梯调度:事件驱动 vs 时间驱动模型
🎯 场景题型:给定电梯调度场景,选择合适模型并解释原因
模型类型 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
事件驱动模型 | 响应快、资源高效 | 实现复杂 | 按键触发、请求频繁的系统 |
时间驱动模型 | 实现简单 | 易浪费资源、延迟高 | 状态稀疏、周期任务 |
💡 答题模板(事件 vs 时间):
在本场景下,电梯响应的关键为用户输入(如按钮按下),且要求快速响应,因此应采用事件优先模型。此模型可在每次事件触发时直接调度处理,避免定期轮询所带来的资源浪费与响应延迟。
28. 行存储 和 列存储 分别是啥意思
“行存储”和“列存储”是数据库中两种数据物理存储方式。它们的最大区别在于:是按行还是按列来组织和存储数据。下面我给你通俗又系统地解释一下:
✅ 什么是行存储(Row-Oriented Storage)
📦 存储方式:
把一整行的数据作为一个整体存储在一起。
例如,有这样一张表:
ID | Name | Age | Gender |
---|---|---|---|
1 | Alice | 23 | F |
2 | Bob | 30 | M |
在行存储中,物理存储是这样的(按行):
[1, Alice, 23, F], [2, Bob, 30, M]
✅ 特点:
- 适合事务型操作(OLTP),例如插入/更新/删除整行。
- 每次访问都能获得一行完整信息,适合频繁的随机访问。
✅ 什么是列存储(Column-Oriented Storage)
把每一列的数据存储在一起。
上述表的数据在列存储中会变成这样:
ID: [1, 2]
Name: [Alice, Bob]
Age: [23, 30]
Gender: [F, M]
✅ 特点:
- 适合分析型操作(OLAP),例如统计、聚合、筛选某一列。
- 只读取相关列即可,大大减少 I/O。
- 通常配合压缩算法,节省空间。
特性 | 行存储(Row Store) | 列存储(Column Store) |
---|---|---|
数据组织方式 | 按行存储整条记录 | 按列存储每个字段 |
适合场景 | 高并发写入、快速读取整行数据(OLTP) | 快速聚合分析、少列访问(OLAP) |
查询优化 | 快速读取一整行 | 快速读取某几列,列压缩效率高 |
示例系统 | MySQL(InnoDB)、PostgreSQL | ClickHouse、Amazon Redshift、HBase |
🎯 你要查“所有用户的平均年龄”
-
行存储要把所有行读出来(包括姓名、性别等),然后提取年龄列
-
列存储只读年龄列即可,更快、更省资源
I/请求投票RPC RequestI/请求投票RPC Response type RequestVoteRequest struct {type RequestVoteResponse struct { termint自己当前的任期号termint川自己当前任期号 candidateld int // 自己的IDvoteGranted bool //自己会不会投票给这 lastLoglndex int//自己最后一个日志号//个candidate lastLogTermint //自己最后一个日志的任期
✅ 可能含义 1:Raft 中的任期(Term)变化与时钟模型
Raft 中“时钟题”一般是围绕:
-
任期(
term
)的变化规则 -
各节点间时间(选举超时、心跳超时)的关系
-
事件发生顺序:如谁先成为 Candidate、是否会分裂投票
📌 常见题型:Raft 时钟题例子
给定多个节点的超时时间和网络延迟,请判断哪个节点最可能先成为 Leader?是否会发生分裂投票?如何避免?
🧠 解题思路:
-
超时时间短的节点更可能最早成为 Candidate。
-
**分裂投票(split vote)**发生在多个 Candidate 几乎同时发起选举。
-
引入随机超时是防止分裂投票的重要机制。
✅ 示例题(考试常见):
Raft 集群中有 3 个节点 A、B、C,其选举超时分别为 150ms、200ms、250ms。若网络没有延迟,谁最可能成为 Leader?为什么?
答案:
-
A 最先超时,先发起投票。
-
如果 B、C 接收到请求且没投过票,A 会赢得多数选票 → 成为 Leader。
✅ 可能含义 2:Lamport 时钟 / 向量时钟题
如果“RA 时钟题”不是 Raft 的简写,也可能是:
分布式系统考试里经典的“逻辑时钟”题,用来判断事件的先后关系
🎯 题型特点:
-
给出一组事件、节点间通信
-
要你给每个事件分配 Lamport 时钟值
-
或者用向量时钟判断“是否并发”、“谁先发生”
示例:Lamport 时钟
事件 A1 → A2
A1 发消息给 B2
→ 问各事件的 Lamport 时钟是多少?
✅ 总结:RA 时钟题常考点
类型 | 涉及内容 | 考察点 |
---|---|---|
Raft 任期时钟题 | 超时、term 增长、选举过程 | 选举机制是否正确、防止 split vote |
Lamport 时钟 | 事件编号、消息传递 | 逻辑因果顺序 |
向量时钟 | 多节点时间戳数组 | 并发性判断(是否存在先后关系) |
29. 超时例题
Raft 集群中有 3 个节点 A、B、C,选举超时分别是:
-
A:150ms
-
B:200ms
-
C:250ms
网络无延迟。问:谁最可能成为 Leader?为什么?
✅ 关键概念:Raft 中的选举超时
Raft 中每个节点在 “Follower 状态” 时会启动一个“选举超时计时器”:
-
如果超时时间到了还没有收到来自 Leader 的心跳(AppendEntries),它就认为 Leader 挂了,会:
-
转为 Candidate
-
启动一次选举
-
✅ 步骤解析(无网络延迟):
-
150ms 到达时:
-
A 的选举超时先触发 → A 转为 Candidate
-
A 向 B 和 C 发送
RequestVote
请求
-
-
B 和 C 的状态:
-
当前任期一致(假设都是 term=1)
-
没有投过票(
votedFor == null
) -
如果 A 的日志不落后 → 会投票给 A
-
-
A 获得了 自己 + B 或 C 的票数 = 2 票
-
超过半数(3 个节点的一半是 2)
-
→ A 成为 Leader
-
❗为什么不会发生分裂投票?
因为 A 先触发,B 和 C 还没超时,也就是说:
-
B、C 都还是 Follower 状态
-
都愿意投票给第一个发来 RequestVote 的 Candidate(A)
🔄 如果网络有延迟或超时很接近怎么办?
假设 A、B 几乎同时超时(如 A=150ms,B=151ms),就可能出现:
-
A 和 B 同时发起选举、都给自己投票
-
A 请求到 C 时,C 可能已经投给 B
-
→ 三个节点各自一票 / 或 2:1 分裂
这种情况就叫 分裂投票(split vote)
点 | 原因 |
---|---|
谁先超时 | 谁先变成 Candidate,先发起投票 |
谁能赢选举 | 谁先获得多数投票(> N/2) |
如何得票 | 其他节点还没投票,并且你日志不落后 |
如何避免冲突 | 随机选举超时(避免多个节点同时选) |
如果你想,我可以给你扩展出多个变种题(比如有日志不一致、网络延迟、有节点宕机的情况),帮助你训练这类题目的答题思维。要来几道练练吗?
30.三阶段提交(3PC)的三个阶段
1️⃣ 阶段一:CanCommit(询问阶段)
协调者向所有参与者发送 “Can you commit?”
参与者检查自己是否可以提交(如资源锁是否可用),然后回复 Yes/No。
(这里跟 2PC 的 prepare 阶段类似)
2️⃣ 阶段二:PreCommit(预提交阶段)
如果所有参与者都回复 Yes:
协调者发送 PreCommit 给所有参与者,要求进入预提交状态(写 WAL 日志、锁定资源,但暂不真正执行 Commit)。
参与者执行预提交,写入日志,并回复 ACK。
(2PC 在这里会直接 Commit,而 3PC 多了一个过渡状态)
3️⃣ 阶段三:DoCommit(正式提交阶段)
协调者收到所有参与者的 ACK 后,发送 DoCommit。
参与者正式执行 Commit,释放锁,并反馈已提交。
如果在阶段 2 或 3 发生网络异常,超时后可以通过超时和超时恢复来避免阻塞。