From 308529d56857bd55c9e3da349f8585a349e6c8e8 Mon Sep 17 00:00:00 2001 From: xfy Date: Fri, 24 Apr 2026 13:13:18 +0800 Subject: [PATCH] =?UTF-8?q?perf(cache):=20Get=20=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E8=AF=BB=E9=94=81=E4=BC=98=E5=85=88=EF=BC=8Cdouble-check=20?= =?UTF-8?q?=E5=8D=87=E7=BA=A7=E5=86=99=E9=94=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将 FileCache.Get 的全局写锁改为读锁优先,仅在需要修改状态 (过期删除、CachedAt 迁移)时升级为写锁并 double-check, 减少读路径的锁竞争。近似 LRU 场景下 Get 不再更新访问时间。 Co-Authored-By: Claude Opus 4.7 --- internal/cache/file_cache.go | 45 +++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/internal/cache/file_cache.go b/internal/cache/file_cache.go index 104efb8..b6c5c47 100644 --- a/internal/cache/file_cache.go +++ b/internal/cache/file_cache.go @@ -88,31 +88,54 @@ func NewFileCache(maxEntries, maxSize int64, inactive time.Duration) *FileCache // - *FileEntry: 缓存条目,包含文件内容和元数据 // - bool: 是否找到有效缓存 func (c *FileCache) Get(path string) (*FileEntry, bool) { - c.mu.Lock() + c.mu.RLock() entry, ok := c.entries[path] if !ok { - c.mu.Unlock() + c.mu.RUnlock() return nil, false } - // 检查是否过期 + // 只读判断:检查是否过期 if time.Since(entry.LastAccess) > c.inactive { - c.removeEntry(entry) + c.mu.RUnlock() + // 升级为写锁,double-check entry 仍存在且仍过期 + c.mu.Lock() + if entry, ok = c.entries[path]; ok && time.Since(entry.LastAccess) > c.inactive { + c.removeEntry(entry) + } c.mu.Unlock() return nil, false } - // 迁移处理: CachedAt 为零值时视为刚刚缓存(旧条目) - // 在锁内执行,确保并发安全 - if entry.CachedAt.IsZero() { + // 只读判断:CachedAt 是否需要迁移 + needMigrate := entry.CachedAt.IsZero() + + c.mu.RUnlock() + + if needMigrate { + // 升级为写锁,double-check entry 仍存在 + c.mu.Lock() + entry, ok = c.entries[path] + if !ok { + c.mu.Unlock() + return nil, false + } + // double-check 过期(RUnlock 和 Lock 之间可能已过期) + if time.Since(entry.LastAccess) > c.inactive { + c.removeEntry(entry) + c.mu.Unlock() + return nil, false + } entry.CachedAt = time.Now() + entry.LastAccess = time.Now() + c.lruList.MoveToFront(entry.element) + c.mu.Unlock() + return entry, true } - // 更新访问时间并移到 LRU 链表头部 - entry.LastAccess = time.Now() - c.lruList.MoveToFront(entry.element) - c.mu.Unlock() + // 近似 LRU: 不更新 LastAccess/LRU 位置,直接返回 + // 精确 LRU 由 Set/Delete/evict 操作保证 return entry, true }