商城项目扣减库存分布式事务实现

谷粒商城原版的提交订单,扣减库存,支付业务,仍然是单体事务,且没有分库分表,也没有用到redis,性能是低的,本文基于消息队列和mysql,redis数据一致性两方面知识,将单体事务变成分布式事务,提高性能:

mysql,redis设计:

mysql, redis将会按照一级分类的进行分库,比如电子设备相关的sku会分在一个mysql,redis节点.服饰类会分在另一个mysql,redis节点里.每个redis,mysql节点都有自己的唯一id;

本身思考过同一个一级分类再次划分为多个库(不同的仓库Id分到不同的数据库,redis中),提升高可用性, 但这样会带来订单项的分布式扣减.其实也可以实现.这种情况下,每个订单项都会再次被划分,此次划分后,理论上单个节点的崩溃基本不会造成整体崩溃,可用性大大提高

1.扣减库存:

库存扣减将放在redis里进行,redis扣减库存前记录redo_log,用于redis内部延时关单,若redis节点崩溃,使用mysql重新构建redis数据,而不是使用将子库扶正为主库的策略(该策略不可避免超卖),由于节点崩溃,因此会对sku数据将会分散到不同的redis节点上(按照一级分类的标签进行路由),

redis库存扣减成功后,提交订单方法写两个消息队列, 一个是库存扣减消息队列(数据库扣减库存),一个是延时关单消息队列(用于延时关单),任何一个失败都表示该次提交订单失败.

2.延时关单

由于使用mq操作redis恢复库存无法保证幂等性,因此mysql,redis的延时关单业务有各自实现,mysql方面依靠唯一消息状态表实现幂等性,redis基于节点内部的redis_redo_log_queue(数据结构为zset)实现, 倘若redis节点崩溃,该节点将暂停10Min以上,直到所有订单都完成收单,然后基于该库对应的mysql数据恢复.

以上的业务都依赖于unique id;本项目参考美团的snowFlakeId实现唯一id,每一个订单项,订单都会有自己的唯一Id.来保证消息消费的幂等性;

各消息队列职责如下:

订单扣减分布式事务:

        1.提交订单时发送两个消息队列:        

                        1.扣减消息队列:扣减数据库库存:                          tbmall-order

                        2.延时消息队列,关单:                                           tbmall-order-delay-check

        2.扣减订单成功后,再发消息队列,用于更新订单状态:              tbmall-order-orderitem-deduct-status

        3.延时关单时发送回退库存消息队列:                                    tbmall-ware-restock

        4.回退库寸成功后,更新回退成功消息队列:                            tbmall-ware-restock-status

        5.支付成功,回调地址写支付成功消息队列:                            tbmall-order-payment

mysql,redis相关技术:

mysql方面使用了shardingJDBC实现分库分表情景下的路由

redis方面基于Dubbo和RedisConnectionFactory实现了多源的redis客户端

全部评论
看头像好像是之前id是杜兰特,哈工大的哥,最近咋样
点赞 回复 分享
发布于 10-29 23:44 湖北

相关推荐

10-29 16:14
四川大学 Java
点赞 评论 收藏
分享
10-29 04:05
同济大学 C++
目标大厂,冲就完事儿了- 实现输入输出的一个小程序:- 读取输入 scanf("%f", &var); 根据读取的输入(var), 然后对var进行后续的处理- 同时代码读取规范也同样重要,如果突然在代码中间加了一个数字的话,没有任何说明这样可读性很差。- 对于一些恒定的变量可以用预处理器提前声明。define SCALAR = 32类似这样- CPU、内存、外部设备。内存、虚拟内存。程序、进程。进程调用的是内存还是虚拟内存,程序调用的是内存还是虚拟内存?- 格式化输出:1. Printf: f -> format(格式化,控制输出的格式)2. 具体printf从键盘读取到最终打印出来的流程:[图片1]3. 格式化输出的语句格式:printf(格式串,表达式1,表达式2,。。。)int i = 10;float f= 3.14f;printf("i=%d, f = %f\n", i, f)4. printf具体的原理是:打印格式串的内容,并用后面的表达式替换格式串中的转换说明5. 格式串的具体语句:->对于普通字符,也就是字符串,他直接原样输出->对于转换说明,他的作用是占位符比如 %d %f等等。他有具体格式: %m.pX %-m.pX比如%d, %2d, %6d 分别代表的意思是占一位、占二位、占六位。%.2d, %.4d, %.6d分别代表保留几位小数点。 比如 %10.2d:10代表占10位,2代表保留2位有效数字。6. %d%f在读取键盘输入的时候代表的是什么意思呢?%d:忽略前置的空白字符(' ',\n,\t,\r,\f),匹配有效的10进制整数;%f忽略前面的空白字符(' ',\n,\t,\r,\f)匹配一个浮点数7. scanf本质就是一个匹配函数,类似于正则表达式。8.  gcd -> if b == 0 return a else return gcd(b, a%b)- 整型:1. 无符号整数:unsigned short (int), unsigned(int),unsigned long, unsigned  long long2. 有符号整数:short (int), int, long(int), long long(int)- 编码(位运算)1. 无符号整数:类似于正常2进制转化为10进制: 1010_1010(2)的值为=128 + 32 + 8 +2 =1702. 有符号整数:有符号整数一律采取补码:1010_1010(2) = -128 + 32 + 8 + 2 = -86。3. 补码的性质:a) 如果一个有符号整数他的二进制表示都是1那么他的10进制表示的值就是-1。[图片2]b) 有符号整数 a + (~a) = 1111...(2)  = -1 (10) ps:~a代表按位取反 ---性质1c) 有符号整数 a + (-a) =100...000000(2) = 0 (10) ps:这里的2进制表示的1会被丢弃所以结果会变成0.一个问题:有符号整数二进制表示为 11010100(2)他的相反数的二进制表示为?[图片3]- 浮点数:float, double, long, double IEEE 754标准- Char ASCII: 码点0 32 48 65 97 -> 字符‘\o’ 空字符 ; ‘ ’空格; '0';  'A' ; 'a'1. 控制字符:0-31这个是不可以打印的。2. 转义字符:[图片4]3. 数字转义字符:[图片5]4. !!!C语言是把字符当成小的整数来进行处理的 !!!=》整数能支持的操作,字符类型也能支持5. 字符分类函数:查表 大小写转换函数[图片6]6. 如何和用户交互:输入输出输出:printf  ; putchar(c)- 类型转换1. (隐式转换)不同类型进行加法运算的时候最后的类型会根据一个优先级别表进行分配。比如long long + long = long long 而 int + long = long- 最好尽量避免使用无符号整数,特别是不要把他和有符号整数混合使用:[图片7]2. (显式转换)强制转换。int i; float f = 6.17f   i = (int)f- 计算浮点的小数部分- 注释作用- 精确控制转换类型- 强制类型转换避免溢出[图片8]- sizeof:计算某一类型的值,所占内存的长度,单位为字节
点赞 评论 收藏
分享
评论
点赞
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务