先说结论
很多开发者第一次真正意识到 Odoo 有强缓存,不是在读源码时,而是在调试时。
典型场景是:
- 菜单权限改了,前台还是旧结果
- 配置参数改了,代码读出来还是旧值
- 视图或模板动了,表现像没刷新
这类现象背后,经常不是数据库没写进去,而是:
你命中了
ormcache,但还没触发正确的缓存失效。
所以 ormcache 不是“性能小技巧”,而是 Odoo 核心框架里大量使用的结果缓存机制。
官方源码怎么定义 ormcache
在 /home/ubuntu/odoo-temp/odoo/tools/cache.py 里,ormcache 的注释写得非常直接:
- 它是一个 给 model method 用的 LRU cache decorator
- 你传给装饰器的字符串表达式会参与构造缓存 key
- 返回值不应该是 recordset,因为底层 cursor 迟早会关闭
这三句话已经足够说明它的边界:
1. 它缓存的是“方法结果”,不是单纯字段值
比如:
- 某个 model 的访问域
- 某个参数读取结果
- 某个模板、菜单、资产包计算结果
2. 它的 key 不是随便生成的
源码里会把:
self._name- 方法对象本身
- 你在装饰器里写的参数表达式
一起拼成 key。
所以你少放一个影响结果的上下文因素,就可能把本不该复用的结果缓存到一起。
3. 它不是用来缓存 recordset 的
这是很关键的开发提醒。
因为 recordset 绑定 env、cursor、权限和缓存语境,拿它做跨调用缓存非常危险。
为什么它会让人产生“改了没生效”的错觉
因为 ormcache 的目标就是:
同一个 key,后面别再重复算。
源码里的 lookup() 很清楚:
- 先按 key 去 LRU 里取
- 命中就直接返回
- miss 才真正执行方法并把结果写进缓存
这意味着,只要 key 没变、缓存没清:
- 你就算底层数据改了
- 结果也可能继续返回旧值
所以很多“没生效”本质上是:
- 业务数据已变
- 缓存语义还认为结果可复用
事务命中统计为什么也值得关注
lookup() 里还有一个很容易被忽略的细节:
- Odoo 还会记录当前 transaction 里这个 key 是不是第一次 lookup
- 会统计
hit / miss / err以及事务内第一次命中的情况
这说明 Odoo 不是把缓存当黑盒,而是把它视作框架级性能结构的一部分。
换句话说,ormcache 不只是“有没有缓存”,而是:
- 哪些方法在高频命中
- 哪些缓存桶在工作
- 某个事务里是不是不断重复请求同一结果
这也是为什么服务层还提供了日志统计入口。
Odoo 哪些地方真的在广泛用 ormcache
扫官方源码会发现,使用范围非常广:
ir.config_parameterir.modelir.actionsir.ui.menuir.ui.viewir.httpres.usersir.rule- QWeb / assets / routing 等
也就是说,它不是某个边角模块的技巧,而是:
Odoo 基础设施级别的性能机制。
这也解释了为什么很多“权限、菜单、模板、配置改了怎么不立刻反映”的问题,最终都会走到缓存失效边界上。
clear_caches() 真正该怎么理解
很多人把 clear_caches() 理解成“万能刷新按钮”。
这不够准确。
更准确的理解是:
当某类框架级缓存依赖的数据已经变化,而缓存 key 本身不足以自然区分新旧结果时,你需要主动失效缓存。
典型场景包括:
- 你改了
ir.config_parameter这类被缓存的方法依赖源 - 你改了菜单、规则、模型元数据、模板结构
- 你在自定义代码里对
@ormcache方法的依赖数据做了写入
此时如果没有同步失效,旧结果就会继续命中。
最容易踩的几个坑
坑 1:缓存 key 漏了上下文因素
比如结果其实跟这些东西有关:
- 用户
- 语言
- 公司
- context 某个键
但你的装饰器 key 里没带进去。
结果就是:
- A 用户算出来的结果
- 被 B 用户直接复用
这不是缓存太强,而是 key 设计不完整。
坑 2:把 recordset 或带游标语义的对象塞进缓存
源码已经明确提醒不要这么做。
因为缓存生命周期常常长于当前 cursor / env 语境。
坑 3:数据变了,却忘了失效
这是最常见的“明明数据库已经变了,页面还是旧的”。
坑 4:一遇到缓存问题就全局乱清
clear_caches() 有时必要,但如果把它当日常兜底:
- 你会失去缓存带来的性能收益
- 还会把真正的 key 设计问题掩盖掉
开发时更健康的判断顺序
我更建议你按这个顺序思考:
第一步:这段结果真的值得缓存吗?
如果计算不重、调用不频繁、依赖又很动态,未必适合上 ormcache。
第二步:影响结果的因素是否都进入 key?
尤其是:
- uid
- su
- lang
- company
- context 中的关键值
第三步:依赖数据变更时,失效点在哪?
也就是:谁写数据,谁负责清相关缓存。
只有这三步都想清楚,ormcache 才是加速器;否则它更像延迟爆炸的 bug 放大器。
一句话收尾
ormcache的本质不是“缓存一下结果”,而是“在明确 key 和失效边界的前提下复用框架级计算结果”。很多“改了没生效”,其实都是缓存边界没讲清。
DISCUSSION
评论区