目录
幂等
幂等是指执行1次和执行N次,对资源状态的改变效果是等价的。
select 幂等
insert 非幂等
update 更新增减类字段时非幂等,更新非增减类字段时幂等。
delete 幂等
防止表单重复提交其实是针对非幂等的数据处理请求。即
- 新增数据。
- 涉及更新到增减类字段。
若除了这两种情况外,还有别的情况,那么可能是程序设计不符合RESTful规范。
重复提交原因
导致出现数据重复提交的原因
粗分可分为两类:重复点击提交、网络重发。
细分可分为七类:
- 点击提交按钮两次或多次。
- 点击了刷新按钮。
- 点击了浏览器里的后退按钮。
- 使用浏览器历史记录重复提交表单。
- 浏览器重复发送数据处理请求。
- 网站服务器重复发送数据处理请求。
- 分布式系统重复发送数据处理请求。
预防措施
1. 表单提交时通过js将表单提交按钮修改为禁用状态,防止用户重复点击按钮。
2. 使用PRG【Post/Redirect/Get】模式,即用post方式提交表单后执行页面重定向,跳转到提交结果页面。
3. 在后台针对每次进入表单填写页面的请求,用带上时间类标识的方式生成随机数,将这个随机数存在session里,然后将这个随机数存入表单填写页面的一个隐藏表单域里。当用户点击提交表单后,后台接收到数据处理请求后就判断提交数据里的那个随机数跟session里存放的随机数是否相同,如果相同就表示是首次提交,那么就先删除存放在session里的这个随机数再进行其它操作,如果不相同则表明是在重复提交表单,不继续执行此次数据处理请求,并给出错误提示。
如果用户禁用了cookie,那么可以把sessionid保存在URL的查询字符串里,以避免session失效。
4. 数据表里设置唯一索引和version字段【乐观锁】。唯一索引可以保障不重复新增数据,version字段用于更新数据前先读取该条记录的版本号,然后更新内容里对version字段进行加一操作,在更新条件【where子句】里加上version条件。
5. 在数据更新前先对要更新的数据设置悲观锁【select ... for update】,但要注意避免死锁问题,适合请求并发量不大的情况。
6. 对于单机部署的应用可使用本地锁,对于分布式部署的应用可使用分布式锁。
理解误区
1. 认为只要在后台程序里新增数据前查询一下数据库里有没有重复数据【没有就新增,有就不新增】就能解决表单重复提交问题。
纠正:
后台程序进行数据库查询,再对返回结果进行处理这个过程是需要耗费一小段时间的,如果用户提交表单数据时双击提交按钮,又遇到网络延迟,那么就容易出现重复数据同步提交导致数据表里出现重复数据的情况。
参考链接
一.《8种方案解决重复提交问题》