编辑
2023-10-14
Csharp/dotNet
0
请注意,本文编写于 406 天前,最后修改于 406 天前,其中某些信息可能已经过时。

目录

场景描述
解决思路
思路1:预扣库存
思路2:异步通知
悲观/乐观锁 使用场景
基本概念
悲观
乐观
结合场景
实际代码应用
为了解决A问题二又带来的B问题解决方案
引子:

进来准备重新找工作面试,顺便梳理一下自己过往的经验总结...

本人理论词汇掌握不多,大多数时候都会讲人话

场景描述

1.用户下单,商品库存已经不足了,但还是扣减了

2.医生开方,药品不足了,但还是被开了出去

··· 类似场景

解决思路

思路1:预扣库存

用户下单时,系统先进行预扣库存操作,然后后将“下单业务”发布到MQ(消息队列)进行处理,成功通知,失败回滚预扣库存操作

  • 对于预扣库存时可能出现的“超卖”现象,可以采用针对数据库表的悲观/乐观锁,具体用哪种,看场景,下文有讲

思路2:异步通知

用户下单时,直接提交,后台丢入MQ,然后进行业务处理,前端直接提示操作完成

  • 这个处理方式,极大的提升吞吐量,因为不需要“即刻”处理,后续获取结果方式则可通过各种方式来通知,如微信公众的消息订阅模板消息,短信、邮件,等。
  • 这个场景在BAT产品中可以找到相关例子,例如早期微信充值手机花费的早期处理思路,就是提示“充值操作完成,具体结果稍后通知” (现在已经改了,页面等待即会通知)

悲观/乐观锁 使用场景

基本概念

悲观

阻塞(等待),超时报错

  • 假设会冲突,好处是能保持数据一致性,设计思想是不管咋样先无脑上锁 ,说人话就是“我每次出门都先带一瓶防狼喷雾,不管我会不会遇到危险情况,我假设可能我被坏蛋盯上了”显而易见,这样我出门花费的时间成本肯定增加了,代码层面就是耗费的性能增加了

乐观

假设不冲突,提交更新对比版本号,失败报错,设计思想就是是尽量减少锁的使用

  • 说人话就是“出门不带防狼喷雾了,我觉得世界坏蛋不多

  • 高并发的修改操作,乐观会频繁更新冲突报错,如果你逻辑写了重试操作,那么久一直报错重试N循环,这时候已经不适用乐观,应该使用悲观

结合场景

  • 用户下单,医生开方,规模到达一定量时,肯定要保证不要出现“超卖”现象,因此扣减库存使用悲观无可厚非
  • 其他场景,如基础字典数据维护,用户修改个人信息,扣减用户余额等一系列操作,一般稳妥期间都会加上乐观锁,其场景就是并发量并不大

实际代码应用

这种锁机制,不管你用什么基本都很成熟了

针对数据库,我这里推荐的sqlsugar这个ORM,使用sqlsugar能够很容易的针对数据库表进行乐观/悲观锁的操作,其稳定和成熟性也经过了多年多家大头公司认可了,使用方法看官方文档很详细

如果是分布式锁,这里推荐的是FreeRedis它里面有基于redis封装好了分布式锁的使用,调用十分简便,我个人实际公司业务上也是使用了这个

为了解决A问题二又带来的B问题解决方案

引子:

通过上诉解决方案后,理论上技术层面性能问题顶住没啥问题了

(这如果还有问题,想必你司业务及其牛X,更进阶的方案也没啥实际上的技术思路提升,无非就是把处理业务的模块继续拆分,拆分后的各个粒度更小的业务服务再加大规模的集群,也就是现在大厂普遍的微服务)

但是可能会有一个新的问题,例如:A客户看中一个热门商品满心期待的去下单,下单完心里美滋滋幻想着用上这个商品然后去睡觉,第二天起来才看到 “对不起,下单失败,库存不足”,直接各种恶毒的语言开始诅咒开发这个产品的人

某些业务场景下这种情况是允许的,而在某些产品上,这种情况则是不被允许存在的,实际解决其实也简单,如果项目有ws,可以考虑在页面等待ws获取结果,如果没有则轮询请求某个api返回最终结果

有没有发现,我本来 “普通一个接口搞定的事情,现在为了解决‘可能存在的’问题要做的事情反而多了”所以具体公司业务开发的时候,更多的是要掌握好实际需求以及实际预估场景,一句话自己根据实际场景把握好,“编程永远没有万金油”

温馨提示

这就是现如今利用各类技术解决问题后,衍生出来的新问题,因此请实实在在的确保你的项目实际上存在这么大的流量和高并发,真真切切存在此类问题需要解决,才考虑这些技术手段的解决方案,千万不要成为段子中的:“技术上异步填谷削锋,运行时用户白天20晚上15” 我经常提醒自己,“不要为了写代码而写代码”

本文作者:宁骑

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!