有点好奇微信读书的书币系统是怎么实现的。微信读书每天阅读可以获得书币奖励,基本每天能领取 1 到 2 个,而这些领取的书币都有独立的有效期,貌似一个月。如果每笔领取记录都单独过期,意思是一个月可能会有 30 多笔记录。假设每笔都是 1 个书币,难道买本 30 块的电子书要更新 30 条记录?每次查看余额都要 sum 一下记录?

余额就是一个数字,过期时间到了给你扣掉不就好了

硬件足够的话,数据越细越好不需要每次计算,缓存,或者保存预计算结果就可以了

不能吧,他付款肯定是先用旧的奖励记录,那就必须每笔记录是否被消费都记

更新 30 条记录也没啥吧,余额可以存另外一个表里

跟游戏开发中的道具类似, 每次获得的书币道具, 只是 configId 一样, 但是 instanceId 是新增的, 不同 instanceId 的道具都有独立的有效期. 扣减书币道具的时候, 优先扣减即将到期的

每次领取都是一条领取记录。领取的商品有过期时间。到时间了。减一下总表的 书币数就好了

想了想还有一个情况是,30 块的交易,用户有 29 个 1 块书币奖励和 1 个 2 块书币奖励,还得在最后一个 2 块奖励里记录消费了 1 块,还有 1 块没被消费,这太不优雅了吧

这只能解决 sum 的问题,实际消费还得一条一条的更新奖励记录

参考比特币的区块嘛做一个表,只记录交易信息,譬如 xxxx(时间戳) 入账 1 币,一个月后该条交易记录失效消费的时候就是手动将依然在有效期内的前 3 条 1 币交易记录失效,如果是入账 3 币只消费 2 币的记录就失效掉 3 币的记录重新生成一条基于原时间戳的 1 币记录余额计算就是简单的所有有效入账记录求和

是的,就是觉得这逻辑太重了,在微信读书这种大体量用户下,这个独立过期逻辑有点自己坑自己的意思

不重啊......你自己试一下就知道了,没啥计算量的,也就 IO 多一些。不过这种程度的 IO 相对微信本身那就是毛毛雨了

每条领取记录都是库里一条记录定期跑离线任务去库里把数据都过期掉算余额会把库里的值全部加起来这里会出现几个问题:跑全量任务更新过期会不会太重了? (记录很多, 真正要过期的很少)每次算余额要把所有的记录加起来会不会慢查询?(留为作业吧)

领取的时候就写入了这些币的过期时间,到时间自动过期了。

我感觉也是 写个定时任务扫描过期

之前做一个简单的系统想积分带过期功能,硬着头皮做了类似的方案,基本也实现了需求,结果最后做到退款功能时,还是给整破防了,因为还要考虑退款时退积分,退的积分要根据退款金额计算,退的积分还要保持原来的过期时间,还要考虑退款时,退回的积分有可能已经过期了,过期的积分是直接过期,还是根据规则折算成新积分,感觉本来一个简单的功能越做越复杂,最后索性不做积分过期功能了

按每月存 1 条记录,初始值为 32 个 0 ,签到的时候把当前天数那一位改成获得的金币数,过期只要把上个月当前天那一位改成 0 ,扣钱的时候,从上个月当天开始循环减。这样数据库操作就少了

先进先出呗。先获取的,先到期/先使用

balance 另有个 detail 表,独立过期时间就行了,不是啥麻烦事

存成 sortedMap ,key 是过期时间,value 是积分,每次登录把过期的删除,积分就是未过期的所有 value 的和,使用时候优先扣过期时间近的

使用书币是先用快过期的还是先用新领取的。

我也觉得短期过期太复杂了,话费信用卡的积分都一年清算一次

感觉最简单的方法就是你正文提到的了,别的逻辑都很复杂。1. 购买时更新 30 条记录也没什么2. 总额可以做缓存,余额变动时更新3. 1 个 2 块书币奖励可以设置成两条 1 书币记录

感觉是类似 NFT 的实现,每个 token 有自己的 id 和过期时间,然后整体也会维护一个 token 数量信息

sum 也没什么问题,冗余存也没什么问题,量不大,没有大事物都不是问题

