我们的限购是耦合在商品,当作商品和sku属性,而不是单独的限购模块。
限购需要在结算页及提交订单时进行校验。购物车和商品详情不校验历史,只校验当前加购数。
所以重点及难点在于下单,就以下单为例说明限购设计。
下单时,在校验完参数及优惠券、活动有效性等校验之后,增改业务之前,调用限购校验服务。
限购校验服务的流程是,当前此次订单用户购买的sku,加上查询出的用户历史购买这些sku的购买数,在商品级别及sku级别有没有超过限购数。剔除已退款及已取消订单。
难点在于,怎样防并发超限,以及怎样在此基础上尽可能做到高性能。
方案一:分布式锁
朴素的情况看来,是可以通过分布式锁来保证超限的,因为我们的业务只针对用户ID限购,所以锁的粒度设置为用户ID即可。锁开始于限购服务查询历史订单前,终止于订单保存db后。
但是,我们的下单方法上加了事务,异常处理也依靠事务回滚。由此引发,事务未提交时的数据隔离性。如果用户以某些方式飞快下了多单,前一个请求还没有提交事务,后一个请求来捞历史订单,就产生了超限的情况。
所以如果只使用分布式锁,若事务等级是可重复读或以下,则必须在限购服务查询历史订单前加锁,在事务提交后释放锁。在我们项目里则是在下单的方法外部解锁。
我们需要考虑到下单的异常情况,则需要在下单方法外部进行try catch,在finally中解锁。
但我们不确定的是,在哪一步抛出的异常或返回,如果是在限购加锁之前,则会出现释放了别的线程的锁或不存在的锁的情况。
且这种方式入侵原有业务较多,不能当成独立的限购服务。