Skip to Content

Odoo 19 缓存策略与性能优化实操手册

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

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

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

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

一、Odoo 中的缓存是什么?

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

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

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

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

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

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

基础用法

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

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)

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

@tools.ormcache('self.env.uid', 'model_name')
def _get_access_rights(self, model_name):
    # 按用户、模型分别缓存结果
    ...

2. @tools.ormcache_context(*args, keys=())

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

@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()

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

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

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

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

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

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

# 代码位置: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)

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

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

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

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

Odoo 19 缓存策略与性能优化实操手册
中国 Odoo, 苏州远鼎 June 12, 2026
Tags
Archive