learn.lianglianglee.com/专栏/左耳听风/046 弹力设计篇之“补偿事务”.md.html
2022-05-11 19:04:14 +08:00

1051 lines
42 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
<link rel="icon" href="/static/favicon.png">
<title>046 弹力设计篇之“补偿事务”.md.html</title>
<!-- Spectre.css framework -->
<link rel="stylesheet" href="/static/index.css">
<!-- theme css & js -->
<meta name="generator" content="Hexo 4.2.0">
</head>
<body>
<div class="book-container">
<div class="book-sidebar">
<div class="book-brand">
<a href="/">
<img src="/static/favicon.png">
<span>技术文章摘抄</span>
</a>
</div>
<div class="book-menu uncollapsible">
<ul class="uncollapsible">
<li><a href="/" class="current-tab">首页</a></li>
</ul>
<ul class="uncollapsible">
<li><a href="../">上一级</a></li>
</ul>
<ul class="uncollapsible">
<li>
<a href="/专栏/左耳听风/000 开篇词 洞悉技术的本质,享受科技的乐趣.md.html">000 开篇词 洞悉技术的本质,享受科技的乐趣.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/001 程序员如何用技术变现(上).md.html">001 程序员如何用技术变现(上).md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/002 程序员如何用技术变现(下).md.html">002 程序员如何用技术变现(下).md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/003 Equifax信息泄露始末.md.html">003 Equifax信息泄露始末.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/004 从Equifax信息泄露看数据安全.md.html">004 从Equifax信息泄露看数据安全.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/005 何为技术领导力.md.html">005 何为技术领导力.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/006 如何拥有技术领导力.md.html">006 如何拥有技术领导力.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/007 推荐阅读:每个程序员都该知道的事.md.html">007 推荐阅读:每个程序员都该知道的事.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/008 Go语言Docker和新技术.md.html">008 Go语言Docker和新技术.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/009 答疑解惑:渴望、热情和选择.md.html">009 答疑解惑:渴望、热情和选择.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/010 如何成为一个大家愿意追随的Leader.md.html">010 如何成为一个大家愿意追随的Leader.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/011 程序中的错误处理:错误返回码和异常捕捉.md.html">011 程序中的错误处理:错误返回码和异常捕捉.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/012 程序中的错误处理:异步编程和最佳实践.md.html">012 程序中的错误处理:异步编程和最佳实践.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/013 魔数 0x5f3759df.md.html">013 魔数 0x5f3759df.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/014 推荐阅读机器学习101.md.html">014 推荐阅读机器学习101.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/015 时间管理:同扭曲时间的事儿抗争.md.html">015 时间管理:同扭曲时间的事儿抗争.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/016 时间管理:投资赚取时间.md.html">016 时间管理:投资赚取时间.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/017 故障处理最佳实践:应对故障.md.html">017 故障处理最佳实践:应对故障.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/018 故障处理最佳实践:故障改进.md.html">018 故障处理最佳实践:故障改进.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/019 答疑解惑:我们应该能够识别的表象和本质.md.html">019 答疑解惑:我们应该能够识别的表象和本质.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/020 分布式系统架构的冰与火.md.html">020 分布式系统架构的冰与火.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/021 从亚马逊的实践,谈分布式系统的难点.md.html">021 从亚马逊的实践,谈分布式系统的难点.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/022 分布式系统的技术栈.md.html">022 分布式系统的技术栈.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/023 分布式系统关键技术:全栈监控.md.html">023 分布式系统关键技术:全栈监控.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/024 分布式系统关键技术:服务调度.md.html">024 分布式系统关键技术:服务调度.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/025 分布式系统关键技术:流量与数据调度.md.html">025 分布式系统关键技术:流量与数据调度.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/026 洞悉PaaS平台的本质.md.html">026 洞悉PaaS平台的本质.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/027 推荐阅读:分布式系统架构经典资料.md.html">027 推荐阅读:分布式系统架构经典资料.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/028 编程范式游记1- 起源.md.html">028 编程范式游记1- 起源.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/029 编程范式游记2- 泛型编程.md.html">029 编程范式游记2- 泛型编程.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/030 编程范式游记3 - 类型系统和泛型的本质.md.html">030 编程范式游记3 - 类型系统和泛型的本质.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/031 Git协同工作流你该怎样选.md.html">031 Git协同工作流你该怎样选.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/032 推荐阅读:分布式数据调度相关论文.md.html">032 推荐阅读:分布式数据调度相关论文.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/033 编程范式游记4- 函数式编程.md.html">033 编程范式游记4- 函数式编程.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/034 编程范式游记5- 修饰器模式.md.html">034 编程范式游记5- 修饰器模式.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/035 编程范式游记6- 面向对象编程.md.html">035 编程范式游记6- 面向对象编程.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/036 编程范式游记7- 基于原型的编程范式.md.html">036 编程范式游记7- 基于原型的编程范式.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/037 编程范式游记8- Go 语言的委托模式.md.html">037 编程范式游记8- Go 语言的委托模式.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/038 编程范式游记9- 编程的本质.md.html">038 编程范式游记9- 编程的本质.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/039 编程范式游记10- 逻辑编程范式.md.html">039 编程范式游记10- 逻辑编程范式.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/040 编程范式游记11- 程序世界里的编程范式.md.html">040 编程范式游记11- 程序世界里的编程范式.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/041 弹力设计篇之“认识故障和弹力设计”.md.html">041 弹力设计篇之“认识故障和弹力设计”.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/042 弹力设计篇之“隔离设计”.md.html">042 弹力设计篇之“隔离设计”.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/043 弹力设计篇之“异步通讯设计”.md.html">043 弹力设计篇之“异步通讯设计”.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/044 弹力设计篇之“幂等性设计”.md.html">044 弹力设计篇之“幂等性设计”.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/045 弹力设计篇之“服务的状态”.md.html">045 弹力设计篇之“服务的状态”.md.html</a>
</li>
<li>
<a class="current-tab" href="/专栏/左耳听风/046 弹力设计篇之“补偿事务”.md.html">046 弹力设计篇之“补偿事务”.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/047 弹力设计篇之“重试设计”.md.html">047 弹力设计篇之“重试设计”.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/048 弹力设计篇之“熔断设计”.md.html">048 弹力设计篇之“熔断设计”.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/049 弹力设计篇之“限流设计”.md.html">049 弹力设计篇之“限流设计”.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/050 弹力设计篇之“降级设计”.md.html">050 弹力设计篇之“降级设计”.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/051 弹力设计篇之“弹力设计总结”.md.html">051 弹力设计篇之“弹力设计总结”.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/052 区块链技术 - 区块链的革命性及技术概要.md.html">052 区块链技术 - 区块链的革命性及技术概要.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/053 区块链技术 - 区块链技术细节 - 哈希算法.md.html">053 区块链技术 - 区块链技术细节 - 哈希算法.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/054 区块链技术 - 区块链技术细节 - 加密和挖矿.md.html">054 区块链技术 - 区块链技术细节 - 加密和挖矿.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/055 区块链技术 - 去中心化的共识机制.md.html">055 区块链技术 - 去中心化的共识机制.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/056 区块链技术 - 智能合约.md.html">056 区块链技术 - 智能合约.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/057 区块链技术 - 传统金融和虚拟货币.md.html">057 区块链技术 - 传统金融和虚拟货币.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/058 管理设计篇之分布式锁.md.html">058 管理设计篇之分布式锁.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/059 管理设计篇之配置中心.md.html">059 管理设计篇之配置中心.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/060 管理设计篇之边车模式.md.html">060 管理设计篇之边车模式.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/061 管理设计篇之服务网格.md.html">061 管理设计篇之服务网格.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/062 管理设计篇之网关模式.md.html">062 管理设计篇之网关模式.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/063 管理设计篇之部署升级策略.md.html">063 管理设计篇之部署升级策略.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/064 性能设计篇之缓存.md.html">064 性能设计篇之缓存.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/065 性能设计篇之异步处理.md.html">065 性能设计篇之异步处理.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/066 性能设计篇之数据库扩展.md.html">066 性能设计篇之数据库扩展.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/067 性能设计篇之秒杀.md.html">067 性能设计篇之秒杀.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/068 性能设计篇之边缘计算.md.html">068 性能设计篇之边缘计算.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/069 程序员练级攻略2018开篇词.md.html">069 程序员练级攻略2018开篇词.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/070 程序员练级攻略2018零基础启蒙.md.html">070 程序员练级攻略2018零基础启蒙.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/071 程序员练级攻略2018正式入门.md.html">071 程序员练级攻略2018正式入门.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/072 程序员练级攻略2018程序员修养.md.html">072 程序员练级攻略2018程序员修养.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/073 程序员练级攻略2018编程语言.md.html">073 程序员练级攻略2018编程语言.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/074 程序员练级攻略:理论学科.md.html">074 程序员练级攻略:理论学科.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/075 程序员练级攻略2018系统知识.md.html">075 程序员练级攻略2018系统知识.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/076 程序员练级攻略2018软件设计.md.html">076 程序员练级攻略2018软件设计.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/077 程序员练级攻略2018Linux系统、内存和网络.md.html">077 程序员练级攻略2018Linux系统、内存和网络.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/078 程序员练级攻略2018异步IO模型和Lock-Free编程.md.html">078 程序员练级攻略2018异步IO模型和Lock-Free编程.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/079 程序员练级攻略2018Java底层知识.md.html">079 程序员练级攻略2018Java底层知识.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/080 程序员练级攻略2018数据库.md.html">080 程序员练级攻略2018数据库.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/081 程序员练级攻略2018分布式架构入门.md.html">081 程序员练级攻略2018分布式架构入门.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/082 程序员练级攻略2018分布式架构经典图书和论文.md.html">082 程序员练级攻略2018分布式架构经典图书和论文.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/083 程序员练级攻略2018分布式架构工程设计.md.html">083 程序员练级攻略2018分布式架构工程设计.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/084 程序员练级攻略2018微服务.md.html">084 程序员练级攻略2018微服务.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/085 程序员练级攻略2018容器化和自动化运维.md.html">085 程序员练级攻略2018容器化和自动化运维.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/086 程序员练级攻略2018机器学习和人工智能.md.html">086 程序员练级攻略2018机器学习和人工智能.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/087 程序员练级攻略2018前端基础和底层原理.md.html">087 程序员练级攻略2018前端基础和底层原理.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/088 程序员练级攻略2018前端性能优化和框架.md.html">088 程序员练级攻略2018前端性能优化和框架.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/089 程序员练级攻略2018UIUX设计.md.html">089 程序员练级攻略2018UIUX设计.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/090 程序员练级攻略2018技术资源集散地.md.html">090 程序员练级攻略2018技术资源集散地.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/091 程序员面试攻略:面试前的准备.md.html">091 程序员面试攻略:面试前的准备.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/092 程序员面试攻略:面试中的技巧.md.html">092 程序员面试攻略:面试中的技巧.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/093 程序员面试攻略:面试风格.md.html">093 程序员面试攻略:面试风格.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/094 程序员面试攻略:实力才是王中王.md.html">094 程序员面试攻略:实力才是王中王.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/095 高效学习:端正学习态度.md.html">095 高效学习:端正学习态度.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/096 高效学习:源头、原理和知识地图.md.html">096 高效学习:源头、原理和知识地图.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/097 高效学习:深度,归纳和坚持实践.md.html">097 高效学习:深度,归纳和坚持实践.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/098 高效学习:如何学习和阅读代码.md.html">098 高效学习:如何学习和阅读代码.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/099 高效学习:面对枯燥和量大的知识.md.html">099 高效学习:面对枯燥和量大的知识.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/100 高效沟通Talk和Code同等重要.md.html">100 高效沟通Talk和Code同等重要.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/101 高效沟通:沟通阻碍和应对方法.md.html">101 高效沟通:沟通阻碍和应对方法.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/102 高效沟通:沟通方式及技巧.md.html">102 高效沟通:沟通方式及技巧.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/103 高效沟通:沟通技术.md.html">103 高效沟通:沟通技术.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/104 高效沟通:好老板要善于提问.md.html">104 高效沟通:好老板要善于提问.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/105 高效沟通:好好说话的艺术.md.html">105 高效沟通:好好说话的艺术.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/106 加餐 谈谈我的“三观”.md.html">106 加餐 谈谈我的“三观”.md.html</a>
</li>
<li>
<a href="/专栏/左耳听风/107 结束语 业精于勤,行成于思.md.html">107 结束语 业精于勤,行成于思.md.html</a>
</li>
</ul>
</div>
</div>
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
<div class="sidebar-toggle-inner"></div>
</div>
<script>
function add_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.add('show')
}
function remove_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.remove('show')
}
function sidebar_toggle() {
let sidebar_toggle = document.querySelector('.sidebar-toggle')
let sidebar = document.querySelector('.book-sidebar')
let content = document.querySelector('.off-canvas-content')
if (sidebar_toggle.classList.contains('extend')) { // show
sidebar_toggle.classList.remove('extend')
sidebar.classList.remove('hide')
content.classList.remove('extend')
} else { // hide
sidebar_toggle.classList.add('extend')
sidebar.classList.add('hide')
content.classList.add('extend')
}
}
function open_sidebar() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.add('show')
overlay.classList.add('show')
}
function hide_canvas() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.remove('show')
overlay.classList.remove('show')
}
</script>
<div class="off-canvas-content">
<div class="columns">
<div class="column col-12 col-lg-12">
<div class="book-navbar">
<!-- For Responsive Layout -->
<header class="navbar">
<section class="navbar-section">
<a onclick="open_sidebar()">
<i class="icon icon-menu"></i>
</a>
</section>
</header>
</div>
<div class="book-content" style="max-width: 960px; margin: 0 auto;
overflow-x: auto;
overflow-y: hidden;">
<div class="book-post">
<p id="tip" align="center"></p>
<div><h1>046 弹力设计篇之“补偿事务”</h1>
<p>前面,我们说过,分布式系统有一个比较明显的问题就是,一个业务流程需要组合一组服务。这样的事情在微服务下就更为明显了,因为这需要业务上的一致性的保证。也就是说,如果一个步骤失败了,那么要么回滚到以前的服务调用,要么不断重试保证所有的步骤都成功。</p>
<p>这里,如果需要强一性的需求,那么,在业务层上需要使用“两阶段提交”这样的方式。但是好在我们的很多情况下并不需要这么强的一致性,而且强一致性的最佳保证最好是在底层完成。或是像之前说的那样 Stateful 的 Sticky Session 那样在一台机器上完成。在我们接触到的大多数业务,其实只需要最终一致性就好。</p>
<h1>ACID 和 BASE</h1>
<p>谈到这里,有必要先说一下 ACID 和 BASE 的差别。传统关系型数据库系统的事务都有 ACID 属性即原子性Atomicity、一致性Consistency、隔离性Isolation又称独立性、持久性Durability</p>
<ul>
<li>原子性: 整个事务中的所有操作要么全部完成要么全部不完成不可能停滞在中间某个环节。事务在执行过程中发生错误会被回滚Rollback到事务开始前的状态就像这个事务从来没有执行过一样。</li>
<li>一致性: 在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。</li>
<li>隔离性: 两个事务的执行是互不干扰的,一个事务不可能看到其他事务运行时,中间某一时刻的数据。两个事务不会发生交互。</li>
<li>持久性: 在事务完成以后,该事务对数据库所做的更改便持久地保存在数据库之中,并不会被回滚。</li>
</ul>
<p>事务的 ACID 属性保证了数据库的一致性,比如银行系统中,转账就是一个事务,从原账户扣除金额,以及向目标账户添加金额,这两个数据库操作的总和构成一个完整的逻辑过程,是不可拆分的原子操作,从而保证了整个系统中的总金额没有变化。</p>
<p>然而,这对于我们的分布式系统来说,尤其是微服务来说,这样的方式是很难适合高性能的要求的。我们都很熟悉 CAP 理论——在分布式的服务架构中一致性Consistency、可用性Availability、分区容忍性Partition Tolerance在现实中不能都满足最多只能满足其中两个。</p>
<p>所以,为了提高性能,出现了 ACID 的一个变种 BASE。</p>
<ul>
<li><strong>Basic Availability</strong>:基本可用。这意味着,系统可以出现暂时不可用的状态,而后面会快速恢复。</li>
<li><strong>Soft-state</strong>:软状态。它是我们前面的“有状态”和“无状态”的服务的一种中间状态。也就是说,为了提高性能,我们可以让服务暂时保存一些状态或数据,这些状态和数据不是强一致性的。</li>
<li><strong>Eventual Consistency</strong>:最终一致性,系统在一个短暂的时间段内是不一致的,但最终整个系统看到的数据是一致的。</li>
</ul>
<p>可以看到BASE 系统是允许或是容忍系统出现暂时性的问题的,这样一来,我们的系统就能更有弹力。因为我们知道,在分布式系统的世界里,故障是不可避免的,我们能做的就是把故障处理当成功能写入代码中,这就是 Design for Failure。</p>
<p>BASE 的系统倾向于设计出更加有弹力的的系统,这种系统的设计特点是,要保证在短时间内,就算是有数据不同步的风险,我们也应该允许新的交易可以发生,而后面我们在业务上将可能出现问题的事务给处理掉,以保证最终的一致性。</p>
<p>举个例子,网上卖书的场景。</p>
<p>ACID 的玩法就是,大家在买同一本书的过程中,每个用户的购买请求都需要把库存锁住,等减完库存后,把锁释放出来,后续的人才能进行购买。于是,在 ACID 的玩法下,我们在同一时间不可能有多个用户下单,我们的订单流程需要有排队的情况,这样一来,我们就不可能做出性能比较高的系统来。</p>
<p>BASE 的玩法是,大家都可以同时下单,这个时候不需要去真正地分配库存,然后系统异步地处理订单,而且是批量的处理。因为下单的时候没有真正去扣减库存,所以,有可能会有超卖的情况。而后台的系统会异步地处理订单时,发现库存没有了,于是才会告诉用户你没有购买成功。</p>
<p>BASE 这种玩法,其实就是亚马逊的玩法,因为要根据用户的地址去不同的仓库查看库存,这个操作非常耗时,所以,不想做成异步的都不行。</p>
<p>在亚马逊上买东西,你会收到一封邮件说,系统收到你的订单了,然后过一会儿你会收到你的订单被确认的邮件,这时候才是真正地分配了库存。所以,有某些时候,你会遇到你先收到了下单的邮件,过一会又收到了没有库存的致歉的邮件。</p>
<p>有趣的是ACID 的意思是酸,而 BASE 却是碱的意思因此这是一个对立的东西。其实从本质上来讲ACID强调的是一致性CAP 中的 C而碱BASE强调的是可用性CAP 中的 A</p>
<h1>业务补偿</h1>
<p>有了上面对 ACID 和 BASE 的分析,我们知道,在很多情况下,我们是无法做到强一致的 ACID 的。特别是我们需要跨多个系统的时候,而且这些系统还不是由一个公司所提供的。比如,在我们的日常生活中,我们经常会遇到这样的情况,就是要找很多方协调很多事,而且要保证我们每一件事都成功,否则整件事就做不到。</p>
<p>比如,要出门旅游, 我们需要干这么几件事。第一,向公司请假,拿到相应的假期;第二,订飞机票或是火车票;第三,订酒店;第四,租车。这四件事中,前三件必需完全成功,我们才能出行,而第四件事只是一个锦上添花的事,但第四件事一旦确定,那么也会成为整个事务的一部分。这些事都是要向不同的组织或系统请求。我们可以并行地做这些事,而如果某个事有变化,其它的事都会跟着出现一些变化。</p>
<p>设想下面的几种情况。</p>
<ol>
<li>我没有订到返程机票,那么我就去不了了。我需要把订到的去程机票,酒店、租到的车都给取消了,并且把请的假也取消了。</li>
<li>如果我假也请好了,机票,酒店也订好了,只是车没租到,那么并不影响我出行这个事,整个事还是可以继续的。</li>
<li>如果我的飞机因为天气原因取消或是晚点了,那么我被迫要去调整和修改我的酒店预订和租车的预订。</li>
</ol>
<p>从人类的实际生活当中,我们可以看出,上述的这些情况都是天天在发生的事情。所以,我们的分布式系统也是一样的,也是需要处理这样的事情——就是当条件不满足,或是有变化的时候,需要从业务上做相应的整体事务的补偿。</p>
<p>一般来说,业务的事务补偿都是需要一个工作流引擎的。亚马逊是一个超级喜欢工作流引擎的公司,这个工作流引擎把各式各样的服务给串联在一起,并在工作流上做相应的业务补偿,整个过程设计成为最终一致性的。</p>
<p>对于业务补偿来说,首先需要将服务做成幂等性的,如果一个事务失败了或是超时了,我们需要不断地重试,努力地达到最终我们想要的状态。然后,如果我们不能达到这个我们想要的状态,我们需要把整个状态恢复到之前的状态。另外,如果有变化的请求,我们需要启动整个事务的业务更新机制。</p>
<p>所以,一个好的业务补偿机制需要做到下面这几点。</p>
<ol>
<li>要能清楚地描述出要达到什么样的状态(比如:请假、机票、酒店这三个都必须成功,租车是可选的),以及如果其中的条件不满足,那么,我们要回退到哪一个状态。这就是所谓的整个业务的起始状态定义。</li>
<li>当整条业务跑起来的时候,我们可以串行或并行地做这些事。对于旅游订票是可以并行的,但是对于网购流程(下单、支付、送货)是不能并行的。总之,我们的系统需要努力地通过一系列的操作达到一个我们想要的状态。如果达不到,就需要通过补偿机制回滚到之前的状态。<strong>这就是所谓的状态拟合</strong></li>
<li>对于已经完成的事务进行整体修改。其实可以考虑成一个修改事务。</li>
</ol>
<p>其实,在纯技术的世界里也有这样的事。比如,线上运维系统需要发布一个新的服务或是对一个已有的服务进行水平扩展,我们需要先找到相应的机器,然后初始化环境,再部署上应用,再做相应的健康检查,最后接入流量。这一系列的动作都要完全成功,所以,我们的部署系统就需要管理好整个过程和相关的运行状态。</p>
<h1>业务补偿的设计重点</h1>
<p>业务补偿主要做两件事。</p>
<ol>
<li>努力地把一个业务流程执行完成。</li>
<li>如果执行不下去,需要启动补偿机制,回滚业务流程。</li>
</ol>
<p>所以,下面是几个重点。</p>
<ul>
<li>因为要把一个业务流程执行完成,需要这个流程中所涉及的服务方支持幂等性。并且在上游有重试机制。</li>
<li>我们需要小心维护和监控整个过程的状态,所以,千万不要把这些状态放到不同的组件中,最好是一个业务流程的控制方来做这个事,也就是一个工作流引擎。所以,这个工作流引擎是需要高可用和稳定的。这就好像旅行代理机构一样,我们把需求告诉它,它会帮我们搞定所有的事。如果有问题,也会帮我们回滚和补偿的。</li>
<li>补偿的业务逻辑和流程不一定非得是严格反向操作。有时候可以并行,有时候,可能会更简单。总之,设计业务正向流程的时候,也需要设计业务的反向补偿流程。</li>
<li>我们要清楚地知道,业务补偿的业务逻辑是强业务相关的,很难做成通用的。</li>
<li>下层的业务方最好提供短期的资源预留机制。就像电商中的把货品的库存预先占住等待用户在 15 分钟内支付。如果没有收到用户的支付,则释放库存。然后回滚到之前的下单操作,等待用户重新下单。</li>
</ul>
<h1>小结</h1>
<p>好了,我们来总结一下今天分享的主要内容。首先,我介绍了 ACID 和 BASE 两种不同级别的一致性。在分布式系统中ACID 有更强的一致性但可伸缩性非常差仅在必要时使用BASE 的一致性较弱,但有很好的可伸缩性,还可以异步批量处理;大多数分布式事务适合 BASE。</p>
<p>要实现 BASE 事务,需要实现补偿逻辑,因为事务可能失败,此时需要协调各方进行撤销。补偿的各个步骤可以根据具体业务来确定是串行还是并行。由于补偿事务是和业务强相关的,所以必须实现在业务逻辑里。下篇文章中,我们讲述重试设计。希望对你有帮助。</p>
<p>也欢迎你分享一下你的分布式服务用到了怎样的一致性?你是怎么实现补偿事务的?</p>
</div>
</div>
<div>
<div style="float: left">
<a href="/专栏/左耳听风/045 弹力设计篇之“服务的状态”.md.html">上一页</a>
</div>
<div style="float: right">
<a href="/专栏/左耳听风/047 弹力设计篇之“重试设计”.md.html">下一页</a>
</div>
</div>
</div>
</div>
</div>
</div>
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
</div>
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"709978354d573cfa","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
</body>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-NPSEEVD756');
var path = window.location.pathname
var cookie = getCookie("lastPath");
console.log(path)
if (path.replace("/", "") === "") {
if (cookie.replace("/", "") !== "") {
console.log(cookie)
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
}
} else {
setCookie("lastPath", path)
}
function setCookie(cname, cvalue) {
var d = new Date();
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
}
return "";
}
</script>
</html>