并发请求量指某时刻“同时活跃”的请求数,非时间段内总请求数;group by仅统计各时间粒度请求数,无法识别起止时间重叠,故不能直接使用。

什么是并发请求量,为什么不能直接用 GROUP BY
并发请求量指的是在某个时间点上“同时活跃”的请求数量,不是某段时间内的总请求数。比如 10:00:01 开始的请求持续到 10:00:05,它在 10:00:02、10:00:03、10:00:04 这三个时刻都算“正在运行”。GROUP BY 只能统计每个时间粒度(如每秒)的请求数量,但无法体现“重叠”——它把开始时间和结束时间当成了独立事件。
用 UNION ALL 拆解起止事件 + SUM() OVER (ORDER BY ...) 累计变化
核心思路是把每个请求拆成两条记录:一条标记“+1”(开始),一条标记“-1”(结束后一时刻),然后按时间排序累加。这样任意时刻的累计值就是当前并发数。
- 必须对
end_time加 1(或使用微秒偏移),否则同一时刻的结束会提前抵消开始,导致低估 - 时间字段需统一为
TIMESTAMP或INT(如 Unix 时间戳),避免字符串比较出错 -
ORDER BY event_time, delta中delta排序很重要:让 +1 优先于 -1,保证“同一时刻先增后减”
SELECT event_time, SUM(delta) OVER (ORDER BY event_time, delta DESC) AS concurrent_count FROM ( SELECT start_time AS event_time, 1 AS delta FROM requests UNION ALL SELECT end_time + INTERVAL '1 second' AS event_time, -1 AS delta FROM requests ) AS events
如何按固定时间窗口(如每秒)输出并发峰值
上面的结果是“事件驱动”的稀疏时间点,但业务常需要每秒一个值(哪怕没事件发生)。这时得生成时间序列再左连接。
- PostgreSQL 可用
GENERATE_SERIES();MySQL 8.0+ 需用递归 CTE 或预建时间表 - 连接时用
time_series.ts >= event_time并取最近的前一行,或改用LAG()+COALESCE()填充空缺 - 注意:直接
LEFT JOIN后SUM() OVER会重复计算,必须先聚合事件再关联
性能关键点:索引和数据量控制
窗口函数本身不慢,但事件表膨胀快——10 万请求会变成 20 万行;若要每毫秒精度,数据量指数上升。
- 务必在
start_time和end_time上建复合索引:INDEX (start_time, end_time) - 生产环境建议先按业务周期(如按天)切分查询,避免全表扫描
- 如果只关心“最大并发数”,可用
MAX(SUM(...) OVER (...))而非物化全部中间结果
真正难的不是写对窗口函数,而是定义清楚“并发”的业务语义:是否包含正在排队的请求?超时未响应的要不要剔除?这些逻辑一旦嵌套进事件模型,很容易漏掉边界 case。











