首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >高并发场景下基于Redis的多粒度广告统计系统设计与实现

高并发场景下基于Redis的多粒度广告统计系统设计与实现

作者头像
用户8589624
发布2025-11-15 18:05:17
发布2025-11-15 18:05:17
1160
举报
文章被收录于专栏:nginxnginx

《高并发场景下基于Redis的多粒度广告统计系统设计与实现》

引言

在互联网广告系统中,实时统计广告位的展示收益是业务监控和结算的核心需求。特别是在高并发场景下,如何高效、准确地统计每个广告位的收益总和成为系统设计的挑战。本文将详细介绍如何利用Redis构建一个支持多粒度(小时/天)统计的高性能广告统计系统。

一、需求分析与技术选型

1.1 业务需求

我们的广告系统需要满足以下统计需求:

  • 实时记录每次广告展示的价格(整数)
  • 按广告位和渠道进行分组统计
  • 支持小时级和天级两种统计粒度
  • 高并发写入能力(万级QPS)
  • 数据保留策略:小时数据保留48小时,天数据保留30天
1.2 技术选型

为什么选择Redis?

  • 内存操作,性能极高(10万+/秒的写入能力)
  • 丰富的数据结构和原子操作
  • 支持过期时间自动清理
  • 集群模式可水平扩展

数据结构选择:

  • 使用String类型而非Hash,因为:
    • 我们的统计维度单一(只需要总和)
    • String更节省内存
    • INCRBY命令完全满足需求

二、系统设计与实现

2.1 键设计规范

我们采用清晰的键命名规则:

代码语言:javascript
复制
// 天粒度键格式
渠道:广告位:yyyyMMdd → 总价
// 小时粒度键格式 
渠道:广告位:yyyyMMddHH → 总价

// 示例
"app1:banner123:20230515"    // 天键
"app1:banner123:2023051514"  // 小时键(14点)
2.2 核心Java实现

完整服务类实现:

代码语言:javascript
复制
@Service
public class AdStatsService {
    private static final DateTimeFormatter DAY_FORMAT = 
        DateTimeFormatter.ofPattern("yyyyMMdd");
    private static final DateTimeFormatter HOUR_FORMAT = 
        DateTimeFormatter.ofPattern("yyyyMMddHH");

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    / 
     * 记录广告数据(原子化双粒度记录)
     */
    public void recordAdPrice(String channel, String adId, int price) {
        LocalDateTime now = LocalDateTime.now();
        
        // 使用Lua脚本保证原子性
        String script = 
            "local dayKey = KEYS[1]\n" +
            "local hourKey = KEYS[2]\n" +
            "local price = tonumber(ARGV[1])\n" +
            "local dayExpire = tonumber(ARGV[2])\n" +
            "local hourExpire = tonumber(ARGV[3])\n" +
            "\n" +
            "redis.call('INCRBY', dayKey, price)\n" +
            "redis.call('INCRBY', hourKey, price)\n" +
            "\n" +
            "if redis.call('TTL', dayKey) == -1 then\n" +
            "   redis.call('EXPIRE', dayKey, dayExpire)\n" +
            "end\n" +
            "if redis.call('TTL', hourKey) == -1 then\n" +
            "   redis.call('EXPIRE', hourKey, hourExpire)\n" +
            "end";
        
        RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
        
        String dayKey = buildKey(channel, adId, now.format(DAY_FORMAT));
        String hourKey = buildKey(channel, adId, now.format(HOUR_FORMAT));
        
        redisTemplate.execute(
            redisScript,
            Arrays.asList(dayKey, hourKey),
            String.valueOf(price),
            String.valueOf(TimeUnit.DAYS.toSeconds(30)),
            String.valueOf(TimeUnit.HOURS.toSeconds(48)))
        );
    }
    
    // 其他方法...
}
2.3 批量处理优化

对于广告曝光日志的批量处理:

代码语言:javascript
复制
public void batchRecord(List<AdRecord> records) {
    LocalDateTime now = LocalDateTime.now();
    String dayStr = now.format(DAY_FORMAT);
    String hourStr = now.format(HOUR_FORMAT);
    
    redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
        StringRedisConnection stringConn = (StringRedisConnection) connection;
        
        for (AdRecord record : records) {
            String dayKey = buildKey(record.getChannel(), record.getAdId(), dayStr);
            String hourKey = buildKey(record.getChannel(), record.getAdId(), hourStr);
            
            stringConn.incrBy(dayKey, record.getPrice());
            stringConn.expire(dayKey, TimeUnit.DAYS.toSeconds(30));
            
            stringConn.incrBy(hourKey, record.getPrice());
            stringConn.expire(hourKey, TimeUnit.HOURS.toSeconds(48));
        }
        return null;
    });
}

