幂等性的概念
对于同一操作发起的请求,执行多次对资源本身产生的影响与执行一次相同,不会因为多次相同操作而产生副作用。
eg. 用户注册,点击提交,由于某些原因(比如服务器负载大),长时间转圈圈,用户不耐烦疯狂点提交按钮,发起了多次相同的请求,最终应该只插入一条用户记录,而不是点几次提交就插入几条用户记录。
eg. 双11下单,点提交,卡了,什么破手机|网络,疯狂点提交,虽然发起了多次请求,但多次发起的请求都完全相同,最终应该只产生一个订单,而不是每次点提交都产生一个订单。
消息中间件与幂等性
消息消费者经常要实现幂等性,保证消费者不重复消费同一消息。
eg. rabbitmq为保证消息的可靠性投递,往往要延时再次投递消息进行确认,需要在消息消费者中实现幂等性,如果之前已消费了同样的消息,就不再消费该消息。
消息消费者需不需要实现幂等性,视业务需求而定,看对于同一消息是否要重复消费。
http请求方式与幂等性
请求方式 | 应用 | 是否幂等 |
---|---|---|
GET | 获取资源 | 幂等,查询一次或多次,结果相同 |
POST | 创建资源 | 非幂等,eg. 主键自增,多次插入相同的用户记录,是会增加多条记录的 |
PUT | 更新资源 | 幂等,eg. 把name字段修改为chy,不管执行1次或多次,这个资源结果都是name=chy |
DELETE | 删除资源 | 幂等,不管删除多少次,这个资源结果都是没了 |
实现幂等性的常见方式
核心思想
使用唯一的业务单号来标识一个业务,存储执行过的业务的业务单号,先根据业务单号查询这笔业务是否已经执行过了,如果没执行过,才执行。(去掉重复的业务)
业务单号可以用自增的流水号,也可以使用唯一id+指纹码,总之要能唯一标识一笔业务。
eg. 订单id可以唯一标识一个订单,但不能标识是这个订单的哪个业务,是增改删查中哪个操作?第几次操作?标识不了,需要再加一个指纹码,比如时间戳、业务规则。
1、数据库主键去重
单独用一张表存储处理过的业务的业务单号,把业务单号作为主键(unique)。
执行业务时先查数据表有没有该业务单号,有就说明该业务已处理过,不再处理,没有就说明该业务未处理过,开始处理。
缺点:增加了数据库IO次数,数据库操作是很耗时的,数据库性能容易成为系统性能的瓶颈。
2、分布式锁(推荐)
处理业务时先setnx把业务单号作为key存储在redis上,返回1说明没有这个key、设置成功,开始处理业务;返回0说明这个key已存在(已处理过该业务)、设置失败,不再处理。处理业务失败时应该删除对应的key。