幂等设计详解
The following article is from 京东技术 Author 京东物流-廖宗雄
Tech
导读
本文主要从研发人员的角度,结合研发人员日常常见的各类业务场景,从经典系统框架的每一层入手分析幂等处理的时机。希望通过这篇文章的分析,让开发者在日常开发中对幂等的处理不再陌生。抓住导致请求、接口不幂等的本质,在工作中避免再陷入这个陷阱中。
幂等、幂等性这词,作为一个研发人员是再熟悉不过的,那是否有深入思考过幂等产生的背景、为什么需要幂等,如何做才是幂等的?今天将结合业务场景及请求的过程来分析解决幂等(性)的方法。
01概念
02业务场景
03架构分析
从系统架构上进行分析,幂等该在哪一层去做,怎么做?
图1 经典系统框架图
上图为一个最常见的经典系统框架图,Web端发起一个请求到后端,幂等该在哪一层来处理呢?不妨一层一层的分析。
Nginx是否需要做幂等,Nginx的主要功能是做Web服务器、反向代理、负载均衡等,把请求转发到后端的服务器上,本身不参与具体的业务,所以Nginx是不需要做幂等处理的;Gateway是负责权限校验、安全防御、认证鉴权、流量控制、协议转换、日志审计、监控等,本身也不含对任何业务的处理,所以其也不需要做幂等处理;Service层通常是对业务逻辑进行处理、编排,可能会改变数据,但对于数据的改变结果,最终也还是需要通过数据访问层,写入到数据库,所以Service层也不需要做数据幂等;DAO层主要是和数据库交互,把Service层的结果写入数据库,对Service层提供读取、写入数据库的功能。
在写入数据库的时候,针对每一次的写入,可能返回不同的结果,此时就需要按场景进行具体的分析对待;DataBase层,主要提供数据的存储,并不参与具体的业务逻辑计算。所以,通过对该架构的每一层的功能分析,得出对于请求的幂等处理,需要在DAO层做处理,以便保证多次请求和一次请求的结果是一致的。
04数据库操作分析
通过上面的分析,得出幂等需要在DAO层来处理,再进一步分析,得出DAO层的操作主要就是CRUD。下面逐一对每一种操作分析是否需要做幂等,以及怎么做。
R(read):对应的操作SQL语句为select。只要查询条件不变,在一定的时间内,执行一次和执行多次返回的结果肯定是相同的,所以其本身是幂等的,不需要再做处理。
select * from user where id = 1;
C(create):对应的操作SQL语句为insert。此时,需要分情况,如果用到的数据库主键为数据库自增,不考虑业务主键防重的情况下,每一次写入数据库就不是幂等的,所以为了保证幂等,需要在数据insert前做业务防重或是在数据库表上对业务主键加唯一索引。如果数据库主键不是自增,是由业务系统写入的,需要在业务系统里把数据库主键和业务主键做一对一映射,或是由独立服务提供数据库主键和业务主键的映射关系,保证多次请求获取到的数据库主键和业务主键是一致的,确保写入数据库操作是幂等的。综合来说,就是相同的数据多次写入数据库后,能否保证只有一条数据。
insert into user (id,age,sex,ts) values(1,10,‘male’,2021-07-20 10:22:23);
U(update):对应的操作SQL语句为update。更新操作时,一定是要用绝对值进行更新操作,而不要用相对值进行更新,相对值更新可能导致更新操作不幂等。
幂等:
update user set age = 10 where id = 1;
非幂等:
update user set age++ where id = 1;
D(delete):对应的操作SQL语句为delete。删除操作时,如果删除的是一个范围,生产上最好是禁止该类操作;比较推荐的做法是把按范围操作删除转换为先按范围查询,再按查询的主键进行删除。而且按范围删除的操作不是幂等的。
幂等:
delete from user where id = 1;
非幂等:该类操作要禁止。
delete from user where id in (select id from user order by id desc limit 10);
05常见业务场景
保证幂等的实现方式有多种,此处例举几类常见的业务场景,在实际应用中,根据业务场景进行选用。
1. 前端页面提交时,页面token机制。进入页面时,从服务器获取token,在服务器端把token进行存储,提交时把token带到服务器端进行验证;常见的处理流程如下:
图2 页面token机制处理流程
乐观锁机制,使用数据库的版本号实现乐观锁,数据库更新时,判断版本号是否与查询时保持一致,一致更新成功,否则更新失败;
select+insert,数据写入前,先查询数据是否存在,存在直接返回,不存在则写入数据,保证写入数据库的数据正确性;常用于并发不高的一些后台系统或是防止任务的重复执行;
悲观锁机制,一般id为主键或唯一索引,仅锁定当前记录;
select * from table where id = '1234' for update;
去重表,每一次写入或更新业务表时,先查询去重表是否已经存在记录,再操作业务表。
数据库唯一索引,为业务表建立唯一索引,避免业务数据多次写入;
状态机,业务状态在变更之前是有条件的,必须按设定的状态条件进行更新;
在实际开发中,保证提供的接口或服务的幂等(性),是一个最基本的技术要求,希望通过该分析,能对还未理解幂等(性)的研发人员有所帮助。
参考阅读:
技术原创及架构实践文章,欢迎通过公众号菜单「联系我们」进行投稿。