notes notes
首页
读书笔记
系统设计
项目实战
学习笔记
源码
运维
其它
极客时间 (opens new window)
GitHub (opens new window)
首页
读书笔记
系统设计
项目实战
学习笔记
源码
运维
其它
极客时间 (opens new window)
GitHub (opens new window)
  • 电商

    • mall
      • ES 缓存商品信息
      • 规格参数
      • OAuth2
      • 单点登录 SSO
      • 购物车
        • 合并购物车
        • 查询购物项
      • 订单
        • 订单确认页(结算页)
        • 提交订单
        • 分布式事务
      • 秒杀
        • 商品上架
        • 优化
  • 短链
  • 项目实战
  • 电商
starry
2023-08-07
目录

mall

# ES 缓存商品信息

存入 spu 还是 sku?存入 sku

  • spu 没有具体的价格
  • spu 没有具体规格,不便于搜索和过滤
  • spu 嵌套 sku,多层嵌套,不便于 es 搜索

每个 sku 都拥有相同的规格属性,要不要存入 es?要存

  • 用空间换时间,如果不存就需要再查一次库,占用网络 IO
  • 直接每个 sku 都存规格属性,冗余存储,直接在 es 返回页面所需的所有数据

# 规格参数

商品详情页展示不同规格(销售属性),选中不同规格如何知道对应的 spu?

返回的每个属性携带拥有此属性的 sku 集合,选中不同种类的属性,其中共同拥有的 sku 就是此 sku。

返回给前端的数据

销售属性 具体属性 skuIds
颜色 白色 10,11
颜色 黑色 12,13
内存 4G 10,12
内存 8G 11,13

sku 对应属性

skuId 颜色 内存
10 白色 4G
11 白色 8G
12 黑色 4G
13 黑色 8G

界面展示

颜色 白色 黑色
内存 4G 8G

选中白色,对应 skuIds 为 10,11

  • 内存 4G 对应 skuIds 为 10,12,选中属性都有的 skuId 为 10,即:白色+4G 对应的 skuId 为 10
  • 内存 8G 对应 skuIds 为 11,13,选中属性都有的 skuId 为 11,即:白色+8G 对应的 skuId 为 11

# OAuth2

Web 网站的授权

  1. 网站跳转到三方授权界面,携带 client_id 和 redirect_url(回调地址)
  2. 用户授权成功,浏览器跳转到 redirect_url 并携带 code 参数
  3. redirect_url 收到 code 参数,通过认证服务器使用 code 来换取 access token
  4. 使用 access token 访问资源服务器来获取用户的开放信息 授权机制说明 - 微博API (opens new window)

# 单点登录 SSO

一个网站登录,其他网站(不用域名)也是登录状态。

网站A 和 网站B 使用同一个认证服务,认证成功将 token 存入认证服务 session(分布式 session)

  • 网站A 使用认证服务时跳转到认证界面
  • 认证成功在浏览器 session 中存入用户凭证(token)
  • 跳转到 网站A 的 redirect_url 并携带 token
  • 并将 token 存入网站A 的 session 中,网站A 通过 token 可以获取的身份信息
  • 网站B 使用认证服务时跳转到 认证界面
  • 由于 session 有当前用户的信息(token),直接跳转到网站B 的 redirect_url 并携带 token
  • 并将 token 存入网站B 的 session 中,网站B 通过 token 可以获取的身份信息
  • 网站A 和网站B 都有具体身份信息了。

# 购物车

需求:

  1. 未登录可选购商品加入(临时)购物车
  2. 登录成功将临时购物车中的商品合并到用户购物车 即: 未登录用户请求:
  • 第一次使用,无 cookie,创建一个临时 cookie(uuid,30day)
  • 有 cookie,购物项存入临时 cookie 登录用户:临时 cookie 中的购物项合并到用户购物车

购物车读多写多,使用 Redis 存储,类型为 Hash。

  • key 为 userId 或 临时购物车 id(uuid,过期时间 30day)
  • field 为 skuId
  • value 为 sku 详情,包含 skuInfo 和 销售属性(Json 存储)

mall:cart:1

{

    "check": true,

    "count": 2,

    "image": "https://mall-starry.oss-cn-shenzhen.aliyuncs.com/2023-08-05/50ff54ca-5d4a-4fb4-89d4-ff55b4731b8a_28f296629cca865e.jpg",

    "price": 4388,

    "skuAttrValues": [

        "颜色:黑色",

        "套装:8+128"

    ],

    "skuId": 35,

    "title": "华为/HUAWEI P60 超聚光XMAGE影像 双向北斗卫星消息 黑色 8+128",

    "totalPrice": 8776

}

# 合并购物车

合并购物车时,sku 相同直接修改 sku 数量。sku 不同直接在 Redis 添加一个属性。合并完删临时 key。

# 查询购物项

