某小时内任意时刻同时在线的最大连接数需将每连接生命周期拆解为小时重叠区间后统计每小时最大值,而非简单按小时分组计数;须补全连续小时序列并用范围连接匹配,避免临界连接漏算。

用 DATE_TRUNC 或 DATEPART 对连接时间做小时对齐
并发连接数不是简单计数,而是要还原「某小时内任意时刻同时在线的最大连接数」。核心思路是把每个连接的生命周期(connect_time 和 disconnect_time)拆解成按小时粒度的重叠区间,再统计每小时内的最大并发数。PostgreSQL 用 DATE_TRUNC('hour', connect_time) 可直接截断到小时起点;SQL Server 得用 DATEPART 拼接:DATEFROMPARTS(YEAR(t), MONTH(t), DAY(t), DATEPART(HOUR, t), 0, 0)。MySQL 8.0+ 支持 DATE_SUB(connect_time, INTERVAL MINUTE(connect_time) + SECOND(connect_time) SECOND),但更稳的方式是用 FLOOR(UNIX_TIMESTAMP(connect_time) / 3600) 转为小时戳——避免时区和日期函数行为差异带来的错位。
生成每小时的时间点序列并关联连接生命周期
不能只查原始表里有记录的小时,必须补全所有目标时间段内连续的小时点(否则空闲小时会消失)。常见做法是用递归 CTE 或数字表生成小时序列,再与连接日志做范围连接。例如 PostgreSQL 中:
WITH hours AS (
SELECT generate_series(
'2024-01-01 00:00'::timestamp,
'2024-01-02 00:00'::timestamp,
'1 hour'::interval
) AS hour_start
)
SELECT h.hour_start,
(SELECT COUNT(*)
FROM connections c
WHERE c.connect_time h.hour_start) AS concurrent_count
FROM hours h;
关键点:条件必须是 c.connect_time 且 <code>c.disconnect_time > h.hour_start,才能捕获在该小时区间内「至少重叠一瞬」的连接。漏掉等号或方向反了,就会少算临界连接。
避免用 COUNT(*) OVER (PARTITION BY ...) 直接分组
这是最常踩的坑:对原始连接记录按小时分组后 COUNT(*),得到的是「该小时新建连接数」,不是并发数。并发是动态叠加的过程,同一连接可能跨越多个小时,而一个用户在 13:59 连入、14:01 断开,它应同时计入 13 点和 14 点的并发基数。若强行用窗口函数或分组聚合,会丢失时间维度上的覆盖关系。必须把单条连接展开为它所影响的每一个小时槽位(哪怕只占几秒),再对每个槽位做计数。
性能瓶颈通常卡在范围连接和数据量上
当连接日志超百万行,上述范围 JOIN 会迅速变慢。优化路径有三条:
- 给
connect_time和disconnect_time建复合索引:CREATE INDEX idx_conn_time ON connections(connect_time, disconnect_time) - 预计算每条连接影响的起止小时戳(两个整型字段),用整数范围查询替代时间计算
- 改用事件驱动法:把连接和断开视为 +1/-1 事件,按时间排序后用变量累计,每小时取累计值最大值——这需要支持变量或窗口函数的数据库(如 MySQL 8.0+、PostgreSQL 14+)
真正难的不是写出语句,而是确认日志里 disconnect_time 是否可靠。很多系统只记连接时间,断开靠超时推断,这时峰值就只能估算——得先补全断开时间,再谈按小时统计。











