关于我们

质量为本、客户为根、勇于拼搏、务实创新

< 返回新闻公共列表

自建 Odoo 别按公司人数配机器:并发、报表、导入和附件,这四个变量才是规格的来源

发布时间:2026-10-08

同样是"公司五十人"的 Odoo,有人 4 核 8G 跑得顺,有人 16 核 32G 还卡。差别不在人数,在于这五十人里有几个人在月底同时导报表。

这句话听着有点像抬杠,但它恰恰是自建 Odoo 选型里最常被搞错的一件事。采购的时候,问出口的是"我们公司多少人";回答那边按人数报一台机器;机器开出来,装好,跑几个月,开始卡;卡了就加配置,加完还是卡,卡的地方没变,钱倒是先花出去了。

问题出在最前面那句问话上。Odoo 的资源消耗不是"人数"的函数,是"峰值并发 × 操作类型"的函数。两家都是五十个人用同一套系统:A 公司每天干的是录单据、查库存、开送货单;B 公司的五个财务每到月初那一周,同时导出上月的应收应付和进销存流水,同时还有人在对半年的库存明细做分组筛选,晚上还要跑一次物料主数据的批量导入。人口一样,机器上跑的东西完全不在一个量级。

更麻烦的地方在于,被漏算的往往是最贵的那一类操作。单据录入对机器几乎构不成压力,真正决定内存峰值和磁盘随机读写能力的是报表和批量导入,而按人数配机器的时候,这两项从来不会出现在需求表上。

下面把这件事从头拆一遍:一台跑 Odoo 的机器里到底有几样东西在抢资源,四类操作各自抢的是哪一样,以及数据库和附件这两本账为什么必须分开算。文中涉及的都是典型部署思路并非特指某一真实客户,也不含任何现场测量得出的具体数值。

五十人的公司,两种完全不同的机器规格:差别出在哪

先把两家公司的画像摆清楚。

A 公司,五十人,日常动作高度同质。销售开报价单和销售订单,仓库做出入库与调拨,采购录收货,财务月底打几张汇总表。每个人在系统里停留的时间长,单位时间里发出的请求却很少——点开一张单据,填十几个字段,保存,然后去干别的。这套动作的特征是:单个请求很轻,请求之间留出大段空闲。

B 公司,也是五十人,分工不同。五个财务岗,每个月初那一周要把上月的凭证、应收应付、进销存流水分别导出成表格再加工;两条产品线,每月跑一次需求运算;每个季度上线一批新物料,用 Excel 批量导进来。这批人的动作特征是:单个请求极重,一次请求可能要把几十万行记录聚合、排序、渲染,而且常常是几个人在同一分钟里同时点下去。

按人口去配机器,这两家公司应该拿到同一台机器。可实际能跑顺的规格差出好几倍——不是软件不同,是同一套软件被两种完全不同的方式在用。

这里就是本文要讲清楚的那条线:规格的来源不是人,是操作。把"我们五十人"翻译成"峰值并发有多少,其中多少比例的并发是报表和导入",机器规格才有了出处,否则那只是一个报价参考量。

Odoo 一台机器上到底跑着几样东西:应用进程、数据库、附件目录、长轮询

很多人心里的"跑 Odoo"是一件事。实际上,一台最小形态的自建 Odoo 上至少同时在跑四样东西,它们抢的资源互不相同。

其一,Odoo 应用进程本身。它是 Python 写的 Web 应用,接收请求、跑业务规则、校验权限、渲染界面。它吃的是 CPU 和内存,而且一个请求进来的时候,主要由一条执行路径在算。

其二,PostgreSQL 数据库。Odoo 官方使用的数据库是 PostgreSQL,业务数据、权限规则、多公司隔离逻辑最终都落在它上头。它关注的资源和应用完全不同:更重要的是缓存数据页的内存、排序用的工作内存、连接本身的开销,以及磁盘的随机读写。

