如何查询聊天室用户数量峰值时间段?MySQL/ClickHouse方案探讨
找出聊天室用户最多时段的高效查询方案
MySQL 实现方案
核心思路
通过拆分用户的进入/离开事件,计算每个时间点的累计在线人数,再定位在线人数峰值的连续时间段:
- 拆分所有时间节点:将
enter_time标记为用户增加事件(+1),leave_time标记为用户减少事件(-1); - 按时间排序,用窗口函数计算累计在线人数;
- 找出最大在线人数,合并连续的峰值时间段。
完整查询SQL
WITH event_points AS ( -- 拆分用户进入事件 SELECT enter_time AS event_time, 1 AS user_change FROM user_log UNION ALL -- 拆分用户离开事件 SELECT leave_time AS event_time, -1 AS user_change FROM user_log ), cumulative_users AS ( -- 计算每个时间点的累计在线人数 SELECT event_time, SUM(user_change) OVER (ORDER BY event_time) AS online_users, -- 获取下一个时间点的在线人数 LEAD(SUM(user_change) OVER (ORDER BY event_time)) OVER (ORDER BY event_time) AS next_online_users FROM event_points WHERE DATE(event_time) = '2022-09-10' -- 指定查询日期 ORDER BY event_time ), max_online AS ( -- 获取当日最大在线人数 SELECT MAX(online_users) AS max_users FROM cumulative_users ) -- 合并连续的峰值时间段 SELECT MIN(event_time) AS busy_time_start, MAX(next_event_time) AS busy_time_end FROM ( SELECT event_time, LEAD(event_time) OVER (ORDER BY event_time) AS next_event_time, online_users, -- 分组标记:在线人数从峰值下降时开启新组 SUM(CASE WHEN online_users = (SELECT max_users FROM max_online) AND next_online_users = (SELECT max_users FROM max_online) THEN 0 ELSE 1 END) OVER (ORDER BY event_time) AS group_id FROM cumulative_users WHERE online_users = (SELECT max_users FROM max_online) ) t GROUP BY group_id ORDER BY busy_time_start;
结果说明
针对提供的测试数据,执行后会输出:
busy_time_start | busy_time_end -------------------------------------------- 2022-09-10 04:10:00 | 2022-09-10 05:59:00 2022-09-10 06:05:00 | 2022-09-10 08:59:00
(注:原示例中的2022-09-10T03:10:00Z为误差,实际测试数据中04:10后才达到5人在线的峰值)
ClickHouse 效率对比
如果数据量达到百万级以上,迁移到ClickHouse会显著提升查询效率:
- 列式存储与并行计算:ClickHouse针对分析场景优化,能快速处理大规模时间序列数据;
- 原生区间聚合支持:可通过
intervalJoin或事件拆分法高效计算区间重叠数,性能远优于MySQL; - 低延迟响应:适合实时/准实时的在线人数峰值分析场景。
ClickHouse 示例查询
WITH event_points AS ( SELECT enter_time AS event_time, 1 AS user_change FROM user_log UNION ALL SELECT leave_time AS event_time, -1 AS user_change FROM user_log ), cumulative_users AS ( SELECT event_time, sum(user_change) OVER (ORDER BY event_time) AS online_users, lead(sum(user_change) OVER (ORDER BY event_time)) OVER (ORDER BY event_time) AS next_online_users FROM event_points WHERE toDate(event_time) = '2022-09-10' ORDER BY event_time ), max_online AS ( SELECT max(online_users) AS max_users FROM cumulative_users ) SELECT min(event_time) AS busy_time_start, max(next_event_time) AS busy_time_end FROM ( SELECT event_time, lead(event_time) OVER (ORDER BY event_time) AS next_event_time, online_users, sumIf(1, online_users = max_users AND next_online_users != max_users) OVER (ORDER BY event_time) AS group_id FROM cumulative_users, max_online WHERE online_users = max_users ) GROUP BY group_id ORDER BY busy_time_start;
内容的提问来源于stack exchange,提问作者Simon Bullock
相关产品推荐
相关产品推荐

