【高并发项目实战】千万级并发的购物车系统设计与代码详解

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 Tair(兼容Redis),内存型 2GB
简介: 本文主要介绍redis在千万级系统中设计架构方案,如何在设计上抛弃mysql,主架构设计、缓存一致性方案、大value处理方案和redis限流和故障恢复降级方案设计与代码详解

前言

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

🏆CSDN专家博主/Java领域优质创作者、阿里云专家博主、华为云享专家、51CTO专家博主🏆

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

本文导读

购物车写多读多,大量高并发的写大量高并发的读,可以把主数据存储转为 redis 缓存,基于 redis 做主存储,高性能读写异步的把数据同步到 mysql 持久化。本文主要介绍redis在千万级系统中设计架构方案,包括主架构设计、缓存一致性方案、大value处理方案和redis限流和故障恢复降级方案

一、购物车数据模型分析

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

购物车数据量存储在上亿甚至更高,但是由于写入多读取多更新多删除多,日常流量高的时候偶发慢sql,当前购物车的实现:同步写mysql库+异步写Redis缓存,对于购物车来说,如果是在搞一些活动(拼单,满减等非单个商品必须进入购物车下单)的时候——购物车写多读多。购物车可能会出现高并发的写,购物车的写操作同步落库,可能会导致数据库的压力很大。

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

二、缓存架构主架构图

主架构中包括缓存集群、缓存限流、缓存大value处理、HotKey探测系统、redis故障恢复降级和redis-mysql数据同步

网络异常,图片无法展示
|

1、缓存一致性方案设计

一、对于所有的DB操作都不去添加具体的删除缓存的操作,而是通过canal监听binlog的方式

二、待数据确认已提交到数据库后,通过监听的变化,解析出对应的数据后,过滤掉非增删改的binlog

三、然后通过常量类配置的需要处理数据一致性的相关表以及关键字段和缓存前缀key,进行组装出需要进行删除的缓存key。并且通过mq的ack机制来保证缓存一定会被删除掉。

网络异常,图片无法展示
|

2、缓存限流设计

防止redis崩溃之后,系统无法正常运转,所以我们需要做一个降级处理。系统操作redis的所有方法一般都是通过RedisCache和Redislock两个类来处理的,所以我们通过AOP切面的方式,对这两个类中的所有方法做一个切面,如果在这里执行redis操作的时候,redis挂掉了,连接失败了,那么方法会抛异常,我们在切面处理方法上,捕捉异常,然后记录一下是redis挂掉还是网络暂时的波动

网络异常,图片无法展示
|

3、大Value处理设计方案

一、搭建的redis集群

二、在canal中已经创建好了监听,存储到mysql中redis_large_key_log表的canal instance

三、在每天凌晨3点,会将Redis中的大key(value)数据,通过canal instance发送到 MQ四、消费redis_large_key_log表的binlog数据,该数据包含Redis的大key(value)信息五、将大value切分成多个key,返回时拼接组装

网络异常,图片无法展示
|

三、复杂缓存与异步落库代码设计

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

同时购物车里是需要包含很多数据的,分成多个缓存 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预热的加载,把数据库的数据加载到缓存。

1、mq系统或mysql崩溃

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

2、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

总结

购物车系统写多读多,大量高并发的写大量高并发的读,可以把主数据存储转为 redis 缓存,基于 redis 做主存储,高性能读写异步的把数据同步到 mysql 持久化。本文主要在redis在千万级系统中设计架构方案,包括主架构设计、缓存一致性方案、大value处理方案和redis限流和故障恢复降级提供方案设计和代码设计

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
数据采集 并行计算 Java
【文末送书】Python高并发编程:探索异步IO和多线程并发
【文末送书】Python高并发编程:探索异步IO和多线程并发
235 0
|
4月前
|
监控 应用服务中间件 nginx
高并发架构设计三大利器:缓存、限流和降级问题之Nginx的并发连接数计数的问题如何解决
高并发架构设计三大利器:缓存、限流和降级问题之Nginx的并发连接数计数的问题如何解决
|
4月前
|
设计模式 安全 NoSQL
Java面试题:设计一个线程安全的单例模式,并解释其内存占用和垃圾回收机制;使用生产者消费者模式实现一个并发安全的队列;设计一个支持高并发的分布式锁
Java面试题:设计一个线程安全的单例模式,并解释其内存占用和垃圾回收机制;使用生产者消费者模式实现一个并发安全的队列;设计一个支持高并发的分布式锁
63 0
|
4月前
|
设计模式 存储 缓存
Java面试题:结合建造者模式与内存优化,设计一个可扩展的高性能对象创建框架?利用多线程工具类与并发框架,实现一个高并发的分布式任务调度系统?设计一个高性能的实时事件通知系统
Java面试题:结合建造者模式与内存优化,设计一个可扩展的高性能对象创建框架?利用多线程工具类与并发框架,实现一个高并发的分布式任务调度系统?设计一个高性能的实时事件通知系统
50 0
|
6月前
|
缓存 架构师 算法
高并发系统简单玩!Alibaba全新出品亿级并发设计速成笔记真香
如何提升系统性能,设计出一个靠谱的系统是每一个架构师或者正在往架构师方向进阶的同僚们都需要考虑的问题。公司所处的行业,业务场景决定了你设计的系统演进过程,不过万变不离其宗,系统设计和优化的思想都是相通的(当然如果你刚入行没多久,目前肯定还不需要苦恼这种问题,但是工作用不到,不代表面试不问)。
|
6月前
|
消息中间件 缓存 监控
直呼内行!阿里大佬离职带出内网专属“高并发系统设计”学习笔记
我们知道,高并发代表着大流量,高并发系统设计的魅力就在于我们能够凭借自己的聪明才智设计巧妙的方案,从而抵抗巨大流量的冲击,带给用户更好的使用体验。这些方案好似能操纵流量,让流量更加平稳得被系统中的服务和组件处理。
|
6月前
|
消息中间件 Java 程序员
阿里巴巴高并发架构到底多牛逼?是如何抗住淘宝双11亿级并发量?
众所周知,在Java的知识体系中,并发编程是非常重要的一环,也是面试的必问题,一个好的Java程序员是必须对并发编程这块有所了解的。
|
12月前
|
缓存 监控 Linux
C++项目实战-高并发服务器详析(二)
C++项目实战-高并发服务器详析(二)
71 0
|
12月前
|
Linux 调度 C++
C++项目实战-高并发服务器详析(一)
C++项目实战-高并发服务器详析(一)
204 0
|
存储 缓存 应用服务中间件