其三,附件目录,也就是 Odoo 里的 filestore。产品图、合同扫描件、往来邮件的附件、导出来的临时文件,在默认形态下这些字节并不存在数据库里,而是以文件的形式落在磁盘的一个目录树上。这是很多选型清单里压根不会提的一样东西,但它决定了磁盘账目的一半。

其四,长轮询通道。Odoo 界面中"不用刷新就能看到新消息"的那类功能,是靠浏览器维持长连接、服务端专门有通道在管来实现的。这条通道平时不怎么吃 CPU,但它占连接数、占少量常驻内存,而且它让"有多少人开着页面"变成一份持续存在的负担——哪怕这些人一分钟也没点一下。

把这四样摆在一起看,第二章的结论其实已经出来了:它们抢的不是同一样东西。所以"加内存"并不是一个通用解法——加内存能缓和应用峰值与数据库缓存之间的争夺,但对磁盘随机读写到顶没有任何帮助,也不会让 filestore 里的文件变少一个字节。

人数不是负载单位,并发才是:日活和峰值并发差一个量级

采购口径说"人",工程口径说"并发"。这两个数之间没有固定的换算率,换算率取决于这家公司的业务节奏。

并发是什么?粗略地说,是同一瞬间正在等待服务端返回结果的那批请求的数量。注意是"等待",不是"在线"。一个人开着单据页面盯着看十分钟没点保存,对服务器来说这十分钟他是空的;他在三秒内连点了五次保存,那一刻他就贡献了五个请求。

所以五十人的公司,常态并发远低于五十,在以录入为主的场景里,个位数到十几是常见的。真正把并发推上去的不是日常,是集中作业:月初财务集中导出报表那二十分钟里,同时并发的请求可以比白天常态高出一个量级;批量灌一批主数据的那几分钟里,可能只有一个人在点按钮,但这一下打出来的压力比二十个人同时录单还狠。

于是按日活配机器必然偏。机器配的是"能不能扛住最坏的那半小时",不是"平均水位看上去舒不舒服"。平均值好看没有任何意义,用户在月末那二十分钟里的体验,才是这套系统被评价的时刻。

顺带说一个"五十人"这个数字本身的误导:它算的通常是有账号的人数,不是同时在用的人数,更不是同时在重度操作的人数。这三层数字分别问出来,选型才有起点。

四类操作的资源画像:录入、报表、批量导入、邮件与定时任务

只有把操作分了类,资源画像才画得出来。这四类分别是日常单据录入、报表与导出、批量导入与期初数据、邮件收发与定时任务。它们看着都是"在系统里点了几下",落到机器上完全是四件事。

日常单据录入。打开一个表单要跑一遍视图和权限,保存的时候写一到几张表、更新几条索引。它在 CPU 上是短促的小任务,在内存上基本不形成峰值,在磁盘上是几次零散的小随机写。四类比下来它最轻,它决定的是"基础算力够不够利索",而不是"机器会在什么时候到顶"。

报表与导出。一次请求可能覆盖相当大体量的记录,要做聚合、分组、排序,最后还要生成表格文件。它在内存上的表现是单个 worker 迅速膨胀——结果集要在内存里组织成形;在磁盘上的表现是密集的读写,因为放不下的排序中间结果会落成临时文件,导出动作本身还要再写一遍。它是四类里唯一同时推向内存峰值和磁盘随机读写的那一类。

批量导入与期初数据。逐行走校验、逐行走业务规则,同时索引要维护、约束要检查、事务要提交。它的形状和报表不一样:报表是瞬间冲高,导入是把 CPU 和连续写压在一条长时间的高位线上。导入中断这类高频问题的成因,绝大多数也埋在这里,下文会专门拆。

邮件收发与定时任务。日常看不见,但它抬高基线。它不像前两类那样制造尖峰,它做的是把整台机器的"起始水位"往上抬,让白天可用的余量变小。

把四类操作各自的特征并排放一张表,这台机器上到底哪一样会先撑不住,就不再是凭感觉猜了:

