用户积分系统怎么设计
- 积分总表(User_Point_Wallet)
作用: 相当于"钱包",只存总额,用于快速读取(展示给用户看)。
java
CREATE TABLE user_point_wallet (
user_id BIGINT PRIMARY KEY,
total_balance INT DEFAULT 0, -- 当前可用总积分
version INT DEFAULT 0, -- 乐观锁版本号
update_time DATETIME
);
- 积分流水表(Point_Flow_Log)
作用: 相当于"银行流水",记录每一笔增减操作,不可修改,用于对账。
java
CREATE TABLE point_flow_log (
flow_id BIGINT PRIMARY KEY,
user_id BIGINT,
amount INT, -- 变动金额(+100 或 -20)
type TINYINT, -- 类型:1-签到,2-购物,3-兑换,4-过期
ref_id VARCHAR(64), -- 关联业务单号
create_time DATETIME
);
- 积分明细/分桶表(Point_Detail_Bucket)
作用: 记录每一笔入账积分的余额和过期时间。
java
CREATE TABLE point_detail_bucket (
id BIGINT PRIMARY KEY,
user_id BIGINT,
initial_amount INT, -- 初始入账金额(如 100)
current_balance INT, -- 当前剩余金额(初始 100,消费后变 80)
expire_time DATETIME, -- 过期时间(决定了它的生死)
status TINYINT, -- 0-有效,1-已用完,2-已过期
INDEX idx_user_expire (user_id, expire_time) -- 关键索引:按过期时间排序
);
消费时,基于这张表做 FIFO 扣减。
当用户消费 20 分时:
查询 point_detail_bucket,按 expire_time ASC 排序。
找到第一条快过期的记录(1月入账的),current_balance 够扣就扣,不够就扣完这条再找下一条(递归扣减)。
更新 user_point_wallet 总数。 这就是标准的"账本拆分"逻辑。
怎么高效提醒用户积分快过期了?"
错误解法:实时扫描
每天扫数据库 WHERE expire_time = 明天。 死穴: 5 亿用户扫不动。而且如果用户今天把积分花光了,你明天还发短信说"你有积分快过期",用户会觉得你系统有 Bug。
王者解法:离线计算 + 惰性清理
- 提醒策略(T+1 离线计算)
针对 5 亿这种体量,别做实时提醒,成本太高且没必要。
方案: 利用大数据平台(Hive/Spark)。
逻辑: 每天凌晨,把 point_detail_bucket 的快照同步到数仓。在数仓里跑一个 SQL,算出"未来 7 天过期的 Bucket 总额 > 0"的用户清单。
触达(MQ 削峰): 拿到清单后,千万别直接调短信接口!5 亿用户哪怕只有 1% 过期,也是 500 万条短信。必须引入 MQ 进行削峰填谷,控制发送速率(如 5000 QPS),避免早高峰把短信网关打挂。
备选方案(轻量级): 如果公司没有大数据基建,可以退回到 '过期日历表' 方案。建一张表按"过期日期"聚合用户 ID,每天扫描。虽然维护成本高一点,但胜在不需要维护 Hadoop 集群,适合中小体量。
- 清理策略(惰性 + 定时)
不要每天去 DB 里执行 UPDATE point_detail_bucket SET status = 过期。
读时触发: 用户查积分时,前端根据 expire_time 过滤掉已过期的,展示"有效余额"。
写时触发: 用户消费时,后端过滤掉过期的 Bucket,只扣有效的。
物理归档: 针对长期不活跃的"垃圾数据",后台低频 Job 慢慢搬运到历史冷库。