⚡️ perf(fqapi): 移除缓存层全局串行锁

- 删除 cache_manager/audio_proxy/device_key_pool 中 21 处 cacheMutex:database/sql 连接池本身并发安全,SQLite 已配 WAL + busy_timeout,PostgreSQL 无并发写限制
- contentRequestCount 三函数由互斥锁改为 atomic.Int64
- cache_manager 中 config.GetConfig() 改为 GetAppConfig(),消除配置未初始化时 log.Fatal 崩溃整个进程的隐患
- VACUUM 与写操作的低频并发冲突由 busy_timeout 兜底(已注释说明)
- 新增 TestConcurrentCacheAccess:8 goroutine 并发读写缓存,-race 验证无死锁无竞争
This commit is contained in:
chenxin committed 2026-09-29 23:21:10 +08:00
1 parent 24c7e4b6fd
commit f0d89b462b
4 files changed
+68 -68

No files matched your search

+11 -53
View File
@@ -10,7 +10,7 @@ import (
"net/url"
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
_ "github.com/jackc/pgx/v5/stdlib"
@@ -22,10 +22,8 @@ var (
cacheDB *sql.DB
dbDriver string // "sqlite3" | "pgx"
redisCache *redis.Client
cacheMutex sync.Mutex
cacheLogger = common.NewLogger("cache_manager", "INFO", "")
contentRequestCount int // content接口访问计数
countMutex sync.Mutex
contentRequestCount atomic.Int64 // content接口访问计数
cacheCleanupTicker *time.Ticker // 缓存清理定时器
)
@@ -49,7 +47,7 @@ func q(query string) string {
// InitRedis 初始化 Redis 缓存(启用后正文缓存与音频代理缓存走 Redis)
func InitRedis() error {
cfg := config.GetConfig()
cfg := config.GetAppConfig()
if cfg == nil || !cfg.Cache.Redis.Enabled {
return nil
}
@@ -90,7 +88,7 @@ func InitDB() error {
return nil // 已初始化
}
cfg := config.GetConfig()
cfg := config.GetAppConfig()
backend := "sqlite"
dsn := "./cache.db"
if cfg != nil {
@@ -250,7 +248,7 @@ func createTables() error {
// isCacheEnabled 检查正文内容缓存是否启用
func isCacheEnabled() bool {
cfg := config.GetConfig()
cfg := config.GetAppConfig()
if cfg == nil {
return false
}
@@ -271,9 +269,6 @@ func GetCache(key string) string {
return ""
}
cacheMutex.Lock()
defer cacheMutex.Unlock()
var value string
err := cacheDB.QueryRow(q("SELECT value FROM cache WHERE key = ?"), key).Scan(&value)
if err != nil {
@@ -316,9 +311,6 @@ func UpdateCache(key string, value string, expi int) error {
return nil
}
cacheMutex.Lock()
defer cacheMutex.Unlock()
cacheData := CacheData{
Data: value,
Expi: float64(time.Now().Unix() + int64(expi)),
@@ -341,7 +333,6 @@ func UpdateCache(key string, value string, expi int) error {
return nil
}
// CloseCache 关闭缓存数据库
func CloseCache() {
if cacheDB != nil {
@@ -349,18 +340,12 @@ func CloseCache() {
}
}
// CleanupAllCache 立即清理所有正文缓存
func CleanupAllCache() error {
if cacheDB == nil {
return fmt.Errorf("数据库未初始化")
}
cacheMutex.Lock()
defer cacheMutex.Unlock()
// 删除所有缓存数据
_, err := cacheDB.Exec("DELETE FROM cache")
if err != nil {
@@ -401,7 +386,7 @@ func CleanupAllCache() error {
// CleanupExpiredCache 清理过期的正文缓存
func CleanupExpiredCache() error {
cfg := config.GetConfig()
cfg := config.GetAppConfig()
if cfg == nil {
return fmt.Errorf("配置未加载")
}
@@ -420,9 +405,6 @@ func CleanupExpiredCache() error {
return fmt.Errorf("数据库未初始化")
}
cacheMutex.Lock()
defer cacheMutex.Unlock()
// 计算过期时间点(当前时间 - cleanup_seconds)
expiredTime := time.Now().Unix() - int64(cfg.Cache.CleanupSeconds)
@@ -446,7 +428,7 @@ func CleanupExpiredCache() error {
// StartCacheCleanup 启动缓存自动清理任务
func StartCacheCleanup() {
cfg := config.GetConfig()
cfg := config.GetAppConfig()
if cfg == nil || !cfg.Cache.CleanupEnabled || cfg.Cache.CleanupSeconds <= 0 {
cacheLogger.Info("缓存自动清理未启用或配置无效,跳过启动")
return
@@ -488,36 +470,21 @@ func StopCacheCleanup() {
}
// IncrementContentRequestCount 增加content接口访问计数
func IncrementContentRequestCount() int {
countMutex.Lock()
defer countMutex.Unlock()
contentRequestCount++
return contentRequestCount
}
func IncrementContentRequestCount() int { return int(contentRequestCount.Add(1)) }
// ResetContentRequestCount 重置访问计数
func ResetContentRequestCount() {
countMutex.Lock()
defer countMutex.Unlock()
contentRequestCount = 0
}
func ResetContentRequestCount() { contentRequestCount.Store(0) }
// GetContentRequestCount 获取当前访问计数
func GetContentRequestCount() int {
countMutex.Lock()
defer countMutex.Unlock()
return contentRequestCount
}
func GetContentRequestCount() int { return int(contentRequestCount.Load()) }
// VacuumDatabase 压缩数据库,释放磁盘空间
// ponytail: 删了全局串行锁,VACUUM 与写操作的并发冲突由 busy_timeout(5s) 兜底,失败仅记日志
func VacuumDatabase() error {
if cacheDB == nil {
return fmt.Errorf("数据库未初始化")
}
cacheMutex.Lock()
defer cacheMutex.Unlock()
cacheLogger.Info("开始执行数据库压缩...")
if dbDriver == "pgx" {
cacheLogger.Info("PostgreSQL 由 autovacuum 自动管理,无需手动压缩")
@@ -539,9 +506,6 @@ func GetDatabaseStats() (map[string]interface{}, error) {
return nil, fmt.Errorf("数据库未初始化")
}
cacheMutex.Lock()
defer cacheMutex.Unlock()
stats := make(map[string]interface{})
// 获取cache表记录数
@@ -596,9 +560,6 @@ func SaveStatistics(stats map[string]int64) error {
return fmt.Errorf("数据库未初始化")
}
cacheMutex.Lock()
defer cacheMutex.Unlock()
_, err := cacheDB.Exec(q(`
UPDATE statistics
SET total_requests = ?,
@@ -630,9 +591,6 @@ func LoadStatistics() (map[string]int64, error) {
return nil, fmt.Errorf("数据库未初始化")
}
cacheMutex.Lock()
defer cacheMutex.Unlock()
var total, search, detail, catalog, content, manga, audio, video, category int64
err := cacheDB.QueryRow(`