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

相关推荐
流星白龙11 小时前
【Redis】4.基本全局命令
数据库·redis·缓存
流星白龙14 小时前
【Redis】3.Redis安装与命令行客户端
数据库·redis·缓存
我不想名字重复19 小时前
redis缓存和数据库数据保持一致
数据库·redis·缓存
时空无限20 小时前
vllm 大模型启动缓存相关环境变量 export
linux·缓存·vllm
流星白龙1 天前
【Redis】6.计数其他命令
数据库·redis·缓存
正儿八经的少年1 天前
redis 的大 key 和热 key 详解
数据库·redis·缓存
X-⃢_⃢-X1 天前
十、Redis之布隆过滤器
数据库·redis·缓存
Database_Cool_1 天前
阿里云 Tair(企业级内存数据库)vs 腾讯云 Redis 云缓存深度对比:性能/数据结构/成本全维度 Benchmark
数据库·阿里云·缓存
流星白龙1 天前
【Redis】1.Redis特性
数据库·redis·缓存
chexus1 天前
21. 深入 Nginx HTTP 缓存源码:CDN功能
nginx·http·缓存