横扫SQL面试——事件流处理(峰值统计)问题

news/2025/4/1 2:25:11/

横扫SQL面试

📌 事件流处理(峰值统计)问题

在这里插入图片描述

“会议室预定冲突怎么查? 🔍 服务器瞬时负载如何算?🎢 健身房的‘人挤人’高峰究竟出现在几点?🏃‍♂️”

这些看似毫不相干的问题,在SQL面试中却共享着同一套解题框架——事件流处理中的峰值统计问题

这类问题要求你从时间重叠的事件流中🕐,快速找到某个指标的最大并发数(如同时在线人数、并发请求数、亮灯数量等),是数据岗高频面试,也是区分SQL“背题党”与“真高手”的试金石。

许多初学者的第一反应是逐行遍历时间点暴力统计,但面对千万级数据时,这种解法会直接让数据库“原地爆炸”。 💻🚀

而真正的优雅解法,只需一行窗口函数+时间轴思维,就能以O(n log n)的时间复杂度瞬间锁定答案。

话不多说——直接上题:🎈🎈🎈🎈🎈🎈🎈


一、泳池人流量峰值统计🏊‍♂️🏊‍♂️🏊‍♂️

根据用户进出记录表,统计游泳池单日内最大同时在泳池人数对应时刻。(千万数据量级~)

字段名数据类型描述
user_id整数用户的唯一标识,用于区分不同用户
flag字符串标记用户的行为,取值为“enter”表示进入游泳池,“leave”表示离开游泳池
time日期时间型记录用户进入或离开游泳池的具体时间,格式为“YYYY-MM-DD HH:MM:SS”,这里所有时间均属于同一天(2024-04-07 )
user_idflagtime
1enter2024-04-07 08:01:00
2enter2024-04-07 08:20:00
1leave2024-04-07 09:30:00
3enter2024-04-07 10:01:00
4enter2024-04-07 10:25:00
2leave2024-04-07 11:22:00
4leave2024-04-07 12:20:00
5enter2024-04-07 12:30:00
6enter2024-04-07 13:05:00
3leave2024-04-07 14:20:00
5leave2024-04-07 14:30:00
7enter2024-04-07 14:45:00
8enter2024-04-07 15:18:00
7leave2024-04-07 15:32:00
6leave2024-04-07 15:45:00
9enter2024-04-07 16:01:00
10enter2024-04-07 16:10:00
9leave2024-04-07 16:25:00
8leave2024-04-07 16:35:00
10leave2024-04-07 16:55:00

核心思路:事件流扫描法

  1. 事件标记:将enter标记为+1,leave标记为-1 ——生成事件流

在这里插入图片描述

  1. 时间轴扫描:按时间排序后计算累积在线人数

在这里插入图片描述

  1. 特殊处理:相同时间点时,优先处理leave事件(避免虚增人数)

在这里插入图片描述

🔍 为什么优先处理离开事件?

在事件流处理中,同一时间点的进入(+1)和离开(-1)事件的顺序会直接影响峰值计算结果

假设泳池在 08:00:00 同时发生两个事件:

  • 用户A离开flag='leave'
  • 用户B进入flag='enter'

若处理顺序不同,计算结果会如何变化?


情况1:优先处理进入事件(错误示例)
事件时间事件类型当前人数变化实时累积人数
08:00:00+1+110 → 11
08:00:00-1-111 → 10

问题:在 08:00:00 时刻,系统会记录到瞬时人数为 11(虚增峰值),但实际上用户A离开和用户B进入是同时发生,泳池人数应保持 10 不变🤣🤣。


情况2:优先处理离开事件(正确做法)
事件时间事件类型当前人数变化实时累积人数
08:00:00-1-110 → 9
08:00:00+1+19 → 10

结果:在 08:00:00 时刻,实时人数正确地从 10 → 9 → 10,未产生虚增的峰值


  • 离开事件优先:认为在某一时刻,用户会先离开泳池,再允许新用户进入。这是对现实场景的合理抽象(例如:闸机需先关闭后开启)。
  • 若反序处理,相当于假设“用户在进入的同时还未离开”,导致逻辑矛盾。

SQL实现关键
在排序时,按时间升序,且同时间下事件类型(type)升序ORDER BY time, type),使 -1(离开)排在 +1(进入)之前:

sql">select time,-- 窗口函数累加变化值,实现实时人数统计sum(change) over (order by time, change) as current_people  /* 排序规则说明:order by time 保证按时间顺序处理order by change 保证同时间时先处理-1(离开),再处理+1(进入)*/from events

如果同一时间点有多个离开和进入事件,如何处理?

  • 优先级顺序-1(离开)始终先于 +1(进入),无论事件数量多少。
  • 示例:时间点 08:00:00 的事件顺序为 [-1, -1, +1, +1],计算过程如下:
    初始值: 10
    -1 → 9
    -1 → 8
    +1 → 9
    +1 → 10
    
    该时刻的峰值统计值为 8(离开事件的中间状态),而非最终的 10。这确保统计的是时间点内的最低值,避免虚高。