操作类型 CPU 取向 内存取向 磁盘与 IOPS 取向 最先到顶的是哪一项
日常单据录入 单个请求短促,以单核的计算为主,多核很难被这种负载填满 只产生短生命周期的小对象,几乎不形成可见峰值 零散的小块写与少量索引维护,四类里量级最低 通常不是它自己先到顶,它只是把别的操作抬起来的那部分更早暴露
报表与导出 聚合与排序集中在单个请求内部,把单核的占用时长明显拉长 结果集要在内存里成形,峰值随筛选范围、行宽与分组层级上升 放不下的排序结果落成临时文件,形成密集的随机读写 内存峰值与磁盘 IOPS 两项几乎同时逼近上限,用户先感知到的是等待变长
批量导入与期初数据 逐行走校验与业务规则,长时间压在多核和事务提交上 持续增长但不如报表那么陡,回收后重新累积 索引维护、预写日志连续写,断点重来还要额外扫描一遍 IOPS 与数据库的连接、锁等待最容易先顶不住,表现为导入中断或长时间无响应
邮件收发与定时任务 收取、解析、归类持续占用少量算力,夜间批处理会短时间抬升 常驻占用抬高整体基线,压缩其余操作可用的余量 附件落盘写进 filestore,正文归档产生连续的小块写 不是突然到顶,而是把基线抬高,使白天的可用余量变小
月末多人并发 多个重型请求同时抢占,多核短暂满载,排队开始出现 多个 worker 同时膨胀,峰值不再是单个请求的水平,而接近叠加 临时文件读写叠加,预写日志与索引写同时发生 最先到顶的大概率是内存:worker 触及上限被回收,用户看到的就是请求中断

Python 这类应用的特点:单个请求吃单核,worker 数量决定并行度

理解了这张表之后,就能解释一件让很多人困惑的事:为什么核数加上去了,那张大报表还是慢。

Odoo 走的是多进程的工作模式,一个请求进来由一个 worker 进程处理,worker 之间彼此隔离。这意味着一个重型报表请求,在它自己的生命周期里,绝大部分时间只沿着一条执行路径在算。给它更多的核,不会让它这一趟算得更快;更多的核能让"同一时刻算这类请求"的个数变多。换句话说,核数决定的是并行度,不是单个请求的速度。

并行度由 worker 数量决定,而 worker 数量是写在配置文件里的那个 workers 参数。问题在于 worker 数量是一个两头受气的量:开得太少,第 N+1 个请求就得排队等着,用户看到的是页面转圈;开得太多,每个 worker 都自带一份常驻内存占用,worker 数乘上单份常驻占用就是固定开销,而这还不到峰值那部分。

核数和内存在这里形成了耦合:核多,就想多开 worker;多开 worker,就要更多内存;内存不够,worker 只能少开;worker 少开,核又闲着。这就是"配了十几个核还是卡"的一个典型成因——不是核不够,是 worker 数量被内存锁死在低位,核空转。

关于 workers 到底配多少,这里不提供任何数字,也不建议去照抄网上流传的各种"几核配几个 worker"的公式。那些公式之所以在别人那里成立,是因为别人的操作构成里报表占了某个比例;你的构成不同,同一个公式落到你这里就是把分布推错方向。这个值只能按你自己的实际内存、实际并发和操作构成去调。

除了处理 HTTP 请求的 worker 之外,这台机器上还有另外两类工作者:负责长轮询通道的那部分,和负责定时任务的线程(配置文件里能看到 max-cron-threads 这一类的参数,它决定定时任务能并行几个)。它们同样占进程或线程的位置,同样吃一部分常驻内存,在做容量规划时同样不能漏掉。

内存为什么总在报表和导入时炸:峰值不是均值

内存是自建 Odoo 里最容易被估错的一项,估错的方式高度一致:拿均值当配置依据。

平时看监控图,内存水位常年压在一条平稳的线上,看着还有一半空间。这不是因为峰值不存在,是因为峰值出现的时间窗太短,被平均值抹平了。一台"平均用了一半内存"的机器,完全可以在月末的某二十秒里把内存推到顶。

