分布式转账面试题的核心,在于从三种方案中选择与场景匹配的技术路径。
分布式事务必要性:单机环境下@Transactional可通过同一数据库连接保证原子性,但分布式场景中跨库操作涉及多个连接,该注解无法生效,需引入分布式事务方案。
Seata AT 模式:阿里开源框架,通过添加@GlobalTransactional注解实现 0 侵入式开发,原理是记录undo log用于失败回滚,但存在全局锁导致性能较差,适用于并发量低的后台管理系统。
TCC 模式:分为try(冻结资源)、confirm(确认执行)、cancel(释放资源)三阶段,无全局锁且性能较好,但需手动编写三个接口,开发成本高,需配合数据库冻结字段使用。
本地消息表 + MQ:将跨库操作拆分为两个本地事务,通过消息队列串联,核心是扣款与写消息表在同一事务中保证原子性,定时任务投递消息,最终通过重试机制实现一致性,无全局锁且性能最优,是生产环境常用方案。
视频最后提出问题:本地消息表方案中,若 B 服务消费消息持续失败应如何处理。
仅回答 Seata 或 MQ 会触发阿里 P7 面试的死亡四连问,需掌握各分布式事务方案的深层问题及解决思路。
Seata AT 模式:零侵入但一阶段释放锁导致脏读,需 GlobalLock 配合 SELECT FOR UPDATE 解决;适合低并发后台业务。
TCC 模式:依赖 Try/Confirm/Cancel 三步业务逻辑,需改造表结构(如积分表加冻结字段);需解决空回滚(事务控制表校验 Try 记录)、悬挂(Try 前校验 Cancel 记录)、幂等性(全局事务 ID 拦截重复操作)三大坑,适合核心扣减场景。
Saga 模式:无需改表,通过补偿接口回滚,是第三方接口或老系统的无奈选择,但需保证幂等性。
方案选择维度:从一致性、性能、侵入性、表结构改造、适用场景五个维度决策,如核心扣减用 TCC、外部接口用 Saga、充值发货用 MQ。
分库分表按用户 ID 分片的设计,会导致非分片键查询触发全分片扫描,引发性能问题。
问题核心:订单系统按用户 ID 哈希分到 16 个库,客服通过订单号查询时,因订单号不是分片键,需扫描所有分片,导致 SQL 执行时间长达几十秒。
性能差异:用户查自己订单仅需几十毫秒,而客服查询因全分片扫描,性能差距显著。
解决方案:建立 "订单号→用户 ID" 映射表,客服先查映射表获取用户 ID,再走分片键查询;或使用 ElasticSearch 处理多维查询,避免直接访问分库分表。
考核重点:考察对分库分表设计中业务查询全局性的理解,而非仅掌握基础分片技术。
视频通过具体场景,展示了分库分表设计中分片键选择对不同查询场景的影响。
Redis 做消息队列丢数据的核心原因,在于 List 弹出即销毁、pubSub 发完即忘的机制缺陷,而 Stream 虽能规避但存在架构边界。
List 队列缺陷:采用 "拿到就删" 机制,消费者处理消息前(如扣减库存时)服务器崩溃,Redis 已删除消息导致数据丢失。
pubSub 模式问题:发完即忘不存储数据,消费者断网瞬间的消息会被直接丢弃。
Stream 机制优势:支持磁盘持久化,引入待处理列表备份消息,仅在业务执行完成并确认后销毁备份。
Stream 架构边界:作为内存数据库易因海量消息堆积撑爆内存,主从异步同步可能丢数据,无法实现业务与消息强一致性。
视频明确 Stream 适合发短信、记日志等轻量级异步通知,核心订单支付需用专业 MQ。
数据库主键选型的核心考察点是对 B + 树存储逻辑的理解,而非单纯记忆选项。
自增 ID 优势:以 "电影院排队" 类比,自增 ID 按顺序追加写入 B + 树,无页分裂,体积为八字节长整型,可优化非聚集索引的存储与查询效率。
UUID 缺陷:随机无序特性导致插入时需 "插队",引发页分裂和随机读,写入性能下降;字符长度超过 32 个,导致索引膨胀,内存缓存减少,查询效率降低。
雪花算法适配:分布式场景下,通过时间戳、机器号、流水号拼接生成 ID,既保证全局唯一,又因时间有序避免页分裂,解决分库分表主键冲突问题。
视频最后提出雪花算法是否存在坑的问题,引导观众在评论区讨论。