前言
博主最近在做一个购物商城,正好设计到购物车模块,于是乎全面的来聊一聊购物车模块实现的一些核心要点吧,很值得反复品味的设计,当需要实现购物车的时候,本文应该拿来就能用。
目录
1.需要解决的核心问题清单
2.购物车的存储
3.如何进行实时更新与促销
4.商品的失效和排序
5.加车前的校验
6.结算时的数据一致性与吞吐量的考量
7.redis中的存储数据结构
8.大key、热key问题
1.需要解决的核心问题清单
-
购物车的存储
-
购物车中的商品如何进行实时更新
-
购物车中的商品如何参加促销
-
购物车的商品如何进行失效
-
加入购物车前的校验
-
结算时的数据一致性与吞吐量的考量
-
数据的存储结构
-
大key、热key问题
2.购物车的存储
购物车里存什么数据?存在哪里?
-
首先明确购物车中存商品的副本数据,还是存ID?应该是存ID,因为存副本不好进行状态的关联与更新,而且数据的大小也大
-
需要的可靠性高,就存在磁盘中,需要的可靠性没那么高,就可以考虑存在内存中。很明显购物车的数据可靠性并不需要很高,放在内存中即可。
-
购物车的数据变化比较频繁,也就是读写操作比较频繁,如果进行落库的话,很明显代价太大了。基于这一点也应该将数据放在内存中
-
内存开起持久化,这样即使内存崩了,也不至于导致购物车全崩,用户大不了重新添加一下购物车,代价也是可以接受的。
3.如何进行实时更新与促销
购物车中存的是商品的ID,所以商品属性是能进行实时更新的,但是还有一些添加到购物车中的商品开起促销该怎么处理?
假如把商品A放入到购物车中一直没下单,不久后商品A开启了促销:10个库存中拿出5个来进行打折售卖。
很明显提前就加入购物车的用户对于才参加活动需要手动去抢,去选择商品+下单的用户来说是不公平的,所以对于在促销开始之前就加入了购物车的商品,不能参与促销活动。
如何实现?
实现方式:
-
所有购物车中的该商品均失效,很明显这是一个代价很高的方案,操作成本太大。
-
商品属性中追加一个销售类型用来区分销售策略,是正常销售还是促销
4.商品的失效和排序
商品失效是指因为商家下架了商品,导致购物车中的商品失效,失效的商品无法进行结算。由于购物车中存储的是商品ID,所以商品的状态是实时同步的,这一点在打开购物车的时候进行联合查询即可。
排序的话根据时间维度进行降序排序即可。
5.加车前的校验
在添加商品到购物车的时候肯定要进行校验能否添加到购物车,比如这个账户被拉黑了或者其它原因,原因是多种多样的,校验规则也是多变的,所以在进行校验时要注意考虑不到可扩展性,一般用:
-
门面模式+工厂模式
-
责任链模式
来实现可扩展的校验代码。
6.结算时的数据一致性与吞吐量的考量
购物车的结算流程如下:
结算时肯定是要求数据一致性的,避免产生超卖等异常库存扣减的情况出现。要保证一致性肯定是要锁数据的,至于怎么锁就要分情况了。
-
系统的吞吐量要求不高时,锁全流程,到生成订单完后再释放锁。
-
系统的吞吐量要求高时,就要细化锁的粒度了,细化到需要强一致性的最小单元。
结算时需要强一致性的最小单元是:付款+扣库存,生成订单都可以稍后来做。然后再衡量一下是这个吞吐量有多高,一般高的话,不增加系统的复杂性,考虑用异步任务即可(诸如JDK自带的任务编排类Completablefuture),要是很高的话,就考虑引入一个MQ中间层来做流量削峰了:
7.redis中的存储数据结构
用hash去存购物车
//key值
uid
//value值
//商品列表
items:[{product_id一些基础属性(sku)
},
......
]
8.大key、热key问题
购物车中是不太会存在热key问题的,毕竟是每个用户看自己购物车,有权限隔离的。但是大key问题是很容易遇见的,只要购物车里的商品够多,就会变成一个大key。
大key会影响两个方面:
-
读value值,速度会慢,这个可以通过分页来解决
-
浪费内存空间,这个没办法,数据就是要存,也没办法
所以综上所述购物车场景中会遇见大key问题,但是也无需去解决。