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

如何才能做到尽可能快的实现热点数据变动并记录?

  •  
  •   qxdo1234 ·
    qxdo · 217 天前 · 1965 次点击
    这是一个创建于 217 天前的主题,其中的信息可能已经有所发展或是发生改变。
    之前遇到的一个面试题,一直在想有没有什么好的答案。
    大概意思就是说,在支付宝里,假设我经营着一个很火爆的业务,这个业务的个人客户认为是无限多的,但是我们公司账户只有有限个。我和我的客户要转来转去,量也可以认为是相当大的。公司账户需要写账户余额表,账户流水表(包含变动之前余额,变动金额,变动之后金额)完整的记录下来,寻找最快的解决方案。现在的方案是采用 MySQL 事务,每个账户是一条数据记录,钱都在这一条数据里,在人多的时候,会出现很多失败的情况,因为业务量很大的情况下,一个账户变成了热点数据,一行记录被很多线程抢,就会出现问题。之前一个大佬 的想法是 把一个账户的钱,拆分出来,拆成 N 多个小账户,每次转账找到其中一个,结掉再根据此时的其他账户的总余额,写交易记录。相当于是把一条数据变成 N 多条,每次取 1 条,但是他这个方案,是不是在找账户的时候 要引入一定的随机性,不然 每次按照一定的规律去找,符合条件的这一条数据也会变成新的热点数据,问题实际上还是存在的。
    9 条回复    2024-04-12 10:16:38 +08:00
    qxdo1234
        1
    qxdo1234  
    OP
       217 天前
    有类似的解法的,也可以一起回复,大家一起开放讨论,不限制具体使用的存储和计算的方式,但是想了解一下,有没有比较完善的解决方案。
    wenxueywx
        2
    wenxueywx  
       217 天前   ❤️ 1
    一、同时跟你发生交易的客户数量是有限的;
    二、采用 mysql 事务实现,受限于行锁,高并发下会有严重的锁等待现象,分桶可以缓解,例如你可以把公司账号分为 0-9 共计 10 个子账号。查找账号时引入随机性没有用,因为你总的并发不会因为你的随机性降低。
    三、 高并发下,随着并发数( n )的增加,innodb 的死锁检测占用的资源会以 n^2 级数上升,你可以简单暴力地关闭死锁检测,但是这治标不治本;治本的方法是在 innodb 中明确地需要串行执行地操作不要进入引擎层,可以在业务上进行排队,排队时还可以对队列进行优化,比如同时 100 人并发转入到子账号 0 ,此时数据库中给子账号 0 增加余额,以及记录流水的操作是明确需要串行执行的,如果进行业务上进行排队优化,你可以合并这些转入操作到一次数据库变更中。
    wenxueywx
        3
    wenxueywx  
       217 天前
    @wenxueywx 简单说就是把串行操作变成批处理
    KIDJourney
        4
    KIDJourney  
       217 天前   ❤️ 1
    啥业务要求即时到账啊,在线转离线呗

    搞个队列或者搞个 mysql 任务表慢慢离线扫去
    qxdo1234
        5
    qxdo1234  
    OP
       217 天前
    @KIDJourney 假定的是一个对并发,以及到账处理时效要求比较高的场景,毕竟你也不想你的客户做完操作以后 还要等大概 1-2min 之后才能离线看到自己的账户变动嘛,你可以理解成,比如我们运营一个超高价值波动的产品,晚 1-2min 的话 就一个天上一个地下了。肯定在有解决方案的情况下,要求更快的处理时效,如果真的是没有解决方案的话,才可以接受大概延迟几秒的处理时效。慢慢扫 可以解决问题,但是不是太符合本场景,不过还是谢谢。
    waitingChou
        6
    waitingChou  
       217 天前   ❤️ 1
    分桶是一个简单的抗高并发的思路,还要继续优化出一个复杂思路的话,感觉 innodb 处理写入操作的思路也可以参考一下。

    简单来说就是把数据加载到内存, 有请求先改内存数据, 再写入到 "redolog"。 后台任务有空再把 redolog 落盘,落盘的时候没有并发,可以做得比较快。

    把高并发的冲突操作改成了纯内存 + 无锁顺序写
    justRua
        7
    justRua  
       217 天前   ❤️ 1
    是不是可以把加钱、扣钱两个动作区分对待,扣钱失败的场景一般是用户余额不足之类的业务异常需要立马回滚事务,但是加钱失败则可能是出现锁竞争、网络之类的系统异常导致,重试就好了不需要回滚整个事务,可以适当延迟。

    用户余额加钱逻辑:
    1.写入账户流水;
    2.发送 MQ ,流水表加一个同步状态字段。
    后面异步执行:
    3.用消费者根据 MQ 消息去扫描流水表、批量的把多个加钱的记录金额合并为一个 update 操作用户余额,把同步状态更新为已同步。

    查看用户余额逻辑:
    查询用户余额表 + 流水表未同步的记录金额

    扣钱逻辑不变,但可能出现扣钱时,余额还没加上流水表未同步的加钱金额,导致余额不足扣钱失败,应该也还好。
    dode
        8
    dode  
       217 天前
    引入 Redis 记录公司账号变化状态
    haxixi
        9
    haxixi  
       216 天前
    公司账户的变化用 redis 记录
    账户流水用 mq ,然后写流水表
    如果 redis 出现宕机消息丢失,用流水表恢复 redis 丢失的消息
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5510 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 21ms · UTC 03:40 · PVG 11:40 · LAX 19:40 · JFK 22:40
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.