分布式事务思想实现-最终一致性(实际产品实现的分享)

本文阐述了通过消息服务实现分布式事务的思想,确保消息从生产到消费的可靠性与数据一致性。利用预发送机制与消息状态控制,结合消息生产确认与消费确认服务,保障消息的可靠发送与消费。

 

分布式事务思想:利用前置的消息服务保证消息生产到消息发送至消息中间件时可靠的,即必须发。
通过“消息服务”中消息的状态来保证可靠性。

描述个简单单一版本的分布式事务,用的就是最终一致性的概念(比较简陋,但是就是这种思想,条条大路通罗马)

1.预发送要执行的消息到消息服务,并持久化存储该消息(如一条要操作的信息的id及相关信息)。
2.返回存储结果(如成功时返回“预发送”,失败时返回“预发送失败”)
3.生产方得到返回结果时,如为成功就可以开始执行业务操作,如写到数据库一条记录,并把执行结果发送给消息服务。
4.消息服务得到该消息后,如是成功的,则可以“预发送”消息状态改为"发送中"状态,并发送改消息到kafka.
如是失败,即不执行后续操作即可。

以上操作执行时可能在任意过程出现问题,如网络断,服务异常等等等。
这时可以用“消息生产确认”这样的一个服务来根据消息生产方和消息服务的消息的状态来做一个确认和重发。
如,在2步骤时返回失败了,但是消息服务确实持久化了一条预发送的信息,我们就可以通过“消息生产确认”服务来通知消息生产方
进行业务上的重新执行,也就是从3步骤继续执行。
再比如,在3步骤又失败了,也就意味着生产者那方已经执行了3步骤的“写到数据库一条记录”,但是正向消息服务发送执行结果时
因为某些原因发送失败了。这时正常情况下,生产者业务正常处理并存到mysql,同时消息服务应该已经把该条消息的状态
改为"发送中"并发送的kafka。出现异常肯定是不对,就可以通过“消息生产确认”服务来对该数据轮询查看,如每五秒查看一下消息服务里面
的状态为"预发送"状态的数据,再到消息生产方根据消息id进行一个可查询的操作,如发现该消息在生产方已入库,即为成功执行了
一个用户操作,继续执行3步骤,保证了消息的完整。
再这种会出现异常的情况下,可以看到如果同一条消息异常出现的很频繁,如五秒内重发了很多次还是没有发送成功,当有很多类似的消息时
肯定对系统产生不良影响,我们可以为消息服务里面的消息执行重发多少次就不发了的这种终止操作,由“消息生产确认”服务,定时删除
生产方和消息服务两个里面的数据。保证一致性。

经过上面步骤,已经保证了消费发送到kafka的可靠性。后面说消费方如何消费,并如何保证和生产方的数据一致。

5.消费方消费kafka里面的消息,进行业务处理,持久化。
6.返回结果(带着消息的唯一标识等),如成功了,则调用消息服务将里面对应记录由"发送中"状态改为“已消费”状态。这样就完成了一整条
完整链路,从生产到消费数据强一致性。

但是问题是步骤5/6都可能异常。即意味着消费不到kafka的数据,这时可以参考上面说的那个“消息生产确认”服务。用一个
类似的服务“消息消费确认”来保证消费者一定能消费到生产过来的数据。
如,当该“消息消费确认”服务看到消息服务中一条状态为“发送中”的数据存在了五秒,就认为该消息没被消费方消费,接下来把这条消息重发即可,
这样又形成了闭环,由于消费方的业务是幂等的,所以不会对业务有影响。

 

