【高并发实战】千万级购物车系统缓存优化及实践

举报
小明的混沌之路 发表于 2022/07/31 14:06:38 2022/07/31
【摘要】 购物车数据量存储最少在亿级左右,但是由于写入多读取多更新多删除多,日常流量高的时候偶发慢sql,当前购物车的实现:同步写库+异步写缓存

前言:📫 作者简介:小明java问道之路,专注于研究计算机底层,就职于金融公司后端高级工程师,擅长交易领域的高安全/可用/并发/性能的设计和架构📫 

🏆 Java领域优质创作者、阿里云专家博主、华为云专家🏆

🔥 如果此文还不错的话,还请👍关注点赞收藏三连支持👍一下博主哦

本文导读

购物车数据量存储最少在亿级左右,但是由于写入多读取多更新多删除多,日常流量高的时候偶发慢sql,当前购物车的实现:同步写库+异步写缓存

一、电商中购物车数据模型分析

购物车中实现的功能:加购物车、查看购物车、编辑购物车sku、编辑掉商品数据、选中购物车里的多个商品去结算、查看购物车商品总数、删除购物车,分享购物车等等

购物车数据量存储在亿级左右,但是由于写入多读取多更新多删除多,日常流量高的时候偶发慢sql,当前购物车的实现:同步写库+异步写缓存

购物车来说,如果是在搞一些活动(拼单,满减等非单个商品必须进入购物车下单)的时候——购物车写多读多。购物车可能会出现高并发的写,购物车的写操作同步落库,可能会导致库的压力很大。

解决方案:购物车写多读多,大量高并发的写大量高并发的读,可以把主数据存储转为 redis 缓存,基于 redis 做主存储,高性能读写异步的把数据同步到 mysql 持久化。

二、购物车的多种redis数据结构实战(购物车复杂缓存实战与异步落库)

购物车写多读多场景,缓存作为主数据存储,抗高并发写和读,落库异步,商品系统一般来说也会使用缓存架构来提供读的接口的实现。

同时购物车里是需要包含很多数据的,分成多个缓存 key,进行缓存操作。

// 选用redis中的hash结构,更新缓存中的商品扩展信息
cart_hash {
        {skuid1}:cartSkuInfo;
        {skuid2}:cartSkuInfo;
}

// 更新缓存中的数量
cart_hash { cartType_userId
        {skuid1}:2;
        {skuid2}:1;
}

// 更新排序 sorted set(可排序的set集合)
// 把每个skuid和加入购物车的时间,写入到sorted set
sorted set [{skuId -> score(时间)}]

// 购物车缓存模型:
hash:{skuId -> count} ,hash :{skuId -> cartSkuInfo}, zset : [{skuId -> timestamp}]

三、购物车异步落库的消息丢失与不一致

购物车的主数据存储是通过redis来实现的,写和读redis没有不一致的问题(不崩的情况下)

用redis数据存储,万一redis集群全盘都崩溃了以后,这个时候就会导致我们的购物车的主数据都没了,mysql数据库来进行降级, 降级提供购物车的写和读,等缓存恢复了以后,再进行缓存prewarn预热的加载,把数据库的数据加载到缓存。

case1:mq系统或mysql崩溃

如果要是我们刚刚写完缓存了之后,还没来得及发送消息到mq里去,mq系统崩了,导致缓存写成功了,但是异步消息没过去,mq出了一些故障,导致消息没发送成功,redis里有数据, mq和数据库里面没有消息和数据,导致redis和mysql数据不一致,只要redis中有数据就可以了,因为读写redis,mysql只做备份,mysql需要备份增量

case2:redis崩溃

redis崩溃,但是mysql没有同步到,结果->丢一条数据(临时性存储空间,极端情况,不影响主业务使用)业务上来说,购物车是临时性的数据,仅仅是把一些商品再购物车里进行暂存而已,迟早购买或忘记,要不然会直接去发起购买,这些数据就得删除,时间一长,用户也不清楚加了什么。极端情况下少了商品,需要用户重新添加购物车。

四、购物车加入商品多线程并发问题解决

使用redis提供的分布式锁:

String updateCartLockKey = RedisLockKeyConstants.UPDATE_CART_LOCK_KEY +userId+skuId;
boolean locked = redisLock.blockedlock(updateCartLockKey);
if(!locked) {
    throw new BaseBizException("商品加入购物车失败,购物车中已有此商品");|
}

try{

} catch(){
} finally {
      redisLock. unlock (RedisLockKeyConstants.UPDATE_CART_LOCK_ KEY);
}

五、应用新缓存数据结构,购物车的查询功能实践

查询缓存操作:

// 从缓存中获取有序的商品ID列表
// 把sorted set里所有的数据都查出来,写入数据的时候默认就已经排序过了
Set<String> orderSkuIds = redisCache.zrevrange(RedisKeyConstant.SHOPPING_CART_ZSET+userId,INDEX,END);

// 获取购物车信息
Map<String, String> cartInfo = redisCache.hGetAll(RedisKeyConstant.SHOPPING_CART_EXT+userId;

// sorted set + hash来实现的
// 先查询按时间排序的商品skuId集合,每个skuId对应商品信息
// 再序列化成对象
...

// 除了查询操作,都需要加redisLock

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。