
教程, Odoo
Odoo性能优化总结
Odoo 的性能优化可以大致分为前端优化、Python 代码优化和数据库优化三大类。以下是对这些优化要点的总结
14次点击19分钟阅读
Odoo性能优化总结 by 超级Odoo(QQ:1069010)
Odoo 的性能优化可以大致分为前端优化、Python 代码优化和数据库优化三大类。以下是对这些优化要点的总结:
1. 前端优化 (JavaScript/客户端)
- 减少不必要的工作 (Don't Do It)
- 移除僵尸代码 (Zombie Code),即不再使用但仍在代码库中的代码。
- 只做必要的工作,避免提前计算可能不需要的值。
- 尽可能将计算推到服务器端执行,因为服务器硬件通常更强大,而客户端设备性能各异。
- 利用缓存 (Don't Do It Again)
- 缓存网络请求结果,在 Odoo 生态中,会话周期通常较短,无界缓存的风险相对较低。
- 缓存 DOM 元素尺寸测量结果,因为这些操作可能很昂贵,会强制浏览器重新计算布局。
- 使用局部变量、
memoize函数(无界缓存)或自定义有界缓存来存储昂贵纯函数的计算结果。
- 减少执行频率 (Do It Less)
- 避免在浏览器无法绘制时执行工作,例如在动画帧之间。
- 对动画使用
throttle,确保每帧最多只调用一次函数。 - 对用户交互(如输入)使用
debounce,等待用户停止操作一段时间后再触发服务器请求,避免频繁发送请求。
- 延迟计算/按需渲染 (Do It Later)
- 只在需要时才计算值。
- 使用
t-if指令代替d-noneCSS 类来避免渲染不可见的组件,减少不必要的 CPU 周期消耗。 - 对于昂贵的属性,使用 getter 方法实现懒加载,只在访问时才计算。
- 对于大型列表,使用分页(每次显示少量记录)和列表虚拟化 (
useVirtualhook),只渲染视口中可见的元素。
- 利用空闲时间 (Do It When No One Is Looking)
- 在用户不活跃时执行低优先级任务,例如垃圾回收或预取数据。
- 使用
setTimeout(0)或requestIdleCallback来调度这些任务,以最小化对用户体验的影响。
- 并发执行 (Do It Concurrently)
- 对于不相互依赖的网络请求,使用
Promise.all等工具并发执行。 - 避免在循环中使用
await,应使用map结合Promise.all来并行处理异步操作。 - 将繁重的数据处理任务卸载到 Web Worker 线程中执行,以避免阻塞主 UI 线程。
- 对于不相互依赖的网络请求,使用
- 前端性能工具
- Console 语句:
console.log用于基本输出;console.trace用于获取堆栈跟踪(可能很嘈杂);console.time和console.timeEnd用于测量代码块的执行时间。 - Lighthouse:集成在 Chrome DevTools 中,提供页面加载性能、SEO 和可访问性评分,并给出优化建议及预估节省时间。
- 内存分析器:用于诊断内存泄漏(堆快照)和垃圾回收抖动(时间线上的分配检测)。
- 性能分析器:生成火焰图(或冰柱图),显示函数中花费的时间,帮助识别性能瓶颈。
- Owl DevTools:Chrome 扩展,提供组件树视图,支持跳转到编译代码,并高亮显示组件重新渲染,以帮助发现和解决不必要的渲染问题。
- Console 语句:
2. Python 代码优化 (ORM/服务器端 Python)
- 模型设计与算法复杂度
- 良好的模型设计是关键:糟糕的模型设计无法实现高效的算法。
- 避免高复杂度算法:避免
O(N^2)或更高复杂度的算法,因为它们在大数据量下会非常慢。目标是线性或略高于线性的复杂度。
- 优化 ORM 与 SQL 查询
- ORM 的批量操作优势:Odoo ORM 的
create、read、write、delete方法被设计为批量操作,可高效处理数据。 - ORM 预取机制:访问记录集中的字段时,ORM 会自动预取该字段以及该记录集(默认最多 2000 条记录)中其他存储字段的数据到内存中,以减少后续数据库查询。
- ORM 延迟更新:ORM 会将字段更新缓冲起来,只在必要时才一次性推送到数据库,将多次更新合并为少量 SQL 语句,避免 PostgreSQL 在同一事务中多次检查约束的开销。
- 避免循环内的查询:将
search_count或search等操作移出循环,对整个批量数据进行一次查询,例如使用read_group,而不是 N 次查询。 - 显式刷新与缓存失效:如果直接通过 SQL 修改了数据库或需要确保 ORM 缓存与数据库同步,应使用
self.flush()或self.env.cache.invalidate()。 - 为计算字段提供提示:对于非存储计算字段,可以通过
self.fetch()明确指定需要预取的依赖字段,避免 ORM 预取不必要的存储字段。
- ORM 的批量操作优势:Odoo ORM 的
- 不良实践警告
- 避免低效记录集遍历:避免在记录集上使用
range(len(self))结合self[i]访问,这会禁用 ORM 的预取优化。 - 避免混用存储与非存储计算字段:在同一个计算方法中混用存储和非存储计算字段是非常糟糕的做法,可能导致不必要的数据库写入和记录锁定,甚至拖垮生产环境。
- 避免依赖上下文的计算字段:计算字段的值不应依赖于用户、环境、货币或语言等上下文,其值应保持一致。
- 避免低效记录集遍历:避免在记录集上使用
- SQL 与 Python 约束
- SQL 约束更快,但更新困难(需要数据库升级);Python 约束更容易维护(只需重启服务器),但性能较慢。
- 高级 ORM 查询技巧
- 使用子查询优化 ID 列表:当查询需要过滤大量 ID 时,使用
in_q_in_select操作符并在 domain 中传入查询(子查询)比直接传入长 ID 列表更高效。但需注意,这可能绕过记录规则。 - 使用
_search操作符:利用_search(Odoo 16+ 版本有显著改进)在 domain 中触发子查询,它既高效又兼容记录规则。 any操作符:简化子域搜索,在一行代码中实现子查询。
- 使用子查询优化 ID 列表:当查询需要过滤大量 ID 时,使用
- 排序优化
- 警惕
_order属性:模型中的_order属性或search方法中的order参数可能触发昂贵的 JOIN 操作用于排序。 - 按需排序:如果算法不需要特定顺序,可以在
search方法中明确设置order='id'或order=False,避免不必要的排序开销。
- 警惕
- 最终用户行为影响
- 最终用户在列表视图中进行低效搜索(例如,对 Many-to-one 字段的名称而不是 ID 进行
LIKE搜索,或使用排除性而非包容性筛选)会严重影响性能。
- 最终用户在列表视图中进行低效搜索(例如,对 Many-to-one 字段的名称而不是 ID 进行
- 并发处理
- 串行化错误:当多个事务尝试修改同一条记录时可能发生。解决方案包括使用追加而非直接更新计数器,或者合理调度 Cron 任务避免并发冲突。Odoo 默认会重试失败的事务五次。
- Python 性能分析工具
- Odoo 日志:每行日志末尾的三个数字(SQL 查询数、SQL 执行时间、Python 代码执行时间)可快速判断瓶颈在 SQL 还是 Python。
- 内置性能分析器:在开发者模式下对管理员可用。可记录 SQL 查询、堆栈跟踪和 QWeb 调试信息。以火焰图形式可视化代码执行,帮助定位耗时方法。自 Odoo 15/16 引入。
kill -3信号:向 Odoo 进程发送kill -3 <PID>信号,可在日志中输出堆栈跟踪,不会终止进程。重复发送可追踪进程执行路径。- Shell 分析器:通过 Odoo shell 导入分析器作为上下文管理器,仅对特定代码块进行分析,输出火焰图且不提交数据库更改,便于重复测试。
- 外部脚本/负载测试工具:使用 XML-RPC/JSON-RPC 脚本或 Locust 负载测试工具模拟多用户并发,在有足够数据量的情况下重现并隔离性能问题。
- 测试数据管理
- 足够真实的数据:使用真实且大规模的数据集进行测试,而不是小型演示数据。
populate工具:使用 Odoo 内置的populate工具(新版本使用纯 SQL 方式生成,速度快得多)快速生成数百万条测试数据。一个数据库只能填充一次。
- 通用 Python 最佳实践
- 遵循“先让它工作,再让它正确,最后再让它快速”的原则。
- “常识”是最好的分析工具。
- 如果已熟练掌握其他 Python 分析器,可继续使用。
3. 数据库优化 (PostgreSQL)
- 日志与监控
- PostgreSQL 日志:配置 PostgreSQL 记录慢查询(
log_min_duration_statement,建议设置为 0-100ms,但在生产环境长时间启用需谨慎)。日志需设置为英文 Unicode 格式。 - pgBadger:分析 PostgreSQL 日志并生成 HTML 报告的工具。可提供最常执行、最耗时和最慢查询的信息。
- PG Activity:类似于 Unix 的
top命令,实时显示数据库活动和耗时查询。 - EXPLAIN / EXPLAIN ANALYZE:
- 使用
EXPLAIN查看 PostgreSQL 估算的查询执行计划。 - 添加
ANALYZE实际执行查询并获取真实的执行时间、缓冲区使用等信息。 - 可使用
explain.depesz.com或explain.dalibo.com等在线工具可视化执行计划。 - 注意:
EXPLAIN ANALYZE会执行数据修改查询,因此在生产环境谨慎使用或在事务中进行回滚测试。
- 使用
- PostgreSQL 日志:配置 PostgreSQL 记录慢查询(
- 索引优化
- 索引作用:将顺序扫描
O(N)的操作转换为索引扫描O(log N),显著提升查询速度。 - 何时添加索引:当查询缓慢,特别是
EXPLAIN显示顺序扫描时。 - 索引类型:
- B-tree(默认):适用于等值、范围、排序等操作。主键和唯一约束会自动创建 B-tree 索引。
- 唯一索引:强制字段唯一性。
- 复合索引:在多个列上创建,列顺序很重要(例如
(col1, col2)可用于WHERE col1=X或WHERE col1=X AND col2=Y,但不能单独用于WHERE col2=Y)。 - 部分索引/条件索引:只对表中满足特定条件的行子集创建索引,可减少索引大小并提高特定查询速度。
- 覆盖索引:在索引中包含非键列,允许“仅索引扫描”,即无需访问表即可获取所需数据。
- 函数索引:对函数结果(如
LOWER(column)或unaccent(column))创建索引,用于大小写或重音不敏感搜索。查询时必须使用相同的函数才能利用索引。 - Trigram (GIN) 索引:用于
LIKE/ILIKE文本搜索,将字符串分解为三字符组合进行索引。需要pg_trgm扩展。Odoo 16+ 用于翻译字段和 JSON 字段,仅索引内容而非语言键。 - Hash 索引:提供恒定时间复杂度的查找,开销低,适用于选择字段的等值检查。
- Odoo 中创建索引:
- 在字段定义中设置
index=True(创建 B-tree 索引)。 - 可使用
index='btree_not_null'或index='trigram'。 - SQL 约束会自动创建唯一索引。
- 更复杂的索引可在
_init_方法中定义。
- 在字段定义中设置
- 索引的局限与考量:
- 写入成本:索引会加速读取,但增加写入(插入、更新、删除)操作的开销,因为索引本身需要同步更新。
- 磁盘空间:索引会占用额外磁盘空间,特别是 Trigram 索引在长文本字段上可能非常大。
- 选择性:PostgreSQL 查询优化器根据字段的选择性(数据唯一性或分布情况)决定是否使用索引。对于选择性低的字段(如布尔字段中 90% 为 False),索引可能无用,除非是部分索引。
- 字段不相关性假设:PostgreSQL 默认假设 WHERE 子句中的字段不相关,即使它们在业务逻辑上相关,这可能导致错误的行数估算。
- 外键索引:对于涉及级联操作的删除/更新,在外键上添加索引可以加速操作,尽管这不总在
EXPLAIN中显式显示。 - 始终使用 EXPLAIN:在添加索引前必须使用
EXPLAIN分析查询,添加后也要再次分析以验证索引是否被实际使用。
- 索引作用:将顺序扫描
- 数据库健康与维护
- VACUUM / VACUUM ANALYZE:PostgreSQL 的 MVCC 机制在更新/删除时会创建“死元组”,
VACUUM回收空间,ANALYZE更新统计信息供查询优化器使用。 - 序列化错误:并发事务修改同一记录时发生。
- VACUUM / VACUUM ANALYZE:PostgreSQL 的 MVCC 机制在更新/删除时会创建“死元组”,
- PostgreSQL 配置
- max_connections:根据 Odoo worker 数量及其他服务计算并合理设置。监控实际连接数可帮助微调。
- pgtune:作为 PostgreSQL 配置的起始点非常可靠,可根据硬件自动生成推荐配置。
- 远程连接:配置
postgresql.conf监听网络接口,并在pg_hba.conf中允许来自 Odoo 服务器 IP 范围的连接。 - 用户权限:Odoo 连接 PostgreSQL 的用户不应是超级用户,以确保安全性。
- SSL 连接:在
pg_hba.conf中强制 Odoo 与 PostgreSQL 之间使用 SSL 加密连接。
- 部署架构优化
- 单服务器 vs. 多服务器:负载测试表明,对于相同的总 worker 数量,一台大型服务器与多台小型服务器的性能是等效的。Odoo 性能与 CPU 频率呈 1:1 关系。
- Odoo 与 PostgreSQL 分离:将 Odoo 前端(Web 服务器、HTTP/Cron/WebSocket worker)与 PostgreSQL/文件存储部署在不同机器上。要求两台机器间网络延迟足够低。
- 数据库复制:流复制或逻辑复制可实现高可用性(故障转移)。同步复制会影响性能,异步复制更常见。复制不等于备份。
- 备份策略:至关重要,与复制分开。需定义恢复点目标(RPO)和恢复时间目标(RTO)。可使用 Barman 等工具进行 PostgreSQL 备份。
- 共享文件存储:对于多个 Odoo 前端,需共享文件存储(例如通过 NFS),这同样适用于源代码以确保资产生成一致性。
- Odoo Worker:生产环境应使用 worker 模式(进程而非线程,受 Python GIL 限制)。不支持 Windows。主进程管理 worker 生命周期。需在 Odoo 前端部署 Web 服务器(Nginx)路由 HTTP 和 WebSocket 流量。Cron 任务可拥有独立的进程/机器。
- Python 库安装:使用系统包管理器(如 Debian/Ubuntu 的 APT)安装 Python 依赖,而非
pip,以确保在系统升级时 C 库兼容性。 - Nginx 配置:关键在于路由 HTTP 和 WebSocket 请求,并添加必要的头部信息。Odoo 配置中需启用
proxy_mode。 - db_filter:通过 URL 自动选择数据库(
%d,%h)。 - --no-database-list:生产环境必备,禁用数据库创建/删除/备份的 Web 界面。
- --without-unaccent:用于不区分重音的搜索。
- 会话管理:会话存储在文件系统,通过 cron 任务进行清理。
sessions.max_inactivity_seconds参数控制不活跃会话清理时间。多服务器共享会话意味着共享文件存储。 - Worker 限制:设置
limit_memory_soft、limit_memory_hard、limit_time_cpu、limit_time_real、limit_request等参数来控制 worker 的内存和 CPU 使用,并定期回收 worker。



