第九章 数据一致性与分布式事务

第九章 数据一致性与分布式事务

在微服务架构中,数据一致性是最具挑战性的问题之一。当你把一个单体应用拆分成多个服务时,原本简单的数据库事务变成了复杂的分布式事务。这一章,我想分享一些实战经验,帮助你在这个"没有银弹"的领域做出明智的决策。

9.1 分布式事务的现实挑战

9.1.1 为什么传统ACID事务不再适用

还记得我第一次做微服务系统时,天真地以为可以用分布式事务协调器来解决所有问题。结果证明,这是一个巨大的错误。

传统2PC(两阶段提交)的问题

// ❌ 这就是问题所在 - 阻塞和资源锁定
public class DistributedOrderService
{
    public async Task CreateOrder(CreateOrderRequest request)
    {
        using (var transaction = new TransactionScope(
            TransactionScopeOption.Required,
            new TransactionOptions 
            { 
                IsolationLevel = IsolationLevel.Serializable 
            }))
        {
            // 1. 扣减库存 - 锁定库存表
            await _inventoryService.DeductStock(request.Items);
            
            // 2. 创建订单 - 锁定订单表
            var order = await _orderRepository.CreateAsync(request);
            
            // 3. 处理支付 - 锁定账户表
            await _paymentService.ProcessPayment(order.TotalAmount);
            
            // 在所有操作完成之前,所有相关的表都被锁定
            // 其他请求必须等待,这就是性能问题的根源
            transaction.Complete();
        }
    }
}

2PC的问题不在于它不能工作,而在于它违背了微服务的核心原则:

  • 可用性牺牲:协调器故障会导致整个系统阻塞
  • 性能问题:资源锁定时间过长
  • 扩展性差:无法支持大规模分布式系统

9.1.2 接受最终一致性

在分布式系统中,我们需要接受一个现实:强一致性是有代价的。大多数情况下,最终一致性已经足够。

电商系统的最终一致性示例

// 订单创建流程 - 最终一致性版本
public class OrderApplicationService
{
    public async Task<CreateOrderResult> CreateOrder(CreateOrderCommand command)
    {
        // 1. 本地事务:创建订单
        using var transaction = await _dbContext.Database.BeginTransactionAsync();
        
        try
        {
            var order = Order.Create(command.CustomerId, command.Items);
            await _orderRepository.AddAsync(order);
            await _dbContext.SaveChangesAsync();
            
            // 2. 发布领域事件(本地事务的一部分)
            await _eventPublisher.PublishAsync(new OrderCreatedEvent(
                order.Id,
                order.CustomerId,
                order.Items.Select(i => new OrderItemDto(i.ProductId, i.Quantity, i.Price)).ToList(),
                order.TotalPrice,
                DateTime.UtcNow
            ));
            
            await transaction.CommitAsync();
            
            // 3. 返回成功,但此时库存可能还没扣减
            return CreateOrderResult.Success(order.Id);
        }
        catch (Exception ex)
        {
            await transaction.RollbackAsync();
            return CreateOrderResult.Failure(ex.Message);
        }
    }
}

// 库存服务监听订单创建事件
public class OrderCreatedEventHandler : IEventHandler<OrderCreatedEvent>
{
    public async Task HandleAsync(OrderCreatedEvent @event)
    {
        try
        {
            // 1. 检查库存
            foreach (var item in @event.Items)
            {
                var stockAvailable = await _inventoryRepository.CheckStockAsync(item.ProductId);
                if (stockAvailable < item.Quantity)
                {
                    // 库存不足,发布补偿事件
                    await _eventBus.PublishAsync(new StockInsufficientEvent(
                        @event.OrderId,
                        item.ProductId,
                        item.Quantity,
                        stockAvailable
                    ));
                    return;
                }
            }
            
            // 2. 预留库存
            var reservedItems = new Dictionary<Guid, int>();
            foreach (var item in @event.Items)
            {
                await _inventoryService.ReserveStockAsync(item.ProductId, item.Quantity);
                reservedItems[item.ProductId] = item.Quantity;
            }
            
            // 3. 发布库存预留成功事件
            await _eventBus.PublishAsync(new StockReservedEvent(
                @event.OrderId,
                reservedItems,
                DateTime.UtcNow
            ));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error handling OrderCreatedEvent for order {OrderId}", @event.OrderId);
            throw; // 重新抛出,让消息队列重试
        }
    }
}

