分布式事务原理(高并发系统下的数据一致性保障)
事务:对数据库的一批操作增删改(写操作) @Transactional
为什么需要分布式事务?
分布式事务的核心目的是在跨服务、跨数据库、跨网络的分布式系统中,确保多个独立操作要么全部成功,要么全部失败,从而维护数据一致性。
一、分布式事务的根源:架构拆分带来的挑战
1. 微服务架构
- 业务被拆分为多个独立服务(如订单、库存、支付),每个服务有私有数据库。
- 本地事务失效:单个服务的数据库事务无法跨服务边界(例如:订单库的事务不能控制支付库的回滚)。
2. 数据库分片/分库分表
- 单表数据拆分到不同数据库节点(如用户表按ID分库),一次操作可能涉及多个节点。
- 示例:转账时,付款方和收款方账户位于不同分片,需保证两边余额同步更新。
3. 跨资源类型操作
- 同时操作数据库、消息队列、缓存等异构系统(如:扣款(DB)后发消息(Kafka)需保证原子性。
二、、分布式事务的核心需求
1. 原子性(Atomicity)
- 所有参与方必须达成一致提交或回滚,避免部分成功。
2. 最终一致性(Eventual Consistency)
- 允许短暂不一致,但需通过补偿(如重试、冲正)最终达成一致。
3. 隔离性(Isolation)
- 防止并发操作导致中间状态被误读(如:读到未提交的脏数据)。
三、何时需要分布式事务?
强一致性场景:金融转账、库存扣减、机票订座。
弱一致性可替代场景:可考虑异步通知+对账(如物流状态更新)。
分布式事务是分布式系统在拆分架构和异构资源下维持数据一致性的必要手段,但需根据业务场景权衡一致性、性能和复杂度。
CAP理论(分布式事务中不可能的三角)
- 一致性C(要么全部成功,要么全部失败)强一致性、最终一致性
- 可用性A(出错系统是否还可用)
- 分区容错性P(在不同的服务上数据是否一样)
分布式事务原则
- 尽量避免分布式事务
- 服务要高内聚、低耦合
- 服务粒度不宜太小
- 服务边界即业务边界(康威定律)
- 最终一致性vs强一致性
分布式锁
并发操作(竞争问题 Race Condtition)
方案一:“库存”字段 加锁
方案二:SKU->记录->购买记录(Unique key = SKU记录)数据库的唯一索引保证不会超卖
乐观锁
假定大部分情况下都不会有锁冲突
实现方式:update 带版本号/时间戳,WATCH命令、synchronized
悲观锁
假定大部分情况下都会有冲突
实现方式:SELECT ... FOR UPDATE,SETNX + EXPIRE
Redisson悲观锁
- Redis锁操作实现比较复杂
- 减少重复代码
- Redis库
RLock lock = redisson.getLock("goods" + goodsId);
try {// 加锁,默认30秒超时,支持自动续期lock.lock();// 或者指定超时时间// lock.lock(10, TimesUnit.SECONDS);
} finally {//释放锁lock.unlock();
}
Redisson公平锁 (先来的先得到)
RLock fairLock = redisson.getFairLock("myFairLock");
try {fairLock.lock();// 执行业务逻辑// ...
} finally {fairLock.unlock();
}
Redisson乐观锁
RBUcket<MyObject> bucket = redisson.get("myObject");
RAtomicLong version = redisson.getAtomicLong("myObject:version");// 读取数据
MyObject obj = bucket.get();
long currentVersion = version.get();// 修改数据
obj.setSomeField("new value");// 尝试更新
boolean updated = bucket.compareAndSet(obj, newObj);
if(!updated) {//版本冲突,处理冲突逻辑//...
}
注意事项
1.锁超时:总是设置合理的锁超时时间,避免死锁
2.异常处理:确保在finally块中释放锁
3.锁续期: Redisson默认支持锁续期(看门狗机制)
4.网络问题:考虑Redis 集群故障时的处理策略
5.性能影响:分布式锁会影响性能,只在必要时使用
最佳实践
- 读多写少:乐观锁
- 写多读少:悲观锁
- 尽量减少锁的持有时间
消息队列
Tell Don't Ask 天然事务边界
高内聚/低耦合
高内聚
- 同一个业务放在一个服务里
- 使用数据库控制业务内事务
低耦合
- 不同业务放到不同的服务
- 使用消息队列传递事件
- 通过同步接口查询数据
分布式框架SEATA