如何在Redis中实现两种交易累计统计的动态计数器?
Redis实现两种交易累计统计方案
针对自然周(周一至周日)和近7×24小时的交易次数、金额、天数统计需求,以下是基于Redis的落地实现方案:
一、基础数据存储设计
1. 实时交易数据存储(Sorted Set)
用Sorted Set存储用户每笔交易,利用时间戳作为排序依据,快速实现范围查询:
- Key格式:
user:trans:{uid}(如user:trans:u0001) - Score:交易时间的Unix时间戳(秒级,确保排序和范围筛选精度)
- Value:交易金额的整数形式(如
$21存为2100,以分为单位避免浮点数精度问题) - 写入命令:
ZADD user:trans:u0001 1726716916 2100 # 对应9-19 14:15:16的$21交易
2. 每日预聚合数据存储(Hash)
为了提升统计效率,按天预聚合交易数据,避免实时遍历大量交易:
- Key格式:
user:daily_stats:{uid} - Field格式:
{yyyyMMdd}:count(当日交易次数)、{yyyyMMdd}:amount(当日交易总金额) - 写入命令(每笔交易触发):
HINCRBY user:daily_stats:u0001 20240919:count 1 HINCRBY user:daily_stats:u0001 20240919:amount 2100
二、自然周累计统计实现
1. 确定统计时间范围
根据查询时间,计算所在自然周的时间边界:
- 起始时间:查询日期所在周的周一00:00:00的Unix时间戳
- 结束时间:查询日期所在周的周日23:59:59的Unix时间戳
2. 高效统计方案(基于预聚合Hash)
步骤1:生成本周所有日期列表
如查询时间为9-28(周三),本周日期为20240926、20240927、20240928、20240929、20240930、20241001、20241002
步骤2:批量获取每日统计数据
HMGET user:daily_stats:u0001 20240926:count 20240926:amount 20240927:count 20240927:amount ...
步骤3:计算最终结果
- 总交易次数:累加所有非空的
{date}:count值 - 总交易金额:累加所有非空的
{date}:amount值,再转换为元格式(如1800→$18.00) - 交易天数:统计存在非空统计值的日期数量
以示例查询为例,最终结果为:
Total times Total amount Total days 3 $18 2
三、近7×24小时累计统计实现
1. 确定统计时间范围
根据查询时间query_ts,计算时间边界:
- 起始时间:
query_ts - 7*86400(往前推7天的同一时刻) - 结束时间:
query_ts - 1(查询时间的前一秒,避免包含查询时刻的交易)
2. 高效统计方案(预聚合+实时查询结合)
将时间范围拆分为三部分分别统计,再合并结果:
- 完全包含的日期:如查询时间为9-28 15:00,9-22至9-27这6天完全在统计范围内,直接用预聚合Hash的统计值累加
- 起始日期的部分时段:9-21仅统计15:00之后的交易,用Sorted Set查询:
ZCOUNT user:trans:u0001 1726911600 1726995599 # 9-21 15:00到23:59:59的交易次数 ZRANGEBYSCORE user:trans:u0001 1726911600 1726995599 # 获取金额并求和 - 结束日期的部分时段:9-28仅统计00:00至14:59的交易,用Sorted Set查询:
ZCOUNT user:trans:u0001 1727481600 1727516399 # 9-28 00:00到14:59:59的交易次数 ZRANGEBYSCORE user:trans:u0001 1727481600 1727516399 # 获取金额并求和
合并结果
- 总次数:三部分次数相加
- 总金额:三部分金额相加后转换为元格式
- 交易天数:完全包含的有交易日期数 + 起始日期有交易则加1 + 结束日期有交易则加1
以示例查询为例,最终结果为:
Total times Total amount Total days 7 $45 5
四、数据过期与清理策略
- 实时交易Sorted Set:保留3个月数据,定期删除旧数据:
ZREMRANGEBYSCORE user:trans:u0001 -inf 1721020800 # 删除90天前的交易 - 每日预聚合Hash:保留1年数据,定期清理过期日期字段:
HSCAN user:daily_stats:u0001 0 MATCH *:count | grep -E "^[0-9]{8}:count$" | awk '{if ($1 < 20230928) print "HDEL user:daily_stats:u0001 "$1" "$1":amount"}' | redis-cli
内容的提问来源于stack exchange,提问作者Lizhi Liu
相关产品推荐
相关产品推荐

