notebooks
  • notebooks
  • _planning
    • 2022 OKR
    • basketball
    • swimming
  • communication
    • Dubbo
    • Kafka
    • Messaging
    • RPC
    • Thrift
  • computation
    • map-reduce
  • cs-basic-knowledge
    • computer-architecture
    • data-structure-and-algorithms
    • networks
    • os
  • devops
    • Docker
    • Linux
    • Prometheus
    • operations
    • security
    • trouble-shooting
  • distributed-knowledge
    • Zookeeper_CMD
    • distributed-system
  • game-engine
    • Unity
  • others
    • appium使用
  • protocols
    • http(s)协议
    • 官方链接
    • sip
  • storage
    • Elasticsearch
    • GuavaCache
    • MySQL
    • MySQL_CMD
    • NoSQL
    • Redis
    • Redis_CMD
  • system-design
    • system-design
  • tools
    • Git
    • IDEA
    • Mac
    • VScode
    • Vim
  • _working
    • doc-template
      • backend-design-review
      • correction-of-error
      • service-review
    • process
      • domain-backup
      • oncall
  • blogs
    • history
      • 8088/8086微处理器
      • 8088/8086指令系统
      • CSS-DOM
      • CSS定位
      • CSS工作原理
      • CSS控制背景
      • CSS浮动布局
      • CSS盒模型
      • Chrome开发者工具使用方法
      • DOM
      • Django Model模型层学习
      • Django-REST-framework Serializers学习
      • Django-REST-framework Views和ViewSets学习
      • Django View视图层学习
      • Gvim下Emmet安装及使用教程
      • HTTP协议简介
      • HashMap原理初探
      • JavaScript简史
      • JavaScript语法
      • Java内存模型和GC机制
      • Java基础——Lambda学习
      • Java基础——方法引用
      • Java基础——枚举类型
      • Java类加载机制
      • KMP算法
      • Kafka学习
      • Linux下用命令行编译Java程序
      • MathJax简介和基本用法
      • Python实现常见数据结构
      • Python装饰器总结
      • TCP协议的三次握手和四次挥手
      • Thrift学习
      • asyncio学习
      • markdown的常用语法
      • 修改hosts文件实现翻墙
      • 充实文档的内容
      • 关系数据库
      • 关系数据库标准语言SQL(一)
      • 关系数据库标准语言SQL(二)
      • 关系数据理论
      • 关系查询处理和查询优化
      • 内联元素和块级元素
      • 剑指offer算法题练习
      • 动态创建标记
      • 图形化用户界面
      • 在Eclipse中使用Maven构建Java Web项目
      • 增加微博秀遇到的一些问题
      • 处理机调度
      • 如何用github和hexo搭建个人博客
      • 存储管理
      • 存储系统的层次结构
      • 学习模仿lionhit网站首页的过程总结
      • 实用的GitHub小技巧
      • 并发控制
      • 循环与分支程序设计
      • 指令系统的设计
      • 指令级并行及其开发——硬件方法
      • 搭建自己的VPN服务器
      • 操作系统用户界面
      • 数据库安全性
      • 数据库完整性
      • 数据库恢复技术
      • 数据库绪论
      • 数据库编程
      • 数据库设计
      • 数据抽象
      • 文件系统
      • 文法和语言
      • 最佳实践
      • 案例研究:JavaScript图片库
      • 案例研究:图片库改进版
      • 汇编语言程序格式
      • 汇编语言程序设计基础知识
      • 流水线技术
      • 深度优先搜索和广度优先搜索
      • 牛客网——网易2017秋招编程题集合
      • 用JavaScript实现动画效果
      • 第一篇博客
      • 经典排序算法总结(Java实现)
      • 经典查找算法总结(Java实现)
      • 综合示例
      • 编译原理引论
      • 背包、队列和栈
      • 虚拟机安装Linux系统及常用软件
      • 计算机操作系统绪论
      • 计算机系统结构的基础知识
      • 设备管理
      • 设计模式之代理模式
      • 设计模式之单例模式
      • 设计模式之工厂模式
      • 设计模式之策略模式
      • 设计模式之观察者模式
      • 词法分析
      • 进程管理
      • 闭包
      • 阻止Google自动跳转到香港服务器的方法
      • 项目部署过程
  • programming-language
    • C#
      • C#
    • C&C++
      • C
    • C&C++
      • C++
    • Java
      • GoogleGuice
    • Java
      • JVM
    • Java
      • Java
    • Java
      • Maven
    • Java
      • Mybatis
    • Java
      • Spring知识
    • Java
      • SpringBoot
    • Java
      • Tomcat
    • Python
      • Python
    • Shell
      • Shell
  • wheels
    • dcc
      • 产品调研
      • 方案设计
    • red-envelope
      • 方案设计
    • short-url
      • 短链接服务
    • sso
      • 方案设计
