幂等解疑

在编程中,幂等(idempotent)特点就是其任意多次执行所产生的影响均与一次执行的影响相同。幂等的场景有很多,这里不再列举。然而很多人对幂等有一些误解:提到幂等就侃侃而谈悲观锁、乐观锁和分布式锁。这是不对的,其实幂等并不是并发场景下的才有问题,幂等的核心是确保唯一性,重点是防止数据重复

关于并发

可以用幂等的手段来解决部分并发场景,也可以使用并发的手段来解决部分幂等,也可参阅之前文章。

回到主题,如何确保唯一性呢?

唯一索引

大概80%的人都会想到:在数据库中建立唯一索引,用作幂等记录,可以防止插入重复的数据。一般采用的手段都是:幂等函数中先执行一次查询操作,如存在幂等记录则返回第一次执行的结果,如不存在幂等记录则继续执行。

好处:很简单就能满足分布式场景,并发场景下也可以保证只有一个线程插入成功。

缺点:如果所以场景都先落数据到数据库并结合事务使用,成功后再向下执行,一方面存储了很多一次性的数据浪费空间,另一方面数据库性能可能会成为瓶颈。

状态机约束

在单据相关的业务,基本会涉及到状态机。通过约束状态机的跳转,就可以做到有限状态机的幂等。比如:订单状态的流转最好满足正向更新,即后一次更新一定大于等于前一次。

CAS之version 字段

该方案应用非常广泛,version 字段每次更新自动增长,作为乐观锁(compare and sweep)依据,理论上没有性能瓶颈,但局限性很大:仅适用于update。

SETNX

在分布式场景中,新增除了上面利用唯一索引外,还可以使用redis、memcache和zookeeper都可以实现唯一数据。以redis的SETNX:『SET if Not eXists』(如果不存在,则 SET) 。它的使用很特别巧妙,利用一个较短的时间窗口(key的有效期)便可保障一段时间内唯一(并发)。

消息有序

对于有些业务如果上面的手段不再适合时,消息端的有序变得尤为重要,应从集群消费端上加以控制(必定单机控制手段很多,比如:一致性 hash 负载均衡策略),可以修改负载均衡策略或mq拉取的规则(如订单号取模),保证同一笔订单的变更始终会被推送到集群中某一台机器上处理,再分配到其中一个内存队列中强制排队来确保他们的顺序性,这样就保证一个订单始终在一个内存队列中,并由一个线程去消费,是不是有点RocketMQ局部有序性的味道。优势很明显,放弃唯一索引和SETNX,高性能。但该方案并发完美,如果集群中某一台宕机会需考虑单机控制手段是否满足。

很多时候我们要做到真正幂等,主要考虑分布式集群的环境、去重、并发等。

你可能感兴趣的:(#,规约)