报表为什么占内存?符合条件的记录要被组织成表格结构才能往下走,行数越多、列越宽、分组层级越深,这个中间结构越大;导出成表格文件的时候,还要再生成一份可供写入的表示。这个过程不是一个常量,它是随筛选范围变化的量——所以同一张报表,用户选一个月和选半年,对机器来说是两个完全不同的请求。

导入为什么占内存?批量导入通常是在一个事务里逐行走对象关系层的操作,操作过程中会产生大量的记录缓存,事务不提交就一直攒着。批次切得越大,累积得越久,峰值就越高。

所以内存的正确算法是两项相加:一项是常驻总量(worker 数乘以单 worker 常驻占用,加数据库自身那部分,加系统与日志等其他占用),另一项是最坏情况下同时在跑的重型操作带来的增量。只算前一项,结果就是"看着够用,月底炸"。

一句话把它记住:配置按峰值算,容量按峰值算,别按舒服的平均值算。

worker 会被内存上限回收,回收时用户看到的是什么

Odoo 的多进程模式里带了内存上限相关的参数(limit-memory-soft / limit-memory-hard 这一类,含义与取值口径以官方文档为准)。worker 在自己的内存超过软上限之后,会在合适的时机被回收,再拉一个干净的进程起来;如果碰到了更硬的那条线,正在处理的事情可能被直接打断。

这套机制本身是好的——它是防止单个失控请求拖垮整机的保护。但它有一个副作用,就是会产生一批看起来完全不相干的故障现象。

用户侧看到的是这样的:导一张大表,导到一半没反应了;点保存,转了很久圈然后报错;某些页面偶发打不开,刷新一下又好了;定时任务跑到一半没了下文,日志里留半截记录。

这些现象的共同特征是:偶发、难复现、跟操作对象强相关——大报表一报一个准,小表单从来不犯。正因为如此,它们最容易被归错因,被判断成网络抖动、浏览器问题、甚至一句笼统的"系统不太稳定"。而它的真实位置非常清楚:内存上限碰到了,进程被回收了,请求没跑完。

还有一件事要说透,不然排障的时候会走弯路:"监控上明明还有不少空闲内存"不能作为排除依据。整机监控看到的是采样的平均值,而 worker 触及上限这件事发生在单个进程的生命周期里,那个峰值可能只存在几秒钟。要看这类问题,得看应用的日志和数据库侧的错误记录,不是看那条平滑的内存曲线。

这组参数的具体取值本文不提供。它们需要按实际内存、实际并发和操作构成来调,照抄数字是最省事也最容易出错的一种做法。

磁盘有两本账:数据库一本,附件目录一本

磁盘这一章要分成两条线来算,因为它们的增长逻辑完全不同。

头一本账是 PostgreSQL 的数据目录。它跟着单据数量、操作记录、消息与历史追踪字段增长,也跟着失败重试留下的残留增长。它的文件形态是数据库自己管理的一组数据文件,增长相对连续。

第二本账是 filestore,也就是附件目录。它跟着产品图、合同扫描件、往来邮件附件、导出动作生成的临时文件增长。它的形态和上一条完全不同:海量小文件。

海量小文件有两个独立的坑。一个是计数配额:容量还剩一大半,可用文件数或者索引节点可能已经接近上限,这时候写不进去的原因完全不在容量上。另一个是时间成本:备份、复制、遍历比对的耗时主要跟文件个数线性相关,跟总字节数不是一回事——几十万个小文件即便总容量不大,完整一次同步也可以很慢。

所以磁盘监控至少要看三样:容量、索引节点或文件数的使用率、以及两个目录各自的增长曲线。只看容量的结果就是某天突然发现备份窗口跑不完,或者某个写操作报错而磁盘显示还剩很多空间。

两边各自的维护手段也不同。数据库侧靠 PostgreSQL 自身的维护机制(清理与统计信息更新这一类)来回收与整理;filestore 侧只能靠删除附件本身,而且要注意,在 Odoo 里删掉一条记录,历史上留下的附件版本未必立即从磁盘上消失。

只备份数据库不备份附件目录,恢复出来的是残缺系统