9.2 Saga模式:分布式事务的实用方案

Saga模式是目前最实用的分布式事务解决方案。它将长事务分解为一系列本地事务,通过事件串联起来。

9.2.1 编排式Saga vs 协同式Saga

编排式Saga(Orchestration)

// 编排器 - 中央协调者
public class OrderProcessingSagaOrchestrator
{
    private readonly IOrderRepository _orderRepository;
    private readonly IInventoryServiceClient _inventoryService;
    private readonly IPaymentServiceClient _paymentService;
    private readonly IEventBus _eventBus;
    private readonly ILogger<OrderProcessingSagaOrchestrator> _logger;
    
    public async Task ProcessOrder(Guid orderId)
    {
        var sagaState = await _sagaStateRepository.GetOrCreateAsync(orderId);
        
        try
        {
            switch (sagaState.CurrentStep)
            {
                case OrderProcessingStep.OrderCreated:
                    // 步骤1:预留库存
                    var order = await _orderRepository.GetByIdAsync(orderId);
                    var stockReserved = await _inventoryService.ReserveStockAsync(
                        order.Items.Select(i => new StockReservationRequest(i.ProductId, i.Quantity)).ToList());
                    
                    if (!stockReserved)
                    {
                        await CancelOrder(order, "Insufficient stock");
                        return;
                    }
                    
                    sagaState.MoveToStep(OrderProcessingStep.StockReserved);
                    await _sagaStateRepository.SaveAsync(sagaState);
                    
                    // 继续下一步
                    goto case OrderProcessingStep.StockReserved;
                    
                case OrderProcessingStep.StockReserved:
                    // 步骤2:处理支付
                    var paymentResult = await _paymentService.ProcessPaymentAsync(orderId);
                    
                    if (!paymentResult.Success)
                    {
                        await CompensateStockReservation(orderId);
                        await CancelOrder(order, "Payment failed");
                        return;
                    }
                    
                    sagaState.MoveToStep(OrderProcessingStep.PaymentCompleted);
                    await _sagaStateRepository.SaveAsync(sagaState);
                    
                    // 继续下一步
                    goto case OrderProcessingStep.PaymentCompleted;
                    
                case OrderProcessingStep.PaymentCompleted:
                    // 步骤3:确认订单
                    var orderToConfirm = await _orderRepository.GetByIdAsync(orderId);
                    orderToConfirm.Confirm();
                    await _orderRepository.UpdateAsync(orderToConfirm);
                    
                    sagaState.MoveToStep(OrderProcessingStep.Completed);
                    await _sagaStateRepository.SaveAsync(sagaState);
                    
                    _logger.LogInformation("Order processing saga completed for order {OrderId}", orderId);
                    break;
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error in order processing saga for order {OrderId}", orderId);
            await HandleSagaFailure(sagaState);
        }
    }
    
    private async Task CompensateStockReservation(Guid orderId)
    {
        // 释放已预留的库存
        await _inventoryService.ReleaseStockAsync(orderId);
        _logger.LogInformation("Stock reservation compensated for order {OrderId}", orderId);
    }
}

协同式Saga(Choreography)

// 每个服务监听事件并决定下一步动作
public class PaymentService
{
    public async Task HandleStockReserved(StockReservedEvent @event)
    {
        try
        {
            // 处理支付
            var paymentResult = await ProcessPayment(@event.OrderId, @event.TotalAmount);
            
            if (paymentResult.Success)
            {
                // 支付成功,发布事件
                await _eventBus.PublishAsync(new PaymentCompletedEvent(
                    @event.OrderId,
                    paymentResult.TransactionId,
                    DateTime.UtcNow
                ));
            }
            else
            {
                // 支付失败,发布补偿事件
                await _eventBus.PublishAsync(new PaymentFailedEvent(
                    @event.OrderId,
                    paymentResult.ErrorMessage,
                    DateTime.UtcNow
                ));
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error processing payment for order {OrderId}", @event.OrderId);
            throw;
        }
    }
}

public class OrderService
{
    public async Task HandlePaymentCompleted(PaymentCompletedEvent @event)
    {
        try
        {
            // 确认订单
            var order = await _orderRepository.GetByIdAsync(@event.OrderId);
            order.Confirm();
            await _orderRepository.UpdateAsync(order);
            
            // 发布订单确认事件
            await _eventBus.PublishAsync(new OrderConfirmedEvent(
                @event.OrderId,
                @event.TransactionId,
                DateTime.UtcNow
            ));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error confirming order {OrderId}", @event.OrderId);
            throw;
        }
    }
    