<think>我们正在讨论分布式事务最终一致性实现方法。根据引用[1]和[2],实现最终一致性的常用方法是基于消息队列,核心包括事务消息、幂等消费和对账机制。此外,引用[3]提到有6种方案,但未具体列出,因此我们主要依据引用[1][2]展开。 最终一致性的核心思想:允许系统在某个时间段内存在不一致状态,但通过异步补偿机制最终达到一致。适用于对强一致性要求不高,但需要高可用性和分区容错性的场景。 实现步骤: 1. 事务消息:确保消息的可靠发送和事务的原子性。 2. 幂等消费:确保消息被多次消费不会导致数据不一致。 3. 对账兜底:通过定时核对数据,修复不一致。 下面详细说明: 一、事务消息(以RocketMQ为例,RabbitMQ可通过插件或本地事务表实现类似机制) - 事务消息流程: 1. 生产者发送半消息(预提交消息)到MQ。 2. MQ返回半消息发送成功。 3. 生产者执行本地事务。 4. 生产者根据本地事务结果提交或回滚消息(提交则消息可见,回滚则删除)。 5. 若生产者未确认,MQ会回查事务状态。 二、幂等消费 - 原因:网络重传、生产者重试、消费者重试可能导致消息重复。 - 实现方式: 1. 唯一键:在业务表中增加唯一键(如业务ID+类型),插入重复时忽略。 2. 状态机:业务状态机设计,确保状态转换幂等(如订单状态从“未支付”到“已支付”只能发生一次)。 3. 去重表:记录已处理消息的唯一标识(如消息ID),处理前先查表。 三、对账机制 - 定时任务:扫描业务数据,与关联系统核对(如支付系统与订单系统对账)。 - 修复:发现不一致时,触发补偿操作(如补单、退款等)。 四、数学模型(最终一致性的理论基础) - 最终一致性可用概率模型描述。设系统在时间t达到一致性的概率为P(t),则: $$ \lim_{t \to \infty} P(t) = 1 $$ - 消息队列的可靠性:设消息发送成功率为p,则n次重试后失败的概率为$(1-p)^n$,当n足够大时,失败概率趋近于0。 五、代码示例(使用RabbitMQ和Python) 步骤1:发送事务消息(使用本地事务表确保消息发送与业务操作原子性) 步骤2:消费端实现幂等 示例代码框架: 1. 生产者(使用本地事务表): ```python # 伪代码,实际需结合具体框架 def execute_transaction(): # 1. 开启本地事务 try: # 2. 执行本地业务操作(如更新数据库) db.update("UPDATE account SET balance = balance - 100 WHERE user_id = 1") # 3. 记录消息到本地事务表(与业务操作在同一个事务中) db.insert("INSERT INTO transaction_log (msg_id, content, status) VALUES ('123', 'transfer', 'pending')") # 提交事务 db.commit() except: db.rollback() # 异步任务:扫描事务表,发送消息 def send_message_from_log(): messages = db.query("SELECT * FROM transaction_log WHERE status='pending'") for msg in messages: # 发送消息到RabbitMQ channel.basic_publish(exchange='', routing_key='transfer_queue', body=msg.content) # 更新状态为已发送 db.update("UPDATE transaction_log SET status='sent' WHERE msg_id=?", (msg.msg_id,)) ``` 2. 消费者(幂等消费): ```python # 使用唯一消息ID确保幂等 processed_messages = set() # 实际应使用持久化存储(如Redis或数据库) def callback(ch, method, properties, body): msg_id = properties.message_id if msg_id in processed_messages: ch.basic_ack(delivery_tag=method.delivery_tag) # 已处理,直接确认 return # 处理业务(如更新账户) try: # 业务操作(注意:这里也要保证业务操作的幂等性,例如通过唯一键) db.update("UPDATE account SET balance = balance + 100 WHERE user_id = 2") # 记录已处理消息 processed_messages.add(msg_id) # 实际应持久化到数据库或Redis ch.basic_ack(delivery_tag=method.delivery_tag) # 确认消息 except: ch.basic_nack(delivery_tag=method.delivery_tag) # 处理失败,重试 ``` 六、实际应用场景 1. 电商支付:支付成功后,异步更新订单状态。 2. 跨行转账:银行A扣款,银行B加款,通过消息队列异步通知。 七、学习资源推荐(根据引用[1][2][3]) - 引用[1]:详细介绍了RabbitMQ实现最终一致性- 引用[2]:基于消息队列的设计与实现,包含事务消息、幂等消费和对账。 - 引用[3]:提到有6种分布式事务方案,可进一步学习(如TCC、Saga等)。 注意:最终一致性方案不适用于所有场景,强一致性场景需考虑2PC等方案。 相关问题: 1. 如何设计消息队列的事务消息机制? 2. 幂等消费的具体实现有哪些技术细节? 3. 对账机制如何保证分布式系统的数据一致性?</think>## 分布式事务最终一致性实现方法 最终一致性分布式系统中保证数据可靠性的核心方案,其核心思想是**允许系统在某个时间段内存在不一致状态,但通过异步补偿机制最终达到一致**。以下是主流实现方法: --- ### 一、基于消息队列的实现(最常用) #### 核心流程 ```mermaid graph LR A[服务A] -->|1. 本地事务+发消息| B[MQ] B -->|2. 投递消息| C[服务B] C -->|3. 消费消息+本地事务| D[达成一致] ``` #### 关键组件 1. **事务消息**(以RocketMQ为例): - 半消息机制:先发送预备消息,本地事务提交后再确认 - 事务回查:MQ主动查询未确认消息的状态 ```java // RocketMQ事务消息示例 TransactionSendResult result = producer.sendMessageInTransaction(msg, (Message msg, Object arg) -> { // 执行本地事务 if (doLocalTransaction(arg)) return LocalTransactionState.COMMIT_MESSAGE; else return LocalTransactionState.ROLLBACK_MESSAGE; }, null); ``` 2. **幂等消费**: - 唯一ID:每条消息携带全局唯一ID(如UUID) - 消费记录表: ```sql CREATE TABLE consumed_messages ( msg_id VARCHAR(64) PRIMARY KEY, status TINYINT, created_at TIMESTAMP ); ``` 3. **对账兜底**: - 定时扫描业务数据 - 修复策略(补单/退款/日志告警) #### 数学保证 最终一致性可用概率模型描述: $$ P(\text{一致}) = 1 - (1-p)^n $$ 其中 $p$=单次消息投递成功率,$n$=重试次数。当 $n \to \infty$ 时 $P(\text{一致}) \to 1$[^1][^2] --- ### 二、TCC模式(Try-Confirm-Cancel) #### 三阶段操作: 1. **Try**:资源预留(如冻结余额) ```sql UPDATE account SET frozen = 100 WHERE id = 1; ``` 2. **Confirm**:确认执行(实际扣款) 3. **Cancel**:回滚预留(释放冻结) #### 适用场景: - 资金交易等高敏感业务 - 需要强一致性保证的场景 --- ### 三、Saga事务模式 #### 执行方式: ```mermaid sequenceDiagram participant A as 服务A participant B as 服务B A->>B: 执行操作1 B-->>A: 成功 A->>B: 执行操作2 B->>A: 失败 A->>B: 触发操作1补偿 ``` #### 特点: - 每个操作需提供补偿接口 - 适合长事务(分钟级) - 实现复杂度低于TCC --- ### 四、本地消息表(经典方案) #### 实现步骤: 1. 业务与消息在同一个本地事务中提交 ```sql BEGIN; UPDATE orders SET status='paid'; INSERT INTO msg_table(msg_id,content); COMMIT; ``` 2. 独立进程轮询消息表并投递MQ 3. 下游服务消费并确认 --- ### 五、方案选型对比 | 方案 | 一致性强度 | 复杂度 | 适用场景 | |--------------|------------|--------|-------------------------| | 消息队列 | ★★☆ | ★★☆ | 通用场景(80%案例)[^2] | | TCC | ★★★ | ★★★ | 金融/资金交易 | | Saga | ★★☆ | ★★☆ | 跨系统长事务 | | 本地消息表 | ★★☆ | ★★☆ | 数据库无事务消息支持时 | > 实际应用中常组合使用,如:消息队列 + TCC 关键步骤 --- ### 六、最佳实践建议 1. **消息可靠性**: - 开启MQ持久化 - 设置合理重试次数(建议3-5次) 2. **监控关键指标**: ```math \text{不一致率} = \frac{\text{对账异常数}}{\text{总事务数}} \times 100\% ``` 3. **补偿策略**: - 自动补偿:程序自动修复 - 人工介入:超过阈值告警 > 据阿里云MVP玄剑的实践,电商系统通过消息队列+日对账可将不一致率控制在0.001%以下[^3]。 --- **相关问题**: 1. 如何设计高可靠的幂等消费机制? 2. TCC模式中资源预留的常见实现方式有哪些? 3. 分布式事务中对账系统的具体实现方案? 4. 如何评估不同最终一致性方案的性能瓶颈?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值