套餐这种数据如何设计

2020-02-24 23:17:53 +08:00
 PiersSoCool

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

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

也可以用 REDIS 来做,key 的值为 current, 过期时间为 expire,每次请求来了直接去 decr,如果返回的结果是小于 0 的,就说明原来的余量是不够扣的,把刚才扣的值 incr 回去,当作是没扣过,然后返回一个扣失败。如果过期的话,decr 的结果一定是小于 0 的,也是扣失败。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/647243

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX