オリジナルリンク:https://www.cnblogs.com/savorboard/p/distributed-system-transaction-consistency.html
分散トランザクションの話は、私は解決策について話しましょう
序文
最近、長い時間は、会社の物事は最近、より忙しくしている一因、ブログを書いていない、中にため、他の一方である CAP 次のフェーズを開発し、今終わりに来ています。
誰もが、それは同じではありません理解できたので、次に、そこに分散トランザクションについて話をする私たちの話題の始まり今日は、ある、またはそれが私の目には、分散トランザクションです。
単にチャットする分散トランザクション分散トランザクションは、エンタープライズ統合における技術的困難ですが、また、各分散システム・アーキテクチャは、特にマイクロサービスアーキテクチャでは、関係するものになるだろう、ほとんどが避けられないと言うことができる、本論文。
データベースのトランザクション
分散トランザクションを言って前に、私たちは、データベーストランザクションについて話し始めます。我々はまた、頻繁に、開発プロセスで使用される可能性があるデータベースのトランザクションに精通しています。しかし、そうであっても、それは詳細の一部ためのものであってもよい、多くの人々はまだ知りません。例えば、多くの人々がデータベーストランザクションのいくつかの機能を知っている:アトミック(原子性)、一貫性(一貫性)、単離または独立(分離)と持続性(Durabilily)、ACIDと呼ばれています。しかし、その後、ダウンタイムが何を意味するかのように孤立に聞いて知っている、またはしかし、孤立したデータベースの実装レベルであるか、各レベルに尋ねるか、その違いは何しているものの分離を知らないかもしれません時間は知らないかもしれません。
この記事では、これらのデータベース・トランザクションに関連する情報を検索することができます興味を持ってこれらの事を、ご紹介する予定はありません。しかし、私たちは理解して必要な知識は、トランザクションがコミットされたデータベースの突然の停電ならば、それはそれを復元する方法であるということですがありますか?なぜそれがこの知識を言及?分散システムの中核は、異常な状況の様々な対処することであるため、これは、この「電源オフ」スタンドアローンよりも多くの障害に複雑である、なぜなら、分散ネットワーク環境の複雑な分散システムであるので、我々は、分散やっていますシステムは、最初の考慮事項は、ケースです。これらの異常は、その上のマシンのダウンタイム、ネットワークの異常、メッセージが失われ、オーダーのうちメッセージ、データエラー、信頼できないTCP、保存されたデータが失われ、その他の異常とを有することができます...
私たちは、ときに、ローカルトランザクションデータベースの停止、それはそれのデータの一貫性を確保する方法であることを言うようになりましたか?我々は、我々が通常の状況下では、ログファイルはデータベースファイルよりもはるかに大きくなければなりません、SQL Serverデータベースは、2つのファイル、データベースファイルとログファイルで構成されて使用されていることを知って、例えばSQL Serverを使用します。データベースは最初の操作の前に、我々はトランザクションデータベースは、最初の操作のREDOログ・トランザクションを記録した後、実際にデータベースを操作するようになっただろうに実行ログ、同じトークンを、書くことが任意の書き込み操作の最初のものである場合には突然の停電は、操作が完了していない場合でも、データベースを再起動すると、ログファイルがディスクに書き込まれ、その後、データベースはロールバックされますか、これ強力なデータを確保し、現在のデータの前の状況に応じたREDOロールを元に戻します一貫性。
その後、我々は、分散トランザクションについて言います。
分散理論
ときに私たちの単一のデータベースのパフォーマンスのボトルネック、我々は、データベース・パーティションかもしれませんが、ここで言及したパーティションは、パーティションの後に物理パーティションを指し、異なるサーバ上の異なるライブラリーであってもよく、このとき単一のデータベースACIDは考えるクラスタを達成するのはほとんど困難であることを保証するために、または我々はその効率性と性能を達成することができたとしても、このような状況に適応し、このACID、ACIDのクラスタ環境ではないことができ、実質的に低減されますが、最も重要なは再び困難です新しい拡張パーティション、そして今回、我々はACIDのクラスタは、その後、我々は条件ようなクラスタに合わせて、新たな理論的な原則を導入する必要があり、私たちのシステムが悪くなることになります追求場合、それは、CAPまたはと呼ばれるCAP定理の原理であります、その後、CAP定理は、それが何であるかを参照しますか?
CAP定理
カリフォルニア大学のCAP定理、バークレー校教授エリック・ブリュワーが提案されている、彼はWEBサービスを指摘し、同時に3つの属性を満たすことができません。
- 一貫性(一貫性):クライアントが同時に発生しますアクションのシリーズを知っている(発効)
- アベイラビリティ(可用性):各操作の最後には、に応答すると予想されなければなりません
- 許容パーティション(パーティション公差)障害:単一成分であっても利用できない場合、操作がまだ完了すること
具体的には、分散システムでは、任意のデータベース設計では、Webアプリケーションは上記の二つの属性をサポートします。明らかに、任意の横方向拡大戦略は、データパーティションに依存しなければなりません。そのため、設計者は一貫性と可用性の間で選択する必要があります。
分散システムにおけるこの定理は、これまでに適用されます! なぜあなたは言うのですか?
この時、一部の学生が発言するために、データベースの2PC(2フェーズ・コミット)を置いてもよいです。OK、我々は、提出されたデータベースの二つの段階を見てください。
分散データベースのトランザクションは、データベースもXAトランザクションとして知られ、2PCをサポートしています知っている必要があり、学生の理解を持っています。
MySQLはバージョン5.5からサポートされている、SQL Server 2005はサポートするために始めた、Oracleの7はサポートし始めました。
ここで、XAは、二つの段階に分けられる二相コミットプロトコルは、次のとおりです。
- 第一段階:トランザクションコーディネータは、関連する各データベーストランザクションは(プリコミット)これをプリコミットし、提出するかどうかを反映することが必要です。
- フェーズII:各データベース・トランザクション・コーディネータがデータを提出することが必要です。
任意の拒否権は、データベースの提出があった場合はその中でも、すべてのデータベースがこのトランザクションに関する情報の彼らの一部をロールバックする必要があります。どのような欠陥をやっていることは、それは我々がデータベース・パーティションに一貫性を得ることができますか?一見です。
CAP定理が正しい場合、それは確かに可用性に影響を与えます。
システムの代表の可用性は、すべてのコンポーネントの可用性とに関連する操作を実行する場合。だから、二相の過程での利用可能性に関係する各データベースの可用性を代表して、コミットして。我々は、各データベース内の2フェーズ・コミット処理が99.9%の利用可能性を有しているので、2フェーズ・コミット場合は2つのデータベースを含む、結果は99.8%であると仮定する。システムの可用性は、それが想定され、計算され、99.9%の可用性が99.8%の可用性が毎月43分のダウンタイムの増加に対応し、43114分、43157分月あたり43,200分です。
上記の、あなたはそれを確認することができ、理論的にはCAP定理が正しい、CAPはのは、ここで見てみましょう、とので、次を言うだろう。
BASE理論
分散システムでは、私たちはしばしば求めて、それは、その後、どのように高可用性を実現するためのプログラムの一貫性よりも高くすることが重要だ、利用できるのですか?前者は、さらなる拡大のための定理をキャップするために使用され、別の理論にそのBASE理論を私たちに与えています。BASEの理論は意味します:
- 基本的には利用可能(利用可能な基本)
- ソフト状態(ソフト状態)
- 最終的に一貫性のある(最終的な一貫性)
BASEのCAP理論トレードオフの一貫性と可用性の結果である、理論の核となるアイデアはこれです:私たちは同じ強力な操作を行うことはできませんが、各アプリケーションその動作特性に応じて、システムリーチを作るための適切な方法最終的な一貫性(最終的な一貫性)。
その後上記の理論では、我々は、分散トランザクションの問題を見てください。
分散トランザクション
分散システムでは、分散トランザクション、そのいくつかのソリューションよりも少ない何を実現しています。
まず、2フェーズ・コミット(2PC)
そして、同じ2つのフェーズで述べたデータベース上のXAトランザクションは、XAプロトコルを使用する原理であるコミット、我々は簡単に以下のフローチャートは、このようなミドルをコミットし、中止として、この一部を詳細に見ることができます。
二相は、一貫性の可用性の一部と引き換えに犠牲に属しているこのソリューションをコミットします。実現の観点では、.NETで、あなたはWCFなどは機能のこの部分で達成した2フェーズ・コミットの分散システムによって提供される使用のAPI TransactionScopプログラミングを行うことができます。しかし、複数のサーバ間で、Linuxで、そこにMicrosoft WindowsのMSDTCサービスの下でトランザクションの整合性を完了するために、DTCに依存する必要がより多くの悲劇です。
また、トランザクションは、スレッドの現在のコンテキストに格納されているので、それは、非同期メソッドで明示的なトランザクションコンテキスト転送を必要とする場合TransactionScopのデフォルトコヒーレントトランザクションは、非同期メソッドの間で使用することはできない、と言っています。
長所: 強力なデータの一貫性、重要分野を要求したデータのための強力な一貫性を保証してみてください。(実際には、我々は100%の強力な合意を保証することはできません)
短所: システムがインターフェイスコールに分散されている場合、高性能同時シナリオに適した、.NETコミュニティが現在のプログラムを達成していない、使いやすさを犠牲にして、パフォーマンスに大きな影響を複雑にしませ実装します。
第二に、補償トランザクション(TCC)
アクション(アンドゥ)、各操作の登録確認及び補償は、対応すべきである:TCC補償機構が実際に使用される、核となるアイデアです。それは3つの段階に分かれています。
-
ビジネスのためのメインステージのテストやリソース予約システムをしよう
-
確認ステージは、ビジネスシステムを提出成功した実行ステージを試してみて、確認フェーズを実装するために開始することを確認するために主である場合には、位相デフォルトの確認は間違って行くことはありません。成功した試し限り、ある程度の成功を収めて確認してください:それはあります。
-
メインステージは、予約されたリソースを解放し、キャンセルされたサービスの状態をロールバックする必要性の下で行われ、間違いの実施事業でキャンセルです。
たとえば、偽のボブ・スミスはおそらく考えて、転送したい
私たちは順番に呼び出し、ローカルメソッド、持っている
最初の、1、トライの最初のステージを凍結するスミスとボブのお金へのリモートインタフェースを呼び出します。
図2に示すように、確認相において、操作の転送が成功した解凍を転送、リモート呼び出しを実行します。
3.ステップ2(キャンセル)、次いで転送が成功し、第二段階が失敗した場合、その呼リモートインタフェースは凍結融解方法に対応する成功した場合。
:長所 2PCとの比較、およびプロセスは、いくつかを達成することは比較的簡単ですが、データの整合性が2PCのいくつかよりも悪くなければなりません
短所: 欠点は非常に明白である、ステップ3で失敗する可能性があります。プログラマは、補償の実現に多くのコードを記述する必要があるので、TCCは、補償アプリケーション層の形で、いくつかのシナリオでは、ビジネスプロセスの数が十分に定義されない場合があり、TCCで処理しました。
第三に、ローカル・メッセージ・テーブル(非同期確保)
ローカルニュースはテーブルこの実装は、業界で最も使用される、核となるアイデアは、トランザクション処理に分散トランザクションの費用を分割することですされなければならない、このアイデアは、eBayの由来です。私たちは、以下のフローチャートの詳細の一部から見ることができます:
基本的な考え方はこれです:
メッセージプロデューサ、追加のビルドの必要性メッセージテーブル、およびメッセージの送信状況を記録します。彼らは内部データベースにしたいことを意味し、単一のトランザクションに提出するメッセージテーブルおよびビジネスデータ。メッセージは、MQの消費者を介してメッセージに送信されます。メッセージが失敗した場合、伝送を再試行してください。
ニュース消費者は、このニュースに対処し、彼らのビジネスロジックを完了する必要があります。ローカルトランザクション処理が成功した場合は、この時点では、プロセスが失敗した場合、治療は、成功したことを示す、それが実行を再試行します。上記の操作の失敗した場合、サービスは、生産者が操作をロールバックするように通知する補正メッセージの生産側に送信することができます。
生産、メッセージが再度再送信失敗メッセージを処理し、またはされていないローカルテーブルメッセージのタイミングをスキャンする消費者。自動アカウント和解ロジック補数が飛ぶ場合は、この実施例は非常に実用的です。
このプログラムは、私が実際のビジネスシナリオに適しているプログラムのこれらのタイプは、それが2PC(チェーン長い時間を呼び出すときに、2PC表示されないような複雑な実装であることを信じて、BASE理論を次の究極の一貫性であります可用性は、それが確定していないだろうか、発生する可能性のある状況は、TCCとしてロールバックすることはできません)非常に低いです。
長所: 非常に古典的な実装、最終的な一貫性を達成するために、分散トランザクションを回避することができます。.NETの既製のソリューションがあります。
短所: メッセージテーブルには、多くの雑用が対処する必要があるだろう、ない良いソリューションパッケージが存在しない場合は、業務システムに結合されます。
四、MQトランザクションメッセージ
このようRocketMQなどの一部のサードパーティMQトランザクションメッセージは、ありますが、彼らは取引メッセージをサポートする方法は、2段階の提出の使用に似ていますが、市場の主流の一部は、このようなあるRabbitMQのやカフカなどMQトランザクションメッセージを、サポートしていません。サポートされていません。
ミドルウェアとアリRocketMQは、例えば、アイデアはおおよそ次のとおりです。
最初のステージ準備のメッセージは、メッセージのアドレスを取得します。
ローカル・トランザクションの実装の第二段階、第一段を介して第3段目のメッセージ・アドレスへのアクセスを取得し、状態を変更します。
それはメッセージと受信確認メッセージを送信し、再び、二回のサービス要求方法でメッセージキューを提出するためです。定期的にメッセージのクラスタをスキャンRocketMQ確認メッセージ失敗したトランザクションメッセージが、今回は準備メッセージを見つけた場合、それは生産者がチェックインタフェースを実装する必要があるので、RocketMQが送信者の設定ポリシーに基づいて行われます、メッセージの送信者に確認しますロールバックまたは確認メッセージを送信するために継続するかどうかを決定します。これは、ローカル・トランザクションで送信されたメッセージが成功するか失敗することを保証します。
残念ながら、そうではありません、.NETクライアントをRocketMQ。RocketMQ詳細ニュースは、あなたが見ることができますこのブログを
長所: 最後の一貫性を達成するために、ローカルデータベーストランザクションに依存しません。
短所: 難しいが、大きな達成するために、主流のMQがサポートされていない、何の.NETクライアントが存在しない、RocketMQ取引メッセージは、コードの一部であり、オープンソースではありません。
五、サガのトランザクションモデル
Saga事务模型又叫做长时间运行的事务(Long-running-transaction), 它是由普林斯顿大学的H.Garcia-Molina等人提出,它描述的是另外一种在没有两阶段提交的的情况下解决分布式系统中复杂的业务事务问题。你可以在这里看到 Sagas 相关论文。
我们这里说的是一种基于 Sagas 机制的工作流事务模型,这个模型的相关理论目前来说还是比较新的,以至于百度上几乎没有什么相关资料。
该模型其核心思想就是拆分分布式系统中的长事务为多个短事务,或者叫多个本地事务,然后由 Sagas 工作流引擎负责协调,如果整个流程正常结束,那么就算是业务成功完成,如果在这过程中实现失败,那么Sagas工作流引擎就会以相反的顺序调用补偿操作,重新进行业务回滚。
比如我们一次关于购买旅游套餐业务操作涉及到三个操作,他们分别是预定车辆,预定宾馆,预定机票,他们分别属于三个不同的远程接口。可能从我们程序的角度来说他们不属于一个事务,但是从业务角度来说是属于同一个事务的。
他们的执行顺序如上图所示,所以当发生失败时,会依次进行取消的补偿操作。
因为长事务被拆分了很多个业务流,所以 Sagas 事务模型最重要的一个部件就是工作流或者你也可以叫流程管理器(Process Manager),工作流引擎和Process Manager虽然不是同一个东西,但是在这里,他们的职责是相同的。在选择工作流引擎之后,最终的代码也许看起来是这样的
SagaBuilder saga = SagaBuilder.newSaga("trip")
.activity("Reserve car", ReserveCarAdapter.class)
.compensationActivity("Cancel car", CancelCarAdapter.class)
.activity("Book hotel", BookHotelAdapter.class)
.compensationActivity("Cancel hotel", CancelHotelAdapter.class) .activity("Book flight", BookFlightAdapter.class) .compensationActivity("Cancel flight", CancelFlightAdapter.class) .end() .triggerCompensationOnAnyError(); camunda.getRepositoryService().createDeployment() .addModelInstance(saga.getModel()) .deploy();
这里有一个 C# 相关示例,有兴趣的同学可以看一下。
优缺点这里我们就不说了,因为这个理论比较新,目前市面上还没有什么解决方案,即使是 Java 领域,我也没有搜索的太多有用的信息。
分布式事务解决方案:CAP
上面介绍的那些分布式事务的处理方案你在其他地方或许也可以看到,但是并没有相关的实际代码或者是开源代码,所以算不上什么干货,下面就放干货了。
在 .NET 领域,似乎没有什么现成的关于分布式事务的解决方案,或者说是有但未开源。具笔者了解,有一些公司内部其实是有这种解决方案的,但是也是作为公司的一个核心产品之一,并未开源...
鉴于以上原因,所以博主就打算自己写一个并且开源出来,所以从17年初就开始做这个事情,然后花了大半年的时间在一直不断完善,就是下面这个 CAP。
Github CAP:这里的 CAP 就不是 CAP 理论了,而是一个 .NET 分布式事务解决方案的名字。
详细介绍:
http://www.cnblogs.com/savorboard/p/cap.html
相关文档:
http://www.cnblogs.com/savorboard/p/cap-document.html
夸张的是,这个解决方案是具有可视化界面(Dashboard)的,你可以很方面的看到哪些消息执行成功,哪些消息执行失败,到底是发送失败还是处理失败,一眼便知。
最夸张的是,这个解决方案的可视化界面还提供了实时动态图表,这样不但可以看到实时的消息发送及处理情况,连当前的系统处理消息的速度都可以看到,还可以看到过去24小时内的历史消息吞吐量。
最最夸张的是,这个解决方案的还帮你集成了 Consul 做分布式节点发现和注册还有心跳检查,你随时可以看到其他的节点的状况。
最最最夸张的是,你以为你看其他节点的数据要登录到其他节点的Dashboard控制台看?错了,你随便打开其中任意一个节点的Dashboard,点一下就可以切换到你想看的节点的控制台界面了,就像你看本地的数据一样,他们是完全去中心化的。
你以为这些就够了?不,远远不止:
- CAP 同时支持 RabbitMQ,Kafka 等消息队列
- CAP 同时支持 SQL Server, MySql, PostgreSql 等数据库
- CAP Dashboard 同时支持中文和英文界面双语言,妈妈再也不用担心我看不懂了
- CAP 提供了丰富的接口可以供扩展,什么序列化了,自定义处理了,自定义发送了统统不在话下
- CAP 基于MIT开源,你可以尽管拿去做二次开发。(记得保留MIT的License)
这下你以为我说完了? 不!
你完全可以把 CAP 当做一个 EventBus 来使用,CAP具有优秀的消息处理能力,不要担心瓶颈会在CAP,那是永远不可能, 因为你随时可以在配置中指定CAP处理的消息使用的进程数, 只要你的数据库配置足够高...
前言
最近很久没有写博客了,一方面是因为公司事情最近比较忙,另外一方面是因为在进行 CAP 的下一阶段的开发工作,不过目前已经告一段落了。
接下来还是开始我们今天的话题,说说分布式事务,或者说是我眼中的分布式事务,因为每个人可能对其的理解都不一样。
分布式事务是企业集成中的一个技术难点,也是每一个分布式系统架构中都会涉及到的一个东西,特别是在微服务架构中,几乎可以说是无法避免,本文就分布式事务来简单聊一下。
数据库事务
在说分布式事务之前,我们先从数据库事务说起。 数据库事务可能大家都很熟悉,在开发过程中也会经常使用到。但是即使如此,可能对于一些细节问题,很多人仍然不清楚。比如很多人都知道数据库事务的几个特性:原子性(Atomicity )、一致性( Consistency )、隔离性或独立性( Isolation)和持久性(Durabilily),简称就是ACID。但是再往下比如问到隔离性指的是什么的时候可能就不知道了,或者是知道隔离性是什么但是再问到数据库实现隔离的都有哪些级别,或者是每个级别他们有什么区别的时候可能就不知道了。
本文并不打算介绍这些数据库事务的这些东西,有兴趣可以搜索一下相关资料。不过有一个知识点我们需要了解,就是假如数据库在提交事务的时候突然断电,那么它是怎么样恢复的呢? 为什么要提到这个知识点呢? 因为分布式系统的核心就是处理各种异常情况,这也是分布式系统复杂的地方,因为分布式的网络环境很复杂,这种“断电”故障要比单机多很多,所以我们在做分布式系统的时候,最先考虑的就是这种情况。这些异常可能有 机器宕机、网络异常、消息丢失、消息乱序、数据错误、不可靠的TCP、存储数据丢失、其他异常等等...
我们接着说本地事务数据库断电的这种情况,它是怎么保证数据一致性的呢?我们使用SQL Server来举例,我们知道我们在使用 SQL Server 数据库是由两个文件组成的,一个数据库文件和一个日志文件,通常情况下,日志文件都要比数据库文件大很多。数据库进行任何写入操作的时候都是要先写日志的,同样的道理,我们在执行事务的时候数据库首先会记录下这个事务的redo操作日志,然后才开始真正操作数据库,在操作之前首先会把日志文件写入磁盘,那么当突然断电的时候,即使操作没有完成,在重新启动数据库时候,数据库会根据当前数据的情况进行undo回滚或者是redo前滚,这样就保证了数据的强一致性。
接着,我们就说一下分布式事务。
分布式理论
当我们的单个数据库的性能产生瓶颈的时候,我们可能会对数据库进行分区,这里所说的分区指的是物理分区,分区之后可能不同的库就处于不同的服务器上了,这个时候单个数据库的ACID已经不能适应这种情况了,而在这种ACID的集群环境下,再想保证集群的ACID几乎是很难达到,或者即使能达到那么效率和性能会大幅下降,最为关键的是再很难扩展新的分区了,这个时候如果再追求集群的ACID会导致我们的系统变得很差,这时我们就需要引入一个新的理论原则来适应这种集群的情况,就是 CAP 原则或者叫CAP定理,那么CAP定理指的是什么呢?
CAP定理
CAP定理是由加州大学伯克利分校Eric Brewer教授提出来的,他指出WEB服务无法同时满足一下3个属性:
- 一致性(Consistency) : 客户端知道一系列的操作都会同时发生(生效)
- 可用性(Availability) : 每个操作都必须以可预期的响应结束
- 分区容错性(Partition tolerance) : 即使出现单个组件无法可用,操作依然可以完成
具体地讲在分布式系统中,在任何数据库设计中,一个Web应用至多只能同时支持上面的两个属性。显然,任何横向扩展策略都要依赖于数据分区。因此,设计人员必须在一致性与可用性之间做出选择。
这个定理在迄今为止的分布式系统中都是适用的! 为什么这么说呢?
这个时候有同学可能会把数据库的2PC(两阶段提交)搬出来说话了。OK,我们就来看一下数据库的两阶段提交。
对数据库分布式事务有了解的同学一定知道数据库支持的2PC,又叫做 XA Transactions。
MySQL从5.5版本开始支持,SQL Server 2005 开始支持,Oracle 7 开始支持。
其中,XA 是一个两阶段提交协议,该协议分为以下两个阶段:
- 第一阶段:事务协调器要求每个涉及到事务的数据库预提交(precommit)此操作,并反映是否可以提交.
- 第二阶段:事务协调器要求每个数据库提交数据。
其中,如果有任何一个数据库否决此次提交,那么所有数据库都会被要求回滚它们在此事务中的那部分信息。这样做的缺陷是什么呢? 咋看之下我们可以在数据库分区之间获得一致性。
如果CAP 定理是对的,那么它一定会影响到可用性。
如果说系统的可用性代表的是执行某项操作相关所有组件的可用性的和。那么在两阶段提交的过程中,可用性就代表了涉及到的每一个数据库中可用性的和。我们假设两阶段提交的过程中每一个数据库都具有99.9%的可用性,那么如果两阶段提交涉及到两个数据库,这个结果就是99.8%。根据系统可用性计算公式,假设每个月43200分钟,99.9%的可用性就是43157分钟, 99.8%的可用性就是43114分钟,相当于每个月的宕机时间增加了43分钟。
以上,可以验证出来,CAP定理从理论上来讲是正确的,CAP我们先看到这里,等会再接着说。
BASE理论
在分布式系统中,我们往往追求的是可用性,它的重要程序比一致性要高,那么如何实现高可用性呢? 前人已经给我们提出来了另外一个理论,就是BASE理论,它是用来对CAP定理进行进一步扩充的。BASE理论指的是:
- Basically Available(基本可用)
- Soft state(软状态)
- Eventually consistent(最终一致性)
BASE理论是对CAP中的一致性和可用性进行一个权衡的结果,理论的核心思想就是:我们无法做到强一致,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)。
有了以上理论之后,我们来看一下分布式事务的问题。
分布式事务
在分布式系统中,要实现分布式事务,无外乎那几种解决方案。
一、两阶段提交(2PC)
和上一节中提到的数据库XA事务一样,两阶段提交就是使用XA协议的原理,我们可以从下面这个图的流程来很容易的看出中间的一些比如commit和abort的细节。
两阶段提交这种解决方案属于牺牲了一部分可用性来换取的一致性。在实现方面,在 .NET 中,可以借助 TransactionScop 提供的 API 来编程实现分布式系统中的两阶段提交,比如WCF中就有实现这部分功能。不过在多服务器之间,需要依赖于DTC来完成事务一致性,Windows下微软搞的有MSDTC服务,Linux下就比较悲剧了。
另外说一句,TransactionScop 默认不能用于异步方法之间事务一致,因为事务上下文是存储于当前线程中的,所以如果是在异步方法,需要显式的传递事务上下文。
优点: 尽量保证了数据的强一致,适合对数据强一致要求很高的关键领域。(其实也不能100%保证强一致)
缺点: 实现复杂,牺牲了可用性,对性能影响较大,不适合高并发高性能场景,如果分布式系统跨接口调用,目前 .NET 界还没有实现方案。
二、补偿事务(TCC)
TCC 其实就是采用的补偿机制,其核心思想是:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作。它分为三个阶段:
-
Try 阶段主要是对业务系统做检测及资源预留
-
Confirm 阶段主要是对业务系统做确认提交,Try阶段执行成功并开始执行 Confirm阶段时,默认 Confirm阶段是不会出错的。即:只要Try成功,Confirm一定成功。
-
Cancel 阶段主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放。
举个例子,假入 Bob 要向 Smith 转账,思路大概是:
我们有一个本地方法,里面依次调用
1、首先在 Try 阶段,要先调用远程接口把 Smith 和 Bob 的钱给冻结起来。
2、在 Confirm 阶段,执行远程调用的转账的操作,转账成功进行解冻。
3、如果第2步执行成功,那么转账成功,如果第二步执行失败,则调用远程冻结接口对应的解冻方法 (Cancel)。
优点: 跟2PC比起来,实现以及流程相对简单了一些,但数据的一致性比2PC也要差一些
缺点: 缺点还是比较明显的,在2,3步中都有可能失败。TCC属于应用层的一种补偿方式,所以需要程序员在实现的时候多写很多补偿的代码,在一些场景中,一些业务流程可能用TCC不太好定义及处理。
三、本地消息表(异步确保)
本地消息表这种实现方式应该是业界使用最多的,其核心思想是将分布式事务拆分成本地事务进行处理,这种思路是来源于ebay。我们可以从下面的流程图中看出其中的一些细节:
基本思路就是:
消息生产方,需要额外建一个消息表,并记录消息发送状态。消息表和业务数据要在一个事务里提交,也就是说他们要在一个数据库里面。然后消息会经过MQ发送到消息的消费方。如果消息发送失败,会进行重试发送。
消息消费方,需要处理这个消息,并完成自己的业务逻辑。此时如果本地事务处理成功,表明已经处理成功了,如果处理失败,那么就会重试执行。如果是业务上面的失败,可以给生产方发送一个业务补偿消息,通知生产方进行回滚等操作。
生产方和消费方定时扫描本地消息表,把还没处理完成的消息或者失败的消息再发送一遍。如果有靠谱的自动对账补账逻辑,这种方案还是非常实用的。
这种方案遵循BASE理论,采用的是最终一致性,笔者认为是这几种方案里面比较适合实际业务场景的,即不会出现像2PC那样复杂的实现(当调用链很长的时候,2PC的可用性是非常低的),也不会像TCC那样可能出现确认或者回滚不了的情况。
优点: 一种非常经典的实现,避免了分布式事务,实现了最终一致性。在 .NET中 有现成的解决方案。
缺点: 消息表会耦合到业务系统中,如果没有封装好的解决方案,会有很多杂活需要处理。
四、MQ 事务消息
有一些第三方的MQ是支持事务消息的,比如RocketMQ,他们支持事务消息的方式也是类似于采用的二阶段提交,但是市面上一些主流的MQ都是不支持事务消息的,比如 RabbitMQ 和 Kafka 都不支持。
以阿里的 RocketMQ 中间件为例,其思路大致为:
第一阶段Prepared消息,会拿到消息的地址。
第二阶段执行本地事务,第三阶段通过第一阶段拿到的地址去访问消息,并修改状态。
也就是说在业务方法内要想消息队列提交两次请求,一次发送消息和一次确认消息。如果确认消息发送失败了RocketMQ会定期扫描消息集群中的事务消息,这时候发现了Prepared消息,它会向消息发送者确认,所以生产方需要实现一个check接口,RocketMQ会根据发送端设置的策略来决定是回滚还是继续发送确认消息。这样就保证了消息发送与本地事务同时成功或同时失败。
遗憾的是,RocketMQ并没有 .NET 客户端。有关 RocketMQ的更多消息,大家可以查看这篇博客
优点: 实现了最终一致性,不需要依赖本地数据库事务。
缺点: 实现难度大,主流MQ不支持,没有.NET客户端,RocketMQ事务消息部分代码也未开源。
五、Sagas 事务模型
Saga事务模型又叫做长时间运行的事务(Long-running-transaction), 它是由普林斯顿大学的H.Garcia-Molina等人提出,它描述的是另外一种在没有两阶段提交的的情况下解决分布式系统中复杂的业务事务问题。你可以在这里看到 Sagas 相关论文。
我们这里说的是一种基于 Sagas 机制的工作流事务模型,这个模型的相关理论目前来说还是比较新的,以至于百度上几乎没有什么相关资料。
该模型其核心思想就是拆分分布式系统中的长事务为多个短事务,或者叫多个本地事务,然后由 Sagas 工作流引擎负责协调,如果整个流程正常结束,那么就算是业务成功完成,如果在这过程中实现失败,那么Sagas工作流引擎就会以相反的顺序调用补偿操作,重新进行业务回滚。
比如我们一次关于购买旅游套餐业务操作涉及到三个操作,他们分别是预定车辆,预定宾馆,预定机票,他们分别属于三个不同的远程接口。可能从我们程序的角度来说他们不属于一个事务,但是从业务角度来说是属于同一个事务的。
他们的执行顺序如上图所示,所以当发生失败时,会依次进行取消的补偿操作。
因为长事务被拆分了很多个业务流,所以 Sagas 事务模型最重要的一个部件就是工作流或者你也可以叫流程管理器(Process Manager),工作流引擎和Process Manager虽然不是同一个东西,但是在这里,他们的职责是相同的。在选择工作流引擎之后,最终的代码也许看起来是这样的
SagaBuilder saga = SagaBuilder.newSaga("trip")
.activity("Reserve car", ReserveCarAdapter.class)
.compensationActivity("Cancel car", CancelCarAdapter.class)
.activity("Book hotel", BookHotelAdapter.class)
.compensationActivity("Cancel hotel", CancelHotelAdapter.class) .activity("Book flight", BookFlightAdapter.class) .compensationActivity("Cancel flight", CancelFlightAdapter.class) .end() .triggerCompensationOnAnyError(); camunda.getRepositoryService().createDeployment() .addModelInstance(saga.getModel()) .deploy();
这里有一个 C# 相关示例,有兴趣的同学可以看一下。
优缺点这里我们就不说了,因为这个理论比较新,目前市面上还没有什么解决方案,即使是 Java 领域,我也没有搜索的太多有用的信息。
分布式事务解决方案:CAP
上面介绍的那些分布式事务的处理方案你在其他地方或许也可以看到,但是并没有相关的实际代码或者是开源代码,所以算不上什么干货,下面就放干货了。
在 .NET 领域,似乎没有什么现成的关于分布式事务的解决方案,或者说是有但未开源。具笔者了解,有一些公司内部其实是有这种解决方案的,但是也是作为公司的一个核心产品之一,并未开源...
鉴于以上原因,所以博主就打算自己写一个并且开源出来,所以从17年初就开始做这个事情,然后花了大半年的时间在一直不断完善,就是下面这个 CAP。
Github CAP:这里的 CAP 就不是 CAP 理论了,而是一个 .NET 分布式事务解决方案的名字。
详细介绍:
http://www.cnblogs.com/savorboard/p/cap.html
相关文档:
http://www.cnblogs.com/savorboard/p/cap-document.html
夸张的是,这个解决方案是具有可视化界面(Dashboard)的,你可以很方面的看到哪些消息执行成功,哪些消息执行失败,到底是发送失败还是处理失败,一眼便知。
最夸张的是,这个解决方案的可视化界面还提供了实时动态图表,这样不但可以看到实时的消息发送及处理情况,连当前的系统处理消息的速度都可以看到,还可以看到过去24小时内的历史消息吞吐量。
最最夸张的是,这个解决方案的还帮你集成了 Consul 做分布式节点发现和注册还有心跳检查,你随时可以看到其他的节点的状况。
最最最夸张的是,你以为你看其他节点的数据要登录到其他节点的Dashboard控制台看?错了,你随便打开其中任意一个节点的Dashboard,点一下就可以切换到你想看的节点的控制台界面了,就像你看本地的数据一样,他们是完全去中心化的。
你以为这些就够了?不,远远不止:
- CAP 同时支持 RabbitMQ,Kafka 等消息队列
- CAP 同时支持 SQL Server, MySql, PostgreSql 等数据库
- CAP Dashboard 同时支持中文和英文界面双语言,妈妈再也不用担心我看不懂了
- CAP 提供了丰富的接口可以供扩展,什么序列化了,自定义处理了,自定义发送了统统不在话下
- CAP 基于MIT开源,你可以尽管拿去做二次开发。(记得保留MIT的License)
这下你以为我说完了? 不!
你完全可以把 CAP 当做一个 EventBus 来使用,CAP具有优秀的消息处理能力,不要担心瓶颈会在CAP,那是永远不可能, 因为你随时可以在配置中指定CAP处理的消息使用的进程数, 只要你的数据库配置足够高...