优先处理离开事件,本质是严格区分时间边界,确保计算的峰值不因事件顺序产生误差。这种处理方式符合现实逻辑(物理空间进出顺序),且能避免数据层面的统计失真。🍬🍬


sql">-- 步骤1:将进出记录转换为事件流(+1表示进入,-1表示离开)
with events as (select time, 1 as change from pool where flag = 'enter'  -- 进入事件标记为+1union all  -- 拼接大法!!!qaqselect time, -1 as change from pool where flag = 'leave' -- 离开事件标记为-1
),-- 步骤2:按时间排序(同时间时优先处理离开事件),并计算实时人数
sorted_events as (select time,-- 窗口函数累加变化值,实现实时人数统计sum(change) over (order by time, change) as current_people  /* 排序规则说明:order by time 保证按时间顺序处理order by change 保证同时间时先处理-1(离开),再处理+1(进入)*/from events
)-- 步骤3:找出人数最多的时刻 (需要排序 处理海量数据可能会效率低一些)
select time as peak_time,          -- 峰值时刻current_people as max_people  -- 最大同时在场人数
from sorted_events
order by current_people desc,  -- 按人数降序排列(最大值排第一)time                  -- 相同人数时取最早出现的时间
limit 1;  -- 取第一条记录(即最大同时人数及其对应时刻)

上面最后一步 order by 需要排序 处理海量数据可能会效率低一些。

数据量决定优化策略——对于事件流类问题(泳池、灯泡、服务器负载等),当数据规模达到百万级以上时,优先使用分步计算最大值的写法,避免全表排序瓶颈! 🚀

在这里插入图片描述

sql">-- 步骤3:单独获取最大人数(避免全表排序)
max_people as (select max(current_people) as max_people  -- 直接计算最大值,时间复杂度 O(n)from sorted_events
)-- 步骤4:关联结果并取最早峰值时刻
select time as peak_time,max_people as max_people
from sorted_events, max_people
where sorted_events.current_people = max_people.max_people  -- 仅筛选等于最大值的行
order by time  -- 只需对少量数据排序(假设峰值时刻较少)
limit 1;

优化点性能提升原因
分离最大值计算max(current_people) 只需一次全表扫描(O(n)),远快于全表排序(O(n log n))
减少排序数据量仅对 current_people = max_people 的行按时间排序,数据量通常极低(如单条)
索引加速(可选)sorted_eventscurrent_people 有索引,where 条件可快速定位
方法时间复杂度适用场景
直接排序取第一条O(n log n)小数据量(千级以下)
分步计算最大值O(n) + O(k)大数据量(百万级以上,k为峰值时刻数量)

  1. 小数据量(<10万行)
    直接使用 order by current_people desc, time limit 1,代码更简洁。

  2. 大数据量(≥100万行)或实时计算
    使用分步计算最大值的方法,性能提升显著。若 current_people 字段有索引,效果更佳。

  3. 极端数据(如全天峰值仅一个时刻)
    两种方法性能差异可忽略,优先保证代码可读性。

❤tips:个人习惯用 with公共表达式和关键字小写🤣~ 面试只要思路对 语法细节什么的面试官不会太过深究的哈~



二、千万级灯泡亮灯峰值统计

工厂灯泡亮灭记录中,找到同时亮灯数量最多的时刻及数量。(千万数据量级~)

字段名描述数据类型
id灯泡唯一标识INT
亮灯时间亮灯起始时间DATETIME
灭灯时间亮灯结束时间DATETIME
id亮灯时间灭灯时间
12024-01-01 08:00:002024-01-01 09:00:00
22024-01-01 08:30:002024-01-01 09:30:00

看到这里 想必大家对事件流 套路已经很清晰了🤣🤣🤣

1:生成事件流

在这里插入图片描述

2:时空扫描计算

在这里插入图片描述

3:获取峰值时刻
在这里插入图片描述

sql">-- 步骤1:生成亮灭事件流(+1表示亮灯,-1表示灭灯)
with events as (select 亮灯时间 as event_time, 1 as type   -- 亮灯事件标记为+1from 灯泡信息表union all                                   -- 合并亮灭事件select 灭灯时间 as event_time, -1 as type  -- 灭灯事件标记为-1from 灯泡信息表
),-- 步骤2:按时间排序(同时间时优先处理灭灯事件),并计算实时亮灯数
sorted_events as (select event_time,-- 窗口函数累加变化值,统计实时亮灯数量sum(type) over (order by event_time, type) as current_count/* 排序规则说明:order by event_time 保证按时间顺序处理order by type       保证同时间时先处理-1(灭灯),再处理+1(亮灯)避免虚增同时亮灯数量 */from events
),-- 步骤3:获取最大亮灯数量(可能多个时间点达到相同最大值)
max_count as (select max(current_count) as max_count  -- 找出最大的亮灯数量from sorted_events
)-- 步骤4:筛选并输出结果(取第一个达到最大值的时间点)
select event_time as `同时亮灯数量最多的时刻`,max_count as `同时亮灯数量`
from sorted_events
join max_count on sorted_events.current_count = max_count.max_count  -- 关联最大值
order by event_time  -- 多个峰值时间时取最早出现的
limit 1;  -- 保证输出唯一结果