如果要实现的话要挺容易的,数据库一个字段就能解决。记录的时候 [获得书币 A ,到期时间戳 1 。获得书币 B ,到期时间戳 2 。xxx],每次要写入新数据的时候,先判断是否有已经到期的书币,删除了,再在字段后面添加(新活动书币,新到期时间戳)即可。客户端每次要展示的时候,根据当前时间戳把数据库未到期的 sum 一遍就好了。其他查历史的话看 log 就好。

总和可以是单独的数据,过期是定时任务就能解决,流水信息可以定期归档,实际数据量不大。

领取时一条一条更新使用时只需要更新一下总数字就行了,只有一次啊

两个表:CREATE TABLE PointsRecord ( id INT AUTO_INCREMENT PRIMARY KEY COMMENT '自增 ID', user_id INT NOT NULL COMMENT '用户 ID', points_amount INT NOT NULL COMMENT '积分数量', receive_time TIMESTAMP NOT NULL COMMENT '领取时间', expiration_time TIMESTAMP NOT NULL COMMENT '过期时间', status VARCHAR(20) NOT NULL COMMENT '状态', receive_source VARCHAR(50) NOT NULL COMMENT '领取来源', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '记录时间', updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间') COMMENT '积分领取记录表';CREATE TABLE PointsWallet ( id INT AUTO_INCREMENT PRIMARY KEY COMMENT '积分钱包 ID', user_id INT NOT NULL COMMENT '用户 ID', points_balance INT NOT NULL DEFAULT 0 COMMENT '积分余额', total_points INT NOT NULL DEFAULT 0 COMMENT '积分总额', used_points INT NOT NULL DEFAULT 0 COMMENT '已用积分总额', expired_points INT NOT NULL DEFAULT 0 COMMENT '已过期积分总额', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间') COMMENT '积分钱包表';领取积分: PointsRecord 入库一条记录,PointsWallet 更新 total_points & points_balance ,事务处理过期积分:PointsRecord 更新一条数据,PointsWallet 更新 total_points & points_balance & expired_points ,事务处理积分消费:PointsWallet 更新 used_points & points_balance过期积分用脚本处理积分消费时判断余额够就允许消费。

将积分领取和积分消费解耦,消费时不关心积分的有效期属性,积分的有效属性也不关心消费的具体场景,只关心额度变化即可。

这点数量算什么? 随便一个网络游戏, 一次就加载几千个装备物品, 还不是大把人能做?

两个表,一个当前的 sum ,一个 detail通过事件系统,detial 表的变动通知到 sum ,sum 改余额就完事了而且一天才一两条,不用事件系统,搞定时任务扫都绰绰有余

流水过期

类似物流的.出入库. 入库明细-》 spu,sku,批次,效期次,入库数量

有没有一种可能 实际上都是 1

有道理

你在查找的是不是:数据库

补充思路,可以是 32 个 0-9 ,a-z ,这样每天获取到的就可以非常多了,只需要计算一个 32 的字符串,就可以算出来了。但是缺点有很明显,微信读书可以做到分钟级别的失效,这个就做不到了。

以我浅见,这种余额类的大都是事件溯源的,即你看到的余额不是简单的存储了一个数字,而是由交易记录实时计算出来的,然后定期合并一个快照,减少计算量,那么就可以在交易记录上打标记来实现过期。这是最常用,也是最安全的做法。当然,也有简单的方案,比如将这类会过期的书币存入 redis 的 zset 中,然后将过期时间戳作为 score ,就可以使用 ZRANGEBYSCORE 之类的命令来返回当前时间和过期时间之间的数据,求和就行了。

一切都要看业务。如果你有 15L 说的那样情况,或者其它情况,那么事件溯源,就是这一类问题目前的唯一解,同时兼顾了安全性和灵活性,并且快照设置合适的话,并发性也不差。

又不是严格的金融系统。。要么弄个 job ,过期的全删了,要么 where 条件永远带个时间条件就是了。。资源有的是的话弄个 redis...

余额会单独存储,每次买书同时更新余额和 30 条消费记录DB 性能很强的,现在的硬件太牛逼了

多几个字段,都是 32 个 0-9 ,分别表示个、十、百、千、万位,这样每天的数量取就没有上限了。

每天记录多加一个余额和状态字段. 消费前拉出所有可用余额, 找出要扣除的记录更新对应状态和余额就好了吧?

至于总余额 临时算或找个地方 cache 下. 每条记录的过期的话, 定时任务或找个用户查询/消费的时候进行一次统一计算就行.但用户也就是一次批量查询 + 一次批量更新. 计算量也不大