PiersSoCool
V2EX  ›  数据库

套餐这种数据如何设计

  •  
  •   PiersSoCool · Feb 24, 2020 · 2831 views
    This topic created in 2276 days ago, the information mentioned may be changed or developed.

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

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

    也可以用 REDIS 来做,key 的值为 current, 过期时间为 expire,每次请求来了直接去 decr,如果返回的结果是小于 0 的,就说明原来的余量是不够扣的,把刚才扣的值 incr 回去,当作是没扣过,然后返回一个扣失败。如果过期的话,decr 的结果一定是小于 0 的,也是扣失败。
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   5212 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 110ms · UTC 09:12 · PVG 17:12 · LAX 02:12 · JFK 05:12
    ♥ Do have faith in what you're doing.