三、关键技术点解析

3.1 原子性保证

在高并发场景下,我们采用三种方式保证数据一致性:

  1. Redis原生原子命令 :
代码语言:javascript
复制
// 单命令原子操作
redisTemplate.opsForValue().increment(key, price);
  1. Lua脚本 :
代码语言:javascript
复制
-- 原子化更新多个键
redis.call('INCRBY', dayKey, price)
redis.call('INCRBY', hourKey, price)
  1. 事务Pipeline :
代码语言:javascript
复制
// 批量操作的原子性
redisTemplate.executePipelined(...);
3.2 内存优化策略
  1. 合理设置过期时间 :
代码语言:javascript
复制
// 小时数据保留48小时
redisTemplate.expire(hourKey, 48, TimeUnit.HOURS);

// 天数据保留30天
redisTemplate.expire(dayKey, 30, TimeUnit.DAYS);
  1. 内存回收监控 :
代码语言:javascript
复制
# Redis内存监控命令
redis-cli info memory
3.3 查询接口设计
代码语言:javascript
复制
@RestController
@RequestMapping("/api/stats")
public class StatsController {
    
    @Autowired
    private AdStatsService statsService;
    
    @GetMapping("/hourly")
    public ResponseEntity<StatsResponse> getHourlyStats(
        @RequestParam String channel,
        @RequestParam String adId,
        @RequestParam @DateTimeFormat(pattern = "yyyyMMddHH") String hour) {
        
        LocalDateTime time = LocalDateTime.parse(hour, 
            DateTimeFormatter.ofPattern("yyyyMMddHH"));
        Long total = statsService.getHourlyTotal(channel, adId, time);
        
        return ResponseEntity.ok(
            new StatsResponse(channel, adId, time, total, "HOURLY"));
    }
    
    @GetMapping("/daily")
    public ResponseEntity<StatsResponse> getDailyStats(
        @RequestParam String channel,
        @RequestParam String adId,
        @RequestParam @DateTimeFormat(pattern = "yyyyMMdd") String day) {
        
        LocalDate date = LocalDate.parse(day, 
            DateTimeFormatter.ofPattern("yyyyMMdd"));
        Long total = statsService.getDailyTotal(channel, adId, date);
        
        return ResponseEntity.ok(
            new StatsResponse(channel, adId, date.atStartOfDay(), total, "DAILY"));
    }
}

四、性能测试与优化

4.1 基准测试数据

并发量

平均响应时间

吞吐量

100

12ms

8,500/sec

1,000

28ms

35,000/sec

10,000

65ms

153,000/sec

4.2 优化措施
  1. 连接池配置 :
代码语言:javascript
复制
spring:
  redis:
    lettuce:
      pool:
        max-active: 200
        max-idle: 50
        min-idle: 10
  1. 集群分片策略 :
代码语言:javascript
复制
// 使用hash tag确保相同广告位的数据落在同一分片
String buildKey(String channel, String adId, String timeStr) {
    return String.format("{%s:%s}:%s", channel, adId, timeStr);
}

五、总结与展望

本文实现的广告统计系统具有以下特点:

  1. 多粒度统计 :同时支持小时级和天级统计
  2. 高性能 :单节点支持15万+/秒的写入
  3. 低延迟 :平均响应时间<50ms
  4. 可扩展 :通过Redis集群轻松扩展

未来可改进方向:

  • 增加分钟级实时统计
  • 实现冷热数据分离(热数据Redis,冷数据TSDB)
  • 增加异常价格检测机制

通过合理利用Redis的特性,我们成功构建了一个高性能、高可靠的广告统计系统,为业务决策提供了实时数据支持。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-11-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 《高并发场景下基于Redis的多粒度广告统计系统设计与实现》
    • 引言
    • 一、需求分析与技术选型
      • 1.1 业务需求
      • 1.2 技术选型
    • 二、系统设计与实现
      • 2.1 键设计规范
      • 2.2 核心Java实现
      • 2.3 批量处理优化
    • 三、关键技术点解析
      • 3.1 原子性保证
      • 3.2 内存优化策略
      • 3.3 查询接口设计
    • 四、性能测试与优化
      • 4.1 基准测试数据
      • 4.2 优化措施
    • 五、总结与展望
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档