缓存穿透是查询不存在的数据导致请求直达数据库;击穿是热点key过期瞬间大量并发请求打穿缓存;雪崩是大量key同时失效引发数据库压力突增。

Beego 的 cache.NewCache 多实例调用会覆盖前一个配置
直接多次调用 cache.NewCache("file", config) 并不能创建多个独立的文件缓存实例——第二次调用会重写全局适配器状态,返回的对象和第一次完全共享底层存储。这意味着你设了两个不同目录(比如 ".cache/my" 和 ".cache/other"),最终所有读写都只落在后一个目录里。
这不是 bug,是 Beego 缓存模块的设计限制:内部用 adapters["file"] 全局变量单例管理,不支持同类型多实例。
- 别写
MyCache := cache.NewCache("file", cfg1)再写OtherCache := cache.NewCache("file", cfg2) - 改用单实例 + 命名空间前缀,例如
keyWithPrefix("user:", "1001")→"user:1001" - 清理时可用
SharedCache.DeleteAllWithPrefix("user:")隔离操作
Redis 缓存驱动下如何避免缓存穿透和击穿
Beego 默认不自动处理缓存穿透(查不存在的 key)或击穿(热点 key 过期瞬间大量请求打穿)。一旦业务中存在 ID 拼接、用户输入构造 key 的场景,就容易触发。
关键不是换驱动,而是加一层兜底逻辑:
- 对空结果也缓存(如
cache.Put("user:999999", nil, 60)),但注意区分“查无此数据”和“查失败”,后者不应缓存 - 热点 key 设置随机 TTL 偏移(比如基础 300s ± 30s),防止雪崩式集体过期
- 用
cache.IsExist()+cache.Get()组合无法防击穿;应改用原子操作或加分布式锁(如 RedisSETNX)控制回源
max_prepared_stmt_count 错误本质是 Prepare Statement 泄漏
错误 Error 1461: Can't create more than max_prepared_stmt_count statements 不代表 SQL 写得多,而是 Beego 在 1.12.x 后虽引入 LRU 缓存,但若用户绕开 ORM 直接拼接 SQL 字符串(如 "SELECT * FROM user WHERE id = " + id),每次都会生成新 stmt,且旧 stmt 未被及时释放。
Beego 1.12.1 已修复:LRU 淘汰时带引用计数,等所有 goroutine 释放后才真正 Close()。但前提是——你得用参数绑定。
- 必须用
o.Raw("SELECT * FROM user WHERE id = ?", id).Exec(),而非字符串拼接 - 检查 MySQL 的
max_prepared_stmt_count当前值:SHOW VARIABLES LIKE 'max_prepared_stmt_count'; - 若仍频繁触发,可临时调大该值,但根因一定是未绑定参数或连接池复用率低
静态文件缓存占用内存失控的三个开关
Beego 的 cache.NewCache("file", ...) 默认会把所有响应文件全量加载进内存,大文件或高并发下载时极易 OOM。1.12.1 起加入三项硬性约束,必须显式配置:
-
"CacheSize": 1000:最多缓存 1000 个文件(LRU 淘汰) -
"MaxFileSize": 1048576:超过 1MB 的文件跳过缓存(单位字节) -
"DirectoryLevel": 2:配合CachePath控制目录层级,避免单目录 inode 爆满
这三个字段缺一不可。没设 MaxFileSize,哪怕只跑一个 500MB 的 ZIP 下载,也会把整个进程内存吃光。











