golang如何实现滑动窗口计数器_golang滑动窗口计数器实现思路
滑动窗口计数器不能只用map定时清理因会漏统计非整点对齐的请求必须保留带时间戳事件或时间分片常用环形数组实现按需shift比ticker更精准高效。滑动窗口计数器为什么不能只用 map 定时清理直接用 map[string]int 存请求次数、再起 goroutine 每秒删过期 key看似简单但会漏统计。比如窗口是 60 秒当前时间戳是 1717023450你只保留 key 对应的“最后更新时间”那 17170234001717023449 这 50 秒内的请求就全算不到当前窗口里——因为它们在定时清理时被删了而新请求还没触发重置逻辑。本质问题是滑动窗口要求“任意时刻往前推 N 秒”的累计值不是“整点对齐的桶”。必须保留带时间戳的原始事件或至少保留足够粒度的时间分片。用环形数组 时间分片实现低开销滑动窗口最常用且平衡内存与精度的做法把窗口切分成固定数量的 slot如 60 秒窗口切 60 个 1 秒 slot用环形数组存每个 slot 的计数再记录每个 slot 对应的起始时间戳。每次计数前先滑动指针、清空已过期 slot。slotDuration 要能整除窗口时长否则边界计算易错推荐用 1s/100ms 级别数组长度 windowSeconds / slotDuration必须是整数否则向下取整后窗口实际变短每次 Inc() 前先调用 shift() 检查当前 slot 是否已过期过期则归零并移动索引并发安全需用 sync.RWMutex 或 atomic 操作 slot 内计数如果 slot 元素是 uint64type SlidingWindowCounter struct { slots []uint64 timestamps []int64 slotDur int64 // 单位毫秒 windowDur int64 // 单位毫秒 size int mu sync.RWMutex currentIndex int}pfunc (c *SlidingWindowCounter) Inc() {c.mu.Lock()defer c.mu.Unlock()now : time.Now().UnixMilli()c.shift(now)atomic.AddUint64(c.slots[c.currentIndex], 1)}/ppfunc (c *SlidingWindowCounter) shift(now int64) {slotStart : now - (now % c.slotDur)for (now - c.timestamps[c.currentIndex]) c.windowDur {c.slots[c.currentIndex] 0c.timestamps[c.currentIndex] slotStartc.currentIndex (c.currentIndex 1) % c.sizeslotStart c.slotDur}}time.Ticker 驱动清理 vs 按需 shift 的取舍有人用 time.Ticker 每 100ms 触发一次全局 slot 清理看起来更“主动”但实际引入额外 goroutine 和锁竞争且无法保证清理时机与请求到达严格同步——可能刚清完一个 slot下一毫秒就来 1000 个请求导致瞬时计数虚高。立即学习“go语言免费学习笔记深入” Cleanup.pictures 智能移除图片中的物体、文本、污迹、人物或任何不想要的东西
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2528711.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!