Powered by GitBook
On this page
  • 具体设计
  • 缓存
  • 消息队列
  • 前端/客户端
  • 扣减红包金额
  • 分布式锁:
  • 接口限流
  • 数据库设计
  • 常见问题
  • 对账
  • 压力测试
  • 可优化点
  • 参考资料
  1. wheels
  2. red-envelope

方案设计

具体设计

缓存

选用Redis

缓存穿透解决方案:

当查询时缓存未命中,从数据库load数据到缓存,如果红包不存在,则load一条数据(key为红包id,value标记红包不存在)到缓存中。

缓存击穿解决方案:

加互斥锁,利用redisTemplate.opsForValue().setIfAbsent()方法

循环:

  1. 查缓存

  2. 缓存为空,则尝试加锁,否则跳出循环

  3. 尝试加锁失败,则跳到第1步继续循环,尝试加锁成功,则从DB load数据到缓存

// TODO

1.存储格式优化,改用hash格式

缓存雪崩解决方案:

消息队列

mq选型:RocketMq

具体方案:

先从缓存中查红包剩余数量,如果数量为0则直接返回,如果不为0,则使用mq发送一条红包金额扣减消息,然后返回秒杀中,客户端轮询秒杀结果。数据库层订阅扣减消息,进行红包金额扣减,扣减成功后刷新缓存。

前端/客户端

点击抢后,弹出加载动画,此时后端一般返回正在抢中的状态,然后前端轮询抢红包状态,直到查到抢红包结果确定,结束动画,展示结果。

扣减红包金额

金额全部使用int型,单位为分。

不可重复读:从查询红包余额时加排它锁,防止查询余额后,有其他线程更新了该余额,导致不可重复读。

解决办法:

  1. 排他锁(for update):mybatis实现排他锁不够优雅,抛弃

  2. 分布式锁

分布式锁:

使用stringRedisTemplate.opsForValue().setIfAbsent()方法实现

锁设置超时时间,如果线程卡住了,锁过期后其他线程可以抢占锁。

// TODO

当单次处理时间过长时,锁过期,存在锁泄漏情况。

解决方案:做好监控。

接口限流

使用guava的RateLimter配合aop实现

数据库设计

数据库:red-envelope

数据库表:

1.envelope_detail(红包详情)

CREATE TABLE `envelope_detail` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键',
  `user` varchar(25) DEFAULT NULL COMMENT '发红包者',
  `type` varchar(10) NOT NULL DEFAULT 'UNKONWN' COMMENT '红包类型',
  `amount` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '红包总额',
  `size` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '红包份数',
  `remain_money` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '红包余额',
  `remain_size` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '红包余下份数',
  `created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `updated_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

2.envelope_grabber(抢到红包的人和金额等)

CREATE TABLE `envelope_grabber` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键',
  `envelope_id` int(11) unsigned NOT NULL COMMENT '红包id',
  `grabber` varchar(25) NOT NULL DEFAULT '' COMMENT '抢到用户',
  `money` int(11) unsigned NOT NULL COMMENT '抢到金额',
  `created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `updated_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_envelope_id_grabber` (`envelope_id`,`grabber`)
) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8mb4;

常见问题

1.如何解决抢超问题()

  • 在sql加上判断防止数据边为负数

  • 数据库加唯一索引防止用户重复抢红包

  • redis预减库存减少数据库访问 内存标记减少redis访问 请求先入队列缓冲,异步下单,增强用户体验

对账

压力测试

Jmeter/Siege

可优化点

  1. 数据库分库分表

  2. redis集群宕机解决方案

  3. 应用监控、基础设施监控

  4. 防范脚本抢红包:负载均衡设备中做一些配置,判断如果同一个用户在1分钟之内多次发送请求来进行抢红包,就认为是恶意脚本代抢

  5. 利用负载均衡层来过滤无效请求(红包已抢完)

  6. 红包过期,多余金额退换

  7. set化:红包系统根据红包 ID,按一定的规则(如按 ID 尾号取模等),垂直上下切分。切分后,一个垂直链条上的逻辑 Server 服务器、DB 统称为一个 SET。各个 SET 之间相互独立,互相解耦。并且同一个红包 ID 的所有请求,包括发红包、抢红包、拆红包、查详情详情等,垂直 stick 到同一个 SET 内处理,高度内聚。通过这样的方式,系统将所有红包请求这个巨大的洪流分散为多股小流,互不影响,分而治之

  8. 大key优化

参考资料

https://www.zhihu.com/question/22625187/answer/85530416 https://rocketmq.apache.org/docs/quick-start/ https://www.bilibili.com/video/av19582887/ https://www.infoq.cn/article/2017hongbao-weixin https://www.ibm.com/developerworks/cn/web/wa-design-small-and-good-kill-system/index.html https://zhuanlan.zhihu.com/p/25368538 https://www.zhihu.com/question/54895548 https://www.infoq.cn/article/2017hongbao-weixin

Previousred-envelopeNextshort-url

Last updated 3 years ago