多人同时买一件商品时(假设库存充足),每个人几乎同时下单成功,给人一种并行感觉。但真实情况, 库存只是一个数值,无论是存在mysql数据库还是redis缓存,减值时都要控制顺序,只能串行来扣减,当然为保证安全性,会设计一些锁控制。
主要依赖数据库特性,保证扣减的一致性,逻辑简单,开发部署成本低。
最上面会查询当前的剩余库存(可能不准确,但没关系,这里只是第一步粗略校验),前置校验,如果已经没有库存,前置拦截生效,减少数据库写。毕竟读操作不涉及加锁,并发性能高。
create table t_inventory
(
sku_id bigint null comment '商品规格 id',
leaved_amount int null comment '剩余可购买数量'
);
create table t_inventory_flow
(
id bigint auto_increment comment '主键 id'
primary key,
sku_id int null comment '商品规格 id',
order_detail_id mediumtext null comment '订单明细 id',
quantity_trade int null comment '本次购买扣减的数量'
);
update inventory
set leaved_amount = leaved_amount - #{count}
where sku_id='123' and leaved_amount >= #{count}
乐观锁实现原子性,在 where 条件里判断此次购买的数量≤剩余的数量。在扣减服务的代码,判断此 SQL 的返回值,若:
极端例子:秒杀库存只有5件,活动期间峰值QPS 10W,活动结束后,上面的流水表最终只会插入5条记录,但查询QPS 10W。
所以,数据库扣减方案第一次升级主要针对 库存前置校验 模块的优化,作为前置拦截器,承载流量很大,若将流量全部压到主库,很容易把数据压垮。
考虑数据库架构升级:
采用了 读写分离 方式,新增加了一套从库,借助mysql自带的数据同步能力。 库存校验时读取从数据库。