    public async Task HandlePaymentFailed(PaymentFailedEvent @event)
    {
        try
        {
            // 取消订单
            var order = await _orderRepository.GetByIdAsync(@event.OrderId);
            order.Cancel(@event.ErrorMessage);
            await _orderRepository.UpdateAsync(order);
            
            _logger.LogWarning("Order {OrderId} cancelled due to payment failure: {Error}", 
                @event.OrderId, @event.ErrorMessage);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error cancelling order {OrderId}", @event.OrderId);
            throw;
        }
    }
}

9.2.2 补偿事务的设计

补偿事务是Saga模式的核心,它需要仔细设计。

// 补偿事务接口
public interface ICompensatableAction
{
    Task ExecuteAsync();
    Task CompensateAsync();
    bool CanCompensate { get; }
}

// 库存预留的补偿操作
public class ReserveStockAction : ICompensatableAction
{
    private readonly IInventoryService _inventoryService;
    private readonly Guid _orderId;
    private readonly Dictionary<Guid, int> _reservedItems;
    private bool _executed = false;
    
    public async Task ExecuteAsync()
    {
        foreach (var item in _reservedItems)
        {
            await _inventoryService.ReserveStockAsync(item.Key, item.Value);
        }
        _executed = true;
    }
    
    public async Task CompensateAsync()
    {
        if (!_executed) return;
        
        foreach (var item in _reservedItems)
        {
            await _inventoryService.ReleaseStockAsync(_orderId, item.Key, item.Value);
        }
    }
    
    public bool CanCompensate => _executed;
}

// Saga步骤管理
public class SagaStep
{
    public string StepName { get; }
    public ICompensatableAction Action { get; }
    public bool IsCompleted { get; private set; }
    public bool IsCompensated { get; private set; }
    
    public async Task ExecuteAsync()
    {
        await Action.ExecuteAsync();
        IsCompleted = true;
    }
    
    public async Task CompensateAsync()
    {
        if (Action.CanCompensate && IsCompleted && !IsCompensated)
        {
            await Action.CompensateAsync();
            IsCompensated = true;
        }
    }
}

// Saga管理器
public class SagaManager
{
    private readonly List<SagaStep> _steps = new();
    private readonly ILogger<SagaManager> _logger;
    
    public void AddStep(string stepName, ICompensatableAction action)
    {
        _steps.Add(new SagaStep(stepName, action));
    }
    
    public async Task<SagaExecutionResult> ExecuteAsync()
    {
        var completedSteps = new List<SagaStep>();
        
        try
        {
            foreach (var step in _steps)
            {
                await step.ExecuteAsync();
                completedSteps.Add(step);
                _logger.LogInformation("Saga step completed: {StepName}", step.StepName);
            }
            
            return SagaExecutionResult.Success();
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Saga execution failed at step: {StepName}", 
                completedSteps.LastOrDefault()?.StepName ?? "Unknown");
            
            // 补偿已完成的步骤(倒序)
            for (int i = completedSteps.Count - 1; i >= 0; i--)
            {
                try
                {
                    await completedSteps[i].CompensateAsync();
                    _logger.LogInformation("Saga step compensated: {StepName}", completedSteps[i].StepName);
                }
                catch (Exception compensationEx)
                {
                    _logger.LogError(compensationEx, "Failed to compensate step: {StepName}", 
                        completedSteps[i].StepName);
                }
            }
            
            return SagaExecutionResult.Failure(ex.Message);
        }
    }
}

9.3 幂等性:分布式系统的守护神

在分布式系统中,幂等性是保证数据一致性的关键。

9.3.1 幂等性设计模式

// 幂等操作接口
public interface IdempotentOperation
{
    Guid OperationId { get; }
    DateTime CreatedAt { get; }
}

// 幂等性处理器
public class IdempotencyHandler<T> where T : IdempotentOperation
{
    private readonly IDistributedCache _cache;
    private readonly ILogger<IdempotencyHandler<T>> _logger;
    