🔍 事件流处理本质

操作泳池问题灯泡问题核心价值
事件类型enter(+1)/leave(-1)亮灯(+1)/灭灯(-1)将连续行为转化为离散事件
排序规则时间升序,leave优先时间升序,灭灯优先避免同一时刻的虚增/虚减
计算逻辑累积和(current_people)累积和(current_count)实时统计并发状态
性能表现O(n log n)O(n log n)支持千万级数据的高效计算

Tips:

  1. 异常数据处理

    • 未离开记录:COALESCE(leave_time, '23:59:59') 设置默认值
    • 时间重叠校验:亮灯时间 < 灭灯时间
  2. 分布式优化

    sql">/* 使用Hive分桶计算 */
    SET hive.enforce.bucketing = true;
    CREATE TABLE events_bucketed 
    CLUSTERED BY (event_time) INTO 24 BUCKETS 
    AS SELECT * FROM events;
    
  3. 实时监控改造

    sql">/* Flink SQL流式处理 */
    SELECT  TUMBLE_END(event_time, INTERVAL '1' SECOND) AS window_end,SUM(type) OVER (ORDER BY event_time) AS current_count
    FROM events
    GROUP BY TUMBLE(event_time, INTERVAL '1' SECOND);
    

不同业务场景共享同一套事件流处理范式,后续遇到类似事件流处理的问题

本专栏会持续更新~ 欢迎大家 订阅 关注 ~😊😊😊

在这里插入图片描述


http://www.ppmy.cn/news/1584072.html

相关文章

【持续集成和持续部署】

大致流程&#xff1a; 提交代码--拉取下来新代码并自动构建与部署--应用接口探活--执行自动化测试--输出自动化测试报告 一、持续集成&#xff08;Continuous Integration&#xff0c;CI&#xff09; 持续集成是一种软件开发实践&#xff0c;开发团队成员频繁地将代码集成到…

react native 0.72.5集成react-navigation

新项目需要集成react navigation直接集成了react navigation最新版本7.x安卓运行项目的时候遇到报错generateCodegenArtifactsFromSchema Failed排查问题原因&#xff1a;发现node_modules/react-native/codegen/package.json里面的version是0.77.0&#xff08;当前时刻最新的R…

【Uni-App】嵌入悬浮球全局组件的详细教程和防踩坑点

一、前言 近期接到一个业务需求是为为h5页面嵌入悬浮球&#xff0c;而且还得是全局化&#xff08;这样所有页面就可以看见了&#xff09;&#xff0c;在开发的过程中也遇到一些坑&#xff0c;在此和大家一起学习、讨论。 二、准备工作 下载悬浮球组件代码 悬浮球 - DCloud …

【10分钟掌握Docker、k8s的基础命令】

文章目录 前言一、Docker1.1 docker操作命令1.2 docker查看命令1.3 docker实战 二、kubernetes2.1 k8s基础命令2.2 k8s简单操作命令 前言 Docker介绍 Docker 是一个开源的应用容器引擎&#xff0c;基于 Go 语言 并遵从 Apache2.0 协议开源。 Docker 可以让开发者打包他们的应用…

批量将多个彩色的 PDF 转换为黑白色

我们在编辑 PDF 文档的时候&#xff0c;可以给 PDF 文档插入各种样式的图片&#xff0c;我们也可以给 PDF 文档中文字设置各种颜色的样式。这种 PDF 文档就是彩色的 PDF 文档。但是在某些场景下&#xff0c;比如打印需要我们可能想把彩色的 PDF 文档转换为黑白的 PDF 文档后再打…

51c嵌入式~三极管~合集1

我自己的原文哦~ https://blog.51cto.com/whaosoft/12208603 一、PNP与NPN两种三极管使用方法 分享这篇文章总结下关于NPN和PNP两种型号三极管的使用和连接方法。 在单片机应用电路中三极管主要的作用就是开关作用。 PNP与NPN两种三极管使用方法 上图中&#xff0c;横向左…

Apache Shiro 统一化实现多端登录(PC端移动端)

Apache Shiro 是一个强大且易用的Java安全框架&#xff0c;提供了身份验证、授权、密码学和会话管理等功能。它被广泛用于保护各种类型的应用程序&#xff0c;包括Web应用、桌面应用、RESTful服务、移动端应用和大型企业级应用。 需求背景 在当今数字化浪潮的推动下&#xff…

华为单臂路由 与 策略路由

单臂路由 概念&#xff1a;如图大纲LSW1是二层交换机不具备三层路由功能&#xff0c;并且连接着不同的vlan&#xff0c;并使每个vlan都可以与外网通信&#xff0c;那么就要用单臂路由技术&#xff0c;原理是将图中的AR1的ge0/0/0接口逻辑上分为多个子接口&#xff0c;分别对应…