Redis 中根据 userId 查询到对应的 hash,value 中的价格可能发送变化了,如何确保购物车价格是最新的。

  • 修改价格时,同时更新缓存中的价格。
  • 返回给前端购物项时,再查询一次最新的价格。(浪费性能)

更新缓存时,key 是以用户为维度的,如何更新所有的用户的购物车。

  • 需要额外使用一个 key 来保存 sku 的价格。skuId: price

# 订单

# 订单确认页(结算页)

需要数据:

  • 收获地址列表
  • 购物车为选中状态的购物项,购物项是否有库存
  • 查询用户积分
  • 总价格计算
  • 额外携带一个 token 来防止重复提交

token 存入 redis,提交订单时删除 token,删除成功才创建订单;否则就是重复点击,不做处理。(token 需要设置一个有效期,防止一直占用内存)

# 提交订单

提交订单时把之前订单页的数据都提交到后台。

提交订单时需要再次计算对比价格,防止商家临时改变价格。 远程调用库存服务扣减库存,发送 mq 消息,防止后续步骤失败,通过 mq 消息进行回滚。 处理完成删除购物车中的数据。

# 分布式事务

失败数据回滚:

  • 库存
  • 积分
  • 优惠券 通过 mq 保证数据最终一致性。

创建订单,远程调用

  1. 库存延迟队列 delay 2min
  2. 订单延迟队列 delay 1min

库存解锁 handler,wms 发出的 message,delay 2min

  • 订单成功:
    • 远程查询,订单状态不是取消状态,不做处理。
  • 订单失败:
    • 订单事务回滚,远程查询不到数据,回滚库存。

回滚时需要进行幂等处理,库存的状态必须为锁定状态。

订单失败 handler,order 发出的 message,delay 1min

  • 订单失败:专门处理订单失败的 handler,不用远程查询,直接回滚库存。

# 秒杀

秒杀商品上架,库存预热,提交订单时直接从缓存中扣减库存,扣减成功发送商品信息到订单mq,扣减成功即秒杀成功,订单服务只用创建完订单即可。

库存扣减使用 redis 的 lua 脚本,或者 redisson 的 semaphore

local value = redis.call('get', KEYS[1])
if (value ~= false and tonumber(value) >= tonumber(ARGV[1])) then
	local val = redis.call('decrby', KEYS[1], ARGV[1])
	return 1
end
	return 0

# 商品上架

定时任务,上架后2天内商品。 如何存储? 根据可秒杀的时段进行分类存储,1 小时内秒杀 和 全天秒杀。使用 hash 进行存储。

方案一 1 小时内秒杀:

  • key:秒杀的整点开始时间(系统规定秒杀只能是整点开始)
  • field:skuId
  • value:skuInfo(Json) 全天秒杀:
  • key:当天日期
  • field:skuId
  • value:skuInfo(Json)

方案二 使用 zset 来存储 score:秒杀开始的时间戳 value:skuInfo(Json)

如何查询当前时间秒杀有哪些秒杀商品?

  • 查询当前时间的整点开始时间有哪些商品秒杀 + 查询全天秒杀的商品。
  • 使用 zrange 来查询小于当前时间戳的数据。开始值当前时间戳,结束值 为当天时间的开始时间戳。
ZRANGE key start stop [BYSCORE | BYLEX] [REV] [LIMIT offset count]
  [WITHSCORES]

未在秒杀推荐列表展示,但是参与秒杀,需要根据 skuId 查询是否参与秒杀。

  • 使用方案一时,直接从 hash 中判断是否存在 skuId。HEXISTS key field
  • 使用方案二时需要,上架秒杀商品时,设置一个缓存,缓存参与秒杀的 skuId。

# 优化

  • 服务单一职责,可独立部署。扛不住,直接加机器;挂了不影响其他业务。
  • 秒杀链接加密。防止恶意脚本,防止链接提前暴露,模拟请求。
  • 库存预热,快速扣减。秒杀读多写少。无需每次实时校验库存。库存预热,放到 redis 中。信号量控制进来秒杀的请求。
  • 动静分离。nginx 做好动静分离。保证秒杀和商品详情页的动态请求才打到后端的服务集群。使用 CDN 网络,分担集群压力。
  • 恶意请求拦截。识别非法攻击请求并进行拦截,网关层。
  • 流量错峰。使用各种手段,将流量分担到更大宽度的时间点。比如验证码,加入购物车。
  • 限流、熔断、降级。前端限流 + 后端限流。限制次数,限制总量,快速失败降级运行,熔断隔离防止雪崩。
  • 队列削峰。1 万个商品,每个 1000 件秒杀。双 11 所有秒杀成功的请求,进入队列,慢慢创建订单,扣减库存即可。
上次更新: 2024/03/03, 08:36:37
短链

短链→

Theme by Vdoing | Copyright © 2023-2024 Starry | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式