BASE理论是分布式系统中和ACID相对的概念强调基本可用软状态最终一致性。
很多人对BASE的理解还停留在基础概念层面知道BASE是Basically Available(基本可用)Soft state(软状态)Eventually consistent(最终一致性)的缩写,但是不知道BASE还有很多进阶的技巧和实践。
今天想深入讲解BASE理论的进阶技巧帮大家更深入地理解BASE在分布式系统中更好地应用。
一、BASE理论回顾
在讲进阶技巧之前,先简单回顾一下BASE理论的基础概念。
BASE是对NoSQL数据库特性的总结和传统的关系型数据库的ACID特性相对。
ACID:
- A(Atomicity):原子性事务中的操作要么全部成功要么全部失败。
- C(Consistency):一致性事务执行前后数据保持一致。
- I(Isolation):隔离性多个事务之间,互不干扰。
- D(Durability):持久性事务提交后数据永久保存。
ACID强调强一致性适合对数据一致性要求高的场景,比如银行转账等等。但是在分布式系统中要实现ACID代价很大会影响系统的可用性和性能。
BASE:
- BA(Basically Available):基本可用系统出现故障的时候,允许损失一部分可用性保证核心功能可用。
- S(Soft state):软状态允许系统存在中间状态这个状态不影响系统的整体可用性。
- E(Eventually consistent):最终一致性系统中的数据不需要实时一致,只要在一段时间后能够达到一致就行。
BASE强调可用性和性能牺牲强一致性接受最终一致性适合大规模的分布式系统,比如互联网应用等等。
CAP定理告诉我们在分布式系统中一致性(C)可用性(A)分区容错性(P)三者不可兼得只能,同时满足两个。因为分布式系统必须满足P所以只能在C和A之间,选择。选择C就是CP系统,比如HBaseMongoDB选择A就是AP系统,比如CassandraDynamoDB。
BASE就是AP系统的特性总结强调可用性和最终一致性。
二、最终一致性的实现方式
最终一致性是BASE的核心也是最难实现的部分。下面讲解最终一致性的几种实现方式。
1. 读时修复(Read Repair):
读时修复是在读取数据的时候,发现数据不一致就进行修复。
在分布式存储系统中数据通常会有多个副本存储在不同的节点上。读取的时候,会从多个节点读取数据比较版本,或者时间戳发现不一致的副本就用最新的数据更新旧的副本。
比如Cassandra就用了读时修复的机制读取的时候,会检查各个副本的一致性发现不一致就异步修复。
读时修复的优点是简单不需要额外的后台任务缺点是,只有被读取的数据才会被修复不常读取的数据可能长期不一致。
2. 写时修复(Write Repair):
写时修复是在写入数据的时候,发现节点故障,或者数据不一致就进行修复。
比如写入数据的时候,某个副本节点故障写入失败系统会记录这个失败的写入等节点恢复后再把数据同步过去。这叫提示切换(Hinted Handoff)。
写时修复的优点是能快速修复写入时的不一致缺点是只能修复写入时的问题不能修复已经存在的不一致。
3. 反熵(Anti-entropy):
反熵是通过后台的进程定期比较各个副本的数据发现不一致就进行修复让各个副本的数据达到一致。
反熵通常用Merkle树来比较数据的一致性Merkle树能快速比较大量数据的一致性只需要传输少量的哈希值。
比如DynamoDBCassandra都用了反熵的机制定期同步各个副本的数据。
反熵的优点是能修复所有数据的不一致包括不常读取的数据缺点是需要额外的后台任务会消耗系统资源。
4. 基于消息队列的最终一致性:
在微服务架构中通常用消息队列来实现最终一致性。
服务A完成操作后发送消息到消息队列服务B订阅消息完成自己的操作。如果服务B操作失败会重试直到成功,或者达到最大重试次数进入死信队列人工处理。
这种方式叫事件驱动架构能实现服务之间,的最终一致性松耦合可靠,但是复杂度高需要处理消息丢失重复顺序等问题。
三、幂等性设计
在BASE系统中,因为有重试机制同一个操作可能会被执行多次,所以幂等性设计非常重要。
幂等性是指同一个操作执行一次和执行多次的效果是一样的不会,因为重复执行而产生错误的结果。
幂等性的实现方式:
- 唯一ID:给每个操作分配一个唯一的ID执行操作前先检查这个ID是否已经执行过,如果执行过就直接返回结果不重复执行。
- 状态机:用状态机控制操作的状态每个状态只能从特定的状态转换过来重复的操作不会改变状态。比如订单状态待支付->已支付->已发货->已完成重复支付不会改变已支付的状态。
- 数据库唯一约束:用数据库的唯一约束防止重复插入数据。比如用户注册用用户名,或者手机号作为唯一键重复注册会报错不会创建重复的用户。
- 乐观锁:用版本号,或者时间戳实现乐观锁更新数据的时候,检查版本号是否匹配,如果不匹配说明数据已经被其他操作修改了不执行更新。
- 去重表:用专门的去重表记录已经执行的操作ID执行操作前先查询去重表看是否已经执行过。
幂等性是分布式系统中非常重要的概念特别是在BASE系统中,因为有重试和最终一致性必须保证操作的幂等性,否则会产生数据错误。
四、补偿机制
在BASE系统中,因为是最终一致性可能会出现操作失败的情况需要补偿机制来处理失败的操作保证数据的最终一致性。
Saga模式:
Saga模式是一种常用的补偿机制用于处理跨多个服务的分布式事务。
Saga把一个大的事务拆成多个小的本地事务每个本地事务都有对应的补偿操作。如果某个本地事务失败就执行前面已经成功的事务的补偿操作回滚整个事务。
比如下单流程包括创建订单扣减库存扣减余额三个步骤。如果扣减余额失败就执行补偿操作恢复库存取消订单回滚整个流程。
Saga有两种实现方式:
- 编排式(Choreography):每个服务完成自己的操作后发布事件触发下一个服务的操作。如果失败发布失败事件触发补偿操作。这种方式松耦合,但是流程不直观调试困难。
- 协调式(Orchestration):用一个协调器来控制整个流程协调器调用各个服务的操作,如果某个操作失败协调器调用补偿操作。这种方式流程清晰容易调试,但是协调器是单点需要保证高可用。
补偿机制的注意事项:
- 补偿操作本身要幂等:补偿操作也可能会重试,所以也要保证幂等性。
- 补偿操作可能失败:补偿操作也可能失败需要有重试机制,或者人工介入处理。
- 补偿不是完全回滚:补偿操作不是完全回滚到原来的状态,因为有些操作是不可逆的,比如发送短信邮件等等。补偿只能尽量抵消前面操作的影响。
- 需要记录操作日志:为了方便补偿和排查问题需要记录详细的操作日志包括操作内容时间结果等等。
五、冲突解决
在BASE系统中,因为数据有多个副本可能会出现数据冲突的情况需要冲突解决机制。
常见的冲突解决策略:
- 最后写入胜出(Last Write WinsLWW):用时间戳,或者版本号比较哪个写入更晚就用哪个数据。这种方式简单,但是可能会丢失一些更新,因为时钟可能不同步。
- 读时合并(Read Merge):读取的时候,发现冲突把多个版本的数据合并成一个。比如购物车多个副本的购物车数据合并取并集。这种方式能保留所有更新,但是不是所有数据都能合并。
- 向量时钟(Vector Clock):用向量时钟来检测冲突向量时钟能准确判断数据的因果关系发现并发的写入冲突。发现冲突后可以用最后写入胜出,或者读时合并来解决。
- 用户自定义冲突解决:让用户自己定义冲突解决的逻辑根据业务场景选择合适的解决方式。比如账户余额冲突不能简单合并需要特殊处理。
冲突解决是分布式系统中比较复杂的问题需要根据业务场景选择合适的策略。
六、版本控制
在BASE系统中版本控制很重要能帮助实现最终一致性和冲突解决。
常见的版本控制方式:
- 时间戳:用时间戳作为版本号比较数据的新旧。简单,但是依赖时钟同步时钟不同步会导致问题。
- 单调递增版本号:用单调递增的数字作为版本号每次更新版本号加1。简单可靠,但是在分布式系统中生成全局唯一的单调递增版本号比较困难。
- 向量时钟:用向量时钟作为版本能准确判断数据的因果关系检测并发冲突。但是实现复杂向量的长度会随节点数增长。
- MVCC(多版本并发控制):保留数据的多个版本读取的时候,根据时间点,或者事务ID读取对应版本的数据。能实现读写不阻塞提高并发性能。
版本控制是分布式系统中实现最终一致性的重要手段需要根据场景选择合适的方式。
七、BASE的最佳实践
最后总结一下BASE的一些最佳实践。
1. 明确一致性需求:
不是所有数据都需要强一致性也不是所有数据都能接受最终一致性。要根据业务场景明确数据的一致性需求选择合适的一致性模型。
比如账户余额库存等对一致性要求高的数据可能需要强一致性,或者更强的最终一致性保证。而用户头像商品描述等对一致性要求不高的数据可以接受最终一致性。
2. 设计幂等操作:
所有可能被重试的操作都要设计为幂等的防止重复执行导致数据错误。
3. 完善的补偿机制:
要有完善的补偿机制处理失败的操作保证数据的最终一致性。补偿操作也要幂等可靠。
4. 监控和告警:
BASE系统,因为是最终一致性可能会有数据不一致的情况需要有完善的监控和告警及时发现问题处理问题。
要监控数据的一致性延迟消息队列的积压补偿操作的失败次数等等发现异常及时告警。
5. 人工介入机制:
对于自动补偿无法处理的问题要有人工介入的机制,比如死信队列人工处理后台等等。
6. 合理选择技术方案:
根据业务需求选择合适的技术方案,比如需要强一致性的场景用关系型数据库,或者分布式事务需要高可用和最终一致性的场景用NoSQL数据库,或者消息队列+最终一致性。
不要为了用BASE而用BASE也不要为了强一致性而牺牲可用性要根据实际需求选择。
八、写在最后
以上就是BASE理论的进阶技巧和实践。
BASE不是简单的"最终一致性"四个字它包含了很多进阶的概念和技巧,比如最终一致性的实现方式幂等性设计补偿机制冲突解决版本控制等等。
理解这些进阶的技巧能帮我们在分布式系统中更好地应用BASE构建高可用高性能的系统。
当然BASE也不是银弹不是所有场景都适合用BASE要根据业务需求选择合适的一致性模型和,技术方案。
希望这篇文章能帮大家更深入地理解BASE理论在实际项目中更好地应用。
如果有什么问题,或者不同的看法欢迎在评论区留言我们一起交流。
最后用一句话结束这篇文章:"BASE不是放弃一致性而是在一致性和可用性之间,找到合适的平衡点。"
愿大家都能构建稳定高可用的分布式系统。
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录