如何做外贸网站,wordpress 当数据库,大连市英文网站建设,怎么屏蔽ip段访问网站好久没有分享最新的面经了#xff0c;今天分享一下北京某公司Go开发岗的面经#xff0c;薪资是15K左右#xff0c;看看难度如何#xff1a;
为什么要用分布式事务
分布式事务的核心作用是解决跨服务、跨数据源操作的数据一致性问题。在单体应用中#xff0c;数据库本地事务…好久没有分享最新的面经了今天分享一下北京某公司Go开发岗的面经薪资是15K左右看看难度如何
为什么要用分布式事务
分布式事务的核心作用是解决跨服务、跨数据源操作的数据一致性问题。在单体应用中数据库本地事务如 MySQL 的 InnoDB 事务可通过 ACID 特性保证单库内操作的一致性但在分布式系统中业务操作往往需要跨多个服务如用户服务、订单服务、支付服务或多个数据库此时本地事务无法覆盖所有操作可能出现 “部分成功、部分失败” 的情况。
什么是事务
事务是一组操作的集合需满足 “要么全部成功要么全部失败” 的原子性要求分为数据库事务和分布式事务两类
1. 数据库事务
指单库内的事务如 MySQL、PostgreSQL核心是满足ACID 特性
原子性Atomicity事务中的所有操作要么全执行要么全不执行如转账时 “扣 A 的钱” 和 “加 B 的钱” 必须同时成功或同时失败。一致性Consistency事务执行前后数据从一个合法状态切换到另一个合法状态如转账前后 A 和 B 的总金额不变。隔离性Isolation多个事务并发执行时彼此的操作互不干扰避免脏读、不可重复读、幻读。持久性Durability事务提交后数据修改会永久保存即使数据库崩溃重启后数据仍有效。
以 MySQL InnoDB 引擎为例其事务实现细节包括
隔离级别支持读未提交Read Uncommitted、读已提交Read Committed、可重复读Repeatable Read默认、串行化Serializable。快照读与当前读
快照读如select * from t where id1通过 MVCC多版本并发控制和 Read View 实现。每行数据包含隐藏列DB_TRX_ID最后修改事务 IDDB_ROLL_PTR回滚指针指向历史版本Read View由 “低水位”“高水位”“活跃事务 ID 列表” 组成决定可见的版本读提交时每次查询生成新 Read View可重复读时首次查询生成 Read View。当前读如update/delete/select ... for update会加锁行锁、表锁并读取最新数据。
锁机制更新操作时会加 “意向锁”IX/IS用于快速判断表中是否有行锁避免逐行检查锁提高效率行锁如 Record Lock用于锁定单行数据防止并发修改冲突。
2. 分布式事务
指跨多个服务或多个数据库的事务核心是保证跨节点操作的最终一致性无法像本地事务一样严格保证 ACID而是通过补偿机制实现 “最终一致”。
常见实现方式包括 TCCTry-Confirm-Cancel、SAGA正向 反向补偿、本地消息表、DTM 框架等。以 DTM 为例其核心组件包括
事务管理器TM负责全局事务的创建、提交、回滚协调各子事务的执行顺序解决事务乱序、幂等性重复提交、空补偿未执行 Try 却执行 Cancel等问题通过 “子事务屏障表” 记录全局事务 ID、分支事务 ID 及状态实现。资源管理器RM管理各服务的本地资源如数据库连接负责执行子事务的 Try/Confirm/Cancel 操作并向 TM 汇报执行结果。
分布式事务 CAP 特性
CAP 是分布式系统的三大核心特性由 Eric Brewer 提出三者不可同时满足
一致性Consistency分布式系统中所有节点在同一时间看到的数据是一致的如集群中所有节点的用户余额必须相同。可用性Availability分布式系统中任何节点故障时剩余节点仍能正常响应请求如支付系统不能因某台服务器宕机而无法下单。分区容错性Partition Tolerance当网络分区部分节点与其他节点断开通信发生时系统仍能继续运行分布式系统必须满足此特性因为网络故障不可避免。
在分布式事务中由于必须满足分区容错性P因此需在一致性C和可用性A之间权衡
CP 倾向优先保证一致性牺牲部分可用性如 ZooKeeper 集群leader 宕机后会暂停服务直到新 leader 选举完成期间不可用但数据一致。AP 倾向优先保证可用性接受短暂的不一致如多数业务的支付系统允许 “用户余额扣减后订单状态延迟更新”但最终会通过补偿机制一致。
实际业务中分布式事务通常追求最终一致性属于 AP 倾向即允许中间过程数据不一致但通过补偿操作如 TCC 的 Cancel、SAGA 的反向流程最终所有节点数据会达成一致。
协程了解吗
协程GoroutineGo 语言中的实现是轻量级的 “用户态线程”相比操作系统线程OS Thread其优势在于
资源占用极低初始栈大小仅 2KB可动态扩容至 GB 级而线程初始栈通常为 2MB因此单台机器可创建数十万甚至数百万协程。调度效率高由 Go runtime 而非操作系统调度减少用户态与内核态的切换开销。
Go 语言通过GMP 调度模型实现协程的高效调度核心组件包括
GGoroutine协程本身包含栈、程序计数器、状态如运行中、就绪、阻塞等信息。MMachine操作系统线程负责执行 G真正的 “执行载体”。PProcessor处理器是 G 和 M 的 “桥梁”包含本地协程队列Local Queue、全局队列指针、调度器状态等用于管理可执行的 G。
调度流程细节
初始化程序启动时创建初始线程 M0 和初始协程 G0每个 M 绑定一个 G0负责调度其他 G。协程入队新建的 G 优先放入当前 P 的本地队列容量默认 256本地队列满后放入全局队列。调度策略
本地队列调度M 优先从绑定的 P 的本地队列获取 G 执行。全局队列调度本地队列为空时M 会从全局队列批量获取 G一次取min(全局队列长度/num(P) 1, 64)个。偷取机制Work Stealing若本地队列和全局队列都为空M 会随机选择其他 P 的本地队列偷取一半的 G 执行避免线程空闲提高资源利用率。Hand off 机制当 G 因系统调用如 IO、sleep阻塞时M 会主动释放绑定的 P将 P 放入空闲 P 列表让其他空闲的 M 绑定该 P 并执行其本地队列的 G当阻塞的 G 唤醒后M 会尝试从空闲 P 列表获取 P若获取成功则继续执行 G否则将 G 放入全局队列等待调度。介绍一下 hand off 机制
Hand off 机制是 Go 调度器中用于处理 “协程阻塞” 的协作式调度策略核心目的是避免处理器P闲置提高系统资源利用率。
触发场景
当协程G因以下操作阻塞时会触发 hand off
系统调用如net.Dial、os.Read等 IO 操作同步操作如time.Sleep、mutex.Lock未获取锁时。
具体流程G 阻塞时执行 G 的 M 会检测到 G 进入阻塞状态此时 M 会调用park函数将 G 的状态标记为 “阻塞”并将绑定的 P 从自身解绑放入全局 “空闲 P 列表”。释放 P 后M 进入休眠状态或处理其他任务而空闲 P 列表中的 P 可被其他空闲的 M 绑定继续执行 P 本地队列中的 G避免 P 因 M 阻塞而闲置。G 唤醒时阻塞的 G 被唤醒如 IO 操作完成、锁获取成功此时 M 会调用
unpark函数尝试从空闲 P 列表中获取一个 P
若获取成功M 绑定该 P继续执行唤醒的 G若未获取成功将 G 放入全局队列M 进入休眠等待后续被调度。与抢占式调度的区别
Hand off 是 “协作式调度”G 主动让出 P而 Go 1.14 后引入的 “抢占式调度” 是通过信号中断长时间运行的 G如循环无阻塞的 G强制其让出 P避免单个 G 独占 P 导致其他 G 饿死。两者结合保证了 Go 协程调度的高效性。
gc 了解吗
Go 的垃圾回收GC是自动管理堆内存的机制核心目标是 “在保证程序正确的前提下尽可能减少对业务代码的干扰低延迟”。其核心实现是三色标记法 混合写屏障特点是 “并发标记、并行清扫”大部分阶段与业务代码并发执行仅短暂 STW。
核心流程初始标记STW短暂暂停所有协程几微秒到毫秒级标记 “根对象”全局变量、栈上变量直接引用的对象为灰色开启写屏障防止标记期间对象引用关系被修改导致漏标。并发标记恢复协程执行GC 后台线程从灰色对象出发遍历其引用的对象
若引用的对象是白色标记为灰色遍历完的灰色对象标记为黑色表示 “已处理”。
此阶段与业务代码并发执行写屏障会实时跟踪对象引用变化如黑色对象引用白色对象时通过写屏障修正标记。最终标记STW再次短暂暂停协程处理并发标记期间遗漏的对象如根对象的新增引用关闭写屏障。并行清扫恢复协程执行多个 GC 线程并行清扫所有白色对象不可达对象释放其占用的堆内存。关键技术混合写屏障
写屏障的作用是在并发标记阶段保证对象引用关系变化时GC 能正确跟踪可达性避免 “黑色对象引用白色对象” 导致的漏标漏标会导致白色对象被误判为垃圾回收。
混合写屏障结合了 “插入写屏障” 和 “删除写屏障” 的优势规则如下
当创建新对象时直接标记为黑色避免新对象被误回收当修改对象引用如a.b c时
若旧引用a.b指向的对象是黑色将其重新标记为灰色确保其引用的对象被遍历新引用c指向的对象若为白色标记为黑色避免被误回收。性能优化
Go GC 通过不断迭代优化延迟如 1.5 引入并发标记、1.8 优化 STW 时间、1.19 引入分代回收目前在多数场景下 STW 时间可控制在 100 微秒以内对业务影响极小。
函数的栈帧了解吗
函数栈帧是函数调用时在栈上分配的内存区域用于存储函数的参数、局部变量、返回地址、栈基指针等信息其生命周期与函数调用一致函数返回时栈帧被释放。
栈帧结构以 x86 架构为例
从高地址到低地址依次为
返回地址函数执行完毕后CPU 需跳转回的调用处地址如call func指令会将下一条指令地址压栈。栈基指针BP指向当前栈帧的底部用于定位栈帧内的参数和局部变量函数执行时BP 的值由上一个栈帧的 SP 决定。局部变量函数内定义的变量如int a、string s按声明顺序从 BP 向下分配。函数参数调用方传递的参数从 BP 向上高地址读取如func(a, b int)中a在 BP8b在 BP12取决于 CPU 位数。
逃逸分析与堆分配
Go 编译器会通过 “逃逸分析” 判断变量是否需要 “逃逸” 到堆上
若变量仅在函数内使用无外部引用则分配在栈帧上函数返回后自动释放无需 GC若变量被外部引用如返回局部变量的指针、被闭包引用则会 “逃逸” 到堆上由 GC 管理生命周期。
逃逸分析的目的是减少堆分配堆分配需 GC栈分配更高效提升程序性能。
欢迎关注 ❤
我们搞了一个免费的面试真题共享群互通有无一起刷题进步。
没准能让你能刷到自己意向公司的最新面试题呢。
感兴趣的朋友们可以私信我备注面试群。