这一节是全文最该被记住的一节,因为它是一个高频翻车点,而且翻车的时候往往已经是出事之后。

多数自建 Odoo 的备份脚本是这么起步的:每天夜里做一次数据库导出,压缩,传到备份机,发一封成功邮件。这套东西跑得很稳,日志天天是绿的,于是所有人默认"备份没问题"。

直到真要用的那天。恢复过程顺利,数据库回来了,登录正常,菜单齐全,单据列表也能打开。然后点开某份合同的扫描件——打不开;点开某个产品上的图——裂的;导出的报表里凡是嵌了图片的地方全是空白。

原因就是上一节那本账:默认情况下附件的字节在 filestore 目录里,数据库里存的只是指向它的记录。只有库没有目录,等于只恢复了索引卡片,书架上其实是空的。

这里要把一个边界说清楚,免得误伤:Odoo 的附件存储位置是可以配置的,存在文件系统上和存在数据库里是两种选择。如果一家公司把附件改成了存进数据库,那么数据库备份确实会把附件一并带走,代价是库体积急剧膨胀、备份与恢复都变慢。所以"只备份库算不算残缺"取决于这家公司的实际配置是哪一个;而绝大多数翻车就翻在——从来没人去确认过这一项。

正确的做法由三件事组成,缺一件都不算有备份:

其一,备份必须成对。任何一次"可用的备份"都要同时包含一个一致的数据库快照和一个一致的 filestore 副本。只有其中一半的备份,在恢复那天会比没有备份更危险,因为它会让人以为有退路。

其二,两份东西的时间点要对齐。数据库是凌晨两点导的,附件是白天的热点副本,这两份凑到一起,附件的增删状态就和库里对不上了:库里已经删掉的记录,附件还在;夜里新上传的附件,副本里没有。做法要么在同一个业务静止的窗口里先后取得,要么先把两边都落到同一个时间点的一致状态再复制。

其三,必须有恢复演练。备份脚本的成功日志什么都证明不了,它只证明"导出这一步没报错"。唯一能证明备份可用的动作,是定期真的恢复一次到一台隔离的机器上,登录进去,抽若干条带附件的单据逐个打开看看。这个动作不需要很频繁,但不能没有——它是这套流程里唯一的验收环节。

filestore 的复制本身不难,常规的文件同步手段就够了,难点在时间窗的一致性和"别忘了这回事"。补一句实操提醒:由于 filestore 里是大量小文件,增量同步的成本主要花在遍历比对上,而不是花在传输字节数上,所以策略上要优先减少比对范围,而不是一味压缩单个文件。

数据库和应用放不放同一台:第一个真正的分叉点

聊到具体的机器形态,最先要做的决定就是:数据库和应用放一台,还是分两台。这个决定比选几核几 G 更早,也更难改。

放一台的理由很实在。部署简单,组件间的通信就在本机,没有中间那一层网络的不确定性;备份恢复的动作也简单,停机窗口好控制;成本上一台机器承担全部。对小团队来说这是一个完全理性的选择。

放一台的代价集中在内存上。数据库希望把更多内存拿来缓存数据页,应用在报表来的时候希望把内存拿来装结果集,两边的高峰未必同时到来,但只要同时到来,就开始互相挤压,谁先触线不好预测。而且排障会变难:CPU 高了,要判断是 Python 在算业务逻辑,还是数据库在执行计划;内存吃紧了,要判断是哪一侧在涨——同机部署会把这两件事混在一张图里。

分两台的理由同样清楚。内存可以分别为两个组件规划,互不侵占;数据库那边的缓存类参数可以按自己那一份内存去调,不用给应用留出富余;报表引发的巨大内存峰值不会直接和数据库的缓存需求面对面抢;做物理备份、做快照的时候也不会因为 IO 争用拖慢前台。

代价是要接住多出来的这一层。多一台机器就多一个系统要维护、多一层网络要做监控。而且两台之间的链路质量会被放大:每一次请求都要跨过这层通信,链路一旦抖动或者延迟变高,表现就是全站变慢,而不是某一个功能变慢。所以要拆,就得同时接受"这层链路现在是关键路径"这件事。

