高并发系统的“救命稻草”——BASE 理论
今天我们要聊的话题是互联网架构的“遮羞布”也是高并发系统的“救命稻草”——BASE 理论。如果说 ACID原子性、一致性、隔离性、持久性是传统数据库的“洁癖”要求数据必须时刻保持完美那 BASE 就是互联网架构的“实用主义”。它告诉我们为了系统不挂我们可以先让数据“脏”一会儿只要最后能洗干净就行。今天我们就用硬核的方式深入底层看看如何设计一个“最终一致性”的系统。第一站BASE 理论——互联网架构的“遮羞布”BASE 是三个短语的缩写它不是对 ACID 的否定而是在分布式场景下为了高可用Availability和分区容错性Partition tolerance做出的妥协。BASE 不是偷懒而是一种能量守恒。它把同步阻塞的“高能耗”操作转化为异步非阻塞的“低能耗”操作用时间换取了系统的生存空间。Basically Available基本可用核心功能不能挂系统的“断尾求生”CAP 理论往往让人误以为一致性和可用性是非黑即白的要么 CP要么 AP。但 BASE 理论将一致性变成了一个连续的光谱。这是对资源隔离与故障熔断的极致运用核心思想当系统出现不可预知的故障比如流量洪峰、部分节点宕机时允许系统损失部分非核心功能的可用性保障核心业务链路的正常运行。时间维度的权衡从“实时”到“延时”强一致性要求 TreadTwriteTreadTwrite 。这在物理上受限于光速和网络延迟在工程上受限于锁竞争。BASE 的最终一致性允许 TreadTwriteTreadTwrite 。底层机制向量时钟Vector Clock或版本戳Version Stamp。系统不再追求“此时此刻”所有节点数据一样而是通过记录数据的“因果历史”保证在 TΔtTΔt 时刻所有节点收敛到同一个版本。高水平设计设计系统时你要根据业务容忍度定义 ΔtΔt 。支付余额 Δt≈0Δt≈0 必须强一致BASE 不适用。点赞数 Δt5sΔt5s 用户刷新一下就能看到BASE 完美适用。累计销售额 Δt1hΔt1h T1 对账即可。空间维度的权衡从“全局”到“局部”强一致性要求全系统所有副本 NN 同时更新。BASE 的基本可用允许部分节点 N−xN−x 不可用或数据陈旧只要核心节点 xx 可用即可。底层机制Quorum NWR 模型的变种。在 BASE 架构中我们通常弱化 WW 写入副本数的要求只要写入成功 11 个节点就返回成功后台再慢慢同步给其他节点。这就是写扩散。底层逻辑功能降级电商大促时关闭商品评价、个性化推荐、积分商城等非核心接口将所有服务器资源集中到下单、支付、库存扣减等核心链路上。响应延迟增加正常情况下用户查询订单需 100ms故障时因部分节点不可用路由到备用节点可能需 500ms但仍能返回结果。流量限流通过熔断器如 Sentinel、Hystrix限制非核心接口的流量避免整体系统过载。底层实现舱壁模式Bulkhead像船舱一样把系统隔离成一个个独立的单元。非核心服务如推荐、评论挂了就像船舱进水隔断它保住核心服务下单、支付这艘船不沉。降级策略不仅是功能降级更是算力降级。当 CPU 负载过高系统自动丢弃高耗能的计算如复杂的关联查询返回静态缓存数据。这是一种有损服务但在高并发下是保命的关键。生活化比喻就像一家火爆的餐厅当客流爆满时服务员会告诉你“先生我们的免费小吃和果盘暂时没有了但主菜和米饭管够。”这就是基本可用——保住核心业务吃饭牺牲非核心业务果盘。Soft State软状态允许数据“脏”一会儿核心思想允许系统中的数据存在中间状态且该中间状态不会影响系统的整体可用性允许数据在多个节点副本之间的同步存在延迟。这是对确定性的延迟底层实现中间态管理在分布式系统中任何跨网络的操作都有“不确定性”。软状态就是显式地管理这种不确定性。例如订单状态流转中引入“处理中”、“同步中”等中间状态。这些状态不仅是给用户看的更是系统内部的锁机制替代品。它告诉系统“我知道数据还没对但别急正在修。”缓冲队列软状态在物理上通常体现为消息队列或写缓冲Write Buffer。它把瞬时的并发压力削峰填谷变成了线性的处理流。底层逻辑打破硬状态ACID 要求数据在任何时刻都必须处于一致的状态事务执行过程中的中间状态对外不可见。而软状态打破了这个限制允许系统在异步处理过程中存在短暂的数据不一致。中间状态用户下单支付完成后订单状态更新为“已支付”但物流系统的发货状态、积分系统的积分到账状态不需要和订单状态实时同步可以处于“待处理”的中间状态。生活化比喻就像你给女朋友发微信说“我错了”消息发出去后订单已支付但女朋友还没看到物流未发货或者看到了但还没原谅你积分未到账。这个“消息已发送但未读”的状态就是软状态。Eventually Consistent最终一致性数据终将一致核心思想系统中所有的数据副本在经过一段时间的同步与处理后最终能够达到一个完全一致的状态。它不是“放弃数据一致性”而是放弃了“实时强一致性”通过异步处理、重试补偿等机制保障数据在最终时刻的正确性。这是系统的自愈能力。底层实现反熵进程Anti-Entropy这是亚马逊 Dynamo 论文中提出的核心概念。系统后台运行着独立的进程不断对比不同副本之间的数据差异通过 Merkle Tree 哈希树高效对比发现不一致就进行修复。读写修复Read/Write Repair读修复当你读取数据时系统发现副本 A 版本老副本 B 版本新它会一边返回数据一边在后台悄悄把 A 更新到最新版本。写修复写入时检测到冲突根据策略如“最后写入胜出” LWW解决冲突。底层逻辑时间换空间将实时的强一致性校验转化为异步的最终一致性保障从而大幅提升系统的并发能力与可用性。一致性模型最终一致性不是模糊的概念而是有明确的模型如单调读、会话一致性、因果一致性等。生活化比喻就像你给女朋友发微信道歉虽然她可能不会立刻原谅你但只要你持续道歉重试补偿她最终会原谅你数据一致。第二站如何设计一个“最终一致性”的系统理论讲完了现在我们来点硬核的——如何用代码实现最终一致性核心痛点用户下单后需要扣减库存、增加积分、发送优惠券。这些操作分布在不同的微服务中如何保证它们最终都成功解决方案基于消息队列的最终一致性这是互联网架构中最主流的方案核心思想是先做事后通知失败了就重试。[订单服务] --(1. 发送消息)-- [消息队列 (MQ)] --(2. 消费消息)-- [库存服务] --(3. 消费消息)-- [积分服务] --(4. 消费消息)-- [优惠券服务]代码实现伪代码第一步订单服务生产者// 1. 创建订单 Order order new Order(); order.setUserId(123); order.setProductId(456); order.setStatus(PAID); orderMapper.insert(order); // 2. 发送消息到 MQ // 注意这里要保证本地事务和消息发送的原子性 // 可以使用事务消息如 RocketMQ或本地消息表 Message message new Message(); message.setTopic(ORDER_PAID); message.setBody(JSON.toJSONString(order)); mqTemplate.send(message);第二步库存服务消费者RabbitListener(queues inventory_queue) public void consumeOrderPaid(Message message) { Order order JSON.parseObject(message.getBody(), Order.class); // 3. 扣减库存 try { inventoryService.deduct(order.getProductId(), 1); } catch (Exception e) { // 4. 如果扣减失败抛出异常MQ 会自动重试 throw new RuntimeException(扣减库存失败, e); } }第三步积分服务消费者RabbitListener(queues points_queue) public void consumeOrderPaid(Message message) { Order order JSON.parseObject(message.getBody(), Order.class); // 5. 增加积分 pointsService.add(order.getUserId(), 100); }底层原理异步解耦订单服务不需要等待库存、积分服务处理完成直接返回成功大大提升了响应速度。重试机制如果库存服务扣减失败MQ 会自动重试直到成功为止。最终一致只要 MQ 不丢消息库存、积分服务最终都会处理成功数据达到一致。兜底方案定时任务对账即使有 MQ也可能出现消息丢失、消费者处理失败等极端情况。所以我们需要一个兜底方案——定时任务对账。Scheduled(cron 0 0 2 * * ?) // 每天凌晨 2 点执行 public void reconciliation() { // 1. 查询昨天的订单 ListOrder orders orderMapper.selectByDate(LocalDate.yesterday()); for (Order order : orders) { // 2. 检查库存是否扣减 int inventoryCount inventoryService.query(order.getProductId()); if (inventoryCount ! order.getQuantity()) { // 3. 如果不一致记录日志并报警 log.error(库存不一致: orderId{}, productId{}, order.getId(), order.getProductId()); // 4. 触发补偿逻辑 compensationService.fixInventory(order); } // 5. 检查积分是否增加 int points pointsService.query(order.getUserId()); if (points 100) { log.error(积分不一致: orderId{}, userId{}, order.getId(), order.getUserId()); compensationService.fixPoints(order); } } }底层原理数据校验定时扫描数据库比对不同服务之间的数据是否一致。补偿修复发现不一致时自动触发补偿逻辑修复数据。经典应用不全面电商与交易领域高并发的“主战场”这是 BASE 理论应用最成熟、最典型的场景。电商系统面临着巨大的流量洪峰如双11秒杀如果坚持强一致性ACID系统会瞬间因为锁竞争而瘫痪。秒杀与抢购库存扣减场景描述100台手机100万人同时抢。BASE 应用基本可用在秒杀开始前将库存预热到 Redis 缓存中。用户请求直接打 Redis不碰数据库。软状态Redis 扣减库存成功即返回“抢购成功”此时数据库里的库存还没变。Redis 和 MySQL 之间存在短暂的不一致。最终一致性后台通过异步消息队列慢慢将 Redis 的扣减记录同步到 MySQL或者采用定时任务对账。底层逻辑用缓存Redis抗住流量用异步MQ消化写压力。订单与物流/积分跨服务协作场景描述用户下单后需要扣库存、加积分、发优惠券、通知物流发货。BASE 应用基本可用订单系统只负责创建订单不调用积分和物流接口防止下游挂了拖死上游。软状态订单状态变为“已支付”但此时积分可能还没到账物流单号还没生成。最终一致性订单系统发送一条“支付成功”的消息到 MQ。积分服务、物流服务订阅该消息异步处理。即使积分服务挂了消息还在 MQ 里等服务重启后消费最终积分一定会到账。底层逻辑Saga 模式或事务消息将长事务拆解为异步的短事务。购物车场景描述用户在手机端加了商品PC 端要能看到。BASE 应用购物车数据通常存储在 NoSQL如 MongoDB或缓存中。多端同步时允许有几秒的延迟。你刚加进去换个设备可能看不到刷新一下就有了。底层逻辑利用 NoSQL 的向量时钟或最后写入胜出策略解决冲突。社交与内容领域用户体验的“避风港”社交网络的特点是读多写少且数据量极其庞大海量用户、海量动态。用户对数据的实时性要求其实很低没人关心点赞数是 100 还是 101只要大概对就行。点赞与关注数场景描述一条微博有 10万 点赞。BASE 应用软状态当你点赞时前端数字立刻1乐观更新但后台数据库可能还没写入。最终一致性点赞操作被扔进消息队列后台批量聚合写入数据库比如每100个赞写一次。现象你会发现刷新页面后点赞数可能会变甚至偶尔会“掉赞”并发冲突导致但这完全不影响体验。底层逻辑计数器合并写入将随机写转化为顺序写极大提升性能。Feed 流朋友圈/微博动态场景描述明星发了一条微博几千万粉丝要看到。BASE 应用写扩散推模式明星发微博系统异步将这条微博的 ID 推送到几千万粉丝的“收件箱”Redis 列表中。软状态推送需要时间粉丝 A 可能立刻看到粉丝 B 可能晚几秒看到。最终一致性通过后台任务保证所有粉丝的收件箱最终都包含这条 ID。底层逻辑Cassandra/HBase等 NoSQL 数据库利用其高写入性能和处理最终一致性的能力。评论系统场景描述热门视频下的几万条评论。BASE 应用评论写入后不需要立刻在所有 CDN 节点生效。用户发表评论后自己能看到别人可能要过几秒刷新才能看到。底层逻辑CDN 缓存延迟与数据库主从同步延迟的容忍。大数据与运维监控海量数据的“吞吐机”这类场景数据量极大且通常是“写一次读多次”或“只写不读用于分析”。日志收集与分析场景描述服务器每秒产生数万条日志需要收集到 ELKElasticsearch, Logstash, Kibana栈中。BASE 应用基本可用日志丢失几条通常是可以接受的或者通过冗余发送解决。最终一致性日志先写入 Kafka消息队列Logstash 慢慢消费写入 ES。你在 Kibana 上查日志可能刚产生的日志搜不到过几秒才能搜到。底层逻辑消息队列削峰填谷保证日志系统不会因为流量突增而崩溃。监控指标Prometheus/Grafana场景描述监控成千上万个微服务的 CPU、内存使用率。BASE 应用监控系统通常是拉模式或推模式数据是近似实时的。你看到的 CPU 使用率可能是 10 秒前的但这对于判断系统健康状态足够了。底层逻辑时间序列数据库的采样与聚合牺牲精度换取查询速度。特殊场景柔性事务TCC/Saga虽然 BASE 通常用于非核心业务但通过柔性事务它也能渗透到“准核心”业务中。跨行转账非实时到账场景描述某些银行系统的跨行转账提示“受理成功”但钱还没到对方账上。BASE 应用基本可用先扣款给用户一个回执。最终一致性后台通过银联/网联系统异步清算。如果清算失败对方账户不存在系统触发冲正补偿把钱退回来并短信通知用户。底层逻辑TCCTry-Confirm-Cancel模式将长事务拆分为预留资源、确认、取消三个阶段。场景分类典型业务为什么用 BASE核心痛点解决电商交易秒杀库存、订单状态、购物车流量太大数据库扛不住高并发写入、削峰填谷社交内容点赞数、粉丝数、Feed流、评论读多写少数据量海量高可用读取、多副本同步大数据/运维日志分析、监控报表、推荐系统数据量极大实时性要求低海量吞吐、异步处理边缘业务短信通知、邮件发送、积分赠送失败不影响主流程可重试解耦、容错总结最终一致性不是“放弃一致性”而是通过异步处理、重试补偿、定时对账等机制在保证系统高可用的前提下实现数据的最终正确性。它尊重物理规律承认网络有延迟承认节点会宕机所以不强求实时一致。它尊重人性承认用户对于“毫秒级延迟”是不敏感的只要界面不卡死基本可用数据稍微晚点到最终一致性用户是可以接受的。最后送上金句“最终一致性是互联网架构的‘遮羞布’它让我们在高并发、大流量的场景下既能保证系统不挂又能保证数据最终正确。但记住它不是万能的对于银行转账等强一致性场景还是要用 ACID。”
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2489806.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!