V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
PiersSoCool
V2EX  ›  数据库

套餐这种数据如何设计

  •  
  •   PiersSoCool · 2020-02-24 23:17:53 +08:00 · 2159 次点击
    这是一个创建于 1753 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最简单的套餐,设计到属性比如有初始值、剩余值、生效期的条件。 目前我是这么设计表 package,字段 init、current、expire。 但是这么做,每次扣除次数的时候都要去查表寻找剩余值、再扣除值,涉及到锁 package 表,在并发很高的情况下非常影响性能。假设套餐是流量套餐,扣流量很频繁,数据库压力很大。 想法是加缓存层,但是每次扣流量缓存层就会失效,相当于没缓存;如果更新缓存值又会涉及到缓存一致性的问题。 有没有什么简单粗暴的方法?

    5 条回复    2020-03-24 21:29:42 +08:00
    opengps
        1
    opengps  
       2020-02-24 23:29:37 +08:00 via Android
    算法上每次只扣减当前值,只有出现小于 0 才按照过期选最近一条,填充初始值为当前值,然后执行第二次扣剩余流量。
    操作最密集的是当前值,可以用缓存实时配合定时落盘持久化。如果这一条数据也要求强一致性,那么就只能堆快硬盘的机器来硬抗了
    yuankui
        2
    yuankui  
       2020-02-25 11:09:01 +08:00
    有没有发现,移动公司经常会告诉你超流量了,超了多少,现在剩余流量为-100MB
    可见他并不是实时扣费的,而是延迟批量累加扣费,这样对数据库没压力。
    实操中可以将所有的消费信息全部推倒一个消息队列,然后一个消费程序批量消费,累加 1 分钟内的数据,然后再从总数中减去。
    PiersSoCool
        3
    PiersSoCool  
    OP
       2020-02-25 11:19:02 +08:00
    感谢楼上各位
    alya
        4
    alya  
       2020-02-25 16:02:57 +08:00
    流量很大的话得上 spark 或者 flink 了
    ElmerZhang
        5
    ElmerZhang  
       2020-03-24 21:29:42 +08:00   ❤️ 1
    为什么要查剩余值?如果只是为了确保够扣的话,只要加在扣除的 where 语句里就可以了,不需要用事务去锁表:
    UPDATE package SET current = current - ${VALUE} WHERE current >= ${VALUE} AND expire > NOW();

    也可以用 REDIS 来做,key 的值为 current, 过期时间为 expire,每次请求来了直接去 decr,如果返回的结果是小于 0 的,就说明原来的余量是不够扣的,把刚才扣的值 incr 回去,当作是没扣过,然后返回一个扣失败。如果过期的话,decr 的结果一定是小于 0 的,也是扣失败。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   899 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 20:11 · PVG 04:11 · LAX 12:11 · JFK 15:11
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.