判断依据还是回到那条主线上:看你业务里重型操作的占比。占比小,同机部署足够;占比大到内存开始两边互挤,拆的意义才真正成立。

IOPS 比容量更早见底:报表的临时排序、导入的索引维护

买磁盘的时候所有人看的是容量,用起来先见底的往往是随机读写能力。

报表这一侧,磁盘上的压力有三路:一是排序和分组的中间结果,内存放不下的那部分要落到临时文件去,产生密集的读写;二是某些查询路径上会出现的较大范围扫描,持续读;三是导出动作本身要把生成的内容再写一遍。这三路在同一个请求的时间窗里叠加。

导入这一侧是另一副样子:每写一行,涉及的索引都要同步更新;外键和唯一约束要检查;触发器要走;预写日志要连续写;到最后事务提交时又是一波集中的写。它不像报表那样瞬间冲高,它是把写压力长时间压在一条高位线上,这种持续压制更容易把整机的响应拖慢,而不只是拖慢自己。

还有一个常被忽略的竞争者:数据库自身的后台维护。越是增删改频繁的系统,后台清理和统计信息更新的工作量越大,它和业务读写抢的是同一份磁盘能力。很多时候"白天莫名变慢",查到最后是昨天夜里的批量操作带来的后续维护工作没跑完。

filestore 这一侧也有自己的一份:附件被批量打开时,磁盘层面看到的是大量随机小读,这跟顺序吞吐能力没关系,跟能不能同时承受很多次随机访问有关。

把这些加在一起,选磁盘时的问题顺序就得倒过来:"能同时承受多少随机读写"和"延迟是否稳定"排在前面,"多大容量"排在最后。绝大多数 ERP 场景里,容量还很宽裕的时候随机读写能力就已经开始紧张了。

定时任务和邮件收发是安静的常驻负担

这两样东西的共同特点是:它们从来不制造新闻,但它们决定了这台机器的起始水位。

先说邮件。很多公司会把客服邮箱、采购邮箱接到 Odoo 里,进来的邮件自动转成线索或者单据,发出去的报价单自动归档。这套集成的业务价值不用多讲,代价是三个持续动作:周期性去收信、把邮件正文解析归类、以及把邮件附件写进 filestore。

第三点尤其容易被低估。邮件附件往往不小、数量不少,而且积累起来几乎只增不减。几年下来,邮件附件可以成为 filestore 里占比最大的一块,进而影响备份窗口和磁盘索引节点的使用情况。邮件量大之后,解析与规则匹配还会持续吃一部分算力,失败重试的邮件堆着不处理,这份负担只增不减。

再说定时任务。库存重估、需求运算、订阅到期提醒、各种自动生成动作,它们的共同习惯是集中在夜里或者某个固定时刻。关键点在于:这类负载抬高的是基线。白天可用的余量等于总能力减去基线,基线被抬高,同样的白天负载就更早碰到上限。

而且它排障难。白天查性能问题的时候,人往往忘了看夜里那批任务留下的影响——有没有卡住没跑完的、有没有异常膨胀的表、有没有堆积的失败邮件。这些都会在第二天早上以"系统今天有点慢"的形式出现,而真正的起因在十几个小时之前。

配置上,max-cron-threads 这一类参数决定定时任务能并行几个,它同样要按实际负载去调整,不要照抄。而成本最低的一次优化通常不是改配置,是把定时任务的窗口挪到真正没人用的时段,并且让它和报表的高峰错开。

什么时候该把数据库分出去,什么时候不用急

先说不用急的情形,因为这大概率是多数团队的答案。以录入和查询为主、出报表的是少数几个人且能接受错峰、数据量与附件体积都还有限、出现短暂性能下降时业务能扛、团队里没有专人做运维——满足这几条,同机部署是对的,急着拆除了增加故障面没有别的好处。

再说该分的信号。这些信号不是在监控图上看到的抽象曲线,而是具体的现象:

信号一,报表和导入从偶发变成了日常。如果每天都有人在导大表,那就不是"忍一忍就过去"的问题了,这是业务的常态负载,容量要对它负责。

信号二,内存开始在两侧之间互相挤。典型表现是应用侧的 worker 被回收,同时数据库侧的缓存命中表现变差——两边都在喊不够,说明这一个池子已经装不下两份需求了。

信号三,需要在不影响前台的情况下对数据库做操作。比如要做物理备份、要恢复、要升级,而 IO 争用会让这些操作拖慢前台到不可接受的程度。

信号四,排障已经开始互相干扰。判断一次卡顿是应用层还是数据库层,需要额外的猜测和取证时间,而这段时间业务在等着。

但在真正掏钱拆机器之前,还有三件便宜得多的事应该先做完:把重报表挪到低峰,或者干脆限制导出区间;把批量导入拆小、错开到没人用的时段;检查一下有没有因为缺少合适索引而退化为大范围扫描的高频查询。这三件事做完再评估,很多时候机器不用动,卡的是用法而不是规格。

但也别走到另一个极端。有些报表就是业务必需,就是必须在月初头一天的上午跑完——这种时候再去劝人错峰就没意义了,该加资源就加。顺序是:先过滤掉用法问题,剩下的才是规格问题。

什么时候不该自建:算完这笔账再决定

自建 Odoo 的成本里,机器是这笔账里花得最少的那一块,也是最容易被当成全部成本的那一块。

真正贵的是后面这几样:每天得有人确认备份真的完成了,而且是那个"两份都有的备份";版本升级、模块升级得有人在业务窗口之外做,做完还得回归;出故障的时候得有人能在可接受的时间里定位到层——应用层、数据库层还是磁盘层;以及最容易被漏掉的一条,得有人理解这套系统的资源模型,否则"卡"了之后手上就只剩两个动作:重启,加钱。

判断要不要自建,标准其实很朴素:如果组织里没有一个人能在半小时内说清"这次卡是 worker 被回收、数据库执行计划变了、还是磁盘随机读写到顶",那么自建的风险不在于机器不够大,而在于每一次故障的定位时间不可控。这个代价跟机器规格无关,再大的机器也买不到。

替代形态是客观存在的。Odoo 官方提供托管形态的服务,市场上也有做托管的合作伙伴,取舍的核心是灵活性与责任边界。定制深度很深、模块改动很多的公司,自建带来的自由度是实打实的好处;基本只用标准功能、定制很少的公司,把运维责任交出去通常更划算——注意是"通常",这不是一个放之四海皆准的结论,取决于你的定制程度和内部人员情况。

真决定自建,规格该怎么定?回到本文开头那条线:先把四类操作在你这里的占比列出来,估出月末最坏那半小时的并发构成,再把它翻译成 CPU、内存、磁盘三项各自的取向。各家服务商给出的机器规格都是按 CPU、内存、硬盘这几列组织的,你要做的就是完成这次翻译——而不是拿"我们五十人"去对某一列。

最后给一个明确的立场:别因为"我们人少"就往小里配,也别因为"上次卡了"就一路往上堆。规格要对着最坏的那半小时定,别的地方能省就省;说得再直白点,加错方向的机器,比不加还贵,因为它把真正的问题又掩护了一轮。

自建 Odoo 之前,最该问清楚的七个问题

公司多少人,到底该配几核几 G?
这个问题本身问不出答案,因为它漏掉了所有关键变量。要把它换成三个问题:峰值并发有多少(不是日活);这笔并发里报表与导出占几成、批量导入占几成;数据库体量和 filestore 体积分别是多大、预计怎么增长。这三个问题有了形状,CPU、内存、磁盘三项各自的取向才成立。任何跳过这三问直接给规格的做法,都是拿别人的操作构成替你做决定。