    public async Task<TResult> ExecuteAsync<TResult>(
        T operation, 
        Func<Task<TResult>> operationFunc) where TResult : class
    {
        var cacheKey = $"idempotency:{typeof(T).Name}:{operation.OperationId}";
        
        // 检查是否已经执行过
        var cachedResult = await _cache.GetStringAsync(cacheKey);
        if (cachedResult != null)
        {
            _logger.LogInformation("Operation {OperationId} already executed, returning cached result", 
                operation.OperationId);
            return JsonSerializer.Deserialize<TResult>(cachedResult);
        }
        
        // 执行操作
        var result = await operationFunc();
        
        // 缓存结果
        var serializedResult = JsonSerializer.Serialize(result);
        await _cache.SetStringAsync(cacheKey, serializedResult, TimeSpan.FromHours(24));
        
        return result;
    }
}

// 在API中使用幂等性
[ApiController]
public class OrdersController : ControllerBase
{
    private readonly IdempotencyHandler<CreateOrderOperation> _idempotencyHandler;
    
    [HttpPost]
    public async Task<ActionResult<OrderDto>> CreateOrder(
        [FromHeader(Name = "Idempotency-Key")] string idempotencyKey,
        [FromBody] CreateOrderRequest request)
    {
        if (string.IsNullOrEmpty(idempotencyKey))
        {
            return BadRequest("Idempotency-Key header is required");
        }
        
        var operation = new CreateOrderOperation
        {
            OperationId = Guid.Parse(idempotencyKey),
            Request = request,
            CreatedAt = DateTime.UtcNow
        };
        
        var result = await _idempotencyHandler.ExecuteAsync(
            operation,
            async () => await _orderService.CreateOrderAsync(request));
            
        return Ok(result);
    }
}

9.3.2 消息幂等性

// 消息去重处理器
public class MessageDeduplicationHandler
{
    private readonly IDistributedCache _cache;
    private readonly ILogger<MessageDeduplicationHandler> _logger;
    
    public async Task<bool> ProcessMessageAsync<T>(
        string messageId, 
        Func<Task> messageHandler) where T : class
    {
        var cacheKey = $"message:{typeof(T).Name}:{messageId}";
        
        // 使用分布式锁确保并发安全
        var lockKey = $"lock:{cacheKey}";
        var lockId = Guid.NewGuid().ToString();
        
        try
        {
            // 尝试获取锁
            var lockAcquired = await _cache.SetStringAsync(
                lockKey, 
                lockId, 
                TimeSpan.FromSeconds(30), 
                When.NotExists);
                
            if (!lockAcquired)
            {
                _logger.LogWarning("Message {MessageId} is being processed by another instance", messageId);
                return false;
            }
            
            // 检查是否已经处理过
            var processed = await _cache.GetStringAsync(cacheKey);
            if (processed != null)
            {
                _logger.LogInformation("Message {MessageId} already processed", messageId);
                return false;
            }
            
            // 处理消息
            await messageHandler();
            
            // 标记为已处理
            await _cache.SetStringAsync(cacheKey, "processed", TimeSpan.FromDays(7));
            
            return true;
        }
        finally
        {
            // 释放锁
            var currentLockId = await _cache.GetStringAsync(lockKey);
            if (currentLockId == lockId)
            {
                await _cache.RemoveAsync(lockKey);
            }
        }
    }
}

// 使用消息去重的事件处理器
public class IdempotentOrderCreatedHandler : IEventHandler<OrderCreatedEvent>
{
    private readonly MessageDeduplicationHandler _deduplicationHandler;
    private readonly IOrderService _orderService;
    
