竞态条件引发的Bug:一次惊心动魄的后端历险!

        Hey小伙伴们!👋 最近在咱们的后端世界里,发生了一件让人心跳加速的Bug事件。事情是这样的,一个看似普通的发货操作,竟然没有更新数据!😱 是的,你没听错,就是那个每个电商系统里都有的发货操作。

🧐 问题背景

在咱们的系统中,有两个服务小能手:回收服务和发货服务。它们通过两个接口:发货接口和上报电量接口,来协同工作。但是,这两个接口都在争先恐后地更新 fy_boxes 表,结果就出现了时间上的纳秒级差异,导致更新操作出现了竞态条件。

🐞 Bug现形记

发货接口刚准备把 fy_boxes 的某个字段更新为2,但是电量上报接口就抢先一步读取了旧值1,然后也去更新了这个字段。结果,发货接口的更新被覆盖了,字段值又变回了1。😤

🔍 解决方案大公开

为了解决这个问题,咱们可以采用以下几种策略:

1.乐观锁:给 fy_boxes 表添加一个版本号字段,每次更新都检查版本号是否有变化。

// 假设咱们的实体类是这样的
public class FyBox {
    private Long id;
    private int sort;
    private int version; // 版本号字段
    // 省略其他字段和getter/setter方法
}

// 更新操作时,先检查版本号
public boolean updateBoxSort(Long boxId, int newSort, int currentVersion) {
    return fhAudioTemplateMapper.updateSort(boxId, newSort, currentVersion) == 1;
}

2. 悲观锁:在读取数据时,直接锁定相关记录。

SELECT * FROM fy_boxes WHERE id = ? FOR UPDATE; // SQL中的悲观锁语法

3.数据库序列号:使用数据库的序列号或时间戳功能来控制更新顺序。

// 假设 `fy_boxes` 表有一个时间戳字段 `updated_at`
UPDATE fy_boxes SET sort = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?;

4.分布式锁:使用ZooKeeper或Redis等工具实现分布式锁,确保同一时间只有一个操作可以更新数据。

// 使用Redis分布式锁的伪代码
boolean lockAcquired = redisClient.acquireLock("lock_key", "unique_value");
if (lockAcquired) {
    try {
        // 安全地执行更新操作
    } finally {
        redisClient.releaseLock("lock_key", "unique_value");
    }
}

5.还有一种最简单 的方法,单独new对象更新(争对于对应业务要 更新的字段去set值)

// 因解决并发问题,在另一服务不同纳秒可能会存在重复更新,故此处不要重复更新其他字段
FyBoxes fyBoxes = new FyBoxes();
fyBoxes.setId(box.getId());
fyBoxes.setPower(dto.getPower());
fyBoxes.setPowerTime(RboxCommonUtils.nowTimestampS());
fyBoxes.updateById();

🎉 总结

小伙伴们,通过这次Bug的排查和解决,咱们不仅提升了自己的技术实力,还加深了对并发控制的理解。在多线程和分布式系统中,正确地处理共享资源的更新是非常重要的。希望这次的分享能够帮助到大家,让我们一起成长,一起进步!💪

  • 15
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值