服务器需要管理大量的连接超时,每个连接都有一个 30 秒的超时定时器。当连接数到达 10 万级别时,CPU 占用率开始异常飙升,但业务逻辑其实并没有那么复杂。
用 perf 一看,问题出在定时器管理上——我当时用的是一个基于std::priority_queue的最小堆实现。每次添加或删除定时器,都是 O(log n) 的时间复杂度。10 万个定时器,每秒可能有上万次的添加和删除操作,log n 的常数项在这里被放大了。
后来,我换成了时间轮算法,问题迎刃而解。定时器的添加和删除变成了 O(1),CPU 占用率直接降了一半。
这篇文章,我就带你从零开始,逐层剖析 Linux 内核定时器的实现,看看内核开发者是怎么用时间轮优雅地解决海量定时器管理问题的。
传统定时器方案:看似合理,实则有坑
在深入时间轮之前,我们先来看看传统的定时器实现方案,理解它们的问题所在,才能明白时间轮为什么是更好的选择。
方案一:有序链表
最直观的想法:把所有定时器按到期时间排序,放在一个链表里。
structTimer{uint64_texpire_time<