暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

整理常见的分布式环境下ABA问题解决方案

龙虾编程 2024-12-23
42

    ABA问题通常出现在多线程环境下,假设业务数据表中有个状态字段是status,初始值是0。请求A预期是将status修改为1,请求B预期是将status设置为2,但是请求A请求失败了,请求B请求成功了,此时status更改为了2,由于代码中做了失败重试机制,请求A失败重试后成功的将status的值修改成1,如下所示的流转图:

    上述场景中由于没有关注数据变更程导致最终结果不符合预期,业界称这样的问题为"ABA问题"。下面我们来介绍几种常见的处理分布式系统下的ABA问题的解决方案。

1、Redis加锁的方案

    在并发场景下,我们直接就认为一定存在数据资源的竞争,当请求到达服务器的时候,先去获取Redis锁,如下所示:

(1)请求1和请求2过来的时候,先去Redis上申请锁,假设请求1先申请到了锁资源后,那么请求2申请锁就是失败的。

(2)申请到锁资源的请求1开始处理业务逻辑,如果在处理业务逻辑中出现了异常,那么也不释放锁资源,继续重试请求1的业务。

(3)请求1完成业务之后,释放锁资源。

(4)请求2申请锁资源,申请成功之后执行自身的业务逻辑。

    通过加Redis锁的方式可以从源头阻断了ABA问题的发生,我们强行让对同一个字段的修改行为变成互斥的操作。

2、乐观锁实现方案

    乐观锁机制其实就是在数据表中新增加一个version字段,每次更新数据的时候带着版本号去比较,如果版本号一致则更新成功,反之则更新失败,如下图所示的流程图:

(1)请求1和请求2到达服务器的时候,先查询当前数据的status和版本号version的值,假设查询的结果是status=0,version=0。

(2)请求1先与请求2执行业务逻辑,然后请求1更新的状态为1,并且设置version = 1,更新的语句如下所示:

    update order_table set status = 1, version = version + 1 where id = 1 and version = 0;
    复制

        请求1执行成功后,请求2业务也执行业务逻辑结束了,然后请求2根据之前的查询的version=0去更新status=2的时候就会失败(mysql返回result=0,因为version已经被请求1变更为1

        乐观锁的方式是假设请求之间没有资源冲突为前提,先处理请求的业务逻辑,至于能不能提交结果成功还是要到最后的阶段是否存在资源冲突。

        如果业务中并发较少的情况下,乐观锁机制的性能很高,但是业务并发较高并且业务中有许多重操作(如许多的rpc的调用),那么Redis锁的方案更加合适,因为这样更加的节约资源。

    3、顺序消息的实现方案

        市面上常见的消息中间件RocketMQ、kafka都可以支持顺序消息,那么我们可以利用顺序消息的特性来解决ABA的问题,如下的所示的流程图:

    (1)请求1和请求2请求服务器要处理业务逻辑,服务器接收到请求后将两个请求封装成有序的MQ消息发送给MQ。

    (2)服务消费者从MQ中拉取消息开始消费消息,处理业务逻辑。

        通过顺序消息的方式,可以将请求顺序执行,从而实现数据符合预期的效果。但是设置MQ消息的顺序性、避免由于消息阻塞带来的延迟问题都是需要考虑的。

    总结:

        分布式环境下的ABA问题实质是对同一个数据做更改中的并发问题,可以使用Redis锁、乐观锁、顺序消息的方案来解决。这些方案的共性是保证并发请求是顺序的执行,即就是前一个请求没有执行成功,后一个请求不可以执行。

    文章转载自龙虾编程,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

    评论