Odoo19 内置缓存原理、用法与避坑指南

Odoo19 内置缓存原理、用法与避坑指南

适用版本:Odoo 19、Odoo 企业版、Odoo 社区版

Odoo 性能问题的表现往往十分典型:开发环境运行一切正常,测试环境性能略有下降,而在高负载场景下则会出现严重卡顿。几乎所有这类问题,根源都是对数据库发起了大量冗余查询------反复读取几乎不会变动的相同数据。

Odoo 内置了一套完善的缓存框架,但多数开发者仅用到了其极少一部分功能。本文将全面讲解 Odoo 缓存相关内容,从基础的 ORM 缓存到进阶的 Redis 共享缓存,帮助你大幅提升系统运行性能。

一、Odoo 中的缓存是什么?

简单来说,缓存就是将高开销计算的结果保存下来以供重复使用。借助缓存,相同数据无需每次请求都查询数据库,仅需执行一次计算,后续请求直接从缓存中读取数据。

在 Odoo 中,缓存主要应用于以下场景:

  • 避免对静态数据、低频变动数据重复执行数据库查询
  • 加快系统配置参数的读取速度
  • 缩短复杂业务逻辑的计算耗时
  • 缓存外部 API 的响应结果

Odoo 的缓存逻辑定义在 odoo/tools/cache.py 文件中,缓存作用于进程级别。也就是说,每一个 Odoo 进程都拥有独立缓存,进程之间的缓存互不共享。

二、理解 @tools.ormcache 缓存装饰器

ormcache 是 Odoo 实现缓存的核心方式,它会根据方法的入参,对方法的执行结果进行缓存。

基础用法

以下是根据 ISO 编码查询国家信息的简单示例:

python 复制代码
from odoo import tools
 
class ResCountry(models.Model):
    _name = 'res.country'
 
    @tools.ormcache('code')
    def _get_country_by_code(self, code):
        return self.search([('code', '=', code)], limit=1).id

当使用某个编码调用该方法后,执行结果会存入缓存。后续使用同一编码再次调用时,会直接返回缓存中的 ID,不再执行数据库查询。

ormcache 的不同变体

Odoo 提供了多种 ormcache 变体,可根据实际业务场景选用,具体用法如下:

1. @tools.ormcache(*args)

标准装饰器,根据指定的参数名作为缓存键。

python 复制代码
@tools.ormcache('self.env.uid', 'model_name')
def _get_access_rights(self, model_name):
    # 按用户、模型分别缓存结果
    ...
2. @tools.ormcache_context(*args, keys=())

用法与基础装饰器类似,同时会结合指定的上下文键生成缓存。如果方法返回结果受用户语言、当前公司等上下文影响,建议使用该装饰器。

python 复制代码
@tools.ormcache('product_id', 'self.env.context.get("lang")')
def _get_product_name(self, product_id):
   return self.browse(product_id).with_context(
       lang=self.env.context.get('lang')).name

三、缓存失效机制

ormcache 一大优势是无需手动管理缓存失效,ORM 会自动处理。当模型执行数据写入操作时,对应的缓存条目会自动清空。

触发事件 是否清空缓存
调用模型 create() 方法
调用模型 write() 方法
调用模型 unlink() 方法
Odoo 工作进程重启
手动调用 clear_caches()

如果需要手动强制清空缓存(例如执行数据迁移脚本、直接修改数据库后),可使用以下代码:

python 复制代码
# 清空指定模型的缓存
self.env['res.country'].invalidate_model()

四、Odoo 源码中的实际应用案例

参考 Odoo 原生代码,是掌握 ormcache 最佳用法的有效方式,以下为两个高频应用案例:

1. 系统配置参数(IR Config Parameters)

这是 Odoo 源码中 ormcache 最核心的应用场景。系统全局调用 get_param() 读取配置参数时,会根据参数键做缓存。