    public async Task HandleAsync(OrderCreatedEvent @event)
    {
        var processed = await _deduplicationHandler.ProcessMessageAsync<OrderCreatedEvent>(
            @event.Id.ToString(),
            async () =>
            {
                // 实际的消息处理逻辑
                await _orderService.ProcessNewOrderAsync(@event);
            });
            
        if (!processed)
        {
            _logger.LogInformation("OrderCreatedEvent {EventId} was not processed (duplicate)", @event.Id);
        }
    }
}

9.4 监控和追踪

在分布式系统中,监控和追踪是保证数据一致性的重要工具。

9.4.1 分布式追踪

// 使用OpenTelemetry进行分布式追踪
public class DistributedTracingConfiguration
{
    public static void ConfigureTracing(IServiceCollection services, IConfiguration configuration)
    {
        services.AddOpenTelemetry()
            .WithTracing(builder =>
            {
                builder
                    .AddAspNetCoreInstrumentation()
                    .AddHttpClientInstrumentation()
                    .AddEntityFrameworkCoreInstrumentation()
                    .AddSource("OrderService", "PaymentService", "InventoryService")
                    .SetResourceBuilder(ResourceBuilder.CreateDefault()
                        .AddService("ecommerce-system"))
                    .AddJaegerExporter(options =>
                    {
                        options.AgentHost = configuration["Jaeger:AgentHost"];
                        options.AgentPort = int.Parse(configuration["Jaeger:AgentPort"]);
                    });
            });
    }
}

// 在业务代码中添加追踪
public class OrderService
{
    private readonly ActivitySource _activitySource;
    
    public async Task<Order> CreateOrderAsync(CreateOrderRequest request)
    {
        using var activity = _activitySource.StartActivity("CreateOrder");
        
        activity?.SetTag("order.customer_id", request.CustomerId);
        activity?.SetTag("order.item_count", request.Items.Count);
        
        try
        {
            var order = Order.Create(request.CustomerId, request.Items);
            
            activity?.SetTag("order.id", order.Id);
            activity?.SetTag("order.total_amount", order.TotalPrice.Amount);
            
            await _orderRepository.AddAsync(order);
            
            return order;
        }
        catch (Exception ex)
        {
            activity?.SetStatus(ActivityStatusCode.Error, ex.Message);
            activity?.RecordException(ex);
            throw;
        }
    }
}

9.4.2 一致性监控

// 一致性检查服务
public class ConsistencyMonitor
{
    private readonly IServiceProvider _serviceProvider;
    private readonly ILogger<ConsistencyMonitor> _logger;
    
    public async Task CheckOrderConsistencyAsync()
    {
        // 检查订单状态与支付状态的一致性
        var inconsistentOrders = await _orderRepository.FindInconsistentOrdersAsync();
        
        foreach (var order in inconsistentOrders)
        {
            _logger.LogWarning("Inconsistent order found: {OrderId}, Status: {Status}, PaymentStatus: {PaymentStatus}",
                order.Id, order.Status, order.PaymentStatus);
                
            // 发送告警或自动修复
            await _alertingService.SendAlertAsync(new ConsistencyAlert
            {
                OrderId = order.Id,
                Issue = "Order status inconsistent with payment status",
                Severity = AlertSeverity.High
            });
        }
    }
    
    public async Task CheckInventoryConsistencyAsync()
    {
        // 检查库存数据的一致性
        var inconsistentProducts = await _inventoryRepository.FindInconsistentStockAsync();
        
        foreach (var product in inconsistentProducts)
        {
            _logger.LogWarning("Inconsistent inventory found: {ProductId}, Available: {Available}, Total: {Total}",
                product.Id, product.AvailableQuantity, product.TotalQuantity);
        }
    }
}

9.5 小结

数据一致性是微服务架构中的永恒话题。记住几个关键原则:

  1. 接受最终一致性:强一致性是有代价的,大多数情况下最终一致性已经足够
  2. 设计幂等操作:幂等性是分布式系统的守护神
  3. 使用Saga模式:将长事务分解为可管理的步骤
  4. 监控和追踪:没有监控的分布式系统是不可维护的
  5. 自动化补偿:设计自动化的补偿机制,减少人工干预

最重要的是,数据一致性的解决方案没有标准答案。你需要根据业务需求、系统复杂度和团队能力来选择最适合的方案。

在下一章中,我们将探讨可观测性与监控,这是维护分布式系统健康运行的关键。

posted @ 2026-01-22 21:42  高宏顺  阅读(2)  评论(0)    收藏  举报