专栏首页天马行空布鲁斯Restful API实现乐观锁,应返回409还是412?

Restful API实现乐观锁,应返回409还是412?

近些年Restful API变得很流行,一个重要的原因是其充分利用HTTP协议标准,这样API Consumer消费Restful API的成本就小很多,API开发人员也更加有据可循。

Restful API复用HTTP协议的方法和状态码来指代不同的行为,比如POST代表创建一条资源,创建成功用201表示,请求校验失败用400表示;GET代表获取一条或多条资源,获取成功用200表示;DELETE代表删除一条资源,删除成功用204表示,删除记录不存在用404表示。

个人觉得,Restful API相比传统RPC(比如:SOAP)的区别,就可类比于面向对象编程和面向过程编程。

这里讨论一个问题,对于Restful API的PUT操作,在并发环境下,两个Request更新同一条资源,可能会出现更新内容丢失的情况。针对这个问题,通常可以“加锁”来解决,那么加什么锁呢?一般来说就是:乐观锁或者悲观锁。

假设PUT请求的处理逻辑是先校验资源存不存在;然后存在的话更新资源到数据库。这个逻辑对应就是下面两条sql语句:

select * from product where productid = 110;
update product set stock = 49 where productid = 110;

如果是加悲观锁的话,就是在执行第一条select语句时加一个排他锁(select for update),在update语句执行完了才释放锁,这样两个PUT请求只能一个先执行一个后执行,就不会出现更新内容丢失的情况。

使用悲观锁,由于在相应记录上加了排他锁,并且锁的范围相对较大,会对读操作产生一定影响;其次,如果索引建得不合适,容易导致锁住整个表,进而影响系统吞吐量。

悲观锁有很多应用场景,之前我写过一篇文章(liquibase和flyway中分布式锁实现的区别?)介绍liquibase和flyway,其中flyway就是利用悲观锁实现了分布式锁。

和悲观锁不同,使用乐观锁,其实并不是在相应的记录或者表上加锁,而是在update的时候加一个version的比对。

回到上面例子,select语句查询到对应product的version,然后把version添加到update语句比对条件,如下:

update product set stock = 49 where productid = 110 and version = 5;

如果当前数据库中记录的version是5,则update语句执行成功,version增加;如果当前数据库中记录version不是5,则update语句执行失败,返回相应状态码提示用户请求执行失败。

可以看到,乐观锁并没有添加额外的锁,所以在某些情况下,性能会好过悲观锁;但是,在高并发频繁更新的情况下,可能会导致很多请求失败,对用户体验很不好,用户需要重试很多次。

上面提到update执行失败,返回相应状态码提示用户请求执行失败,那么对于Restful API,应该返回什么状态码呢?

根据HTTP规范,有两个状态码可以使用:409和412。

从409的规范可以看出,当某一个资源的state发生了变化,导致request不能完成,可以返回409,提示用户解决冲突,重新提交请求。

The request could not be completed due to a conflict with the current state of the resource. This code is only allowed in situations where it is expected that the user might be able to resolve the conflict and resubmit the request. https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.10

从412的规范可以看出,412是前置条件不符合,并且前置条件须是位于HTTP的header。

The precondition given in one or more of the request-header fields evaluated to false when it was tested on the server. https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.10

所以,409和412都可以作为乐观锁失败的返回状态码,412需要version信息作为header(ETag、If-Match)传入;409则不需要。

References

  • https://stackoverflow.com/questions/3620203/http-status-412-precondition-failed-and-database-versioning?rq=1

本文分享自微信公众号 - 天马行空布鲁斯(gh_2feda5c053bd),作者:huazailmh

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-03-23

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 关于编码的那些事

    之前做一个POC的时候,Vicky同学遇到一个关于编码的问题,问到我,我觉得当时没有解释得很清楚,于是决定查阅相关的资料文档,写一篇文章,记录这个问题及对背后的...

    Bruce Li
  • 浅析SAP Subscription Billing可扩展性

    SAP Subscription Billing是SAP推出的用于订阅关系(Subscription)管理的一款SaaS产品。通常来说,任意一个标准产品都不可能...

    Bruce Li
  • 关于分布式系统数据一致性的那些事

    近些年,随着SOA、微服务架构的流行,分布式系统数据一致性问题也随之而来成为大家热门关注的一个问题。其实,这个问题在很早之前就存在,因为在现实生活中,很多系统都...

    Bruce Li
  • 「R」使用 Cascadia Code 字体

    最近微软为 Windows Terminal 推出了具有连字特性的字体 Cascadia Code,在试用后发现确实很不错,下图是字体效果,这里结合官方的 RE...

    王诗翔呀
  • kafka线上滚动升级方案记录

    一、修改unclean.leader.election.enabled默认值 Kafka社区终于下定决心要把这个参数的默认值改成false,即不再允许出现unc...

    小勇DW3
  • Galera Cluster for MySQL 详解(四)——性能测试

    本篇使用tpcc-mysql压测工具对实验环境的三节点Galera集群进行一系列性能测试。

    用户1148526
  • 【已解决】对于 XCTest 测试中怎么让测试用例顺序执行?

    我想写一些常规的测试用例,比如注册 登录 查看商品 添加购物车 check out 下单 支付等是否正常。

    君赏
  • 存储性能 - 速度测试代码

    package qqq; import java.util.ArrayList; public class ArrayListTest {     ...

    opengps
  • C#函数重载

    C#允许这样定义函数,而不会报错,传的值的类型不一样,C#会根据值的类型自动选择用哪一个函数C#允许这样定义函数,而不会报错,传的值的类型不一样,C#会根据值的...

    zls365
  • 安利一个API文档查询工具(Windows)

    而我一直想找一个类似的工具,因为平时编程,太多API接口确实不可能一一记住。函数名称还好,函数参数确实不容易记住。

    无道

扫码关注云+社区

领取腾讯云代金券