python 复制代码
# 代码位置:ir.config_parameter 模型
@tools.ormcache('key')
def get_param(self, key, default=False):
    self.env.cr.execute(
        "SELECT value FROM ir_config_parameter WHERE key = %s", [key])
    result = self.env.cr.fetchone()
    return result[0] if result else default

这也是生产环境中系统配置参数读取速度极快的原因:首次请求加载数据后,后续全部从内存缓存读取。

2. 公司货币(Company Currency)

python 复制代码
# 代码位置:res.company 模型
@tools.ormcache('self.env.uid')
def _get_user_currency(self):
    return self.env.user.company_id.currency_id

五、通过 store=True 实现字段级缓存

ormcache 外,还有一种简单方式可以减少重复查询:将计算字段结果持久化到数据库,避免每次读取都重复计算。

python 复制代码
total_amount = fields.Float(
    string='Total Amount',
    compute='_compute_total_amount',
    store=True   # 结果存入数据库,读取时不再重复计算
)
 
@api.depends('order_line.price_subtotal')
def _compute_total_amount(self):
    for record in self:
        record.total_amount = sum(
            record.order_line.mapped('price_subtotal'))

六、缓存使用最佳实践

推荐做法

  1. 对静态数据、低频变动数据启用缓存,例如货币、国家、系统配置参数
  2. 为每一处自定义缓存明确失效触发条件
  3. 若结果受语言、公司上下文影响,使用 ormcache_context
  4. 读写比例悬殊(读多写少)的计算字段,设置 store=True 持久化
  5. 上线前后做性能对比分析,验证缓存的实际优化效果

禁忌做法

  1. 不要缓存用户相关的交易类动态数据
  2. 不要缓存要求实时更新的数据(例如实时库存)
  3. 不要缓存大批量数据集,内存开销会抵消缓存带来的性能提升
  4. 直接操作数据库后,务必在迁移脚本中执行缓存清空操作

Odoo 的缓存功能十分强大,但 @tools.ormcache 只是基础。理解其原理并遇到性能瓶颈后,可组合多种缓存方案:计算字段、各类 ormcache 衍生装饰器、多进程架构下的 Redis 缓存、公开接口的 HTTP 头部缓存等。

优秀的 Odoo 性能优化开发者,都会先定位性能问题、找出慢查询,再按需选择合适的缓存方案,并验证优化效果。盲目全局启用缓存,往往无法达到理想效果。

相关推荐
全栈弄潮儿²⁰²⁴18 小时前
AI Agent 开发实战(30):限流、缓存与成本控制
人工智能·gpt·缓存·agent·限流·agi·成本控制
Together_CZ1 天前
DeepSeek-V4.1-Flash:Pushing the Limits of KV Cache Compression——推动 KV 缓存压缩的极限
缓存·llm·compression·kv cache·deepseek·v4.1-flash·推动 kv 缓存压缩的极限
Pioneer000012 天前
我用 Redis + 网关做多模型 API 路由:缓存命中率 95%+ 的工程实践
人工智能·redis·后端·缓存·性能优化·架构
hweiyu002 天前
Redis命令:MSETNX
redis·缓存
wdfk_prog3 天前
ROS教程08:从 TransportTCP::connect() 追到 TCPROS Connection Header、序列化与 Socket 数据传输
运维·缓存·docker·容器·ros
是Dream呀3 天前
Harness 工程:让 Agent 真正把任务做完
人工智能·分布式·缓存·agent
努力努力再努力wz3 天前
【Redis进阶系列】:从主从复制到 Sentinel,一文建立故障检测、Leader 选举与 Failover 的完整心智模型
数据库·redis·缓存
wdfk_prog4 天前
ROS教程10:从 gtest 到 rostest——Unit Test、Node 集成测试、rosbag 与 rqt 验证闭环
运维·缓存·docker·容器·ros
程序猿乐锅4 天前
【黑马点评 | 第四篇】Redis缓存雪崩
java·数据库·spring boot·redis·缓存
HanhahnaH4 天前
Redis单线程和Tair多线程架构设计对比
分布式·缓存