V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
jdz
V2EX  ›  程序员

一则面试题,如果 10 万人同时打赏某快手主播,对支付系统来说,瓶颈在哪里,该怎么解决呢,组件是 mysql, redis 等, 假如打赏的钱加到主播余额而不是去银行那边进行交易

  •  1
     
  •   jdz · 2020-06-08 10:37:34 +08:00 · 11388 次点击
    这是一个创建于 1632 天前的主题,其中的信息可能已经有所发展或是发生改变。
    58 条回复    2020-06-09 16:13:57 +08:00
    kkkkkrua
        1
    kkkkkrua  
       2020-06-08 10:49:34 +08:00
    这比秒杀系统还要简单把,瓶颈应该在 10W 个并发请求那
    w292614191
        2
    w292614191  
       2020-06-08 10:52:27 +08:00   ❤️ 3
    锁、乐观锁、悲观锁、原子锁、分子锁、粒子锁、分布式锁、队列、缓存。
    一堆技术词堵住你先。
    hantsy
        3
    hantsy  
       2020-06-08 10:54:08 +08:00
    主播余额和走银行有什么差别,现在支持不是都是银行网关通道,大 Boss 都要有记录。
    jdz
        4
    jdz  
    OP
       2020-06-08 10:54:40 +08:00
    @kkkkkrua 并不是啊, 现在是 10 万人给主播加余额啊 数据库会有热点, 由于是支付, 对数据精确性要求很高
    @w292614191
    luckyrayyy
        5
    luckyrayyy  
       2020-06-08 10:57:51 +08:00   ❤️ 3
    快手的面试题?十万人可以分开处理,主播的余额那里是瓶颈吧。我首先能想到的就是并发量太大了,需要做个队列,把加钱的动作串行化,但是效率肯定堪忧。另外突然想起来 JUC 包里 LongAdder 的思想,空间换时间,是不是可以给主播创建多个虚拟账户,先把加钱动作完成,余额数字不追求实时准确了。
    xmh51
        6
    xmh51  
       2020-06-08 11:01:15 +08:00
    可以按照聚合请求然后单次更新来解决
    peyppicp
        7
    peyppicp  
       2020-06-08 11:08:59 +08:00   ❤️ 3
    如果要求实时的话,瓶颈在数据库,主播的账户是热点账户

    不要求实时的话,打赏这种行为可以直接落单,然后 T+1 再进行结算到主播余额
    fifa899
        8
    fifa899  
       2020-06-08 11:11:19 +08:00
    瓶颈应该是 MYSQL IO 吧.打赏记录表只有 Insert.那就做个队列..可以先丢进 redis 队列.再做个定时批量插入 吧. 余额就是 select sum(打赏金额)咯
    wmlhust
        9
    wmlhust  
       2020-06-08 11:12:07 +08:00   ❤️ 7
    分成几个层面:
    1. 接入层:瓶颈在于并发,这个可以通过加资源、优化性能解决
    2. 业务层:对于支付本身的逻辑,实时处理。各种上下游、周边逻辑,比如弹幕播报,特效等,这种尽量通过消息队列去耦合,异步处理。
    3. 存储层:这里是关键,如果设置一个 mysql 余额表,显然撑不住十万的并发写,每一个请求都要对该行加锁。但是又不能完全依靠 redis,可能会导致数据丢失,不一致等。
    一个方案是,使用消息队列+mysql 存储流水,使用 redis 存储余额,定时对账。
    由于流水是追加写,不涉及到加锁,同时可以通过分库分表等提升性能。
    redis 提供余额的高并发读、写,但是对单机 redis,十万 QPS 也扛不住,redis 集群两个方案,主从或 cluster,在这个场景,主从更合适(因为 cluster 对单 key 是固定路由到一个节点,所以没办法提升单 key 的处理能力)。再考虑到十万的并发写,这个只能牺牲一点余额的实时性,大概可以通过 group commit 或 kafka 异步更新缓存。
    -----
    瞎想的,不对的地方,各位老铁随意指出
    chmaple
        10
    chmaple  
       2020-06-08 11:15:33 +08:00   ❤️ 1
    打赏主播不是同时支付。
    支付操作肯定要事务的,但是支付是到账户余额上的,然后从账户的虚拟金额进行抵扣。
    所以这个问题就拆成,并发十万的礼物请求,主播的收入计算,以及最高几千 TPS 的充值请求。

    十万的礼物请求,其实是分开在每个用户账户下的,那么就是相互不影响的库存抵扣了。
    主播的收入计算完全可以做 T+1 延迟结算,没必要做成实时更新同一条目这么大的压力。
    充值请求的话要好好做,那就是支付子系统如何设计的问题了。
    binux
        11
    binux  
       2020-06-08 11:21:17 +08:00 via Android   ❤️ 1
    哪有什么瓶颈,虽然是同时打赏一个主播,但是却没人任何数据需要同步交互。
    你创建 10 万个毫无关联的独立订单,订单完成后再去主播那加一就行了。别说是同时加 10 万次余额,你就是花一个月加完都行啊,反正加少了别人也不知道啊。
    opengps
        12
    opengps  
       2020-06-08 11:23:52 +08:00 via Android
    系统设计成支持水平扩容的,那就没有技术瓶颈,更多的瓶颈来自于监管压力
    xnode
        13
    xnode  
       2020-06-08 11:25:12 +08:00   ❤️ 1
    ... 这有个毛线瓶颈 主播的钱又不是实时到账的,打赏的钱先扣了再说
    None123
        14
    None123  
       2020-06-08 11:26:52 +08:00
    打赏不是直接到主播的支付宝的 。。。
    ajaxfunction
        15
    ajaxfunction  
       2020-06-08 11:52:31 +08:00   ❤️ 2
    为什么只有我感觉 没有瓶颈?
    不就是写入十万条 sql 记录?,而且这位 10 万条还不是同时写入的,也不要求写入顺序, 还不涉及库存什么的
    我感觉事务都用不上
    accacc
        16
    accacc  
       2020-06-08 12:35:12 +08:00
    @ajaxfunction 事务肯定是要有的 用户钱出 主播钱进 不过不要求实时性这问题到简单
    xcstream
        17
    xcstream  
       2020-06-08 12:44:47 +08:00
    tcp 链接比较多, 当然分几台服务器问题不大
    数据先用 redis incr redis 单线程并发可以 10w 的
    然后可以慢慢的用 mysql 跑
    netnr
        18
    netnr  
       2020-06-08 12:59:41 +08:00
    就好比 10W 的请求 日志怎么存储
    firefox12
        19
    firefox12  
       2020-06-08 13:00:55 +08:00   ❤️ 2
    没有人说 kafka, 你们的交易消息直接落 redis, 不怕掉啊?直接打后面 不考虑后面节点挂掉吗?
    yukiloh
        20
    yukiloh  
       2020-06-08 13:16:03 +08:00   ❤️ 1
    我觉得不如解决 10w 人看视频不卡的问题...
    chenzj3126
        21
    chenzj3126  
       2020-06-08 13:29:55 +08:00
    10W 用户,在内部分队列,然后发布到 redis 不同的队列中,多个消费者消费,更新的时候需要加锁
    no1xsyzy
        22
    no1xsyzy  
       2020-06-08 13:42:44 +08:00
    @firefox12 #19 交易消息掉了感觉问题不大,最多打赏失败了想打赏没打上,或者提示失败实际成功罢了。
    liuxu
        23
    liuxu  
       2020-06-08 13:52:19 +08:00
    100k 交易事务,基本上前后端都得集群,瓶颈在数据库事务上,上数据库集群吧,任何交易一定要 100%准确,包括交易日志千万不能有丢失风险,能交易成功的就不要让他失败,加点钱加硬件,一笔交易硬件的钱稳稳的赚回来
    passerbytiny
        24
    passerbytiny  
       2020-06-08 14:14:18 +08:00 via Android
    这是主观题,请不要网上提问式的寻找答案,因为没有标准答案。

    这也是一个随便想起来的题,问题描述本来就有问题。后面“假如”那段,已经把前面问题的答案暴露了。答案是:瓶颈是去银行那边进行交易,尤其是加钱那一块。这很好解释:出钱账户是 N,加钱账户是 1,从 N 个出入到 1 里面,1 那里最容易瓶颈。解决方案因实际情况而宜,我不想费事打字,但是可以肯定 那个“假如”的方案是行不通的,因为 N 至 1 的情况仍然存在(当然也不是一无是出,它可以解决“银行系统垃圾”的瓶颈)而且监管机构也不会同意。
    jaakali
        25
    jaakali  
       2020-06-08 14:19:37 +08:00
    感觉这里的支付因应该是虚拟商品(虽然也是钱买的)打赏吧,都在快手用户的账户里,用户-主播+?
    NoobPhper
        26
    NoobPhper  
       2020-06-08 14:23:53 +08:00
    哪个 sb 问的 哈哈哈哈
    jaakali
        27
    jaakali  
       2020-06-08 14:34:22 +08:00
    好吧,我错了,题主问的是 2 个问题
    hbolive
        28
    hbolive  
       2020-06-08 14:37:31 +08:00
    先把打赏记录 insert 再说,insert 开销很小,10W 根本不在话下,然后再根据打赏记录,把金额同步到主播账户。。
    zouqiang
        29
    zouqiang  
       2020-06-08 14:45:31 +08:00
    热点账户问题,扣减后异步加
    wangdashuai
        30
    wangdashuai  
       2020-06-08 14:56:08 +08:00
    @binux 这个思路好。
    tingfang
        31
    tingfang  
       2020-06-08 15:15:17 +08:00
    记流水,主播账户异步慢慢加。
    LennieChoi
        32
    LennieChoi  
       2020-06-08 15:25:17 +08:00
    这题是不是想考分布式锁啊,redis 做个分布式锁,10w 并发负载均衡进来后,请求落到不同节点,然后用分布式锁控制每一笔打赏入库 mysql,但这样的话 10w 条消化掉会很慢吧,属于要同步消化掉 10w 条。在这个基础上有啥优化的空间呢?(纯属顺着题意想的,正常的话做个订单系统,做 kafka 消费应该没啥瓶颈。但顺着这个题意有啥好的优化吗)
    xiangyuecn
        33
    xiangyuecn  
       2020-06-08 15:43:22 +08:00   ❤️ 2
    “10 万人同时打赏”,是不是意味着至少会有 100 万+的人同时在刷屏?意味着至少 1000 万人挤在一个直播间观看直播?告辞,我要有这能耐,我还拘泥于 mysql redis ?








    他娘的,这个 10 万人同时打赏,瞬时并发可以赶上双十一的支付了的吧😂 就算你扛住了,牵涉到的第三方能不能抗住也是个问题🐶 面试官想套路我造原子弹的核心机密?
    LennieChoi
        34
    LennieChoi  
       2020-06-08 15:59:19 +08:00
    @LennieChoi 想的片面了,都在讲怎么给主播加余额这事,但是在这之前还有一步,打赏者需要实时扣余额,判断够不够打赏,这块我觉得直接 mysql 事物操作,数据库会有压力,是不是可以在某一个人第一次打赏的时候将他自己的余额等关键信息从数据库读到 redis(或许做个过期),然后接下来的打赏中直接改写 redis 判断够不够扣,产生打赏订单后,最终消费订单时同时处理给主播加余额和给打赏者扣余额,这样的话为了分摊读 redis 的压力,redis 做个分片?
    coolmenu
        35
    coolmenu  
       2020-06-08 16:04:15 +08:00 via Android   ❤️ 3
    屏幕打出卡号,让用户自己网银转账!
    我们这是把压力化解到前端,最时髦的做法啦
    ljzxloaf
        36
    ljzxloaf  
       2020-06-08 16:21:43 +08:00
    1. redis 做缓存,只用来展示不做业务强依赖,group commit 降低 redis 压力;
    2. 可靠消息保证分布式事务,也能起到削峰填谷的作用,收到消息再慢慢更新 db 。
    3. redis 根据需求周期性同步 db 数据,保证数据相对准确。就像点赞、分享这类统计数据,不需要实时,但也不能太离谱
    BlackBerry999
        37
    BlackBerry999  
       2020-06-08 16:53:18 +08:00
    @xiangyuecn 被你猜中了!
    ISSSSSSS
        38
    ISSSSSSS  
       2020-06-08 16:55:14 +08:00
    这个问题挺有意思的也是一个比较真实的场景。
    按照题目来分析,那么打赏主播总共分四步。
    1 用户创建打赏订单。
    1.1 同时存储 10w 条数据。数据库吞吐量的问题。如果是 mysql 的话可能需要分库分表。
    1.2 唯一主键问题。比如生成不重复的订单编号。同样也可以用分片的原理,比如给服务本身设定一个不重复的订单前缀等等。
    总结:这个问题的本身就是利用分片可以解决大部分问题。
    2 用户支付打赏订单。
    2.1 支付回调信息处理。总的来说解决了问题 1,这个就不算啥问题。
    3 根据打赏订单创造主播余额充值订单。
    3.1 问题同 1 。
    4 充值订单完成后更新余额。
    4.1 大量请求更新同一个数据的字段。可以考虑批量更新法,比如更新的金额在内存或 redis 累计,积累到一定时间,再更新至数据库中。
    4.2 推荐使用乐观锁。比如通过版本号去更新余额,解决并发更新一条数据的问题。这笔直接使用 for update 效率要高一些,而且与 4.1 搭配更是完美,因为更新不成功,内存中的数字就一直累积下去,也挺好。
    ISSSSSSS
        39
    ISSSSSSS  
       2020-06-08 16:58:15 +08:00
    @ISSSSSSS 补充一下,如果条件限定在 mysql,那么就可以考虑 mysql 多主多从多数据源。重写数据访问中间件,直接按照用户 id 规则去访问不同的数据源。
    lewis89
        40
    lewis89  
       2020-06-08 17:00:31 +08:00
    @ISSSSSSS #38 热点数据写 redis 然后回写 db 是一个不错的思路
    realpg
        41
    realpg  
       2020-06-08 17:04:27 +08:00
    这个 没啥瓶颈……
    我的垃圾 PHP 程序都能 handle
    ISSSSSSS
        42
    ISSSSSSS  
       2020-06-08 17:08:40 +08:00
    @lewis89 对,其实这问题比秒杀要简单。题里的关键的锁是在用户支付后更新主播的余额。而秒杀是购买的那一刻就有资源竞争。前者主播的余额显示不及时其实问题不大,最终一致就可以了,但是后者如果用户无法下单支付就显得十分严重了。解决此类问题其实就是不断的把请求分片。不管是 nginx 还是 mysql 。
    hanhan13
        43
    hanhan13  
       2020-06-08 17:18:28 +08:00
    @ISSSSSSS 有个问题是,你说的 2 、3 、4 是有事务约束的,只要用户支付成功,那就要保证能给主播充值上。采用分布式事务似乎不能满足 QPS 要求,不知道您有什么思路?我的想法是用队列加一组单独的充值处理应用来异步完成 3 、4 。
    ISSSSSSS
        44
    ISSSSSSS  
       2020-06-08 17:24:40 +08:00
    @hanhan13 2 ->3,3->4 的过程 可以存在事物也可以不存在(假设数据已经分片)。但我更倾向于使用生产者消费者模式去解决。比如用 MQ,再通过补偿机制去核对补充。
    总之所有的都是通过最终一致性的思路去设计的。所以并不需要强事务。
    aqqwiyth
        45
    aqqwiyth  
       2020-06-08 17:33:40 +08:00
    支付宝双 11 并发 11W
    xuanbg
        46
    xuanbg  
       2020-06-08 18:02:46 +08:00
    瓶颈在数据库,前面加个队列削峰就行。
    hanhan13
        47
    hanhan13  
       2020-06-08 18:03:06 +08:00
    @ISSSSSSS 可以简单介绍下一般采用什么样的补偿机制去核对吗?对这块不是很清楚,感谢~
    ISSSSSSS
        48
    ISSSSSSS  
       2020-06-08 18:18:04 +08:00
    @hanhan13 补偿机制的话其实就是个业务。
    具体问题具体分析。比如说 2->3 的过程。
    如果是通过 mq 的方式去处理,那么会面临一下几个问题。
    1 MQ 没发出去。2 MQ 发出去了,但是丢了。3 MQ 服务挂掉。4 处理 MQ 的应用发生错误。
    那么最终会反映到数据存上的状态不一致。比如服务 2 有数据 但是服务 3 没数据。或者没处理。
    比较简单的补偿,其实就是让服务 2 重复发送 MQ,但是也存在一定的问题。
    比较高级的处理方式是,直接监控服务 2 、4 的 binlog (或者是其他数据库的 log ),通过 binlog 去比对,如果发生不正常的情况(比如状态不一致,数据丢失),那么再去处理。补偿的同时也要考虑到重复补偿的问题。
    当然即使做到万全准备,还是可能会出现问题。那么这个时候就需要定时程序去核对数据。避免资损。
    lewinlan
        49
    lewinlan  
       2020-06-08 18:21:28 +08:00 via Android
    瓶颈就是主播余额上的锁
    解决思路,用户的钱先锁上,生成订单,然后丢到队列里去一个个用事务转账。
    打赏后延迟几秒几分钟到账咋了?没影响~
    strict
        50
    strict  
       2020-06-08 18:43:22 +08:00
    并发不大的情况下处理流程:
    一用户扣钱:
    1 扣钱前加锁,行锁级别,判断钱是否够,同时防止一个人脚本并发打赏
    2 扣钱动作实时 commit
    3 交易 log 消息队列异步处理去创建
    二主播余额:
    1 消息队列异步增加余额。
    2 redis 做主播信息缓存用,定期同步 redis 和 mysql

    十万并发情况下,发现数据库锁成了瓶颈,考虑到业务成本的锁逻辑不应该由数据库来控制,看扣钱时的数据库加锁换成 redis 锁。
    nl101531
        51
    nl101531  
       2020-06-08 18:58:23 +08:00 via iPhone
    一句话,共享的地方是瓶颈,如果对应一条余额记录的话,那这个是瓶颈,可以锁拆分考虑,拆成多账户。
    nl101531
        52
    nl101531  
       2020-06-08 18:59:31 +08:00 via iPhone
    @xiangyuecn 双十一支付 tps 貌似好几十万了
    mlboy
        53
    mlboy  
       2020-06-08 20:02:23 +08:00 via iPhone
    @wangdashuai 哈哈这个思路不错
    thinkmore
        54
    thinkmore  
       2020-06-09 09:29:10 +08:00
    @coolmenu 兄弟,你是要笑死我!
    danhahaha
        55
    danhahaha  
       2020-06-09 09:34:17 +08:00
    瓶颈是去哪里找这十万人
    bzj
        56
    bzj  
       2020-06-09 10:12:07 +08:00
    你们为啥非要认为打赏一笔就要转账,用户充值和主播提现这两个走支付系统,打赏行为只是个数字的变化而已
    blackboom
        57
    blackboom  
       2020-06-09 10:32:06 +08:00
    这里有 N 个 10W 在同时进行,并没有那么简单。
    liuxingdeyu
        58
    liuxingdeyu  
       2020-06-09 16:13:57 +08:00
    我突然觉得,这 10w 个要是飘屏的大礼物,对 im 的压力应该不小
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2019 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 16:18 · PVG 00:18 · LAX 08:18 · JFK 11:18
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.