内存该按均值还是峰值算?
按峰值,而且是最坏那半小时的峰值。算法是常驻总量(worker 数乘单 worker 常驻占用,加数据库自身那份,加系统与其他占用)再加上同时进行的重型操作带来的增量。按均值算的结果一定是"平时好好的,月底炸",因为 worker 的内存上限碰的是单个进程的瞬时值,不是整机的平均值。

数据库和应用能不能先放一台?
能,多数小团队一开始就该放一台。部署简单、通信在本机、备份恢复动作少,这些都是实打实的好处。等出现这几个信号再考虑拆:重型操作从偶发变成日常、两边开始在内存上互相挤、需要在不影响前台的情况下对数据库做备份或升级、排障时已经分不清是应用层还是数据库层。

附件目录要怎么备份,才算真的有备份?
三件事缺一不可:其一,数据库快照和 filestore 副本必须成对,缺任一半都不算有备份;其二,两份的时间点要对齐,否则附件的增删状态和库对不上;其三,定期真的恢复一次到隔离机器上,抽查带附件的单据能否打开。另外先确认一件事——你这边的附件到底存在文件系统上还是存进了数据库,这个配置决定了你的备份方案的形状。

月末卡,是不是一定要加机器?
不一定,先别急着下单。按顺序做三步:先看应用日志里有没有 worker 因触及内存上限被回收的记录,如果有,问题在内存规划和 worker 数,不在核数;再看高峰期同时进行着的那些重操作能不能错峰,把批量导入和重报表挪到夜里往往是代价最低的一次优化;最后再检查高频查询是否存在缺少合适索引导致的大范围扫描。三步走完仍然卡,才是规格问题。

批量导入老是中断,一般是什么原因?
成因通常集中在四个方向:批次太大导致事务过长、单个 worker 的内存被推到上限后被回收;worker 数量不足叠加长时间独占,导致后续请求排队并被前置超时断开;导入过程中维护索引与约束产生持续的写压力,IOPS 先到顶;以及行级锁等待与已有的夜间维护工作正面撞上。排查的时候别只看最后那句报错,导入时的应用日志和数据库侧的错误记录才是真正的线索。

什么时候该考虑放弃自建?
当这三件事同时成立的时候:没有人在可接受的时间内定位故障属于哪一层;备份的一致性与恢复演练长期没人负责;版本与模块升级一拖再拖,因为"怕升完出事"。这三条凑齐,机器再大也只是把出事的那天往后推。反过来说,只要这三件事有人扛得住,自建带来的定制自由度依然值得。

本文关于 Odoo 部署形态描述的公开依据在哪里

本文涉及 Odoo 部署形态的描述——Odoo 使用 PostgreSQL 作为数据库、附件在默认形态下存放在文件系统上的 filestore 目录、多进程工作模式下存在 worker 数量与内存上限相关的配置参数、系统中存在定时任务线程与长轮询通道——均属于 Odoo 官方文档中部署与配置相关章节所描述的公开内容。为避免断言偏差,本文未写入任何官方未明示、也无法从公开文档确认的功能描述。

workers、limit-memory-soft / limit-memory-hard、max-cron-threads 等参数,本文只说明其存在与作用方向。这些参数的取值需要按实际内存、实际并发与实际操作构成自行调整,不建议照抄网络上流传的固定数字。文中所有性能描述均为定性取向,不含任何现场测量得出的具体数值。

文中对比的两家公司是典型部署思路的抽象,并非特指某一真实客户,也未引用任何客户案例、客户评价或销量数据。一万网络深耕 IDC 19 年(成立于 2007 年),其机器规格同样按 CPU、内存、硬盘这几列组织,本文仅在"机器规格如何与 ERP 负载对应"这一处作为参考对象出现;相关产品规格与价格以官网实时展示为准,需实时询价,选购前可查阅 https://www.idc10000.net/ 。


上一篇:2026 服务器租用瑞士大促三档价格锚不住带宽:100/150/300 元换的其实是内存与 SSD 容量 + 避坑避雷全攻略

下一篇:2026 服务器租用伊斯坦布尔线加价阶梯倒着走:B 到 C 加 250 元、C 到 D 只加 150 元怎么看 + 避坑避雷全攻略