perf(resolver): replace slice-based LRU with container/list for O(1) operations
DNS resolver LRU cache used []string slice for access ordering: - moveToFrontLocked: O(n) linear scan + slice重组 per cache update - storeCache held exclusive write lock during entire O(n) operation, blocking all concurrent reads Replace with container/list + map[string]*list.Element: - storeCache: O(1) MoveToFront via list.Element pointer - evictLRULocked: O(1) Back() + Remove() - DeleteCacheEntry: O(1) instead of O(n) scan This matches the LRU pattern already used by FileCache and FileInfoCache. Write lock duration reduced from O(n) to O(1), significantly reducing read contention under mixed workloads.
This commit is contained in:
parent
ba8c746a2e
commit
12caed5d4e
@ -6,6 +6,7 @@
|
|||||||
package resolver
|
package resolver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"container/list"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -59,23 +60,21 @@ func (r *DNSResolver) GetCacheEntry(host string) (*DNSCacheEntry, bool) {
|
|||||||
// DeleteCacheEntry 删除指定主机的缓存条目。
|
// DeleteCacheEntry 删除指定主机的缓存条目。
|
||||||
func (r *DNSResolver) DeleteCacheEntry(host string) {
|
func (r *DNSResolver) DeleteCacheEntry(host string) {
|
||||||
r.mu.Lock()
|
r.mu.Lock()
|
||||||
|
defer r.mu.Unlock()
|
||||||
delete(r.cache, host)
|
delete(r.cache, host)
|
||||||
// 从 LRU 链表中移除
|
if elem, ok := r.lruIndex[host]; ok {
|
||||||
for i, h := range r.lruOrder {
|
r.lruList.Remove(elem)
|
||||||
if h == host {
|
delete(r.lruIndex, host)
|
||||||
r.lruOrder = append(r.lruOrder[:i], r.lruOrder[i+1:]...)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
delete(r.refreshHosts, host)
|
delete(r.refreshHosts, host)
|
||||||
r.mu.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClearCache 清空所有缓存。
|
// ClearCache 清空所有缓存。
|
||||||
func (r *DNSResolver) ClearCache() {
|
func (r *DNSResolver) ClearCache() {
|
||||||
r.mu.Lock()
|
r.mu.Lock()
|
||||||
r.cache = make(map[string]*DNSCacheEntry)
|
r.cache = make(map[string]*DNSCacheEntry)
|
||||||
r.lruOrder = make([]string, 0, r.config.CacheSize)
|
r.lruList = list.New()
|
||||||
|
r.lruIndex = make(map[string]*list.Element)
|
||||||
r.refreshHosts = make(map[string]struct{})
|
r.refreshHosts = make(map[string]struct{})
|
||||||
r.mu.Unlock()
|
r.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -421,22 +421,28 @@ func TestMockDNSMoveToFrontLocked(t *testing.T) {
|
|||||||
|
|
||||||
resolver := New(cfg).(*DNSResolver)
|
resolver := New(cfg).(*DNSResolver)
|
||||||
|
|
||||||
// 设置缓存
|
|
||||||
resolver.cache = map[string]*DNSCacheEntry{
|
resolver.cache = map[string]*DNSCacheEntry{
|
||||||
"a": {},
|
"a": {},
|
||||||
"b": {},
|
"b": {},
|
||||||
"c": {},
|
"c": {},
|
||||||
}
|
}
|
||||||
resolver.lruOrder = []string{"a", "b", "c"}
|
elemA := resolver.lruList.PushFront("a")
|
||||||
|
elemB := resolver.lruList.PushFront("b")
|
||||||
|
elemC := resolver.lruList.PushFront("c")
|
||||||
|
resolver.lruIndex["a"] = elemA
|
||||||
|
resolver.lruIndex["b"] = elemB
|
||||||
|
resolver.lruIndex["c"] = elemC
|
||||||
|
|
||||||
// 移动 "a" 到前端
|
resolver.lruList.MoveToFront(resolver.lruIndex["a"])
|
||||||
resolver.moveToFrontLocked("a")
|
|
||||||
|
|
||||||
// 验证顺序
|
order := []string{}
|
||||||
|
for e := resolver.lruList.Back(); e != nil; e = e.Prev() {
|
||||||
|
order = append(order, e.Value.(string))
|
||||||
|
}
|
||||||
expected := []string{"b", "c", "a"}
|
expected := []string{"b", "c", "a"}
|
||||||
for i, v := range expected {
|
for i, v := range expected {
|
||||||
if resolver.lruOrder[i] != v {
|
if order[i] != v {
|
||||||
t.Errorf("LRU order[%d] = %s, want %s", i, resolver.lruOrder[i], v)
|
t.Errorf("LRU order[%d] = %s, want %s", i, order[i], v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
package resolver
|
package resolver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"container/list"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
@ -61,41 +62,31 @@ func (r *DNSResolver) storeCache(host string, entry *DNSCacheEntry) {
|
|||||||
r.mu.Lock()
|
r.mu.Lock()
|
||||||
defer r.mu.Unlock()
|
defer r.mu.Unlock()
|
||||||
|
|
||||||
// 已存在则更新并移到头部
|
if elem, ok := r.lruIndex[host]; ok {
|
||||||
if _, ok := r.cache[host]; ok {
|
|
||||||
r.cache[host] = entry
|
r.cache[host] = entry
|
||||||
r.moveToFrontLocked(host)
|
r.lruList.MoveToFront(elem)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否需要淘汰
|
|
||||||
if r.config.CacheSize > 0 && len(r.cache) >= r.config.CacheSize {
|
if r.config.CacheSize > 0 && len(r.cache) >= r.config.CacheSize {
|
||||||
r.evictLRULocked()
|
r.evictLRULocked()
|
||||||
}
|
}
|
||||||
|
|
||||||
r.cache[host] = entry
|
r.cache[host] = entry
|
||||||
r.lruOrder = append(r.lruOrder, host)
|
elem := r.lruList.PushFront(host)
|
||||||
|
r.lruIndex[host] = elem
|
||||||
}
|
}
|
||||||
|
|
||||||
// evictLRULocked 淘汰最久未使用的条目(需持有锁)。
|
// evictLRULocked 淘汰最久未使用的条目(需持有锁)。
|
||||||
func (r *DNSResolver) evictLRULocked() {
|
func (r *DNSResolver) evictLRULocked() {
|
||||||
if len(r.lruOrder) == 0 {
|
oldest := r.lruList.Back()
|
||||||
|
if oldest == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
oldest := r.lruOrder[0]
|
host := oldest.Value.(string)
|
||||||
delete(r.cache, oldest)
|
delete(r.cache, host)
|
||||||
r.lruOrder = r.lruOrder[1:]
|
delete(r.lruIndex, host)
|
||||||
}
|
r.lruList.Remove(oldest)
|
||||||
|
|
||||||
// moveToFrontLocked 将条目移到 LRU 链表尾部(最新)(需持有锁)。
|
|
||||||
func (r *DNSResolver) moveToFrontLocked(host string) {
|
|
||||||
for i, h := range r.lruOrder {
|
|
||||||
if h == host {
|
|
||||||
r.lruOrder = append(r.lruOrder[:i], r.lruOrder[i+1:]...)
|
|
||||||
r.lruOrder = append(r.lruOrder, host)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DNSResolver 实现 Resolver 接口的 DNS 解析器。
|
// DNSResolver 实现 Resolver 接口的 DNS 解析器。
|
||||||
@ -103,8 +94,9 @@ type DNSResolver struct {
|
|||||||
config *config.ResolverConfig
|
config *config.ResolverConfig
|
||||||
stopCh chan struct{}
|
stopCh chan struct{}
|
||||||
refreshHosts map[string]struct{}
|
refreshHosts map[string]struct{}
|
||||||
cache map[string]*DNSCacheEntry // DNS 缓存
|
cache map[string]*DNSCacheEntry
|
||||||
lruOrder []string // LRU 访问顺序(最旧在前)
|
lruList *list.List
|
||||||
|
lruIndex map[string]*list.Element
|
||||||
hits atomic.Int64
|
hits atomic.Int64
|
||||||
misses atomic.Int64
|
misses atomic.Int64
|
||||||
errors atomic.Int64
|
errors atomic.Int64
|
||||||
@ -166,7 +158,8 @@ func New(cfg *config.ResolverConfig) Resolver {
|
|||||||
stopCh: make(chan struct{}),
|
stopCh: make(chan struct{}),
|
||||||
refreshHosts: make(map[string]struct{}),
|
refreshHosts: make(map[string]struct{}),
|
||||||
cache: make(map[string]*DNSCacheEntry),
|
cache: make(map[string]*DNSCacheEntry),
|
||||||
lruOrder: make([]string, 0, cfg.CacheSize),
|
lruList: list.New(),
|
||||||
|
lruIndex: make(map[string]*list.Element),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user