有一张表里面有三个字段,分别是(id,开始时间,结束时间),表中数据量为 5000W,如何统计流量最大的时候有多少条数据?
- 标签:场景题
🏛 业务架构师
拿到这个需求我第一件事不是写 SQL,而是问清业务目的:这是给带宽容量规划做一次性分析,还是要常态化实时监控?两者方案和成本完全不同。
如果是一次性/离线分析(大多数场景)
5000W 数据在现代大数据平台上根本不算大,一条 Hive/Spark SQL 就能出结果,思路是经典的扫描线:
- 把每条记录拆成两个事件:
(开始时间, +1)、(结束时间, -1) - 按时间排序,做前缀和,前缀和的最大值就是最大同时在线/最大流量
sql
SELECT max(cnt) FROM (
SELECT sum(type) OVER (ORDER BY ts) AS cnt
FROM (
SELECT start_time AS ts, 1 AS type FROM t
UNION ALL
SELECT end_time AS ts, -1 AS type FROM t
) e
) s;时间复杂度 O(N log N),交给分布式计算几分钟跑完。
如果要常态化实时监控
说明业务已经到了需要按分钟级观测资源峰值的水位(比如带宽计费、license 并发数),那应该走流式方案:Flink 窗口内维护增减事件,实时输出峰值曲线,并配告警。
我会主动提醒的成本权衡:如果业务方只是偶尔看一次,上一套实时计算集群是典型的过度设计,离线 T+1 加个定时任务就够了。数据方案跟着业务决策频率走,而不是跟着数据量走。
🏗 系统架构师
这是典型的最大重叠区间问题:任意时刻的最大并发数。我不会去逐秒扫描表,而是把每条记录拆成两个事件点。
算法核心 把 5000W 行拆成 (start_time, +1) 和 (end_time, -1) 共 1 亿个事件,按时间排序后做前缀和,前缀和的历史最大值就是答案,复杂度 O(n log n)。
容量估算(决定单机还是分布式) 1 亿个事件,每条按 (long 时间戳, int ±1) 共 12 字节,原始数据约 1.2GB,排序索引翻倍也在 5GB 以内——64G 内存的机器单机就能做,不需要上大数据平台。这是架构师先算再动手的习惯:很多"大数据问题"算完存储就变成单机问题。
执行方案分档
- 数据在 MySQL:不要
SELECT *拖库,按 start/end 两列各建覆盖索引,游标分批拉出(每批 10 万),流式生成事件写外部排序临时文件,或直接ORDER BY分段归并。 - 有数仓/Spark:一条 SQL 即可:
sql
SELECT max(cnt) FROM (
SELECT sum(delta) OVER (ORDER BY ts) AS cnt
FROM (
SELECT start_time AS ts, 1 AS delta FROM t
UNION ALL
SELECT end_time AS ts, -1 AS delta FROM t
) e
) s;- 如果要求实时统计最大在线数,那就是 Flink 开窗 + 状态聚合的流式方案,成本完全不同,我会先和业务确认是离线一次性还是持续输出。
💻 全栈开发专家
这是经典的「区间最大重叠」问题,我实际做过类似的带宽统计。思路是把每条记录拆成两个事件:开始时间 +1、结束时间 -1,然后按时间排序做前缀和,前缀和的最大值就是峰值。
单表 5000W,直接让 MySQL 干
sql
SELECT t, SUM(delta) OVER (ORDER BY t) AS cur, MAX(SUM(delta) OVER (ORDER BY t))
FROM (
SELECT start_time AS t, 1 AS delta FROM records
UNION ALL
SELECT end_time AS t, -1 AS delta FROM records
) e;窗口函数一亿行排序对 MySQL 压力大,我会加 EXPLAIN 确认是否走临时表落盘,必要时调 tmp_table_size、sort_buffer。
工程上我更推荐两种落法
- 程序流式处理:
SELECT start_time, 1 FROM t UNION ALL SELECT end_time, -1 FROM t ORDER BY 1用游标流式读(fetchSize=Integer.MIN_VALUE),Java 里单变量累加求峰值,内存 O(1),只是排序仍压在 DB。 - 外部排序:把时间事件导出成文件,分块排序后多路归并(小顶堆),完全不碰生产库,适合从库资源紧张时。
常见坑
- 开始/结束时间相同的点,-1 要排在 +1 前面还是后面要先和业务对齐(闭区间还是半开区间),不然峰值差 1。
- 时间精度不齐(秒 vs 毫秒)要先归一化。
- 如果只要峰值不要求时刻,还可以按小时/分钟聚合成桶,用小表统计,牺牲一点精度换速度。
这个题本质考的是扫描线思想 + 大数据量下「计算下推还是拉出来算」的取舍。