秋招Day19 - 分布式 - 分布式设计

发布于:2025-07-27 ⋅ 阅读:(13) ⋅ 点赞:(0)

什么是幂等性?

多次调用的效果和一次调用的效果一样,比如DELETE操作,执行多次的结果和执行一次的结果对数据库的影响是一样的。

有些操作不满足幂等性,比如INSERT操作,用户点击了两次表单,数据库就有两条重复的记录。MQ消费者在读取消息的时候,也有可能读取到重复消息。

在分布式系统里,只要下游服务有写操作(插入、更新),就有可能出现幂等性问题。

怎么保证接口幂等性?

  1. insert 前先 select

在保存数据的接口中,在insert前,先根据requestId等字段先select一下数据。如果该数据已存在,则直接返回,如果不存在,才执行  insert操作。

  1. 加唯一索引

加唯一索引是个非常简单但很有效的办法,如果重复插入数据的话,就会抛出异常,为了保证幂等性,一般需要捕获这个异常。

如果是java程序需要捕获:DuplicateKeyException异常,如果使用了spring框架还需要捕获:MySQLIntegrityConstraintViolationException异常。

  1. 加悲观锁

更新逻辑,比如更新用户账户余额,可以加悲观锁,把对应用户的哪一行数据锁住。同一时刻只允许一个请求获得锁,其他请求则等待。

select * from user id=123 for update;

这种方式有一个缺点,获取不到锁的请求一般只能报失败,比较难保证接口返回相同值。

  1. 加乐观锁

更新逻辑,也可以用乐观锁,性能更好。可以在表中增加一个timestamp或者version字段,例如version:

在更新前,先查询一下数据,将 version 也作为更新的条件,同时也更新 version:

update user set amount=amount+100,version=version+1 where id=123 and version=1;

更新成功后,version 增加,重复更新请求进来就无法更新了。

  1. 建防重表

有时候表中并非所有的场景都不允许产生重复的数据,只有某些特定场景才不允许。这时候,就可以使用防重表的方式。

例如消息消费中,创建防重表,存储消息的唯一 ID,消费时先去查询是否已经消费,已经消费直接返回成功。

  1. 状态机

有些业务表是有状态的,比如订单表中有:1-下单、2-已支付、3-完成、4-撤销等状态,可以通过限制状态的流动来完成幂等。

  1. 分布式锁

直接在数据库上加锁的做法性能不够友好,可以使用分布式锁的方式,目前最流行的分布式锁实现是通过 Redis,具体实现一般都是使用 Redission 框架。

  1. token 机制

请求接口之前,需要先获取一个唯一的 token,再带着这个 token 去完成业务操作,服务端根据这个 token 是否存在,来判断是否是重复的请求。


网站公告

今日签到

点亮在社区的每一天
去签到