高并发场景下强一致预算/库存扣减方案

场景描述

对于预算扣减/库存扣减类场景,我们需要根据业务对已有预算/库存做减法,拿发券的场景来举例:

  • 需要满足不同的发券需求,运营可配置预算扣减业务
  • 每次请求扣减一定数量的金额,比如发10元券给用户
  • 需要保持强一致,计划中的预算金额尽可能全部发出去,但不能多发,多发会有资金损失,所以需要有强一致的保证

本文介绍的方案还可以作为秒杀类业务中的一部分

概述

对于预算的扣减需求,可以用2条简单的sql来理解:

  • 第一条SQL,记录流水,流水有幂等的作用,还可用于做对账,SQL简化如下:
insert into budget_log (biz_no, budget_code) values (#{bizNo}, #{budget_code})

其中biz_no表示一次扣减的唯一标识,budget_code是一个预算计划的唯一标识

  • 第二条SQL用于做真正的扣减:
update budget set amount = amount - #{amt} where budget_code = #{budgetCode} and amount >= #{amt}

但是如果对并发的要求是百级别,仅仅依靠这两条sql或许可以满足需求,但是想要达到数十万以上的并发支撑,这样是远远不够的。

我曾经使用的库(全库能支持6k的tps)能支持到300次扣减,数据库资源发挥不出来。为什么会出现这种情况呢?6k的tps只能发挥出600tps的能力(300次流水插入+300次预算扣减),因为数据库在执行扣减sql时,会加上行锁,所有的扣减都会在此排队

有何方案

很多人会使用缓存解决问题,使用缓存后,并发往往能达到要求,但是缓存的另一个问题在于无法保持强一致,且缓存有宕机的风险,宕机后相关数据丢失,对于有强一致需求的场景,使用缓存并不合适,需要考虑基于数据库的方案,

前面说过,扣减sql有行锁导致并发量无法提高,那么我们可以将数据进行拆分,将一行数据拆分成多行,分布在不同的表上,可更进一步使用分库分表的方式提高并发量。

基于分库分表的方案

分表方案

按照我使用的DB的能力,DB能支持6k的tps,一次预算扣减需要一次写流水和一次扣减操作,那么可以算出,DB每秒可支持3000次预算扣减业务,而在单行上只能支持300次扣减业务,我们可以分10个表,将单行转变为10行

假如有100万预算,分10个表,每个表10万,扣减方案如下图:

分表扣减
  • 使用单个流水表,多个数据表的方式,因为流水表不是瓶颈,瓶颈在数据表的行锁上,所以流水表不需要分表
  • 表路由组件可通过biz_no做hash,分配到不同的分表上做扣减

通过分表的方案后,能支撑秒级3000次的扣减业务,但是这样还不够,如果想要支撑数十万,依然远远不够。

分表+分库方案

通过前面的计算,我们知道,分表后,可将单个数据库的性能发挥到极限,想要再进一步提高支持的并发量,可以再加上分库的方式,使用分库分表的方案,单库秒级只能支撑3000次扣减业务,那么如果分100库,则秒级可支撑30万次扣减业务,如下图:


分库分表方案

还是假设有100万预算,当我们使用100库1000表后,单行转变成了1000行,处理能力可扩大1000倍,每个表1000预算

需要注意的是:

  • 在分库分表的方案下,流水表也需要分表,但是每个库一个分表即可,假如是100库,则流水表使用百库百表即可
  • 同一次扣减业务,记流水表必须要和执行扣减发生在同一个分库中,需要通过事务保证一致性

问题与风险分析

通过分库分表的方式,我们确实可以达到非常高的并发量的支持,但是如果结合实际情况,我们会发现以下问题:

解决流量不均的问题

如果流量不均,那么每容易出现某些表很快就扣完了,有些表扣的比较慢,这样导致系统整体容量降低

我们可以通过按需分配的方式解决这个问题,我们将每个表上的一行数据称为一个分桶,我们增加总桶的概念,初始时,预算都在总桶中,然后依次给每个分桶分配少量的预算,比如100(具体的分配金额可根据实际场景确定),那么初始分配后,每个分桶100,1000个分桶一共10万,总桶中还有90万,当一个分桶扣完后,立即向总桶申请一定的预算,这样可以保证分桶中的预算大体上是按流量分配的,方案如下图:


预算申请

解决库被打挂的风险

当总桶无预算时,预算申请都会失败,而当预算申请失败后,某些库可能会出现无预算可扣的情况,在这种场景下,如果换到有预算的库做预算的扣减,那么就可能会出现库被打挂的风险,举个例子:
假设业务上有10万的tps,100库平均每个库1000,当总桶无预算可申请,其中的99个库慢慢的扣完后,10万的tps会打到剩下的一个有预算的库上,这个库肯定无法承受秒级10万的扣减业务

如何解决这个问题呢?在前面描述的按需申请预算的基础上做一个限制,分库路由规则中,如果业务被分到了无预算可扣减的库上,则返回扣减失败,给调用方返回预算已扣完,因为流量大体上是均衡的,所以这样做的问题并不大

分库分表的作用

本文介绍的方案是基于分库分表的,脱离我们这个方法,最后聊一聊分库分表的作用,根据经验,我认为分库分表可解决以下问题:

  • 数据量大导致单表/单库效率低,通过分库分表可解决数据量大的问题
  • 单库/单表能力不足,支撑不了业务量,使用分库分表的方式提高系统能力(本文介绍的方案属于这一种)
  • 容错,降低数据库故障给业务带来的影响,比如分100库,当其中一个库不可用时(软硬件升级导致的故障等因素),只影响1%

所以,在数据量并不大的情况下,也是有分库分表的场景和必要性的

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容